Interactive features in vector layers in openlayers 5 - vector

I am using openlayers 5.1.3 and I confused as to how to create the functionality of clicking on a feature of a vector layer, get exactly the one I clicked and then get its properties. I am following this example that is the only relevant I found.
I have an empty vector source that gets GeoJSON data after search
initialize the map and the vector
this.vectorsource = new VectorSource({});
this.vectorlayer = new VectorLayer({
source: this.vectorsource
});
var selectClick = new Select({
condition: click
});
this.olmap.addInteraction(selectClick);
selectClick.on('select', function(e) {
console.log(e.target);
});
after the search
this.vectorsource.clear();
const fff = (new GeoJSON()).readFeatures(data.data);
this.vectorsource.addFeatures(fff);
The selectClick and addInteraction are the closest I got to what I want. I dont know how to proceed and I dont know if this is the right combination of methods to get the specific feature I clicked, so then I can get its properties. Also, what is weird to me is that I dont see any getFeature (not plular) method or functionality for vector layers.
How can I proceed?
Thanks

e.target (where e is the argument of the select callback function) has a getFeatures() method.
The code below will return the (first) selected feature:
var selectClick = new ol.interaction.Select({
condition: ol.events.condition.click
});
this.olmap.addInteraction(selectClick);
selectClick.on('select', function(e) {
var selectedFeatures = e.target.getFeatures().getArray();
var featuresStr = selectedFeatures[0].get('name');
console.log(featuresStr);
});
proof of concept example
code snippet:
var raster = new ol.layer.Tile({ // TileLayer({
source: new ol.source.OSM()
});
var vector = new ol.layer.Vector({ // VectorLayer({
source: new ol.source.Vector({ // VectorSource({
url: 'https://cdn.rawgit.com/johan/world.geo.json/master/countries.geo.json',
format: new ol.format.GeoJSON()
})
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
var selectClick = new ol.interaction.Select({
condition: ol.events.condition.click
});
map.addInteraction(selectClick);
selectClick.on('select', function(e) {
var selectedFeatures = e.target.getFeatures().getArray();
var featureStr = "none";
if (!!selectedFeatures && selectedFeatures.length > 0) {
featureStr = selectedFeatures[0].get('name');
}
console.log(featureStr);
document.getElementById('status').innerHTML = featureStr;
});
html,
body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
.map {
height: 95%;
width: 100%;
}
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.1.3/build/ol.js"></script>
<div id="status"></div>
<div id="map" class="map"></div>

Related

OpenLayers: Move vector label with mouse

I'm using OpenLayers V4 and I'm trying to see if it's possible to allow a user to click on a feature's vector label and move/drag it to a location of their choice. My initial thought was to capture when a user clicked on the label, and then dynamically calculate and set the offsetX and offsetY properties of the label (ol.style.Text) as the user's mouse pointer moved around. To achieve this, I need to capture when the user clicks on the label and not the feature itself. The main problem is that I can't find a way to distinguish this. It appears as though the label is part of the vector feature because clicking on the feature highlights both the feature and the label and vice versa.
In summary, my question is two fold:
Does anyone have an idea how to create a user draggable vector label in OpenLayers 4?
Is there a way to detect/distinguish between a user clicking on a vector feature itself, or the vector label.
Note: I'm familiar with overlays and realize they might be easier to work with since they have a setPosition property, but the way my web map is constructed I need to display vector labels for each feature and not overlays
It is possible using vector labels in OpenLayers 6 where the modify interaction has access to the features being modified in its style function and can use hit detection of offset labels, but that is not available in version 4. In this example labels move with their features, but can also be moved independently of the features. Clones are needed to avoid changing feature geometries while moving the labels. The labels geometries are then restored and replaced with offsets for styling. When a feature is moved its label clone is kept in sync.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.5.0/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.5.0/build/ol.js"></script>
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<script>
var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;
var modifyStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: width * 2,
fill: new ol.style.Fill({
color: blue
}),
stroke: new ol.style.Stroke({
color: white,
width: width / 2
})
}),
zIndex: Infinity
});
var labelStyle = new ol.style.Style({
text: new ol.style.Text({
offsetY: 10,
font: '12px Calibri,sans-serif',
fill: new ol.style.Fill({
color: '#000',
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 3,
}),
backgroundFill: new ol.style.Fill({
color: 'rgba(0 ,0, 0, 0)',
}),
}),
});
var featureLayer = new ol.layer.Vector({
source: new ol.source.Vector({
url: 'https://mikenunn.net/data/world_cities.geojson',
format: new ol.format.GeoJSON(),
}),
});
var labelLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
renderBuffer: 1e3,
style: function (feature) {
labelStyle.getText().setOffsetX(feature.get('offsetX') || 0);
labelStyle.getText().setOffsetY((feature.get('offsetY') || 0) - 10);
labelStyle.getText().setText(feature.get('CITY_NAME'));
return labelStyle;
},
});
featureLayer.getSource().on('addfeature', function(event) {
var id = event.feature.getId();
var feature = event.feature.clone();
feature.setId(id);
labelLayer.getSource().addFeature(feature);
});
featureLayer.getSource().on('removefeature', function(event) {
var id = event.feature.getId();
var source = labelLayer.getSource();
source.removeFeature(source.getFeatureById(id));
});
var defaultStyle = new ol.interaction.Modify({
source: featureLayer.getSource()
}).getOverlay().getStyleFunction();
var featureModify = new ol.interaction.Modify({
source: featureLayer.getSource(),
style: function(feature) {
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
var geometry = feature.getGeometry().clone();
labelLayer.getSource().getFeatureById(id).setGeometry(geometry);
});
return defaultStyle(feature);
}
});
var labelModify = new ol.interaction.Modify({
source: labelLayer.getSource(),
hitDetection: labelLayer,
style: function(feature) {
var styleFeature;
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
styleGeometry = featureLayer.getSource().getFeatureById(id).getGeometry();
});
modifyStyle.setGeometry(styleGeometry);
return modifyStyle;
}
});
labelModify.on('modifyend', function(event) {
event.features.forEach( function(feature) {
var id = feature.getId();
var labelCoordinates = feature.getGeometry().getCoordinates();
var geometry = featureLayer.getSource().getFeatureById(id).getGeometry().clone();
var featureCoordinates = geometry.getCoordinates();
var resolution = map.getView().getResolution();
var offsetX = (labelCoordinates[0] - featureCoordinates[0]) / resolution + (feature.get('offsetX') || 0);
var offsetY = (featureCoordinates[1] - labelCoordinates[1]) / resolution + (feature.get('offsetY') || 0);
feature.set('offsetX', offsetX, true);
feature.set('offsetY', offsetY, true);
feature.setGeometry(geometry);
});
});
var map = new ol.Map({
layers: [featureLayer, labelLayer],
interactions: ol.interaction.defaults().extend([labelModify, featureModify]),
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([5, 51]),
zoom: 8
})
});
</script>
</body>
</html>

