Leaflet circleMarker popup is now showing - javascript

I can't seem to get CircleMarkers to show their associated popups in Leaflet. Here's what I am doing:
var markerGroup;
fetch("<FILE_NAME_REMOVED>").then(function(response){
return response.text();
}).then(function(text){
var lines = text.split("\n");
var markerArray = [];
for (var i=1; i<lines.length; i++) {
var parts = lines[i].split(",");
if (typeof parts[2] !='undefined') {
marker = new L.CircleMarker([parts[3],parts[2]], {
radius: 4,
color: 'red',
opacity: 0.95,
weight: 1.2,
dashArray: "2,3",
fillOpacity: 0.2
}).bindPopup("I am at " + parts[0] + "," + parts[1]);
markerArray.push(marker);
};
}
markerGroup = L.featureGroup(markerArray).addTo(map);
map.fitBounds(markerGroup.getBounds());
}).catch(function(err){
console.log(err);
});
The popups are just not showing up - in fact when I click on the circles the map just zooms in. If I replace L.CircleMarker with just L.Marker though it works fine, but I actually need circles.
I even tried adding an on click function to the markers, still nothing.
Could someone please help? I am using leaflet 1.3.4.
EDIT:
I didn't mention (because I did't think it mattered) that the markers are added last to the map but the map has a geoJson layer as well. I've noticed (by accident) that the popups do show up just fine over the area of the map not covered by the geoJson layer. This is strange to me because the geojson layer is set to back from the beginning:
var datalayer;
$.getJSON("<FILE_NAME_REMOVED>",function(data){
datalayer = L.geoJson(data ,{
onEachFeature: function(feature, featureLayer) {
featureLayer.setStyle({fillColor: 'white'});
featureLayer.setStyle({weight:1});
featureLayer.setStyle({fillOpacity:0.1});
featureLayer.bringToBack();
}
}).addTo(map);
map.fitBounds(datalayer.getBounds());
});

Related

Leaflet move single point of polygon in GeoJSON layer

