Leaflet: how to swap coordinates received from an ajax call - javascript

I am using Leaflet 1.0.3 and a few plugins including Leaflet.ajax. My L.geo.ajax call is working and returning geojson objects, however, the coordinates are reversed. I created a function to fix this:
var convertLatLng = function (latlng) {
var temp = latlng[y];
latlng[y] = latlng[x];
latlng[x] = temp;
convertedLatLng = latlng;
return convertedLatLng;
console.log('this function is running')
}
But my problem is I don't know where to put it. Do I run it inside my geoJson call? If so, where? Here is a snippet of the ajax call:
var geojson = L.geoJson.ajax('http://www.iotwf.com/deployment_map/json', {
pointToLayer: function (feature, latlng) {
convertLatLng(latlng);
...
},
onEachFeature: function(feature, layer) {
...
}
});
I am also open to other suggestions for what may fix it.

Welcome to SO!
First make sure that your coordinates are indeed reversed.
Note that the GeoJSON format expects [longitude, latitude], whereas Leaflet usually expects [latitude, longitude], EXCEPT in the case of L.geoJSON() factory (and the plugin L.geoJson.ajax()), where it automatically reads the GeoJSON order and builds the layers at the correct coordinates.
If your coordinates are still reversed, the appropriate correction would be obviously to correct the order in your data source directly (or whatever service outputs your data), so that you get actually compliant GeoJSON data. That would solve many future headaches.
If that is not possible, then indeed you could try a workaround within your script.
The most appropriate way to do so would probably be to use the coordsToLatLng option of the L.geoJSON factory.
Changing its default implementation, you would get something like:
L.geoJson.ajax(url, {
coordsToLatLng: function (coords) {
// latitude , longitude, altitude
//return new L.LatLng(coords[1], coords[0], coords[2]); //Normal behavior
return new L.LatLng(coords[0], coords[1], coords[2]);
}
});

Related

How to modify or remove some existing data in GeoJSON Leaflet object?

Recently I asked about referencing the data of an existing GeoJSON Leaflet object. My Leaflet map consists of data coming in a stream to my GeoJSON Leaflet object. User inputs can change a filter for the GeoJSON data, so to make the filter apply to both the existing and new data I am keeping track of my data in an array called myFeatures. Whenever the filters change or an item in myFeatures changes, I do the following:
myGeoJson.clearLayers();
myGeoJson.addData(myFeatures);
This is working to make my map update according to the newly updated feature data or the changes in the filter.
I am applying pop-ups to the GeoJSON object when I initialize my GeoJSON object:
var myGeoJson = L.geoJson(myFeatures, {
style: function(feature) {
...
},
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, geojsonMarkerOptions);
},
filter: function(feature, layer) {
...
},
onEachFeature: function(feature, layer) {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
}
});
When I click on an individual feature, the pop-up appears. However, the pop-up dismisses pretty quickly, thanks to clearLayers and addData being called. :(
Is there some kind of way to stop the pop-up dismissing in this situation?
Or - better question - is there a way to modifying existing data in a GeoJSON object or remove some (not all) data from a GeoJSON object?
To provide some context, my GeoJSON shows circle markers for each feature. The circle markers are colored based on a property of the feature. The property can actually change over time, so the marker's styling needs to be updated. A marker also times out after a while and needs to be removed from the map, but the other markers need to stay on the map.
There are for sure better ways to do that, but if you don't want to modify your code architecture too much, you could just create your popups in a specific layer, which you won't clear when you add your new data.
To give you an idea (markers play below the role of myGeoJson in your example):
var popup_id = {};
var popup_layer = new L.layerGroup();
var markers = new L.layerGroup();
$.each(testData, function(index, p) {
var marker = L.marker(L.latLng(p.lat, p.lon));
markers.addLayer(marker);
popup = new L.popup({offset: new L.Point(0, -30)});
popup.setLatLng(L.latLng(p.lat, p.lon));
popup.setContent(p.text);
popup_id[p.id] = popup;
marker.on('click', function() {
popup_id[p.id].openPopup();
popup_layer.addLayer(popup_id[p.id]);
markers.clearLayers();
})
});
popup_layer.addTo(map);
markers.addTo(map);
You also keep track of all your popups in a dictionary popup_id.
Since you haven't provided us with a JSfiddle it is a bit difficult to find the perfect answer for your case, but I hope that the popup layer (also here in my fiddle) gives you a good direction.

How to getPaths() with geoXML3

I had more than 600 kml files to load in a single google map.
Initially I tried with KmlLayer(), but it didn't work due to the amount of kml files, so I found GeoXML3, and it works really fine.
Now I need to retrieve the path's coords for every polygon created with GeoXML3. Here I found the method getPaths() that seems to be just what I'm looking for, but it doesn't work because now I don't create polygons using the class Polygon but using the class geoxml3
for (i=0; i < controlli.length; i++)
{
appo = kmlurl + controlli[i].id + ".kml";
appo = appo.replace(" ", '_');
area[controlli[i].id] = new geoXML3.parser({
map: map,
zoom: false,
});
area[controlli[i].id].parse(appo);
//here I would like to do something like: 'area[controlli[i].id].getPaths()'
}
How can I do this?
The google.maps.Polygon objects created by geoXml3 to represent the KML polygons can be accessed 2 ways:
area[controlli[0].id].docs[0].placemarks[0].polygon.getPath()
working jsfiddle
area[controlli[0].id].docs[0].gpolygons[0].getPath()
working jsfiddle
where geoXml is a reference to the parser object (your area[controlli[i].id])
and i is a sequential reference to the placemarks (or the polygons) in the KML).
If you are using it on a KML file loaded asynchronously, you need to wait for the parsed event, or use the data in the afterParse function.

