How find all overlapping circles from radius of central circle? - ruby

How to do an intersection or overlap query in mongo shell - what circles overlap my search region? Within relate only to the center position but doesn't include radius of the other circles in searched scope.
Mongo:
# My bad conception:
var search = [[30, 30], 10]
db.places.find({circle : {"$within" : {"$center" : [search]}}})
Now I can obtain only this circles within central point lies in searched area of circle:
Ruby:
# field :circle, type: Circle # eg. [ [ 30, 30 ], 10 ]
field :radius, type: Integer
field :location, :type => Array, :spatial => true
spatial_index :location
Places.within_circle(location: [ [ 30, 30 ], 10 ])
# {"$query"=>{"location"=>{"$within"=>{"$center"=>[[30, 30], 10]}}}
I created example data with additional location (special index) and radius instead circle because circle isn't supported by mongodb geo index:
{ "_id" : 1, "name" : "a", "circle" : [ [ 5, 5 ], 40 ], "latlng" : [ 5, 5 ], "radius" : 40 }
{ "_id" : 2, "name" : "b", "circle" : [ [ 10, 10 ], 5 ], "latlng" : [ 10, 10 ], "radius" : 5 }
{ "_id" : 3, "name" : "c", "circle" : [ [ 20, 20 ], 5 ], "latlng" : [ 20, 20 ], "radius" : 5 }
{ "_id" : 4, "name" : "d", "circle" : [ [ 30, 30 ], 50 ], "latlng" : [ 30, 30 ], "radius" : 50}
{ "_id" : 5, "name" : "e", "circle" : [ [ 80, 80 ], 30 ], "latlng" : [ 80, 80 ], "radius" : 30}
{ "_id" : 6, "name" : "f", "circle" : [ [ 80, 80 ], 20 ], "latlng" : [ 80, 80 ], "radius" : 20}
Desired query result:
{ "_id" : 1, "name" : "a", "circle" : [ [ 5, 5 ], 40 ], "latlng" : [ 5, 5 ], "radius" : 40 }
{ "_id" : 3, "name" : "c", "circle" : [ [ 20, 20 ], 5 ], "latlng" : [ 20, 20 ], "radius" : 5 }
{ "_id" : 4, "name" : "d", "circle" : [ [ 30, 30 ], 50 ], "latlng" : [ 30, 30 ], "radius" : 50}
{ "_id" : 5, "name" : "e", "circle" : [ [ 80, 80 ], 30 ], "latlng" : [ 80, 80 ], "radius" : 30}
Solution below assumes that I get all rows and then filter on the ruby side my radius but it returns only:
{ "_id" : 4, "name" : "d", "circle" : [ [ 30, 30 ], 50 ], "latlng" : [ 30, 30 ], "radius" : 50}

I'm not familiar with mongodb but I assume that in [[x, y], r] values mean
x: value of the center on the axis x.
y: value of the center on the axis y.
r: circle radius.
Say you have circle S which is your search and a random circle A. Then you could calcule the distance between both circles' center (S.center and A.center) and see if it is inferior to those both circles radius added (S.r + A.r).
def distance_between(a, b)
((b.first - a.first)**2 + (b.last - a.last)**2)**0.5
end
elements = [{ _id: 1, name: "a", circle: [ [ 5, 5 ], 40 ] },
{ _id: 2, name: "b", circle: [ [ 10, 10 ], 5 ] },
{ _id: 3, name: "c", circle: [ [ 20, 20 ], 5 ] },
{ _id: 4, name: "d", circle: [ [ 30, 30 ], 50 ] },
{ _id: 5, name: "e", circle: [ [ 80, 80 ], 30 ] },
{ _id: 6, name: "f", circle: [ [ 80, 80 ], 20 ] }]
search = [[30, 30], 10]
elements.select do |elem| circle = elem[:circle]
distance_between(circle.first, search.first) <= circle.last + search.last
end
#{:_id=>1, :name=>"a", :circle=>[[5, 5], 40]}
#{:_id=>3, :name=>"c", :circle=>[[20, 20], 5]}
#{:_id=>4, :name=>"d", :circle=>[[30, 30], 50]}

