How to configure how close together features need to be to form a cluster in openlayers3 - openlayers-3

Is there a parameter to set a minimum radius for features to cluster? - so that when a set of points or features are within some minimum distance, they form a cluster, otherwise not?
ol.source.Cluster() has two parameters that look promising, but don't seem to work as expected.
distance: Minimum distance in pixels between clusters. Default is 20.
extent: An array of numbers representing an extent: [minx, miny,
maxx, maxy].

Not quite sure what "but don't seem to work as expected." means?? How does it not work as expected??
distance property of ol.source.Cluster tells the layer when to group objects based on the distance set. It can be changed when creating the cluster layer. For example:
var locationSource = new ol.source.Vector({
url: loc_url,
format: new ol.format.GeoJSON({
defaultDataProjection :'EPSG:3857'
}),
loader: vectorLoader,
strategy: ol.loadingstrategy.all
});
var LOCclusterSource = new ol.source.Cluster({
distance: 5,
source: locationSource
});
I usually change the distance object until I find the desired distance so group/cluster object look right on the map.
The radius of a group object on a map layer can be change via a style function for the map layer. Many example of style functions exist here on stack.
Here is a hacked up example that I use to increase the radius of cluster/group objects on the map layer so it's visually obvious that it is a group/cluster object:
NOTE: You can have different shapes on the same layer too using a style function. https://openlayers.org/en/latest/examples/regularshape.html
// Location Map Layer Properties
var locLyrProps = {
"radius": 8,
"CORadius": 12,
"groupRadius": 10,
"borderWidth": 2,
"color": [0, 0, 0, 0.5],
"txtMaxRes": 20,
"txtOffsetY": -20
};
var styleFunction = function() {
return function(feature,resolution) {
var style;
var props = locLyrProps;
var radius;
var lyrTyp;
var gotGroup = false;
var features = feature.get('features');
if (features.length == 1) { //Individual map object because length = 1
style = new ol.style.Style({ //Square layer object
image: new ol.style.RegularShape({
radius: radius,
points: 4,
angle: Math.PI / 4,
fill: createFillStyle(feature),
stroke: createStrokeStyle(feature, resolution)
}),
text: createTextStyle(feature, resolution)
});
} else {
var rad = props.radius;
if (features.length > 1) { //If cluster/group of features increase radius of group object so group objects stand out a bit
rad = props.groupRadius; //If cluster/group object is found, set cluster/group radius for it
gotGroup = true;
}
console.log('circle radius: ' + rad);
style = new ol.style.Style({
image: new ol.style.Circle({
radius: rad,
fill: createFillStyle(feature),
stroke: createStrokeStyle(feature, resolution, gotGroup)
}),
text: createTextStyle(feature, resolution, props, gotGroup)
});
}
return [style];
};
};

Related

Custom Hit Detection on Konva Group

I'm attempting to expand the click area of a group of shapes, but there appears to be no hitFunc property on groups.
var patternControl = new Konva.Group();
patternControl.hitFunc(function(context) {
context.beginPath();
context.arc(0, 0, outerRadius + patternWidth, 0, Math.PI * 2, true);
context.fillStrokeShape(this);
});
Is there any way to do apply custom hit functions to a group?
Only shapes can be used for hit detection. As a workaround, you can disable hits for all shapes with shape.listeting(false) and then add a "fake" shape to the group that will be used as a hit area:
var patternControl = new Konva.Group();
var hitShape = new Konva.Shape({
// make it transparent, so it is not visible
fill: 'rgba(0,0,0,0)',
hitFunc: (context, shape) => {
context.beginPath();
context.arc(0, 0, outerRadius + patternWidth, 0, Math.PI * 2, true);
context.fillStrokeShape(shape);
}
});
patternControl.add(hitShape);

384x384px tile to diplay at 384x384px on map (retina)

