OL3: GetFeature from Layers by Coordinate - javascript

I want to get the Feature of a Layer by coordinate.
Furthermore I want to open this feature in a popup, which I have solved so far by an onclick event. But I want to realize by giving the coordinates of a feature and opening the popup of the featue.
I have a layer with the map and a layer with the features:
if (trackMap != null) {
for (var i = 0; i < trackMap.length; i++) {
var trackInfo = trackMap[i];
lat = parseFloat(trackInfo.lat);
lon = parseFloat(trackInfo.lon);
var layergpx = new ol.layer.Vector({
source: new ol.source.Vector({
parser: new ol.parser.GPX(),
url: '${contextRoot}/gps/gpx2' + trackInfo.url
})
});
layers.push(layergpx);
}
}
I want to get the feature of this layer in another Javascript function.
How I open a pop up by clicking on the map:
/**
* The Click Event to show the data
*/
var element = document.getElementById('popup');
var popup = new ol.Overlay({
element: element,
positioning: ol.OverlayPositioning.BOTTOM_CENTER,
stopEvent: false
});
map.addOverlay(popup);
map.on('singleclick', function(evt) {
map.getFeatures({
pixel: evt.getPixel(),
layers: vectorLayers,
success: function(layerFeatures) {
var feature = layerFeatures[0][0];
if (feature) {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
popup.setPosition(coord);
$(element).popover({
'placement': 'top',
'html': true,
'content': feature.get('desc')
});
$(element).popover('show');
} else {
$(element).popover('destroy');
}
}
});
});
But I want this feature not to be opened by clicking on it on the map, but by entering a coordinate in a textfield and the map opens this pop up, like in the onclick event.