Delete a layer in Openlayers

I added a markers by adding layer in OpenLayers.
I use it in my asp project and I load a map and I can add multi icons.
Now I load my locations and show them by that icons.
My code is like this:
for (var i = 0; i < loc.length; i++) {
LocationArray.push(loc[i]);
iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat(loc[i])),
name: 'Null Island',
population: 4000,
rainfall: 500
});
iconFeatures.pop();
debugger
iconFeatures.push(iconFeature);
vectorSource = new ol.source.Vector({
features: iconFeatures //add an array of features
});
iconStyle = new ol.style.Style({
image: new ol.style.Icon(({
anchor: [0.5, 100],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
opacity: 0.8,
src: '/images/icon.png',
}))
});
vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: iconStyle
});
map.addLayer(vectorLayer);
}
Now I want to Delete this layer when I click on it.
I can detect where is it clicked and I response it but I don't know how I can delete it.
My code is like this:
map.on('click', function (evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature;
});
if (feature) {
var coordinates = feature.getGeometry().getCoordinates();
if (confirm("Do you want to delete?")) {
// What should I code here?
}
} else {
// .....
}
});
The forEachFeatureAtPixel callback can also receive the layer, if you save that it is easy to remove the layer:
map.on('click', function (evt) {
var vectorLayer;
var feature = map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) {
vectorLayer = layer;
return feature;
});
if (feature) {
var coordinates = feature.getGeometry().getCoordinates();
if (confirm("Do you want to delete?")) {
map.removeLayer(vectorLayer);
}
} else {
// .....
}
});
If you want to keep the layer but remove the markers you can use the clear() method.
vectorLayer.getSource().clear();
Thanks for response
I've found it by map.removeLayer(layer); code