Plot Centroid of Feature Layer Polygon

Here is the rest service I'm working from:
http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3
My current call for displaying the feature layer is as follows:
var recLayer = new FeatureLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3",{
infoTemplate: recParkTemplate,
outFields: ["STATE_NAME"]
});
map.addLayer(recLayer);
However, instead of plotting the polygon on the map as this is an esriGeometricPolygon. I would rather have it plot on the map like a esriGeometryPoint. I know this method in getting the specific polygon's centroid:
https://developers.arcgis.com/javascript/jsapi/polygon-amd.html#getcentroid
My problem is I can't figure out how to cycle among all polygons in the feature layer and then plot those polygons. I can only point and click and display similar to how this ESRI sample works: https://developers.arcgis.com/javascript/jssamples/util_label_point.html
Thank you for your assistance.
Here is the current site if you would like to take a look at it: http://joshferrell.net/ece_project/
to cycle among all geometries in your feature layer you can do something like this:
recLayer.on("update-end", function changeHandler(evt) {
require(["dojo/_base/array"], function (array) {
array.forEach(recLayer.graphics, function (entry, i) {
console.debug(entry, "at index", i);
});
});
});
inside the loop use the getCentroid and add the result to the map

Edit existing Google Maps element

For a website I'm using Google Maps to add polylines to a map to display a route. Some routes consist of multiple legs (stages) and I'm trying to add the polylines 'on request'. So only if a user chooses to show a leg, it will draw the polyline. Also the user might choose a completely different route and this new set of polylines should be drawn on the map as well.
My problem is that I can't seem to figure out or find how to select an existing map. I start out by creating a map using the following code:
qMap = new google.maps.Map(document.getElementById(mP.target), mapOptions);
mP.target contains a string with the canvas id and mapOptions is just a object with some options, nothing special.
So I do all kind of stuff with qMap, like adding markers, drawing polylines etc. And this shouldn't just be done at map initiation, but also when the user wants to add something. qMap isn't a global variable and I rather not have it being a global either.
I've tried qMap = google.maps.Map(document.getElementById(mP.target)) and other similar methods. With no success. I'm hoping you can help me out finding a way to this without global variables! Thanks!
There are a couple of things you could try.
1) Wrap your code in an immediately invoked function. This way any variables are contained within the function's scope and don't escape to pollute the global variable space which I guess is your main concern.
(function () {
var mapGlobal = new google.maps.Map(target, options);
function thatDoesSomething(){
// do something with mapGlobal
}
}());
2) Use a static object which might be handy for organising your code.
var Map = {
map: new google.maps.Map(target, options),
thatDoesSomething: function () {
// do something with this.map
}
};
3) Or combine them.
(function () {
var Map = {
map: new google.maps.Map(target, options),
thatDoesSomething: function () {
// do something with this.map
}
};
}());

Need to redraw a map with new data (in conjunction with CartoDB.js)

I seem to be having some trouble with reinitializing a Leaflet map object. I am running an init() function that starts by initializing a map, adding a tilelayer and layer with polygons, and then adding sublayers which color the polygons according to a ST_Intersects query. The issue is that this function is tied to an AJAX call that is caused by a click event but does not update the data according to the new parameters sent to the map. I don't think I am explaining this very well, so here is a bit of my code:
success: function(data) {
init(data);
}
function init(data){
// initiate leaflet map
alert("start");
var map = L.map('cartodb-map').setView([40.750028, -73.926768], 11);
alert("map made");
//create sublayers, etc
}
What happens is that the first time init is run, both 'start' and 'map made' alerts work, and the map is made according to the data. Any further calls to init simply alerts with "start" and "map made" is never alerted so I believe the problem is with reinitializing the leaflet map. What should I do to fix this?
Not sure the problem without seeing more of your code, but you could try calling map.remove(); the second time around. So:
if (map) {
map.remove();
// add in new map initialization here
} else {
var map = ...
}

Categories