I have leaflet with a geoJSON layer group and load several geoJSON features, each as a separate layer added to the geoJSON layer group. For a given selected layer, I need to move a point of the polygon on that layer using javascript. So, for example, I may need to move the 3rd vertex to 30.123, -80.123. I cannot figure out how to do this. I can move a marker easily with the setLatLng() method but I can't find anything to change a polygon point.
Here is an example of how I am creating the map and adding my geoJSON features:
function createMap(){
myMap = L.map('locationMap', {
editable: true,
attributionControl: false,
fullscreenControl: true,
fullscreenControlOptions: {
position: 'topleft'
}
}).setView([#Model.MapCenterLat, #Model.MapCenterLong], #Model.MapInitialZoom);
L.tileLayer('#Model.MapUrl2', {
drawControl: true,
maxZoom: 20,
id: 'mapbox.streets'
}).addTo(myMap);
geoJsonLayer = L.geoJson().addTo(myMap);
loadGeoFences('');
}
function loadGeoFences(parentId) {
var url = '#Url.Action("GetGeoFences")';
$.get(url, { parentId: parentId },
function (data) {
if (data.length > 0) {
$.each(data, function (index, value) {
var newLayer = L.geoJson(value,
{
onEachFeature: applyLayerStyle
});
newLayer.addTo(geoJsonLayer);
});
}
});
}
I was able to do this using the leaflet.editing plugin. Once you have the correct layer, the layer.editing.latlngs array can be modified with the desired coordinates. Then call layer.redraw() to update the polygon.
You can change the latlngs while geoJson loading with following:
function onEachFeature(feature, layer) {
if(layer instanceof L.Polyline){
var latlngs = layer.getLatLngs()
var ll = latlngs[0][2];
ll.lat = 51.490056
latlngs[0][2] = ll;
layer.setLatLngs(latlngs);
}
}
L.geoJSON(json,{onEachFeature: onEachFeature}).addTo(map);
https://jsfiddle.net/falkedesign/hvdxo3z7/

Best way to convert Leaflet Geojson layers to Leaflet rectangle vector [duplicate]

I am trying to use leaflet's edit function on polygons that I loaded from my database. When I click on leaflet's edit button I get the error
Cannot read property 'enable' of undefined
This thread describes a similar problem, and user ddproxy said
"Since FeatureGroup extends LayerGroup You can walk through the layers
presented and add them individually to the FeatureGroup used for
Leaflet.draw"
I am confused what he means by "walk through", I thought I was adding a layer group, so i'm not sure what I would be walking through. Does this have to do with the fact that i'm adding the polygons as a geoJSON object? Adding the polygons to the map, binding their popups, and assigning them custom colors works perfectly FYI.
The following is the relevant code:
<script>
window.addEventListener("load", function(event){
//other stuff
loadHazards();
});
//next 6 lines siply add map to page
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
var osmAttrib = '© OpenStreetMap contributors'
var osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib})
var map = new L.Map('map', { center: new L.LatLng(39.255467, -76.711964), zoom: 16 })
osm.addTo(map);
var drawnItems = L.featureGroup().addTo(map);
var Hazards = L.featureGroup().addTo(map);
L.control.layers({
'osm': osm.addTo(map)
},
{
'drawlayer': drawnItems,
"Hazards" : Hazards,
"Tickets": Tickets
},
{
position: 'topleft', collapsed: false
}
).addTo(map);
map.addControl(new L.Control.Draw({
edit: {
featureGroup: Hazards,
poly: {
allowIntersection: false
}
},
draw: {
polygon: {
allowIntersection: false,
showArea: true
},
rectangle:false,
circle:false,
circlemarker:false
}
}));
map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;
drawnItems.addLayer(layer);
});
</script>
And the loadHazards() function:
function loadHazards(){
$.ajax({
type: 'GET',
url:'/loadPolygonFromDatabase',
success : function(polygons){
polygons = JSON.parse(polygons);
var toAdd = [];
for (i in polygons){
var item = {
"type" : "Feature",
"properties":{
"category":"",
"description":"",
"ID":""
},
"geometry" : {
"type":"Polygon",
"coordinates":[],
}
};
item["geometry"]["coordinates"][0] = polygons[i]["coordinates"];
item["properties"]["category"] = polygons[i]["category"];
item["properties"]["description"] = polygons[i]["description"];
item["properties"]["ID"] = polygons[i]["ID"];
toAdd.push(item);
}
//Add information to popup
var layerGroup = L.geoJSON(toAdd, {
onEachFeature: function (feature, layer) {
layer.bindPopup( '<h1>' + feature.properties.category + '</h1>'
+ '<p>' + feature.properties.description + '</p>');
layer.id = feature.properties.ID;
},
style: function(feature){
switch (feature.properties.category) {
case 'Rabid_Beavers': return {color: "#663326"};
case 'Fire': return {color: "#ff0000"};
case 'Flood': return {color: "#0000ff"};
}
}
}).addTo(Hazards);
}
});
}
Thanks in advance!
As mentioned by #ghybs Leaflet.Draw doesn't support Groups or MultiPolygons. I needed the same functionality so a few years ago I created Leaflet-Geoman (previously named leaflet.pm) which supports holes, MultiPolygons, GeoJSON and LayerGroups:
https://github.com/geoman-io/leaflet-geoman
Hope it helps.
Unfortunately Leaflet.draw plugin does not handle nested Layer Groups (same for Feature Groups / GeoJSON Layer Groups).
That is the meaning of the Leaflet.draw #398 issue you reference: they advise looping through the child layers of your Layer/Feature/GeoJSON Layer Group (e.g. with their eachLayer method). If the child layer is a non-group layer, then add it to your editable Feature Group. If it is another nested group, then loop through its own child layers again.
See the code proposed in that post:
https://gis.stackexchange.com/questions/203540/how-to-edit-an-existing-layer-using-leaflet
var geoJsonGroup = L.geoJson(myGeoJSON);
addNonGroupLayers(geoJsonGroup, drawnItems);
// Would benefit from https://github.com/Leaflet/Leaflet/issues/4461
function addNonGroupLayers(sourceLayer, targetGroup) {
if (sourceLayer instanceof L.LayerGroup) {
sourceLayer.eachLayer(function(layer) {
addNonGroupLayers(layer, targetGroup);
});
} else {
targetGroup.addLayer(sourceLayer);
}
}
In your very case, you can also refactor your code with 2 other solutions:
Instead of building your layerGroup (which is actually a Leaflet GeoJSON Layer Group) first and then add it into your Hazards Feature Group, make the latter a GeoJSON Layer Group from the beginning, and addData for each of your single Features (item):
var Hazards = L.geoJSON(null, yourOptions).addTo(map);
for (i in polygons) {
var item = {
"type" : "Feature",
// etc.
};
// toAdd.push(item);
Hazards.addData(item); // Directly add the GeoJSON Feature object
}
Instead of building a GeoJSON Feature Object (item) and parse it into a Leaflet GeoJSON Layer, you can directly build a Leaflet Polygon and add it into your Hazards Layer/Feature Group:
for (i in polygons) {
var coords = polygons[i]["coordinates"];
var style = getStyle(polygons[i]["category"]);
var popup = ""; // fill it as you wish
// Directly build a Leaflet layer instead of an intermediary GeoJSON Feature
var itemLayer = L.polygon(coords, style).bindPopup(popup);
itemLayer.id = polygons[i]["ID"];
itemLayer.addTo(Hazards);
}
function getStyle(category) {
switch (category) {
case 'Rabid_Beavers': return {color: "#663326"};
case 'Fire': return {color: "#ff0000"};
case 'Flood': return {color: "#0000ff"};
}
}