Take a look at this example to see if it helps you:
http://openlayers.org/en/latest/examples/kml.html
var displayFeatureInfo = function(pixel) {
map.getFeatures({
pixel: pixel,
layers: [vector],
success: function(featuresByLayer) {
var features = featuresByLayer[0];
var info = [];
for (var i = 0, ii = features.length; i < ii; ++i) {
info.push(features[i].get('name'));
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
}
});
map.getFeatures() has this success callback where it delivers the features of the layers specified in layers: [vector]. Customize it at will to get what you need.
=== Update ===
In the OpenLayers 3's Map object you have a function: getPixelFromCoordinate
/**
* #param {ol.Coordinate} coordinate Coordinate.
* #return {ol.Pixel} Pixel.
*/
ol.Map.prototype.getPixelFromCoordinate = function(coordinate) {
var frameState = this.frameState_;
if (goog.isNull(frameState)) {
return null;
} else {
var vec2 = coordinate.slice(0, 2);
return ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, vec2, vec2);
}
};

Related

Why the information I save in an event is being lost

good morning
I am trying to use Leaflet to paint an image and save the figures, I have created the following method to start the map:
initmap()
{
var url = 'http://kempe.net/images/newspaper-big.jpg';
var bounds = [[-26.5,-25], [1021.5,1023]];
var image = L.imageOverlay(url, bounds);
var map = new L.Map('image-map', {layers: [image], crs: L.CRS.Simple, minZoom : -5});
map.fitBounds(bounds);
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
position: 'topright',
draw: {
polyline: true,
polygon: false,
circle: false,
marker: true
},
edit: {
featureGroup: drawnItems,
remove: true
}
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'marker') {
layer.bindPopup('A popup!');
}
drawnItems.addLayer(layer);
var type = e.layerType
var shape = layer.toGeoJSON()
var shape_to_json = JSON.stringify(shape);
this.currentMap = shape_to_json;
});
map.on(L.Draw.Event.EDITED, function (e) {
var layers = e.layers;
var countOfEditedLayers = 0;
layers.eachLayer(function (layer) {
countOfEditedLayers++;
});
});
}
As you can see there is an event definition for what to do when someone is drawing, (map.on(L.Draw.Event.CREATED, function (e) )
The point is that I log that method, with console.log to check what is saving in
this.currentMap = shape_to_json;
As you can see:
shape_to_json
installations-management.component.ts:120 {"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[421.202413,315.527832],[421.202413,756.416626],[988.101415,756.416626],[988.101415,315.527832],[421.202413,315.527832]]]}}
Everything seems ok. But when I click on the save button, the data of this.currentMap is missing, and in no other point is being modifidied.So, am I saving it wrong or is modified somewhere because of the event?
Thank you all !

OperLayers: display markers while fetching them

I'm fetching via ajax requests a few thousands points to display on an OSM map.
I get 100 points for each call.
I would like them to show up on the map as soon as they are fetched, while now they show up only when all the calls (around 20) are done.
Here is my code:
var source = new ol.source.OSM();
var map_layer = new ol.layer.Tile({
source: source
});
var map_view = new ol.View({
center: ol.proj.fromLonLat([12, 44]),
zoom: 9
});
var map = new ol.Map({
layers: [map_layer, ],
target: 'map-canvas',
view: map_view
});
var target = $(map.getTargetElement());
target.width(window.innerWidth);
target.height(window.innerHeight * 0.9);
map.updateSize();
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var closer = document.getElementById('popup-closer');
var overlay = new ol.Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250
}
});
map.addOverlay(overlay);
closer.onclick = function() {
overlay.setPosition(undefined);
closer.blur();
return false;
};
var markers = new ol.Collection([], {unique: true})
var vectors = new ol.source.Vector({
features: markers
});
var style = new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: 'pin.jpg'
})
});
var layer = new ol.layer.Vector({
source: vectors,
style: style,
});
map.addLayer(layer);
var data = [];
var url = 'some_url';
while (url) {
$.ajax({
async: false,
url: url
}).done(function(data) {
url = data.next;
var res = data.results;
for (var i=0; i<res.length; i++) {
if (res[i].latitude & res[i].longitude) {
var point = ol.proj.fromLonLat([res[i].longitude, res[i].latitude]);
var feature = new ol.Feature({
geometry: new ol.geom.Point(point),
latitude: res[i].latitude,
longitude: res[i].longitude,
})
markers.extend([feature, ]);
}
}
map.render();
});
}
Last instruction you can see (map.render()) shouldn't do the trick? It isn't.
map.renderSync() isn't helping too.
It would be perfect if it could start rendering the map tiles while fetching those points, now even tiles are rendered after fetching is complete.
Looks like the good strategy here is recursion. Simply doing async calls isn't enough.
While each call is async (this letting the map start rendering as soon as possible) the next call is done after showing markers from the previous one.
var url = 'some_url';
var limit = 100;
var offset = 0;
var total_count = 2000;
fetch_data(offset);
function fetch_data(offset) {
$.ajax({
url: encodeURI(url + '?limit=' + limit + '&offset=' + offset)
}).done(function(data) {
var res = data.results;
for (var i=0; i<res.length; i++) {
if (res[i].latitude & res[i].longitude) {
var point = ol.proj.fromLonLat([res[i].longitude, res[i].latitude]);
var feature = new ol.Feature({
geometry: new ol.geom.Point(point),
latitude: res[i].latitude,
longitude: res[i].longitude,
})
markers.extend([feature, ]);
}
}
offset += limit;
if (offset < total_count) {
fetch_data(offset);
}
});
}

How to assign text from object to marker with same index and show relevant text after click to relevant marker? (Openlayers map)