How to show dynamically multiple popup in openlayers 3 map

Can anyone tell me how to show all popup of markers in openlayers 3 map. I searched many sites but couldn't get any answer please anyone know about this then help me
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.TileJSON({
url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure',
crossOrigin: 'anonymous'
})
})
],
overlays: [overlay],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([0, 50]),
zoom: 2
})
});
var vectorSource = new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([16.37, 48.2])),
name: 'London'
}),
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([-0.13, 51.51])),
name: 'NY'
}),
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([30.69, 55.21])),
name: 'Paris'
})
]
});
var markers = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
image: new ol.style.Icon({
src: '//openlayers.org/en/v3.12.1/examples/data/icon.png',
anchor: [0.5, 1]
})
})
});
map.addLayer(markers);
function showpopup(){
// For showing popups on Map
var arrayData = [1];
showInfoOnMap(map,arrayData,1);
function showInfoOnMap(map, arrayData, flag) {
var flag = 'show';
var extent = map.getView().calculateExtent(map.getSize());
var id = 0;
var element = document.getElementById('popup');
var popup = new ol.Overlay({
element: element,
positioning: 'center'
});
map.addOverlay(popup);
if (arrayData != null && arrayData.length > 0) {
arrayData.forEach(function(vectorSource) {
/* logMessage('vectorSource >> ' + vectorSource); */
if (vectorSource != null && markers.getSource().getFeatures() != null && markers.getSource().getFeatures().length > 0) {
markers.getSource().forEachFeatureInExtent(extent, function(feature) {
/* logMessage('vectorSource feature >> ' + feature); */
console.log("vectorSource feature >> " + markers.getSource().getFeatures());
if (flag == 'show') {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
popup.setPosition(coord);
/* var prop;
var vyprop = ""; */
$(element).popover({
'position': 'center',
'placement': 'top',
'template':'<div class="popover"><div class="popover-content"></div></div>',
'html': true,
'content': function() {
var string = [];
var st = feature.U.name;
if (st != null && st.length > 0) {
var arrayLength = 1;
string = "<table>";
string += '<tr><td>' + st + "</table>";
}
return string;
}
});
$(element).popover('show');
} else {
$(element).popover('destroy');
}
});
}
});
}
};
}
I used this code in my file but it show only one popup on all markers please someone tell me how to show all markers popup simultaneously.
I'm not sure exactly what you're trying to show in your popups, but I would probably try this approach. This extends the ol.Overlay class, allowing you to get the map object and attach a listener which you can use to grab the feature that was clicked. Is this what you're trying to accomplish?
function PopupOverlay() {
var element = document.createElement('div');
$(element).popover({
template: '<div class="popover"><div class="popover-content"></div></div>',
placement: 'top',
position: 'center',
html: true
});
ol.Overlay.call(this, {
element: element
});
}
ol.inherits(PopupOverlay, ol.Overlay);
PopupOverlay.prototype.setMap = function (map) {
var self = this;
map.on('singleclick', function (e) {
map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
ol.Overlay.prototype.setPosition.call(self, feature.getGeometry().getCoordinates());
var el = self.getElement();
$(el).data('bs.popover').options.content = function () {
// EDIT THE POPOVER CONTENT
return feature.get('name');
};
$(el).popover('show');
});
});
ol.Overlay.prototype.setMap.call(this, map);
};
Check out this example
So after your comment, I see what you're trying to do now. I would say that you want to take the same basic approach, make a class that overrides ol.Overlay, but this time just loop through all the features, creating an overlay for each feature.
This Updated Example
function PopoverOverlay(feature, map) {
this.feature = feature;
var element = document.createElement('div');
$(element).popover({
template: '<div class="popover"><div class="popover-content"></div></div>',
placement: 'top',
position: 'center',
html: true
});
ol.Overlay.call(this, {
element: element,
map: map
});
};
ol.inherits(PopoverOverlay, ol.Overlay);
PopoverOverlay.prototype.togglePopover = function () {
ol.Overlay.prototype.setPosition.call(this, this.feature.getGeometry().getCoordinates());
var self = this;
var el = this.getElement();
$(el).data('bs.popover').options.content = function () {
// EDIT THE POPOVER CONTENT
return self.feature.get('name');
};
$(el).popover('toggle');
};
// create overlays for each feature
var overlays = (function createOverlays () {
var popupOverlays = [];
vectorSource.getFeatures().forEach(function (feature) {
var overlay = new PopoverOverlay(feature, map);
popupOverlays.push(overlay);
map.addOverlay(overlay);
});
return popupOverlays;
})();
// on click, toggle the popovers
map.on('singleclick', function () {
for(var i in overlays) {
overlays[i].togglePopover();
}
});
Now when you click anywhere on the map, it should call the togglePopover method and toggle the popover on the individual element.