Toggle layers on and off in Leaflet (more complex scenario)

I am using jQuery's getJSON method to load external line data I've created in QGIS.
What I'm trying to do is toggle my layers on and off - simple check boxes, no radio button for the basemap. I'd also like all the layers to be off when the map is initially loaded.
My code
var map=L.map('map').setView([41.9698, -87.6859], 12);
var basemap = L.tileLayer('http://a.tile.stamen.com/toner/{z}/{x}/{y}.png',
{
//attribution: would go here
maxZoom: 17,
minZoom: 9
}).addTo(map);
//display geoJson to the map as a vector
var x = function(source, map)
{
var layers = L.geoJson(source,
{
style: function(feature){
var fillColor, side=feature.properties.side;
if (side==='Both') fillColor = '#309e2d';
else if (side==='Neither') fillColor = '#d90f0f';
else if (side==='West Only') fillColor = '#e27f14';
else if (side==='East Only') fillColor = '#2b74eb';
else if (side==='North Only') fillColor = '#eae42b';
else if (side==='South Only') fillColor = '#552d04';
else fillColor = '#f0f5f3';
return { color: fillColor, weight: 3.5, opacity: null };
},
onEachFeature: function(feature, geojson){
var popupText="<h1 class='makebold'>Border: </h1>"+feature.properties.name+"<br/>"+"<h1 class='makebold'>Which Side?: </h1>"+feature.properties.side;
geojson.bindPopup(popupText);
}
}).addTo(map);
};
$.getJSON("data/Knox.geojson", function(source){ x(source, map); });
$.getJSON("data/abc.geojson", function(source){ x(source, map); });
$.getJSON("data/xyz.geojson", function(source){ x(source, map); });
I tried assigning a variable before the L.geoJson function (var layers), and then L.control.layers(null, layers).addTo(map); That doesn't seem to work.
How does one create a layer control for multiple external geojson's that are already associated with a few callback functions (L.geoJson, style, and onEachFeature)? Thanks in advance.
EDIT:
Since you clarified that you want just the entire collection to be switched on/off, it is even more simple (and almost like what you tried by assigning your L.geoJson to var layers), but you have to take care of asynchronous processes.
To avoid this issue, you could do something like:
var myLayerGroup = L.layerGroup(), // do not add to map initially.
overlays = {
"Merged GeoJSON collections": myLayerGroup
};
L.control.layers(null, overlays).addTo(map);
function x(source, map) {
// Merge the GeoJSON layer into the Layer Group.
myLayerGroup.addLayer(L.geoJson({}, {
style: function (feature) { /* … */ },
onEachFeature: function (feature, layer) { /* … */ }
}));
}
$.getJSON("data/Knox.geojson", function(source){
x(source, map);
});
Then myLayerGroup will be gradually populated with your GeoJSON features, when they are received from the jQuery getJSON requests and they are converted by L.geoJson.
If my understanding is correct, you would like the ability to switch on/off independently each feature from your GeoJSON data?
In that case, you would simply populate your layers object while building the L.geoJson layer group, e.g. inside the onEachFeature function:
var layers = {};
L.geoJson(source, {
style: function (feature) { /* … */ },
onEachFeature: function(feature, layer){
var popupText = "<h1 class='makebold'>Border: </h1>" +
feature.properties.name + "<br/>" +
"<h1 class='makebold'>Which Side?: </h1>" +
feature.properties.side;
layer.bindPopup(popupText);
// Populate `layers` with each layer built from a GeoJSON feature.
layers[feature.properties.name] = layer;
}
});
var myLayersControl = L.control.layers(null, layers).addTo(map);
If you have more GeoJSON data to load and to convert into Leaflet layers, simply do exactly the same (adding built layer into layers in onEachFeature function) and build the Layers Control only once at the end, or use myLayersControl.addOverlay(layer).
Note: make sure to structure your code to take into account your several asynchronous processes, if you load each GeoJSON data in a separate request. Refer to jQuery Deferred object. Or simply create your Layers Control first and use the addOverlay method.
If you want them to be initially hidden from the map, simply do not add the geoJson layer to the map…
I learned a lot more about layer control in Leaflet than I expected, which is great.
#ghybs offered really helpful suggestions.
My issue was about toggling external geoJson files on and off, particularly with the getJSON jQuery method. I was trying to assign a variable within my multiple callbacks, like:
var layers=L.geoJson(source,{
{style: /*....*/},
{onEachFeature: /*....*/}}
and then just going L.control.layers(null, layers).addTo(map);
That doesn't work (why? I still can't explain-I'm quite the beginner-programmer). The way I did get this to work was by creating my style and onEachFeature functions separately, like this:
function borders (feature){
var fillColor, side=feature.properties.side;
if (side==='Both') fillColor = '#309e2d';
else if (side==='Neither') fillColor = '#d90f0f';
else if (side==='West Only') fillColor = '#e27f14';
else if (side==='East Only') fillColor = '#2b74eb';
else if (side==='North Only') fillColor = '#eae42b';
else if (side==='South Only') fillColor = '#552d04';
else fillColor = '#f0f5f3';
return { color: fillColor, weight: 3.5, opacity: null };
};
and
function popUp (feature, geojson){
var popupText="<h1 class='makebold'>
Border: </h1>"+feature.properties.name+"<br/>"+"<h1 class='makebold'>
Which Side</h1>"+feature.properties.side;geojson.bindPopup(popupText);
};
and then assigning these directly as callbacks into the getJSON method. By doing it this way, I could create a variable before "drawing" my geoJson to the map with L.geoJson(). Then I could assign the variable dynamically(?) to the layer control:
$.getJSON("data/xyz.geojson", function(source){
var xyz = L.geoJson(source, {
style: borders,
onEachFeature: popUp});
togglelayer.addOverlay(xyz, 'This name shows up on the control')});
});
I stored the variable togglelayer like this:
var togglelayer = L.control.layers(null, null,{collapsed: false}).addTo(map);
This post was also helpful: How to add two geoJSON feature collections in to two layer groups