I use OpenLayers map and I would like to get some text from var informations after click to marker. I think thant I need to have "general" function about singleclick in cycle for, because I need do it for each index [i]. But If I click to some marker I don´t see any information.
I tried to move "general" function to down before console.table(window.phpdata.markers). When I run it, I can see last text from "informations" after click on each marker --> there isn´t any problem with getting data from databases.
(This isn´t required result because I don´t want to see last marker. I would like to see relevant text from informations for relevant marker. So with same index [i].
What to do for this result? Thank´s
Object.defineProperty(window.phpdata, "markers", {value: <?php echo !empty($list) ? json_encode($list) : 'null'; ?>, writable: false, configurable: false});
var base = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
target: 'map',
layers: [base],
view: new ol.View({
center: ol.proj.fromLonLat([-74.0061,40.712]), zoom: 2
})
});
var vectors = new ol.source.Vector({});
if(window.phpdata.markers && window.phpdata.markers.length > 0) {
var data = window.phpdata.markers;
for(var i = 0; i < data.length; i++) {
var informations = data[i].info;
var marker = new ol.Feature({
geometry: new ol.geom.Point(
ol.proj.fromLonLat([Number(data[i].lng), Number(data[i].lat)])
),
});
marker.setStyle(new ol.style.Style({
image: new ol.style.Icon(({
src: 'dot.png'
}))
}));
vectors.addFeature(marker);
marker.on('singleclick', function (event) { //general function to move..
if (map.hasFeatureAtPixel(event.pixel) === true) {
document.getElementById("www").innerHTML=informations;
} else {
overlay.setPosition(undefined);
closer.blur();
}
}); // //to there
}
var layer = new ol.layer.Vector({
source: vectors,
});
map.addLayer(layer);
}
document.getElementById('myposition').innerText = "click to map and get coordinates here";
map.on('singleclick', event => {
const coordinate = ol.proj.toLonLat(event.coordinate);
//const innerText = `Lon: ${coordinate[0].toFixed(4)}, Lat: ${coordinate[1].toFixed(4)}`;
const innerText = `${coordinate[0].toFixed(4)}, ${coordinate[1].toFixed(4)}`;
document.getElementById('myposition').innerText = innerText;
});
map.on('pointermove', function(evt) {
map.getTargetElement().style.cursor = map.hasFeatureAtPixel(evt.pixel) ? 'pointer' : '';
});
console.table(window.phpdata.markers)
Features do not have click events. You need to check if there are features where you click the map. You can add the info as a property to the feature and use get() to display it.
for(var i = 0; i < data.length; i++) {
var marker = new ol.Feature({
geometry: new ol.geom.Point(
ol.proj.fromLonLat([Number(data[i].lng), Number(data[i].lat)])
),
info: data[i].info
});
}
....
....
map.on('click', function(evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature) {
return feature;
});
if (feature) {
document.getElementById("www").innerHTML = feature.get('info');
....
};
});

Unable to drag and drop marker on map using PIXI JS and Leaflet Mapping API

