I have created a map using layers I added in GeoServer.
I created GetFeatureInfoUrl function to get the attribute table when clicking on layer.
But when I click on the map, all the info boxes of all the layers show up. Even if a layer (which is on top of another layer) is turned off, its attribute information comes up.
How can I make it so that only one info box shows up at a time? (So if two layers are on top of each other and user click on the map, the attribute information of the layer which is on top of the other will show up.)
One user online explained me how to do it but did not provide code. He offered the following explanation:
loop over the layers list
call get("visible") on each layer to get the visibility status set by the layer switcher
for each visible layer, append its name to a list of visible layer names
join the list of visible layer names into a single string containing comma-separated layer names
pass the string of comma-separated visible layer names as an additional parameter QUERY_LAYERS in the map in the last argument of testSource.getGetFeatureInfoUrl"
How can I create this code?
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Map</title>
<link rel="stylesheet" href="http://openlayers.org/en/v3.12.1/css/ol.css">
<link rel="stylesheet" href="ol3-layerswitcher.css">
<script src="http://openlayers.org/en/v3.12.1/build/ol.js"></script>
<script src="ol3-layerswitcher.js"></script>
</head>
<body>
<div id="map" style="width:100%;"></div>
<script src="javascript4.js"></script>
<div id="info2"></div>
<div id="info3"></div>
</body>
</html>
JavaScript:
var testSource2 = new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/wms',
params: {'LAYERS': 'Marine:Great_Britain', 'TILED': true},
serverType: 'geoserver'
});
var testSource3 = new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/wms',
params: {'LAYERS': 'Marine:Bedrock_Geology', 'TILED': true},
serverType: 'geoserver'
});
var layers = [
new ol.layer.Tile({
source: new ol.source.MapQuest({layer: 'osm'})
}),
new ol.layer.Group({
title: 'Layers',
layers: [
//Implementing layers
new ol.layer.Tile({
title: 'Great Britain',
source: testSource2
}),
new ol.layer.Tile({
title: 'Geology - Bedrock',
source: testSource3
}),
]
})
];
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center: [51480.6, 7216744.2], //UK
zoom: 5
})
});
//Function to get features from layer
map.on('singleclick', function(evt) {
document.getElementById('info2').innerHTML = '';
viewResolution = map.getView().getResolution();
var url = testSource2.getGetFeatureInfoUrl(
evt.coordinate, viewResolution, 'EPSG:3857',
{'INFO_FORMAT': 'text/html'});
if (url) {
document.getElementById('info2').innerHTML =
'<iframe seamless src="' + url + '"></iframe>';
}
});
//Function to get features from layer
map.on('singleclick', function(evt) {
document.getElementById('info3').innerHTML = '';
viewResolution = map.getView().getResolution();
var url = testSource3.getGetFeatureInfoUrl(
evt.coordinate, viewResolution, 'EPSG:3857',
{'INFO_FORMAT': 'text/html'});
if (url) {
document.getElementById('info3').innerHTML =
'<iframe seamless src="' + url + '"></iframe>';
}
});
//Layer switcher to turn layers on and off
var layerSwitcher = new ol.control.LayerSwitcher({
tipLabel: 'Legend'
});
map.addControl(layerSwitcher);
I believe the issue lies that you do not check in your map.on('singleclick') for what layer is being clicked on. Create a single map.on('singleclick') handler, since your code in both listeners are exactly the same.
map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
var source = layer.getSource();
...
});
Now you will get the source for the layer that you have clicked on and be able to use that with your mentioned code.
Related
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 ?
I have a WFS layer:
var sourceVector = new ol.source.Vector({
format: new ol.format.GeoJSON(),
url: function(extent) {
return 'http://myserver:8080/geoserver/wfs?service=WFS&' +
'version=1.1.0&request=GetFeature&typename=mygroup:mylayer&' +
'outputFormat=application/json&srsname=EPSG:4326&';
},
});
var layerVector = new ol.layer.Vector({
source: sourceVector
});
I have a interaction select for the features:
var interactionSelect = new ol.interaction.Select({
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#EAEA1A'
})
})
});
And, programmatically I selected one feature:
var listenerKey = sourceVector.on('change', function(e) {
if (sourceVector.getState() == 'ready') {
interactionSelect.getFeatures().clear()
interactionSelect.getFeatures().push(sourceVector.getFeatureById('mylayer.1853'))
map.addInteraction(interactionSelect);
}
});
How can I leave that feature already selected and disable the other features from the same wfs layer? I did this way so far because there's only one feature selected at the beginning, but also I want to let the user modify that feature, but it has to be that particular feature; with this code above, the user gets the feature selected in red but he can select other features
How can I do this?
If you just want to modify a subset of the features of a source, what you can do is set features property instead of source of the modify interaction. In that way you control which are the features that can be modified.
Take a look at the example I made for you. It uses the countries.geojson source of OL. I pick Uruguay as the only feature that can be modified.
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.3.1/css/ol.css"
type="text/css">
<style>
.map {
height: 400px;
width: 100%;
}
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.3.1/build/ol.js"></script>
<title>OpenLayers example</title>
</head>
<body>
<div id="map" class="map"></div>
<script type="text/javascript">
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var modifyFeatures = new ol.Collection();
var source = new ol.source.Vector({
url: 'https://openlayers.org/en/latest/examples/data/geojson/countries.geojson',
format: new ol.format.GeoJSON(),
wrapX: false
});
source.on('change', function(e) {
if (source.getState() === 'ready') {
var feature = source.getFeatures().find(f => f.get('name') === 'Uruguay');
modifyFeatures.push(feature);
}
});
var vector = new ol.layer.Vector({
source
});
var select = new ol.interaction.Select({
wrapX: false
});
var modify = new ol.interaction.Modify({
features: modifyFeatures
});
var map = new ol.Map({
interactions: ol.interaction.defaults().extend([select, modify]),
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([-55.75, -32.85]),
zoom: 6
})
});
</script>
</body>
</html>
the example shows how to bind a button click event to the canvas and then it returns the image Example. How can I change it, that when I use a call openlayers with a permalink, that it automatically returns me that image? I would like to use a simple get request from an c++ programm to get the image. I have the
e.g. "#map=12/1085115.28/6035092.46/0" as parsing parameters. Any ideas?
Thanks and Greetings
Melina
So far I have the parameter parsing
<!DOCTYPE html>
<html>
<head>
<title>OpenStreetMap</title>
<link rel="stylesheet" href="https://openlayers.org/en/v4.2.0/css/ol.css" type="text/css">
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<script src="https://openlayers.org/en/v4.2.0/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
var center = [0,0];
var zoom = 0;
var rotation = 0;
if (window.location.has !== '')
{
var hash = window.location.hash.replace('#map=', '');
var parts = hash.split('/');
console.log (parts);
if (parts.length === 4)
{
zoom = parseInt(parts[0],10);
center = [
parseFloat(parts[1]),
parseFloat(parts[2])
];
rotation = parseFloat(parts[3]);
var rotation = 0;
}
}
var openStreetMapLayer = new ol.layer.Tile({
source: new ol.source.OSM({
attributions: [
'All maps © openStreetMapLayer',
ol.source.OSM.ATTRIBUTION
],
opaque: false,
// url: '<myosmserver>/hot/{z}/{x}/{y}.png'
})
});
var map = new ol.Map({
layers: [
openStreetMapLayer
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
maxZoom: 20,
center: center,
zoom: zoom
})
});
</script>
</body>
</html>
You cannot add a link that will somehow download the map as an image. You will need to render it somewhere.
This is how it works. When Openlayers renders the map, it renders it in a HTML canvas element. The download feature is not a Openlayers feature but a HTML canvas feature. The canvas has API to take a snapshot of the current canvas. You can download it as an image.
You can either render the map in a browser or render it server-side. I have not tried rendering the Openlayers map on the server but it should be possible.
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>