Unfortunately there is currently no capability in mongo to directly query about overlapping objects, only points within objects.
#oldergod's answer describes the algorithm to calculate whether two circles overlap.
Here is a work-around in the shell based on that calculation:
function distance(a, b) {
return Math.pow(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2), 0.5);
}
On your sample data inserted into collection 'circle':
> db.circle.find().forEach(
function(c) {
if ( (distance(c.latlng, search.latlng) < c.radius + search.radius) )
{
print(c.name);
}
} )
a
c
d
>

Related

How do I sort a Laravel Collection by an array?

I know this has been asked several times, but none of the answers work for me.
I have a collection of products; each has an eloquent relationship containing multiple items.
Here's how I'm retrieving the collection
$collections = Collection::with('items')->get();
And this is the result of return $collections from the controller.
[
{
"id": 1,
"name": "Product One",
"items": [
{
"id": 1,
"product_id": 1,
"name": "Item One",
},
{
"id": 2,
"product_id": 1,
"name": "Item Two",
},
{
"id": 3,
"product_id": 1,
"name": "Item Three",
},
},
{
"id": 2,
"name": "Product Two",
"items": [
{
"id": 1,
"product_id": 2,
"name": "Item One",
},
{
"id": 2,
"product_id": 2,
"name": "Item Two",
},
{
"id": 3,
"product_id": 2,
"name": "Item Three",
},
}
]
I'd like to sort each product's items in a different order.
I'd like to sort Product One's items as 3, 1, 2 and Product Two's items as 2, 3, 1.
So I created an array of the new sort order, and then added a callback
$newSortOrder = [
1 => [3, 1, 2],
2 => [2, 3, 1]
];
$collections = Collection::with('items')->get();
foreach ($collections as $collection) {
$collection->items->sortBy(function ($model) use ($newSortOrder) {
return array_search($model->id, $newSortOrder[$model->id]);
});
}
dd($collections); <-- this is still in the default sort order from the db
However, when I return $collections, the items are still in the default order. What am I doing wrong?
Edit: tried this as well but with same results; $collection items are returned in the default order.
foreach ($collections as $collection) {
$collection->items->sortBy(function ($model) use ($order) {
return array_search($model->getkey(), $order);
});
}
$sortOrder = [
1 => [3, 1, 2],
2 => [2, 3, 1]
];
$sorted = $products->map(function($product) use ($sortOrder) {
$order = $sortOrder[$product->id];
return [
...$product,
'items' => $product->items->mapWithKeys(
fn($item) => [array_search($item['id'], $order) => $item]
)->sortKeys()
];
});
This Outputs
Illuminate\Support\Collection {#4720
#items: array:2 [
0 => array:3 [
"id" => 1
"name" => "product one"
"items" => Illuminate\Support\Collection {#4722
#items: array:3 [
0 => array:2 [
"id" => 3
"name" => "three"
]
1 => array:2 [
"id" => 1
"name" => "one"
]
2 => array:2 [
"id" => 2
"name" => "two"
]
]
#escapeWhenCastingToString: false
}
]
1 => array:3 [
"id" => 2
"name" => "product two"
"items" => Illuminate\Support\Collection {#4721
#items: array:3 [
0 => array:2 [
"id" => 2
"name" => "two"
]
1 => array:2 [
"id" => 3
"name" => "three"
]
2 => array:2 [
"id" => 1
"name" => "one"
]
]
#escapeWhenCastingToString: false
}
]
]
#escapeWhenCastingToString: false
}
you can recreate items using map().
// not recommend because key does not support for correct item's id
// $newSortOrder = [
// 1 => [3, 1, 2],
// 2 => [2, 3, 1]
// ];
$newSortOrder = [
[
"id" => 1,
"sortOrder" => [3, 1, 2]
],
[
"id" => 2,
"sortOrder" => [2, 3, 1]
],
];
$collections = Collection::with('items')->get();
$sortedCollection = collect($collections)->map(function($item) use($newSortOrder) {
$id = data_get($item,'id');
$name = data_get($item,'name');
$originItems = data_get($item,'items');
$sortOrderList = data_get(collect($newSortOrder)->firstWhere('id',$id),'sortOrder');
$items = collect($sortOrderList)->map(function($sortNumber) use($originItems) {
return collect($originItems)->firstWhere('id',$sortNumber);
});
return compact('id','name','items');
});
dd($sortedCollection); // result