I have loaded Markers Or Lat/Long Points on Map using Leaflet.PixiOverlay
Now I have two requirements
Drag and Drop an existing marker/point (PIXI.Sprite) on to the other marker/point
Drag and Drop an existing marker/point (PIXI.Sprite) to another location on map.
I have tried following approach but it is not working
/**
* In the below for loop identify the marker on which mouse down event has occurred.
* In the 'mouseup' event on map, change the position of the selected marker
*
* **Marker here is PIXI.Sprite**
*/
for (var i = 0; i < markersLength; i++) {
var marker = new PIXI.Sprite(texture);
marker.interactive = true;
marker.buttonMode = true;
marker.on('mousedown', (event) => {
self.map.dragging.disable();
marker['dragging'] = true;
self.selectedMarker = marker;
console.log('event mousedown on marker ', event);
});
marker.on('mouseup', (event) => {
marker['dragging'] = false;
console.log('event mouseup on marker ', event);
});
marker.on('mouseupoutside', (event) => {
//marker['dragging'] = false;
console.log('event mouseupoutside on marker ', event);
self.map.dragging.enable();
})
markerArr.push(marker);
childContainer.addChild(marker);
}
/**
* mouseup event on map should move selected marker but it doesnot
*
*/
this.map.on('mouseup', (event) => {
if (this.selectedMarker) {
let markerCoords = this.latLngToLayerPoint(event.latlng, this.map.getZoom());
this.selectedMarker.position.x = markerCoords.x;
this.selectedMarker.position.y = markerCoords.y;
this.selectedMarker = null;
}
})
In order to drag or move the marker with mouse move I did following.
First created PIXI Overlay Layer
var pixiOverlayOptions = {
doubleBuffering: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window['MSStream'],
autoPreventDefault: false,
}
var pixiContainer = new PIXI.Container();
pixiContainer.interactive = true;
pixiContainer.buttonMode = true;
var pixiOverlay = L.pixiOverlay(function (utils, event) {
var zoom = utils.getMap().getZoom();
var container = utils.getContainer();
var renderer = utils.getRenderer();
var project = utils.latLngToLayerPoint;
var layerPointToLatLng = utils.layerPointToLatLng;
var getScale = utils.getScale;
var invScale = 1 / getScale();
var interaction = renderer.plugins.interaction;
switch (event.type) {
// all other events
...
case 'add':{
var texture = resources['markerImage']
var markerSprite = new PIXI.Sprite(texture);
invScale = 1 / getScale(utils.getMap().getZoom());
markerSprite.scale.set(invScale);
markerSprite.on('mousedown', (event) => {
this.mouseDownOnPoint(event, this);
});
container.addChild(markerSprite);
renderer.render(container);
}
break;
case 'pointDrag': {
let latlng = event.latlng;
let newCoords = project([latlng.lat, latlng.lng]);
let marker = container.children[0];
marker.x = newCoords.x;
marker.y = newCoords.y;
invScale = 1 / getScale(utils.getMap().getZoom());
marker.scale.set(invScale);
renderer.render(container);
}
break;
}
},pixiContainer, pixiOverlayOptions);
pixiOverlay.addTo(map);
Here few things are important
Marking Pixi Container with interactive as true and buttonMode as true
Adding autoPreventDefault as false in layer option object, which is passed to layer
Adding a 'mousedown' event handler, which actually marks the marker which is clicked.
Define function for handling 'mousedown' event on marker, see map dragging is disabled here
function mouseDownOnPoint(event, self) {
self.map.dragging.disable();
self.mouseDownOnSelectedMarker = event.currentTarget;
}
Define function for redrawing layer
function redraw(data: { type: PixiLayerEvents, [key: string]: any }) {
pixiOverlay.redraw(data);
}
Add 'mousemove' handler on map, which actually calls redraw function with 'pointDrag' event
map.on('mousemove', (event: L.LeafletMouseEvent) => {
if (this.mouseDownOnSelectedMarker) {
this.redraw({ type: 'pointDrag', latlng: event.latlng });
}
});
Inside 'pointDrag' event, marker position is continuously changed along with mouse move on map.
Once the 'mouseup' event occurs, just mark the 'mouseDownOnSelectedMarker' as null and enable map dragging
map.on('mouseup', (event: L.LeafletMouseEvent) => {
if (this.mouseDownOnSelectedMarker) {
this.mouseDownOnSelectedMarker = null;
}
map.dragging.enable();
})

Google Maps Polygons self intersecting detection

