OpenLayers overriding point radius for all rendering intents - javascript

In OpenLayers, I'm trying to override the point radius for all of the point style rendering intents (default, select and temporary). Currently I do this:
var styleMap = new OpenLayers.StyleMap({
"default": OpenLayers.Util.applyDefaults({pointRadius: radius},OpenLayers.Feature.Vector.style['default']),
"select": OpenLayers.Util.applyDefaults({pointRadius: radius},OpenLayers.Feature.Vector.style['select']),
"temporary": OpenLayers.Util.applyDefaults({pointRadius: radius},OpenLayers.Feature.Vector.style['temporary'])
});
It seems like there should be a way to just say to override point radius in all of them, but I can't figure out how to do that. I would have hoped that by default the point radius is inherited from default into select and temporary, but if I override it in just default (without overriding anything in select and temporary), they use the original default point size.

Try calling vectorLayer.redraw(); afterward.

Related

Detect when user reaches maxBounds using Leaflet

I am using leaflet to show an interactive map to our users.
We want to let them browse through a limited area, and inform them they have to subscribe in case they want to see something too far away (using a pop up or equivalent).
So far I have seen that Leaflet supports a maxBounds option.
This is a good start that lets me prevent users to see larger areas.
Now I would like to be able to detect a maxBounds 'event' to show the user a pop up.
I have been looking into the Leaflet source code, but couldn't find an obvious way to do it.
so far I have found that the maxBounds option is fed into the setView method.
This method itself uses the _limitCenter method to define the center.
This goes a few levels deeper, down to the _getBoundsOffset method that finally uses the bounds.
_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
var projectedMaxBounds = toBounds(
this.project(maxBounds.getNorthEast(), zoom),
this.project(maxBounds.getSouthWest(), zoom)
),
minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
dx = this._rebound(minOffset.x, -maxOffset.x),
dy = this._rebound(minOffset.y, -maxOffset.y);
return new Point(dx, dy);
},
The closest I could find so far would be to hook into the moveend event and check whether the center is out of my bounds manually.
However, it seems like this would be redundant with what leaflet is already doing.
Is there a better to leverage leaflet to achieve this?
Thanks
Just check if your defined bounds contain the map bounds. As long as the map bounds are inside the defined bounds, this will do nothing:
var myBounds = L.latLngBounds(...)
map.on('move moveend zoomend', function(){
if (!myBounds.contains(map.getBounds())) {
// Display popup or whatever
}
});
it seems like this would be redundant with what leaflet is already doing.
Don't worry about that. The overhead is negligible for this use case.

Scaling Raster with Paper.js using Tween.js

I'm think I'm having a similar issue as this in that I can not work out (or know if it exists) whereby I can get access to the scaling applied to a given object (in my instance, a raster).
I need to know this so I can animate the scaling via Tween.js.
Anyone have any ideas or know if indeed it is possible to find out the current scaling applied to a raster (or any) object?
I thought it was an issue with Rasters so I tried tweening the scale property of a Path and then a Group and I couldn't get access to the values in order to animate it.
Because I am using Tween.js I can not simply use the object.scale(value) function.
UPDATE
I even tried applying an arbitrary (animated) number to the scale function and it failed to work... i.e.:
object.scale( 0 );
object.arbitraryNumber = 0;
createjs.Tween.get( object )
.to( { arbitraryNumber:1 } , 1000, createjs.Ease.getPowInOut(2) )
.addEventListener( "change", function( event ) {
event.target.target.scale( event.target.target.arbitraryNumber);
} );
Although this did not work, when the same approach was applied to the x position of the object, it animated fine.
Is there anything that needs to be flagged in order to update scaling of an object?
When calling Item.scale() method on each frame with values from 0 to 1, you are actually scaling down item exponentially because each call scales the item relatively to the previous value.
What you want to do is animate the Item.scaling property instead.
You also have to know that by default, PaperJS use global coordinates system and apply every transformations directly to points.
You can change this behavior by setting Item.applyMatrix property to false.
Doing this, scale change will affect item matrix instead of affecting points coordinates and you will be able to animate it as you expect.
Here is simple Sketch of a scale animation:
var circle = new Path.Circle(view.center, 50);
circle.fillColor = 'orange';
circle.applyMatrix = false;
function onFrame(event)
{
circle.scaling = Math.sin(1 + event.count * 0.05);
}
You should be able to transpose this example to your Tween.js context easily.

OpenLayers 3 hover to highlight only one specified object per time