Google Map: Link to the map according to feature type

I have a google Map with markers on a webpage.
Each marker has a unique feature position and type
This is the scenario I would like to put in place:
On another webpage I have static links to different markers of the map.
If you click on one of those links, you are directed to the map in which, one of these markers is centered (and its info window open).
But the markers latitude and longitude might change while the links will never change.
This means, I need the links not to use latitude and longitude info but markers feature type instead (which are remain the same).
How can I do that?
Here is my sample google Map script so far:
<script>
function initMap() {
var mapDiv = document.getElementById('map');
var map = new google.maps.Map(mapDiv, {
center: {lat: 48.85639, lng: 2.33625}, // default centering
zoom: 18,
styles:
[
{featureType: 'poi',stylers: [{ visibility: 'off' }]},
{featureType: 'transit.station',stylers: [{ visibility: "off" }]}
]
});
var features = [
{position: new google.maps.LatLng(48.85659, 2.33555),type: 'markerone'},
{position: new google.maps.LatLng(48.85619, 2.33695),type: 'markertwo'}
];
var icons = {
'markerone': {icon: 'icon_one.png'},
'markertwo': {icon: 'icon_two.png'}
};
var contents= {
'markerone': {text: 'Content 1'},
'markertwo': {text: 'Content 2'}
};
for (var i = 0, feature; feature = features[i]; i++)
{
var marker = new google.maps.Marker({
position: feature.position,
icon: icons[feature.type].icon,
map: map
});
var content = contents[feature.type].text;
var infowindow = new google.maps.InfoWindow()
google.maps.event.addListener(marker,'mouseover', (function(marker,content,infowindow){
return function() {
infowindow.setContent(content);
infowindow.open(map,marker);
};
})(marker,content,infowindow));
google.maps.event.addListener(marker,'mouseout', (function(marker,content,infowindow){
return function() {
infowindow.close(map,marker);
};
})(marker,content,infowindow));
}
}
</script>
In this sample, I have to markers.
One has a feature type of "markerone" and the second is "markertwo".
How can I set my links to redirect and center the map around a specific marker in this kind of fashion:
http://www.mywebsite.com/mymap.php?myvariable=markertwo
Thank you.
First you would have to get the parameters. The example below gets all parameters and put them into an array. There you can search for your certain paramater like "markerType" and check if it's given or not. If not you have to perform a default action, otherwise you can handle the certain markerType like finding the correct marker, setting the map center to it and open the corrosponding infoWindow.
You just have to call the focusMarkerType-method onload of your page.
function getSearchParameters() {
var prmstr = window.location.search.substr(1);
return prmstr != null && prmstr != "" ? transformToAssocArray(prmstr) : {};
}
function transformToAssocArray( prmstr ) {
var params = {};
var prmarr = prmstr.split("&");
for ( var i = 0; i < prmarr.length; i++) {
var tmparr = prmarr[i].split("=");
params[tmparr[0]] = tmparr[1];
}
return params;
}
function focusMarkerType(){
var params = getSearchParameters();
if(params.markerType!=null){
//Handling the certain marker type
var found = false;
for (var i = 0, feature; feature = features[i]; i++) {
if (feature.type == params.markerType) {
found = true;
map.setCenter(feature.position);
//more...
break;
}
}
if (!found) {
console.log("unknown type")
}
}else{
//Handling default behaviour if no marker type is given
}
}

