Leaflet polygons sorting - sorting

Is there a way, how to z-index polygons using Leaflet nowadays? I am OK when the map is initiated, but when adding new polygons to existing map, I need new polygons to be sorted into existing ones base on their area - so bigger ones will not overlap small ones. I have found this solution:
Leaflet z-index
but it's veeeeery slow, when my map contains bigger amount of features. Any idea?

I have no idea about performance of this answer, but you could give it a try:
GeoJSON Layer Order In Leaflet 0.7.5
The main code is:
// To be called after adding a geoJsonLayer to the map.
function assignZindex(geoJsonLayer) {
geoJsonLayer.eachLayer(function (layer) {
layer._container.zIndex = layer.options.zIndex;
});
}
// To be called after assignZindex().
function reOrderVectorLayers() {
var root = map._pathRoot,
child = root.firstChild,
next;
while (child) {
next = child.nextSibling;
if (!next) {
break;
}
if (next.zIndex < child.zIndex) {
root.insertBefore(next, child);
if (next === root.firstChild) {
continue;
}
child = next.previousSibling;
continue;
}
child = next;
}
}
It assumes the zIndex property is a number defined in options of each of your vector (polygon) layer.
As said in comments of that question, if you use Leaflet 1.x, you can now create your own panes that you can order through CSS z-index, and insert each of your vector layer in a specified pane.

Related

dc.js Grouping for Bubble Chart Removing from wrong groups

I'm trying to create a bubble chart with dc.js that will have a bubble for each data row and will be filtered by other charts on the same page. The initial bubble chart is created correctly, but when items are filtered from another chart and added or removed from the group it looks like they are being applied to the wrong group. I'm not sure what I'm messing up on the grouping or dimensions. I've created an example fiddle here
There's simple pie chart to filter on filterColumn, a bubble chart that uses identifer1, a unique field, as the dimension and xVal, yVal, and rVal to display the data, and a dataTable to display the current records.
I've tried other custom groups functions, but switched to the example from the FAQ and still had problems.
var
filterPieChart=dc.pieChart("#filterPieChart"),
bubbleChart = dc.bubbleChart('#bubbleChart'),
dataTable = dc.dataTable('#data-table');
var
bubbleChartDim=ndx.dimension(dc.pluck("identifier1")),
filterPieChartDim=ndx.dimension(dc.pluck("filterColumn")),
allDim = ndx.dimension(function(d) {return d;});
var filterPieChartGroup=filterPieChartDim.group().reduceCount();
function reduceFieldsAdd(fields) {
return function(p, v) {
fields.forEach(function(f) {
p[f] += 1*v[f];
});
return p;
};
}
function reduceFieldsRemove(fields) {
return function(p, v) {
fields.forEach(function(f) {
p[f] -= 1*v[f];
});
return p;
};
}
function reduceFieldsInitial(fields) {
return function() {
var ret = {};
fields.forEach(function(f) {
ret[f] = 0;
});
return ret;
};
}
var fieldsToReduce=['xVal', 'yVal', 'rVal'];
var bubbleChartGroup = bubbleChartDim.group().reduce(
reduceFieldsAdd(fieldsToReduce),
reduceFieldsRemove(fieldsToReduce),
reduceFieldsInitial(fieldsToReduce)
);
filterPieChart
.dimension(filterPieChartDim)
.group(filterPieChartGroup)
...
;
bubbleChart
.dimension(bubbleChartDim)
.group(bubbleChartGroup)
.keyAccessor(function (p) { return p.value.xVal; })
.valueAccessor(function (p) { return p.value.yVal; })
.radiusValueAccessor(function (p) { return p.value.rVal; })
...
;
This was a frustrating one to debug. Your groups and reductions are fine, and that's the best way to plot one bubble for each row, using a unique identifier like that.
[It's annoying that you have to specify a complicated reduction, when the values will be either the original value or 0, but the alternatives aren't much better.]
The reductions are going crazy. Definitely not just original values and zero, some are going to other values, bigger or negative, and sometimes clicking a pie slice twice does not even return to the original state.
I put breakpoints in the reduce functions and noticed, as you did, that the values were being removed from the wrong groups. How could this be? Finally, by logging bubbleChartGroup.all() in a filtered handler for the pie chart, I noticed that the groups were out of order after the first rendering!
Your code is fine. But you've unearthed a new bug in dc.js, which I filed here.
In order to implement the sortBubbleSize feature, we sort the bubbles. Unfortunately we are also sorting crossfilter's internal array of groups, which it trusted us with. (group.all() returns an internal data structure which must never be modified.)
The fix will be easy; we just need to copy the array before sorting it. You can test it out in your code by commenting out sortBubbleSize and instead supplying the data function, which is what it does internally:
bubbleChart.data(function (group) {
var data = group.all().slice(0);
if (true) { // (_sortBubbleSize) {
// sort descending so smaller bubbles are on top
var radiusAccessor = bubbleChart.radiusValueAccessor();
data.sort(function (a, b) { return d3.descending(radiusAccessor(a), radiusAccessor(b)); });
}
return data;
});
Notice the .slice(0) at the top.
Hope to fix this in the next release, but this workaround is pretty solid in case it takes longer.
Here is a fiddle demonstrating the workaround.