Original issue:
OpenLayers 3 (tested also newest 3.14.1) should have some sense in the way it selects features that overlap each other. When multi=false it selects only one feature, but picks it quite randomly. When you hover over certain features from different directions, different feature is selected.
I have tried to tangle this by setting z-index to features to tell Openlayers the desired order. Indexing is set to order point > line > polygon but still I am not getting point selected first on hover.
Z-Index is applied to layer with:
rendererOptions: { zIndexing: true }
on layer and
zIndex: x
in style for different feature types.
What happened first:
Z-indexing did not work so I was not able to tangle the situation for select. I kept multi=true and filtered results manually from list.
I had a function getSelectedRemovableFeature(event.selected) that took event.selected and returned only one feature out of it.
So, my selection had a code like follows:
var selectionInteraction = new ol.interaction.Select({
layers: [layersModule.getTargetLayer()],
condition: ol.events.condition.click,
multi: true
});
and more..
selectionInteraction.on('select', function(event) {
var selectedFeature = null;
selectedFeature = getSelectedRemovableFeature(event.selected);
if(selectedFeature){
.. some logic..
layersModule.getTargetLayer().getSource().removeFeature(selectedFeature);
}
});
Then I got stucked:
I had
var hoverInteraction = new ol.interaction.Select({
layers: [layersModule.getTargetLayer()],
condition: ol.events.condition.pointerMove,
multi: true
});
..and..
hoverInteraction.on('select', function(event) {
var selectedFeature = null;
selectedFeature = getSelectedRemovableFeature(event.selected);
// here I didn't know what to do..
}
});
I had there a logic like this:
selectControl = new OpenLayers.Control.SelectFeature(vectorLayer);
...
map.addControls([selectControl]);
selectControl.activate();
// unselect any specific feature...
selectControl.unselect(vectorLayer.features[0]);
(https://gis.stackexchange.com/questions/41017/how-can-i-unselect-a-feature-in-openlayers)
.. but when it was there, no selections were removed, it worked like same as without this part.
Back to the origins:
Now I realized, that tangling with the hoverSelection data is of no use, because new multi=true type event is thrown there before I could calculate how to select one only and show that. I believe that is the case, because nothing changes even I manually filter the results and remove features.
In short finally:
How to determine explicit order where the hover / select selection goes?
Hit detection of features on a map works in opposite rendering order. By default, points are rendered last, so they get hit detected (and selected) first. When no zIndex is set on any style, the rendering order is polygons, lines, points, texts.
When you set zIndex on an ol.style.Style, you override this fixed rendering order, allowing you to e.g. render polygons on top of points.
Within these four groups (polygons, lines, points, texts), you can control the rendering order by setting a renderOrder function on your ol.layer.Vector. That is an array sort function called with two features:
new ol.layer.Vector({
renderOrder: function(a, b) {
if (a.get('importance') < b.get('importance')) {
return -1;
} else if (b.get('importance') > a.get('importance')) {
return 1;
}
return 0;
}
});
The above assumes that your features have a numeric 'importance' property. Now when you hit detect (select) features of the same geometry type (e.g. points), those with a higher 'importance' value will be selected first, because they are rendered last.
Also note that there are no rendererOptions like you state in your question.
I noticed that from some reason the draw styles included heavily z-indexing and what made this problem to occur was the following:
Reason:
From some historical reason selected style was forced on top by zIndex: 100. Normal selection had zIndex: 1.
That caused a problem: when you had point and line on top of each other and you select line, it's zIndex rose up to 100. That broke the natural highlight order, because 100 is far more than 1. Thus natural order, which #ahocevar pointed out, gets broken and highlighting becomes dependent of direction where you come near to the feature on map.
Solution:
I put selected and default style both to zIndex: 1 and everything seemed to work ok event with that.
I will need to check still the reason of selected style on zIndex so high, but if there is no reason to continue its usage, this thing solves the problem.
Side note:
Answer is a community wiki, because half of solution is from comment of another user.

Setting connector (edge) points in mxgraph

I'm trying to import a diagram in our custom format to mxgraph, but am stuck on setting the points on the connector. I've tried called the functions in mxEdgeStyle like ElbowConnector:
ElbowConnector(view.getState(edge), model.getSource(edge), model.getTarget(edge), points)
where points is the array of point I want to set. I get the feeling I'm calling entirely the wrong function, or am I using it incorrectly?
The edge styles are style like the other key/value styles. In the link you provide to the API specifications, the top example tells you how to use it:
var style = stylesheet.getDefaultEdgeStyle();
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;

d3.v3 scatterplot with all circles the same radius

Every example I have found shows all of the scatter plot points to be of random radii. Is it possible to have them all the same size? If I try to statically set the radius all of the circles will be very small (I'm assuming the default radius). However, if I use Math.random() as in most examples there are circles large and small. I want all the circles to be large. Is there a way to do that? Here's the code snippet forming the graph data using Math.random() (this works fine for some reason):
function scatterData(xData, yData)
{
var data = [];
for (var i = 0; i < seismoNames.length; i++)
{
data.push({
key: seismoNames[i],
values: []
});
var xVals=""+xData[i];
xVals=xVals.split(",");
var yVals=""+yData[i];
yVals=yVals.split(",");
for (var j = 0; j < xVals.length; j++)
{
data[i].values.push({
x: xVals[j],
y: yVals[j],
size: Math.random()
});
}
}
return data;
}
Math.random() spits out values between 0 and 1 such as 0.164259538891095 and 0.9842195005008699. I have tried putting these as static values in the 'size' attribute, but no matter what the circles are always really small. Is there something I'm missing?
Update: The NVD3 API has changed, and now uses pointSize, pointSizeDomain, etc. instead of just size. The rest of the logic for exploring the current API without complete documentation still applies.
For NVD3 charts, the idea is that all adjustments you make can be done by calling methods on the chart function itself (or its public components) before calling that function to draw the chart in a specific container element.
For example, in the example you linked too, the chart function was initialized like this:
var chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.color(d3.scale.category10().range());
chart.xAxis.tickFormat(d3.format('.02f'));
chart.yAxis.tickFormat(d3.format('.02f'));
The .showDistX() and .showDistY() turn on the tick-mark distribution in the axes; .color() sets the series of colours you want to use for the different categories. The next too lines access the default axis objects within the chart and set the number format to be a two-digit decimal. You can play around with these options by clicking on the scatterplot option from the "Live Code" page.
Unfortunately, the makers of the NVD3 charts don't have a complete documentation available yet describing all the other options you can set for each chart. However, you can use the javascript itself to let you find out what methods are available.
Inspecting a NVD3.js chart object to determine options
Open up a web page that loads the d3 and nvd3 library. The live code page on their website works fine. Then open up your developer's console command line (this will depend on your browser, search your help pages if you don't know how yet). Now, create a new nvd3 scatter chart function in memory:
var testChart = nv.models.scatterChart();
On my (Chrome) console, the console will then print out the entire contents of the function you just created. It is interesting, but very long and difficult to interpret at a glance. And most of the code is encapsulated so you can't change it easily. You want to know which properties you can change. So run this code in the next line of your console:
for (keyname in testChart){console.log(keyname + " (" + typeof(testChart[keyname]) + ")");}
The console should now print out neatly the names of all the methods and objects that you can access from that chart function. Some of these will have their own methods and objects you can access; discover what they are by running the same routine, but replacing the testChart with testChart.propertyName, like this:
for (keyname in testChart.xAxis){console.log(keyname + " (" + typeof(testChart.xAxis[keyname]) + ")");}
Back to your problem. The little routine I suggested above doesn't sort the property names in any order, but skimming through the list you should see three options that relate to size (which was the data variable that the examples were using to set radius)
size (function)
sizeDomain (function)
sizeRange (function)
Domain and range are terms used by D3 scales, so that gives me a hint about what they do. Since you don't want to scale the dots, let's start by looking at just the size property. If you type the following in the console:
testChart.size
It should print back the code for that function. It's not terribly informative for what we're interested in, but it does show me that NVD3 follows D3's getter/setter format: if you call .property(value) you set the property to that value, but if you call .property() without any parameters, it will return back the current value of that property.
So to find out what the size property is by default, call the size() method with no parameters:
testChart.size()
It should print out function (d) { return d.size || 1}, which tells us that the default value is a function that looks for a size property in the data, and if it doesn't exist returns the constant 1. More generally, it tells us that the value set by the size method determines how the chart gets the size value from the data. The default should give a constant size if your data has no d.size property, but for good measure you should call chart.size(1); in your initialization code to tell the chart function not to bother trying to determine size from the data and just use a constant value.
Going back to the live code scatterplot can test that out. Edit the code to add in the size call, like this:
var chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.color(d3.scale.category10().range())
.size(1);
chart.xAxis.tickFormat(d3.format('.02f'));
chart.yAxis.tickFormat(d3.format('.02f'));
Adding that extra call successfully sets all the dots to the same size -- but that size is definitely not 1 pixel, so clearly there is some scaling going on.
First guess for getting bigger dots would be to change chart.size(1) to chart.size(100). Nothing changes, however. The default scale is clearly calculating it's domain based on the data and then outputting to a standard range of sizes. This is why you couldn't get big circles by setting the size value of every data element to 0.99, even if that would create a big circle when some of the data was 0.01 and some was 0.99. Clearly, if you want to change the output size, you're going to have to set the .sizeRange() property on the chart, too.
Calling testChart.sizeRange() in the console to find out the default isn't very informative: the default value is null (nonexistent). So I just made a guess that, same as the D3 linear scale .range() function, the expected input is a two-element array consisting of the max and min values. Since we want a constant, the max and min will be the same. So in the live code I change:
.size(1);
to
.size(1).sizeRange([50,50]);
Now something's happening! But the dots are still pretty small: definitely not 50 pixels in radius, it looks closer to 50 square pixels in area. Having size computed based on the area makes sense when sizing from the data, but that means that to set a constant size you'll need to figure out the approximate area you want: values up to 200 look alright on the example, but the value you choose will depend on the size of your graph and how close your data points are to each other.
--ABR
P.S. I added the NVD3.js tag to your question; be sure to use it as your main tag in the future when asking questions about the NVD3 chart functions.
The radius is measured in pixels. If you set it to a value less than one, yes, you will have a very small circle. Most of the examples that use random numbers also use a scaling factor.
If you want all the circles to have a constant radius you don't need to set the value in the data, just set it when you add the radius attribute.
Not sure which tutorials you were looking at, but start here: https://github.com/mbostock/d3/wiki/Tutorials
The example "Three little circles" does a good step-by-step of the different things you can do with circles:
http://mbostock.github.io/d3/tutorial/circle.html

Categories

Resources