I'm trying to implement a polygon self intersection algorithm from Google Maps API V3 polygons.
The goal is just to detect if yes or no, a simple polygon drawn by the user is self crossing.
I have found this very interesting link, but it assumes that coordinates of the polygon's vertices are given on geoJSON format. However, this isn't my case ; I'm only able to retrieve polygons coordinates using polygon.getPath() into a polygoncomplete event.
This is how i retrieve the coordinates :
google.maps.event.addDomListener(drawingManager, 'polygoncomplete', function(polygon)
{
var polygonBounds = polygon.getPath();
var coordinates = [];
for(var i = 0 ; i < polygonBounds.length ; i++)
{
vertice = {
"Latitude" : polygonBounds.getAt(i).lat(),
"Longitude" : polygonBounds.getAt(i).lng()
}
coordinates.push(vertice );
}
}
How can I transform these coordinates, given by polygon.getpath() into geoJSON format ?
Is there any better way to detect if a Google Maps polygon is self-intersecting ? If so, could you please share some code sample and not just a mathematical explaination ?
PS : I've seen this link but without any code sample, I'm a little bit lost.
You don't need to convert them to GeoJSON to use the jsts library, you need to convert them from google.maps.LatLng objects to jsts.geom.Coordinates. Instead of using this:
var geoJSON2JTS = function(boundaries) {
var coordinates = [];
for (var i = 0; i < boundaries.length; i++) {
coordinates.push(new jsts.geom.Coordinate(
boundaries[i][1], boundaries[i][0]));
}
return coordinates;
};
Use this, which will convert coordinates in a google.maps.Polygon path to the JTS format:
var googleMaps2JTS = function(boundaries) {
var coordinates = [];
for (var i = 0; i < boundaries.getLength(); i++) {
coordinates.push(new jsts.geom.Coordinate(
boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
}
return coordinates;
};
then change "findSelfIntersects" like this:
/**
* findSelfIntersects
*
* Detect self-intersections in a polygon.
*
* #param {object} google.maps.Polygon path co-ordinates.
* #return {array} array of points of intersections.
*/
var findSelfIntersects = function(googlePolygonPath) {
var coordinates = googleMaps2JTS(googlePolygonPath);
var geometryFactory = new jsts.geom.GeometryFactory();
var shell = geometryFactory.createLinearRing(coordinates);
var jstsPolygon = geometryFactory.createPolygon(shell);
// if the geometry is aleady a simple linear ring, do not
// try to find self intersection points.
var validator = new jsts.operation.IsSimpleOp(jstsPolygon);
if (validator.isSimpleLinearGeometry(jstsPolygon)) {
return;
}
var res = [];
var graph = new jsts.geomgraph.GeometryGraph(0, jstsPolygon);
var cat = new jsts.operation.valid.ConsistentAreaTester(graph);
var r = cat.isNodeConsistentArea();
if (!r) {
var pt = cat.getInvalidPoint();
res.push([pt.x, pt.y]);
}
return res;
};
proof of concept fiddle (credit to HoffZ)
code snippet:
var mapOptions = {
zoom: 16,
center: new google.maps.LatLng(62.1482, 6.0696)
};
var drawingManager = new google.maps.drawing.DrawingManager({
drawingControl: false,
polygonOptions: {
editable: true
}
});
var googleMaps2JTS = function(boundaries) {
var coordinates = [];
for (var i = 0; i < boundaries.getLength(); i++) {
coordinates.push(new jsts.geom.Coordinate(
boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
}
coordinates.push(coordinates[0]);
console.log(coordinates);
return coordinates;
};
/**
* findSelfIntersects
*
* Detect self-intersections in a polygon.
*
* #param {object} google.maps.Polygon path co-ordinates.
* #return {array} array of points of intersections.
*/
var findSelfIntersects = function(googlePolygonPath) {
var coordinates = googleMaps2JTS(googlePolygonPath);
var geometryFactory = new jsts.geom.GeometryFactory();
var shell = geometryFactory.createLinearRing(coordinates);
var jstsPolygon = geometryFactory.createPolygon(shell);
// if the geometry is aleady a simple linear ring, do not
// try to find self intersection points.
var validator = new jsts.operation.IsSimpleOp(jstsPolygon);
if (validator.isSimpleLinearGeometry(jstsPolygon)) {
return;
}
var res = [];
var graph = new jsts.geomgraph.GeometryGraph(0, jstsPolygon);
var cat = new jsts.operation.valid.ConsistentAreaTester(graph);
var r = cat.isNodeConsistentArea();
if (!r) {
var pt = cat.getInvalidPoint();
res.push([pt.x, pt.y]);
}
return res;
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
drawingManager.setDrawingMode(google.maps.drawing.OverlayType.POLYGON);
drawingManager.setMap(map);
google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
//var polyPath = event.overlay.getPath();
var intersects = findSelfIntersects(polygon.getPath());
console.log(intersects);
if (intersects && intersects.length) {
alert('Polygon intersects itself');
} else {
alert('Polygon does not intersect itself');
}
});
#map {
width: 500px;
height: 400px;
}
<script src="https://maps.google.com/maps/api/js?libraries=drawing&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/1.4.0/jsts.min.js"></script>
<p>
Draw a polygon on the map
</p>
<div id="map">
</div>

Categories

Resources