I serve TMS tile with in two flavours: 256px or 384px through renderd option scale=1.5.
With Openlayers 3, the only way I found to display these 384px tiles their original size is to transform the canvas context like this:
map.getViewport().getElementsByTagName('canvas')[0].getContext("2d").setTransform(1.5, 0, 0, 1.5, -w, -h);
I think it's not the proper way to go, so what would be the right one?
I played a bit with a special ol.tilegrid but with no success, see here:
https://jsfiddle.net/yvecai/owwc5bo8/8/
The output I aim for is on the right map.
There is no need to create a special tile grid or to apply any canvas scaling. All you need to do is set the tilePixelRatio of the source properly, which would be 1.5 in your case:
source: new ol.source.XYZ({
url: "http://www5.opensnowmap.org/base_snow_map_high_dpi/{z}/{x}/{y}.png?debug",
attributions: [/* ... */],
tilePixelRatio: 1.5
})
Also note that your expectation of the result is wrong. I updated your fiddle to compare the standard 256px tiles (on the right) with the hidpi 384px tiles (on the left). If you are viewing the fiddle on a hidpi display, you'll notice the difference. https://jsfiddle.net/owwc5bo8/9/
To summarize:
If you want to display high-dpi tiles on a mobile device with good sharpness, use tilePixelRatio :
https://jsfiddle.net/owwc5bo8/9/
If you want to display tiles with a size differnet than 256x256, create a proper ol.tilegrid, and a proper ol.view:
https://jsfiddle.net/owwc5bo8/12/
var extent = ol.proj.get('EPSG:3857').getExtent();
var tileSizePixels = 384;
var tileSizeMtrs = ol.extent.getWidth(extent) / 384;
var resolutions = [];
for (var i = -1; i <= 20; i++) {
resolutions[i] = tileSizeMtrs / (Math.pow(2, i));
}
var tileGrid = new ol.tilegrid.TileGrid({
extent: extent,
resolutions: resolutions,
tileSize: [384, 384]
});
var center = ol.proj.fromLonLat(
ol.proj.toLonLat([2, 49], 'EPSG:4326'),
'EPSG:3857');
var zoom = 5;
var view1 = new ol.View({
center: center,
zoom: zoom,
maxResolution: 40075016.68557849 / 384
});

ol.proj.transform Zoomify to EPSG:4326

Is there any way to transform from Zoomify (with a specific width and height) to [long, lat] coordinates ?
I saw the documentation (for 3.6) and it seems that ol.proj.transform method does not accept ZOOMIFY as ol.proj.ProjectionLike parameter.
Currently, I have some marker coordinates saved by LeafletJS library into database and I need to project them into [long, lat] coordinates to display them with a OpenLayer 3.6 map. The tiles for the map are the same as it was for Leaflet library.
If needed, here is the code for initiating the ol3 map:
var proj = new ol.proj.Projection({
code: 'ZOOMIFY',
units: 'pixels',
extent: [0, 0, width, height]
}),
map = new ol.Map({
target: this.get('view').$().attr('id'),
view: new ol.View({
projection: proj,
center: [width/2, - height/2],
zoom: 1,
extent: [0, -height, width, 0]
}),
controls: []
});
......
var layer = new ol.source.Zoomify({
url: url,
size: [width, height],
crossOrigin: 'anonymous'
});
map.addLayer(layer);
Also, the code that I'm using to add the markers is:
pos = ol.proj.transform([posX, posY], 'ZOOMIFY', 'EPSG:4326'),
marker = new ol.Overlay({
position: pos,
positioning: 'center-center',
element: domElement,
stopEvent: false
});
map.addOverlay(marker);
I need to mention that I am really new with this framework, so any hint can be helpful.
Thanks,
It seems that you did not need to transform the coordinates since the frameworks know that the coordinates are in the ZOOMIFY format (from the map creation, see above lines).
I solved the problem with the following code:
pos = [posX, posY],
marker = new ol.Overlay({
position: pos,
positioning: 'center-center',
element: domElement,
stopEvent: false
});
map.addOverlay(marker);

How add feature in a different projection in Openlayers 3.5