How to change a key in gltf file?

I want to change this key value 0.5 on
"baseColorFactor" : [
0.004314713645726442,
0.42002472281455994,
0,
0.5
],
This is the complete object
"materials" : [
{
"alphaMode" : "BLEND",
"doubleSided" : true,
"emissiveFactor" : [
0,
0,
0
],
"name" : "dgray",
"pbrMetallicRoughness" : {
"baseColorFactor" : [
0.004314713645726442,
0.42002472281455994,
0,
0.5
],
"metallicFactor" : 0,
"roughnessFactor" : 0.5
}
},
{
"alphaMode" : "BLEND",
"doubleSided" : true,
"emissiveFactor" : [
0,
0,
0
],
"name" : "Material.004",
"pbrMetallicRoughness" : {
"baseColorFactor" : [
0.034654635936021805,
0.29569298028945923,
0,
0.5
],
"metallicFactor" : 0,
"roughnessFactor" : 0.5
}
}
],
i tried materials[0].pbrMetallicRoughness.baseColorFactor[3] = 1; but doesnt work,
that is from gltf file. i want the gltf to change transparency when the user clicks on it.
This is the code:
const domEvents = new THREEx.domEvents(camera, renderer.domElement);
domEvents.addEventListener(obj, 'click', event => {
materials[0].pbrMetallicRoughness.baseColorFactor[3] = 1;
});

Sort by nested Geo distance properties

