I'm using open layers 6 for an application.
I render mutltiple layers on a map.
On my map for exemple, I have 2 differents layers :
this.layers["PointOfInterest"] = new appli.map.PointOfInterestLayer(this.map, this.layersName.POI);
this.layers[this.layersName.POI].addLayer();
this.layers[this.layersName.POI].addFeaturesPOI();
this.layers["PHOTO"] = new appli.map.PhotoLayer(this.map, this.layersName.PHOTO);
this.layers[this.layersName.PHOTO].addLayer();
this.layers[this.layersName.PHOTO].addFeaturesPHOTO();
My layers :
appli.map.PointOfInterestLayer.prototype = {
addLayer() {
this.source = new ol.source.Vector({
projection: config.projection
});
this[layerProperty] = new ol.layer.Vector({
name: this.type,
source: this.source,
style: (feature) => this.getStyle(feature),
zIndex: 1,
});
this.hoverInteraction = new ol.interaction.Select({
layers:[this[layerProperty]],
});
}
My problem is that, sometimes, the features are place at the exact same location on the map, and we can't see the entire icone representing it.
Is there a way to automatically move the feature to the closest place, in order to have the ability to clic on the 2 differents features ?
Related
I am trying to make an Icon feature that I can click on easier on mobile phones. I have an icon set up nicely and when using a mouse to click on it there is no issue. However when using my finger or thumb on a mobile phone it is quite difficult to get an accurate click. I am using a ol.geom.Point and giving it an icon style. I tried an ol.geom.Circle but I can't get the icon style to work with it.
Here is an example of my working ol.geom.Point:
for (i in spots) {
var spot = spots[i];
var coords = spot['coords'];
var lat = parseFloat(coords.split(',')[0]);
var lng = parseFloat(coords.split(',')[1]);
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([lng, lat])),
type: 'spot'
});
iconFeature.setStyle(spotMarker);
features.push(iconFeature);
}
vectorSourceSpots = new ol.source.Vector({
features: features
});
var vectorLayer = new ol.layer.Vector({
source: vectorSourceSpots
});
map.addLayer(vectorLayer);
Here is the spotMarker style:
var spotMarker = new ol.style.Style({
image: new ol.style.Icon(({
src: 'images/spot.png'
}))
});
I've also tried with a ol.geom.Circle but I could not see my icon when I tried this:
for (i in spots) {
var spot = spots[i];
var coords = spot['coords'];
var lat = parseFloat(coords.split(',')[0]);
var lng = parseFloat(coords.split(',')[1]);
var iconFeature = new ol.Feature({
geometry: new ol.geom.Circle(ol.proj.fromLonLat([lng, lat]), 5),
type: 'spot'
});
iconFeature.setStyle(spotMarker);
features.push(iconFeature);
}
vectorSourceSpots = new ol.source.Vector({
features: features
});
var vectorLayer = new ol.layer.Vector({
source: vectorSourceSpots
});
map.addLayer(vectorLayer);
What I want is to have the icon remain the same size, but just to increase the click radius around the icon. Almost like an invisible circle a bit bigger than the icon with the same center.
Is this possible?
You will use forEachFeatureAtPixel to add event on the features, then you can set hitTolerance on its options parameter.
check this api document: forEachFeatureAtPixel
you may need to write:
var addEvent = function(map, evt) {
map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
}, {
hitTolerance: 10
});
};
map.on('click', function(evt) {
addEvent(map, evt);
});
I am using the Openlayers extent interaction to allow the user to select an AOI. After the user does this and removes the drawn extent I would like the blue dot to also disappear. Currently it just hangs out on the map and then pops back to the mouse once the interactions key is pressed again.
This behavior can be seen in the Openlayers Extent Interaction example here. Shift+drag starts the extent interaction. Shift+click removes the extent but the blue dot is left there. Is there a way to remove this ?? Once the extent is removed there is no other interaction possible with the blue dot, so what is the purpose of it even being there ??
If you check out the API docs for ol.interaction.Extent
You can pass a style to pointerStyle to update the style of the dot.
var extent = new ol.interaction.Extent({
condition: ol.events.condition.platformModifierKeyOnly,
pointerStyle: [] // <-- Makes the dot invisible
});
var vectorSource = new ol.source.Vector({
url: 'https://openlayers.org/en/v3.20.1/examples/data/geojson/countries.geojson',
format: new ol.format.GeoJSON()
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Vector({
source: vectorSource
})
],
target: 'map',
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
var extent = new ol.interaction.Extent({
condition: ol.events.condition.platformModifierKeyOnly,
pointerStyle: []
});
map.addInteraction(extent);
extent.setActive(false);
//Enable interaction by holding shift
this.addEventListener('keydown', function(event) {
if (event.keyCode == 16) {
extent.setActive(true);
}
});
this.addEventListener('keyup', function(event) {
if (event.keyCode == 16) {
extent.setActive(false);
}
});
<link href="https://openlayers.org/en/v3.20.1/css/ol.css" rel="stylesheet" />
<script src="https://openlayers.org/en/v3.20.1/build/ol.js"></script>
<div id="map" class="map"></div>
currently i have a vector map displayed using a KML file as the vector source.
what i want to do is have an image underlay this vector map.
The map is an indoor floormap, the image is the exact same as the vector map only there is more details in it, text written on it etc. What i need is for the map image to underlay the vector map so that the walls of the vector map align perfectly with the walls of the image. This can happen because the KML was created by tracing on top of the images using QGIS.
so far ive been able to have the KML vector map and png image appear on the map, but they are not alligned with each other and are not the same size. This is what i need help with!
here some code for what i currently have:
create the map, no layers yet (maps selected from dropdown boxes)
var map = new ol.Map({
layers: [],
target: 'floormap',
interactions: ol.interaction.defaults({mouseWheelZoom:false}),
view: new ol.View({
center: [0, 0],
zoom: 19,
minZoom: 15,
maxZoom: 30
})
});
add selected map (KML) to map
map.removeLayer(vector);
vector = new ol.layer.Vector({
source: new ol.source.Vector({
url: MAPS_URL + maps[map_id],
format: new ol.format.KML()
})
});
map.addLayer(vector);
setMapExtent(vector);
now i tried to add the image, which works but its not alligned
// this part here i feel may be the problem,
// i just copied and pasted from an example om openlayers.org,
// i dont actually know much about the extent and how to match it to
// the vector map
var extent = [0,0,1024,684];
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
image = new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [
new ol.Attribution({
html: '© xkcd'
})
],
url: MAPS_URL + images[map_id],
projection: projection,
imageExtent: extent
})
});
map.addLayer(image);
the setMapExtent method
function setMapExtent(vectorMap) {
var vectorSource = vectorMap.getSource();
var listenerKey = vectorSource.on('change', function () {
if (vectorSource.getState() === 'ready') {
var extent = vectorSource.getExtent();
map.getView().fitExtent(extent, map.getSize());
vectorSource.unByKey(listenerKey);
}
});
}
at this point i have a vector map with an image sitting way above the map, and the image seems to be smaller too.
Can anyone help me with this issue?
*** Solution! ***
a working solution, although probably not the best way to do it, but it works none the less.
var map = new ol.Map({
layers: [],
target: 'floormap',
interactions: ol.interaction.defaults({mouseWheelZoom:false}),
view: new ol.View({
center: [0, 0],
zoom: 19,
minZoom: 15,
maxZoom: 30
})
});
add new map layer
map.removeLayer(vector);
vector = new ol.layer.Vector({
source: new ol.source.Vector({
url: MAPS_URL + maps[map_id],
format: new ol.format.KML()
})
});
map.addLayer(vector);
setMapExtent(vector);
// call image adding function pass in vector
// to get its extend
addImage(vector);
the addImage function
function addImage(vectorMap) {
var vectorSource = vectorMap.getSource();
// listen for one change on the vector to get the extent of it
// for use in setting the image extent. tried to use on.('load')
// but it didnt work
var listenerKey = vectorSource.once('change', function () {
var extent = vectorSource.getExtent();
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
image = new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [],
url: MAPS_URL + images[map_id],
projection: projection,
imageExtent: extent
})
});
// remove vector layer else they keep stacking up
map.removeLayer(vector);
// add image
map.addLayer(image);
// re-add vector only push so it goes above the image
map.getLayers().push(vector);
});
}
seems to work pretty well! can anyone help me with layer ordering?
Your static image must be georeferenced correctly with the view's projection.
Default view's projection is EPSG:3857 (Spherical Mercator)., the extent of this projection is [-20026376.39, -20048966.10, 20026376.39, 20048966.10]
In your code you specify a projection in pixels for your static layer. You need to use the view's projection, something like this :
// Here the extent of your layer in EPSG:3857 -> [minx, miy, max, mayy]
var extent = [-10000000, -10000000, 10000000, 10000000];
image = new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [
new ol.Attribution({
html: '© xkcd'
})
],
url: MAPS_URL + images[map_id],
imageSize: [1024, 684], // Don't forget the image size here
imageExtent: extent
})
});
map.addLayer(image);
Update:
For layer ordering if you want your vector layer on top use push:
http://openlayers.org/en/v3.8.2/apidoc/ol.Collection.html#push
map.getLayers().push(vector)
I am developing a solution that uses Openlayers 3 to display static images of text documents that have been converted from PDFs. Some of my documents are black text on a white background - the page I'm displaying openlayers on is also black, as is the map background - so the document is invisible.
My current solution is to draw a white filled polygon at the same extent as the image being loaded but behind the text - essentially providing a white background for the extent I'm interested in.
The problem is when I pan around, the polygon is constantly redrawing, causing some undesirable effects. See this jsFiddle for an example.
Is there an alternative way of setting the background colour of a layer or is there a setting that will prevent the polygon from flickering.
CSS
body{
background-color:black;
}
HTML
<div id="map" class="map"></div>
Javascript
var imgURL = 'http://i.stack.imgur.com/9C8Xu.png';
var extent = [0,0,488,198];
var projection = new ol.proj.Projection({
units: 'pixels',
extent: extent
});
//A polygon that will represent a white background
var polyFeature = new ol.Feature({
geometry: new ol.geom.Polygon([
[
[0, extent[1]],
[0, extent[3]],
[extent[2], extent[3]],
[extent[2], extent[1]]
]
])
});
//A base layer to hold the polygon feature
var baseLayer = new ol.layer.Image({
source: new ol.source.ImageVector({
source: new ol.source.Vector({
features: [polyFeature]
}),
style: new ol.style.Style({
fill: new ol.style.Fill({color: 'white'})
})
})
});
var docLayer = new ol.layer.Image({
source: new ol.source.ImageStatic({
url: imgURL,
imageExtent: extent
})});
// build the map
var map = new ol.Map({
layers:[baseLayer,docLayer],
target: 'map',
view: new ol.View({
projection: projection,
center: ol.extent.getCenter(extent),
zoom: 2.5
})
});
I'm working on a OpenLayers map that will display multiple KML layers at once. I want to be able to click on a feature from any layer and have a pop-up show me some info. So far I can only click on the most recently added layer. If I want to click on a previously added layer I have to turn off all the layers that were added before. Obviously this is less than ideal. Here's my code so far:
var select = [];
function addLayer(layerId, layerLink, layerColor)
{
var kmlLayer = new OpenLayers.Layer.Vector("Layer_"+layerId, {
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: layerLink,
format: new OpenLayers.Format.KML({
extractStyles: true,
extractAttributes: true,
maxDepth: 2
})
})
});
kmlLayer.events.on({
"featureselected": onKMLSelect,
"featureunselected": onKMLUnselect
});
select["Layer_"+layerId] = new OpenLayers.Control.SelectFeature(kmlLayer);
map.addControl(select["Layer_"+layerId]);
select["Layer_"+layerId].activate();
map.addLayer(kmlLayer);
}
function onKMLPopupClose(evt) {
for(s in select)
{
select[s].unselectAll();
}
}
function onKMLSelect(event) {
var feature = event.feature;
var content = "<h2>"+feature.attributes.name + "</h2>" + feature.attributes.description;
popup = new OpenLayers.Popup.FramedCloud("chicken",
feature.geometry.getBounds().getCenterLonLat(),
new OpenLayers.Size(100,100),
content,
null, true, onKMLPopupClose);
feature.popup = popup;
map.addPopup(popup);
}
function onKMLUnselect(event) {
var feature = event.feature;
if(feature.popup) {
map.removePopup(feature.popup);
feature.popup.destroy();
delete feature.popup;
}
}
Any help would be greatly appreciated.
Thanks,
I had the same problem a while ago. You can find good example about this from Openlayers Examples: OpenLayers Select Feature on Multiple Layers Example.
Here is main parts of the code:
var map, selectControl;
function init(){
map = new OpenLayers.Map('map');
var wmsLayer = new OpenLayers.Layer.WMS(
"OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0",
{layers: 'basic'}
);
var vectors1 = new OpenLayers.Layer.Vector("Vector Layer 1");
var vectors2 = new OpenLayers.Layer.Vector("Vector Layer 2");
map.addLayers([wmsLayer, vectors1, vectors2]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
selectControl = new OpenLayers.Control.SelectFeature(
[vectors1, vectors2]
);
map.addControl(selectControl);
selectControl.activate();
map.setCenter(new OpenLayers.LonLat(0, 0), 3);
vectors1.addFeatures(createFeatures());
vectors2.addFeatures(createFeatures());
vectors1.events.on({
"featureselected": function(e) {
showStatus("selected feature "+e.feature.id+" on Vector Layer 1");
},
"featureunselected": function(e) {
showStatus("unselected feature "+e.feature.id+" on Vector Layer 1");
}
});
vectors2.events.on({
"featureselected": function(e) {
showStatus("selected feature "+e.feature.id+" on Vector Layer 2");
},
"featureunselected": function(e) {
showStatus("unselected feature "+e.feature.id+" on Vector Layer 2");
}
});
}
this is what i do to have all features on CHOSEN layers become selectable calling to popup windows:
var selectStop = new OpenLayers.Control.SelectFeature([layerKMLClient, layerKMLStops, layerKMLTarget],{onSelect: onFeatureSelect, onUnselect: onFeatureUnselect});
layerKMLStops.events.on({
"featureselected": onFeatureSelect,
"featureunselected": onFeatureUnselect
});
layerKMLClient.events.on({
"featureselected": onFeatureSelect,
"featureunselected": onFeatureUnselect
});
layerKMLTarget.events.on({
"featureselected": onFeatureSelect,
"featureunselected": onFeatureUnselect
});
map.addControl(selectStop);
selectStop.activate();
note that i have multiple other layers (mostly from KML files, but also some vector layers from txt files) whose features are NOT SELECTABLE. you can change your behaviors by customizing the onFeatureSelect for each layer type.
BONUS: if you decide to use cluster strategy on (some of) your layers (believe me, you will, at some point in time) - include a check for feature.cluster being true inside your onFeatureSelected function statement:
function onFeatureSelect(event) {
var feature = event.feature;
if (feature.cluster) {