I'm using Openlayer 3.5 and load an OSM map "EPSG:3857".
var extent = [116.826673, 4.854776, 126.748593, 18.697146];
var philiExtent = ol.extent.applyTransform(extent, ol.proj.getTransform("EPSG:4326", "EPSG:3857"));
var view = new ol.View({
center: ol.proj.transform([121.787633, 11.775961], 'EPSG:4326', 'EPSG:3857'),
zoom: 0,
extent: philiExtent,
resolutions: [2560, 1280, 640, 320, 160, 80, 40, 20, 10, 5, 2.5, 1.2, 0.6],
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map'
});
But my features from webService are in "EPSG:4326"
function showData(data) {
var format = new ol.format.WKT();
var feature;
$.each(data, function (i, link) {
feature = format.readFeature(link.geom);
wktTraffic.addFeature(feature);
})
console.log('done load map');
}
So how I make the map be on 4326 or the new feature be on 3857.
I prefer first option.
Check out the FAQ section: http://openlayers.org/en/master/doc/faq.html#how-do-i-change-the-projection-of-my-map-
How do I change the projection of my map?
There is a good chance that you want to change the default projection of OpenLayers to something more appropriate for your region or your specific data.
The projection of your map can be set through the view-property. Here are some examples:
// OpenLayers comes with support for the World Geodetic System 1984, EPSG:4326:
var map = new ol.Map({
view: new ol.View({
projection: 'EPSG:4326'
// other view properties like map center etc.
})
// other properties for your map like layers etc.
});
// To use other projections, you have to register the projection in OpenLayers:
//
// By default OpenLayers does not know about the EPSG:21781 (Swiss) projection.
// So we create a projection instance for EPSG:21781 and pass it to
// ol.proj.addProjection to make it available to the library for lookup by its
// code.
var swissProjection = new ol.proj.Projection({
code: 'EPSG:21781',
// The extent is used to determine zoom level 0. Recommended values for a
// projection's validity extent can be found at http://epsg.io/.
extent: [485869.5728, 76443.1884, 837076.5648, 299941.7864],
units: 'm'
});
ol.proj.addProjection(swissProjection);
// we can now use the projection:
var map = new ol.Map({
view: new ol.View({
projection: swissProjection
// other view properties like map center etc.
})
// other properties for your map like layers etc.
});
We recommend to lookup parameters of your projection (like the validity extent) over at epsg.io.
To reproject your features to EPSG:3857, you can set the options dataProjection and featureProjection when parsing the features from the WKT string. See also ol.format.WKT#readFeature
var format = new ol.format.WKT();
var feature;
$.each(data, function (i, link) {
feature = format.readFeature(link.geom, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
wktTraffic.addFeature(feature);
})

How to change the rotation point (Feature) on the map after it is created and added to map

How is it possible to change the rotation icon (picture), points (Feature) after creating and adding to the map?
Set the rotation icons on create a point I know, but how to change the rotation later?
For uses such as icons, which follows the direction of rotation?
How to do this correctly with respect to performance? (set point again all parameters???)
thanks... very much
Live demonstration:
http://jsfiddle.net/91mLh1j7/
JS Code:
var map = new ol.Map({
target: 'mapID',
layers: [
new ol.layer.Tile({
source: new ol.source.MapQuest({
layer: 'osm'
})
})],
view: new ol.View({
center: ol.proj.transform([14, 50], 'EPSG:4326', 'EPSG:3857'),
zoom: 11
})
});
var Features = [];
// define style for features
var iconStyle = {
src: "http://google-maps-icons.googlecode.com/files/library-publ.png",
anchorOrigin: "bottom-left", // v KML je počítáno od levého spodního rohu
anchor: [0.5, 0],
anchorXUnits: "fraction",
anchorYUnits: "fraction",
scale: 0.9,
opacity: 0.75,
rotation: 45 * 0.01745329251, // in rad / 360° = 6.28318531 rad = 2PI rad
rotateWithView: "true"
};
var point1 = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([14.01, 50.01], 'EPSG:4326',
'EPSG:3857')),
name: 'Point One'
});
point1.setStyle(new ol.style.Style({
image: new ol.style.Icon(iconStyle)
}));
var point2 = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([13.99, 49.99], 'EPSG:4326',
'EPSG:3857')),
name: 'Point Two'
});
point2.setStyle(new ol.style.Style({
image: new ol.style.Icon(iconStyle)
}));
// add point1, point2 to Features
Features.push(point1);
Features.push(point2);
var vectorSource = new ol.source.Vector({
features: Features // add an array of features
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource // add source for vectorLayer
});
map.addLayer(vectorLayer) // add vectorLayer to map
////////////////////
// how to change the rotation of one point (feature) ? after cration point and add it on map
////////////////////
The ol.style.Image class, which the ol.style.Icon extends, has a setRotation method you can use to set the rotation of the icon. You can try this in your example by adding:
Feature1.getStyle().getImage().setRotation(135 * 0.01745329251);
See live on the updated fiddle: http://jsfiddle.net/91mLh1j7/1/

Resources