I'm trying to sort by geo distance an index with nested geo point mapping.
Here is my simplified mapping :
'organizations' => [
'_doc' => [
'properties' => [
'locations' => [
'type' => 'nested',
'properties' => [
'point' => [
'type' => 'geo_point',
'ignore_malformed' => true
]
]
]
]
]
]
Here, each organizations can have several locations point.
Documents :
PUT /organizations ,
[
{
name: "A",
locations: [
{
point: {
lat: 1.5,
lon: 2
}
},
{
point: {
lat: 1,
lon: 0
}
}
]
},
{
name: "B",
locations: [
{
point: {
lat: 4.2,
lon: 3
}
}
]
},
{
name: "C",
locations: [
{
point: {
lat: 0.4,
lon: 1
}
}
]
}
];
Here is my query (PHP array):
[
"query" => [
"query_string" => [
"query" => "*"
]
]
"sort" => [
0 => [
"_geo_distance" => [
"locations.point" => "1, 0.1"
"order" => "asc"
"unit" => "km"
"nested_path" => "locations"
]
]
]
]
expected result :
1 => name : A
2 => name : C
3 => bame : B
I'd like to get resources ordered by geo_point (check each location, and then check if a location is close to the given lat / lon )
You can meet your goal using function_score.
For example with the query like:
note: code is in js;
"_geo_distance": {
"place.location.geo.coordinates": [
this._geo.lon,
this._geo.lat
],
"order": "asc",
"unit": "km",
"distance_type": "plane"
}
Then in function_score add script_score
function_score["script_score"] = {
"script": {
"source": `1000/doc['place.location.geo.coordinates'].planeDistance(${this._geo.lat},${this._geo.lon}) `
}
The original function_score is like:
'function_score': {
"score_mode": "multiply",
'query': {
"dis_max": {
"tie_breaker": 0.7,
"boost": 1.9,
"queries": this._query
},
},
'boost': 0.06,
'boost_mode': 'sum',
}

How to display data point with various fields in kendo chart?

I want to display all data point in a tooltip.
My data have 2 series,
a series have 2 points,
a point have 3 fields(value , base and date)
I try this http://trykendoui.telerik.com/OCeB but the x-axis are repeated
If there is solution i would like use datasource
Usually the x values in a series should not repeat, you have two options to
fix your chart:
Option 1:
Define both series' values on each datapoint
var internetUsers = [
{
"S1Value" : 1,
"S1base" : 2,
"S2Value" : 3,
"S2base" : 2,
"date" : 2011
},
{
"S1Value" : 5,
"S1base" : 6,
"S2Value" : 4,
"S2base" : 7,
"date" : 2013
},
]
and define a tooltip for each:
series: [{
field: "S1Value",
name: "United States",
tooltip: {
visible: true,
background: "#FFFFFF",
template:
"#= series.name # <br /> " +
"Fecha = #= category # <br /> " +
"Valor = #= value # <br/> " +
"Base = #= dataItem.S1base # ",
format: "n2",
}
},{
field: "S2Value",
name: "Mexico",
tooltip: {
visible: true,
background: "#FFFFFF",
template:
"#= series.name # <br /> " +
"Fecha = #= category # <br /> " +
"Valor = #= value # <br/> " +
"Base = #= dataItem.S1base # ",
format: "n2",
}
}
http://trykendoui.telerik.com/OCeB/2
Option 2:
Split your series into two:
var internetUsersS1 = [
{
"S1" : 1,
"base" : 2,
"date" : 2011
},
{
"S1" : 5,
"base" : 6,
"date" : 2013
}
]
var internetUsersS2 = [
{
"S2" : 3,
"base" : 2,
"date" : 2011
},
{
"S2" : 4,
"base" : 7,
"date" : 2013
}
]
...now give each series it's own datasource:
series: [{
data: internetUsersS1,
field: "S1",
name: "United States"
},{
data: internetUsersS2,
field: "S2",
name: "Mexico"
}
]
http://trykendoui.telerik.com/OCeB/3

Sorting an array of hashes in Ruby (sorting by presence optional key+value)

Got a Ruby array like:
[ { "lat" => 123, "lon" => 456 },
{ "lat" => 789, "lon" => 102, "col" => "red" },
{ "lat" => 442, "lon" => 342 } ]
I would like to sort it so that any hash with col as a key will be pushed to the top or bottom of the array.
Can't figure out the right sort_by syntax/semantics.
a = [ { "lat" => 123, "lon" => 456 },
{ "lat" => 789, "lon" => 102, "col" => "red" },
{ "lat" => 442, "lon" => 342 } ]
If you want to put them on the top, then
a.partition{|h| h.key?("col")}.flatten
If you want to put them at the bottom, then as suggested by the Tin Man,
a.partition{|h| h.key?("col").!}.flatten
A more functional approach I like always:
require 'pp'
a = [ {:foo => "aa","col" => "bar"}, { "lat" => 123, "lon" => 456 },
{ "lat" => 789, "lon" => 102, "col" => "red" },
{ "lat" => 442, "lon" => 342 } ]
arr = a.group_by{|h| h.key?("col")}
pp arr[false] + arr[true] # on the bottom
Output:
[{"lat"=>123, "lon"=>456},
{"lat"=>442, "lon"=>342},
{:foo=>"aa", "col"=>"bar"},
{"lat"=>789, "lon"=>102, "col"=>"red"}]
pp arr[true] + arr[false] #on the top
Output:
[{:foo=>"aa", "col"=>"bar"},
{"lat"=>789, "lon"=>102, "col"=>"red"},
{"lat"=>123, "lon"=>456},
{"lat"=>442, "lon"=>342}]

Resources