How to dynamically add data to google maps API?

I have got a map that I want to have a feed of lat/long data getting pushed to the map data array. I have the function to get the data, but am having trouble getting that to be usable in the map data array.
The idea is to have a new marker drop in when a new coordinate is added to the array. Any ideas? Thanks in advance!
var ID='0';
var DATA=[];
function getData(){
var url = 'http://us7.fieldagent.net/api/newResponses/';
//url = 'http://us7.fieldagent.net/api/newResponses/;
$.post(url,{'id':ID},function(data){
if(data.status_id == 0){
ID = data.id;
console.log('Last Id: '+data.id);
var new_data = data.responses;
var count = 0
$.each(new_data,function(i,v){
count += 1;
var coord = 'new google.maps.LatLng('+v.lat+','+v.lon+'),';
DATA.push(coord);
})
console.log('Added '+count+' responses..')
}
});
}
$(document).ready(function(){
getData();
setInterval(getData,20*1000);
});
function drop() {
for (var i = 0; i < DATA.length; i++) {
setTimeout(function() {
addMarker();
}, i * 500);
}
}
function addMarker(){
markers.push(new google.maps.Marker({
position: DATA[iterator],
map: map,
draggable: false,
icon: 'fatie.svg',
animation: google.maps.Animation.DROP
}));
iterator++;
}
You need to actually add the item to the map. Right now, you're only adding an item to your DATA array. You need to call addMarker with the new data as well.
You seem to want to add these markers to the map at an interval so they drop onto the map over time, while also being able to query for new markers from your server.
Try code like this:
var ID='0';
var DATA=[];
function getData(){
var url = 'http://us7.fieldagent.net/api/newResponses/';
$.post(url,{'id':ID},function(data){
if(data.status_id == 0){
ID = data.id;
console.log('Last Id: '+data.id);
var new_data = data.responses;
var count = 0
$.each(new_data,function(i,v){
count += 1;
var coord = 'new google.maps.LatLng('+v.lat+','+v.lon+'),';
DATA.push(coord);
});
console.log('Added '+count+' responses..');
if (count > 0) addMarker(); //call addMarker if there are new markers
}
});
}
$(document).ready(function(){
getData();
setInterval(getData,20*1000);
});
function addMarker(){
if (DATA.length == 0) return; //exit if DATA is empty
markers.push(new google.maps.Marker({
position: DATA.shift(), //take the first item in DATA
map: map,
draggable: false,
icon: 'fatie.svg',
animation: google.maps.Animation.DROP
}));
if (DATA.length > 0) setTimeout(addMarker, 500); //call again if needed
}
Create a Method which does two things.
Add to the Array
Add the Item to the map

Resources