RX - how to use it in a performant way?

I am trying to understand how to structure my program to use RX in a performant matter.My app has a vector of objects in the 3D world. each object occupied a box, and have a 'hit' stream, which represent a mouse hover over it. I thought of two options of how to structure:
Option 1
struct object_t
{
string name_;
box bounding_box_;
observable<bool> hit_;
};
struct scene_t
{
scene_t(observable<point> mouse) : hit_(hit(mouse))
{
add({"background", {/* ... */}, {}};
}
object_t& add(object_t o)
{
int object_index = objects_.size();
o.hit_ = hit_
.map([=](int index){ return index == object_index; })
.distinct_until_changed();
objects_.push_back(o);
return objects_.back();
}
//! given a stream of mouse points,
//! calculate on which object index(in objects_) the mouse is hover over.
//! 0 if its over the background.
observable<int> hit(observable<point> mouse);
using objects_t = std::vector<object_t>;
objects_t objects_;
observable<int> hit_
};
Option 2
struct object_t
{
string name_;
box bounding_box_;
void signal_hit(boot is_hit) { hit_.get_observer().on_next(is_hit); }
observable<bool> hit() const { return hit_.get_observable(); }
private:
subject<bool> hit_;
};
struct scene_t
{
scene_t(observable<point> mouse) : hit_(hit(mouse))
{
add({"background", {/* ... */}, {}};
hit_
.start_with(0)
.buffer(2, 1) // take two hits together, the current and the previos
.subscribe([this](std::vector<int> indices) {
objects_[indices[1]].signal_hit(false); // we leave this one
objects_[indices[0]].signal_hit(true); // and entering this one
});
}
object_t& add(object_t o)
{
objects_.push_back(o);
return objects_.back();
}
//! ... as above
};
Now the question is how to chain the result of the hit function to the object_t::hit stream. I see two ways:
Option 1, is fully functional, but very poorly performing, since for every mouse point, all objects stream will need to calculate their value.
Option 2. is not fully functional, as I use subject to push the values to the right stream, in an imperative way. but is very performant as only the right (two) object(s) hit stream get to fire.
Note:
The implementation is in rxcpp, but its general to any language we have RX in it, or general FRP paradigm, this is why I tagged rxjs\rx.net\frp etc.
thanks in advance :-)
If there is one source observable and N subscribers then there will have to be at least N computations every time the source emits. There is no way around that which I can think of.

Detecting tiled map layer collision in cocos2d-x

i am new in cocos2d-x3.2 and i am creating a tile based game something like mario. I created a tile map with a collidable layer . but when I am using Dictionary *properties; then its showing error in dictionary . So what can I use for detecting the collision?
Point tileCoord = this->tileCoordForPosition(position);
int tileGid = CollisionLAyer->tileGIDAt(tileCoord);
if (tileGid) {
Dictionary *properties;
properties = _tileMap->propertiesForGID(tileGid);
if (properties) {
String *collision = new CCString();
*collision = *properties->valueForKey("Collidable");
if (collision && (collision->compare("True") == 0)) {
return;
}
}
With 3.2 I believe the properties are created in a ValueMap.
Value properties = this->getPropertiesForGID(0);
if (! properties.isNull())
{
ValueMap dict = properties.asValueMap();
Value collision = dict["Collidable"];
if (! collision.isNull() && collision.asString() == "True") {
return;
}
}
You can also use an additional tile layer if you want and for example use a red semi-transparent single tile tileset. Draw over all tiles that should be collision tiles. Then you can check if the gid on that layer is > 0 for collisions.

Resorting Zoomable Treemaps

This question is abstract, but I'm trying to merge two functions of separate treemaps in d3js, and am asking for an appropriate strategy.
In summary, I want to take the resorting ability of this treemap (count,size):
http://strongriley.github.io/d3/ex/treemap.html
And apply that to the zooming function of this treemap:
http://bost.ocks.org/mike/treemap/
The intention is to allow dynamic transitions for resorting, and then click on a cell to zoom into another tier of the data while maintaining the selected sort. If anyone has suggestions, your help would be much appreciated.
In summary, I'm trying to resort the zoomable treemap by replacing the treemap.values data with the accumulate function. Here's some sample data for a child:
{"name": "DataUtil", "value": 3322,"value1": 45,"value2": 87}
By clicking a button, the accumulate function needs to re-assign data based on value,value1,value2, etc. to the full hierarchy. Then, the visualization needs to be updated.
First attempt (not working) can be found on this JSFiddle:
function accumulate(d, boo) {
if (boo == 0) {
return (d._children = d.children) ? d.value = d.children.reduce(function (p, v) {
return p + accumulate(v, 0);
}, 0) : d.value1;
}
if (boo == 1) {
return (d._children = d.children) ? d.value = d.children.reduce(function (p, v) {
return 1 + accumulate(v, 1);
}, 0) : d.value2;
}
}

Restrict the min/max zoom on a Bing Map with v7 of the AJAX control?

I'm working on a site that makes use of v7 of the Bing Maps AJAX Control. One of the things I need to do is restrict the zoom level so as to prevent users from zoom in past a certain level, or zoom out past a certain level.
I found a "getZoomRange" method on the Map object, after inspecting it, it simply returns an object literal with "min" and "max" properties. So, I figured overloading it would probably do the trick:
// "map" is our Bing Maps object
map.getZoomRange = function ()
{
return {
max: 14
min: 5
};
};
...but no. It has no effect (it actually has something to do with the appearance of the zoom slider when using the default Dashboard).
Hijacking the event and preventing it from proceeding also seems to have no effect.
According to Bing Maps support, the only way to do this (which isn't particularly elegant, and results in some unwelcome jitter on the map) is as follows:
// "map" is our Bing Maps object, overload the built-in getZoomRange function
// to set our own min/max zoom
map.getZoomRange = function ()
{
return {
max: 14,
min: 5
};
};
// Attach a handler to the event that gets fired whenever the map's view is about to change
Microsoft.Maps.Events.addHandler(map,'viewchangestart',restrictZoom);
// Forcibly set the zoom to our min/max whenever the view starts to change beyond them
var restrictZoom = function ()
{
if (map.getZoom() <= map.getZoomRange().min)
{
map.setView({
'zoom': map.getZoomRange().min,
'animate': false
});
}
else if (map.getZoom() >= map.getZoomRange().max)
{
map.setView({
'zoom': map.getZoomRange().max,
'animate': false
});
}
};
I was dealing with a similar issue and I ended up doing something very similar to what MrJamin describes in his answer, with one (subtle, but major) difference: I added a handler for targetviewchanged. According to the official docs on MSDN, 'targetviewchanged' occurs when the view towards which the map is navigating changes. Also, instead of calling Map#getZoom, I used Map#getTargetZoom which returns the zoom level of the view to which the map is navigating. Note, this approach prevents jitter.
Here's the shortened version of my code:
function restrictZoom(map,min,max) {
Microsoft.Maps.Events.addHandler(map,'targetviewchanged',function(){
var targetZoom = map.getTargetZoom();
var adjZoom = targetZoom;
if(targetZoom > max) {
adjZoom = max;
} else if(targetZoom < min) {
adjZoom = min;
}
if(targetZoom != adjZoom) {
map.setView({zoom:adjZoom});
}
});
}
Another way to achieve this is to handle the event thrown when the mouse wheel is moved. http://msdn.microsoft.com/en-us/library/gg427609.aspx
When you handle the mousewheel event, you can check whether the mouse wheel is being scrolled forwards or backwards, and then check the map.targetZoom() in order to compare with a min or max zoom value. If the min or max are exceeded, then set event.handled = true. This prevents the event from being handled by any other handlers which prevents default behaviour. From the documentation:
A boolean indicating whether the event is handled. If this property is
set to true, the default map control behavior for the event is
cancelled.
See below:
var Zoom = {
MAX: 10,
MIN: 2
}
var mouseWheelHandler = function(event) {
// If wheelDelta is greater than 0, then the wheel is being scrolled forward which zooms in
if(event.wheelDelta > 0) {
if(map.getTargetZoom() >= Zoom.MAX) {
event.handled = true;
}
}
else {
if(map.getTargetZoom() <= Zoom.MIN) {
event.handled = true;
}
}
}
Microsoft.Maps.Events.addHandler(map, 'mousewheel', mouseWheelHandler);

Resources