clicking a marker opening a image next to map

I have a leaflet map which has lots of markers and what I would like is to open a image next to the map which is linked to certain marker on marker click. All I know that I need javascript/jquery and ajax to make this work.
Here is an example what it could look like: http://www.washingtonpost.com/wp-srv/special/local/14th-street-businesses/
Any hints/tips/tutorials appreciated.
Thanks in advance!
Here is a solution to your problem: http://franceimage.github.io/leaflet/10/
var selectedMarker = false;
var geojsonMarkerOptions = {
radius: 8,
fillColor: "#ff7800",
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
};
L.geoJson(fi_markers, {
pointToLayer: function (feature, latlng) {
var marker = L.circleMarker(latlng, geojsonMarkerOptions);
marker.on('click', function (e) {
var feature = e.target.feature;
var content = '<h3>' + feature.properties.popupContent + '</h3>' + feature.properties.thumbnail + '';
document.getElementById("events").innerHTML = content;
if(selectedMarker != false) {
selectedMarker.setStyle({ fillColor: "#ff7800"});
}
marker.setStyle({ fillColor: "#000000"});
selectedMarker = marker;
});
return marker;
}
}).addTo(map);
This a way you can do it (there are plenty others)
In this example:
There are no ajax calls (and no jquery either). Data is loaded from a geoJson structure (look at http://franceimage.github.io/leaflet/10/data.geojson)
The html content of the popup is created using the properties of the geojson feature
I have used CircleMarker so that changing the color is a piece of cake
I hope this will help

Google Maps - FusionTablesLayer to Polygon

I'm using Google Maps API and jquery-ui-maps (this questions has nothing to do with the plugin which is working great).
I've created a FusionTablesLayer with all countries except Mozambique. The user could place a marker and reposition it. I'm trying to find a way to block the drag (or alert the user, it doesn't matter now) if he tries to place the marker outside Mozambique (over the FusionTablesLayer).
After some research I discover this method: containsLocation(point:LatLng, polygon:Polygon), which computes whether the given point lies inside the specified polygon.
It should receive a Polygon and I've got a FusionTablesLayer. Any clue how to solve this?
Here's my code:FIDDLE
Try to place a marker and drag it...
//Initialize the map
var mapa = $('#map_canvas').gmap({'center': '-18.646245,35.815918'});
$('#map_canvas').gmap('option', 'zoom', 7);
//create the layer (all countries except Mozambique)
var world_geometry;
$('#map_canvas').gmap().bind('init', function(event, map) {
world_geometry = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '1N2LBk4JHwWpOY4d9fobIn27lfnZ5MDy-NoqqRpk',
where: "ISO_2DIGIT NOT EQUAL TO 'MZ'"
},
styles: [{
polygonOptions: {
fillColor: "#333333",
fillOpacity: 0.3
}
}],
map: map,
suppressInfoWindows: true
});
});
$('#map_canvas').gmap().bind('init', function(event, map) {
$(map).click(function(event) {
$('#map_canvas').gmap('clear', 'markers');
$('#map_canvas').gmap('addMarker', {
'position': event.latLng,
'draggable': true,
'bounds': false
}, function(map, marker) {
}).dragend(function(event) {
//I need to check if the marker is over the FusionTablesLayer and block the drag.
//var test = google.maps.geometry.poly.containsLocation(event.latLng, world_geometry);
}).click(function() {
})
});
});
Since there is no containsLocation in FusionTablesLayer, and since no mouseevents but click is supported (that would have made it a lot easier) - there is no other way round than to check if there is being dragged outside the area itself, Mozambique - not into the FusionTablesLayer. The solution is to create an invisible polygon for Mozambique, and use that polygon to check for containsLocation when dragging is finished.
The polygon can be based on the KML from the row you are excluding, MZ. That can be done using google.visualization.Query.
1) include the Google API loader in your project :
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
2) initialize Visualization :
google.load('visualization', '1.0');
3) define a variable for the polygon holding the Mozambique borders :
var mozambique;
The following is a function that loads the geometry data for Mozambique, and then creates an invisible polygon on the map; google.visualization.Query is used instead of the automated FusionTablesLayer so we can extract the <coordinates> from the KML and use them as base for the polygon.
In basic, this is how to convert KML-data from a FusionTable to a polygon :
function initMozambique(map) {
//init the query string, select mozambique borders
var sql = encodeURIComponent("SELECT 'geometry' FROM 1N2LBk4JHwWpOY4d9fobIn27lfnZ5MDy-NoqqRpk WHERE ISO_2DIGIT ='MZ'");
var query = new google.visualization.Query('http://www.google.com/fusiontables/gvizdata?tq=' + sql);
query.send(function (response) {
var data = response.getDataTable().getValue(0, 0);
//create a XML parser
if (window.DOMParser) {
var parser = new DOMParser();
var kml = parser.parseFromString(data, "text/xml");
} else { // Internet Explorer
var kml = new ActiveXObject("Microsoft.XMLDOM");
kml.loadXML(data);
}
//get the coordinates of Mozambique
var latLngs = kml.getElementsByTagName("coordinates")[0].childNodes[0].nodeValue.split(' ');
//create an array of LatLngs
var mzLatLngs = [];
for (var i = 0; i < latLngs.length; i++) {
var latLng = latLngs[i].split(',');
//<coordinates> for this FusionTable comes in lng,lat format
mzLatLngs.push(new google.maps.LatLng(latLng[1], latLng[0]));
}
//initialize the mozambique polygon
mozambique = new google.maps.Polygon({
paths: mzLatLngs,
fillColor: 'transparent',
strokeColor : 'transparent',
map: map
});
//make the mozambique polygon "transparent" for clicks (pass clicks to map)
google.maps.event.addListener(mozambique, 'click', function(event) {
google.maps.event.trigger(map, 'click', event);
});
});
}
Call the above initMozambique function in your second gmap().bind('init'... :
$('#map_canvas').gmap().bind('init', function(event, map) {
initMozambique(map);
...
Now you can check the mozambique-polygon for containsLocation after dragging
...
}).dragend(function(event) {
if (!google.maps.geometry.poly.containsLocation(event.latLng, mozambique)) {
alert('You are not allowed to drag the marker outside Mozambique');
}
//I need to check if the marker is over the FusionTablesLayer and block the drag.
//var test = google.maps.geometry.poly.containsLocation(event.latLng, world_geometry);
}).click(function() {
})
...
See forked fiddle, working demo with the code above -> http://jsfiddle.net/yb5t6cw6/
Tested in Chrome, FF and IE, ubuntu and windows.

Categories

Resources