I'm quite new in js and leaflet and I need your help. I use leaflet draw for drawing and editing polygons on a map and I need to be able to snap to other polygons while drawing. I used plugin Leaflet Snap for this and it works - unfortunately only when the polygon is already in map so it only works for editing. I can't seem to find solution for this anywhere on the internet. It is working in this demo - http://makinacorpus.github.io/Leaflet.Snap/. BUT - it only works when you initialize drawControl like this var map = L.map('map', {drawControl: true})
The problem is, I need to create toolbar with L.Control.Draw because I only need to draw polygon and marker and not all the other stuff:
let drawControlFull = new L.Control.Draw({
edit: {
featureGroup: this.newLayer
},
draw: {
polyline: false,
circle: false,
rectangle: false,
circlemarker: false,
polygon: {
guideLayers: [this.floorPolygons],
}
}
});
Since I'm building large Vue app I can only provide easy example https://jsfiddle.net/leanna_b/kwp2uh7a/.
Any ideas where the problem might be? Thank you so much for any help!
Related
I'm currently using leaflet and leaflet.draw to create a website where the user can freehand polygons on a map. I'd like to design a text field that pops up when the user is creating/editing the polygon so they can name it, and this name would then be associated with the polygon. Is there anyway to do that?
This is the code snippet I have so far for drawing and editing:
//leaflet draw
var drawnShapes = new L.FeatureGroup();
map.addLayer(drawnShapes);
var drawControl = new L.Control.Draw({
draw: {
circle: false,
circlemarker: false,
polyline: false,
marker: false
},
edit: {
featureGroup: drawnShapes,
remove: true
}
});
map.addControl(drawControl);
Thanks!
-- I haven't been able to find any kind of text field documentation that would work along with Leaflet so wanted to ask here.
I have a leaflet map with a few layers on it.
Whenever layers are not in the viewport, they are hidden untill panning has completed:
Regular view with layers:
Panning right, to show layers outside viewport:
Panning stopped:
As illustrated above, the layers will first become visible once panning has stopped and mouse(finger) released.
I have tried the following, which didn't work
var map = L.map('map',{ bounceAtZoomLimits: false, removeOutsideVisibleBounds: false}).setView([40, 0], 2);
L.geoJson(mapData).addTo(map);
Seems the solution was right in front of me
Adding the following will render the entire map:
var map = new L.Map('map');
map.getRenderer(map).options.padding = 100;
Solution found here
I have 3 d3 svgs I want to put on the same leaflet map. I would like to be able to control them with the same ease as leaflet layers.
Here is code that works, but is janky.
The relevant part is lines 75 to the end, where I create a custom layer control tied specifically to my d3 svg group, instantiate it, and pop it in the overlays hash before addTo(map).
var lineLayer = L.Class.extend({
initialize: function () {
return;
},
onAdd: function (map) {
railLineGroup.style("display", "block");
},
onRemove: function (map) {
railLineGroup.style("display", "none");
},
});
var railLineLayer = new lineLayer();
overlays["Rail Lines"] = railLineLayer;
L.control.layers(baseLayers, overlays).addTo(map);
There's got to be a better way to do this. For instance, because this is a hack, the layer control does not know that the layer has already been activated, so the checkbox is unchecked.
Any help would be greatly appreciated!
I really would recommend you to have a look at Vector OSM, specially at tilejson.js. There you see a proper implementation which makes layer control work with SVG overlay.
Consider the following code:
heatmap = new OpenLayers.Layer.Heatmap( "Heatmap Layer", map, osm,
{visible: true, radius: radiusForScale[zoom], legend: {position: 'br',title: 'title'}},
{isBaseLayer: false, opacity: 0.3, projection: new OpenLayers.Projection("EPSG:4326")}
);
Background: heatmap.js is great for displaying population densities and such... however, I do not want heatmap.js to control the radius of my points. Instead, I wish to dynamically updated the radius based on a custom zoom function I have created, without having to destroy the heatmap and recreate it every time the zoom changes. Now, this is possible if you add the heatmap to a div as follows:
var config = {
element: document.getElementById("heatmapArea"),
radius: 30,
opacity: 50
};
//creates and initializes the heatmap
var heatmap = h337.create(config);
In this example, it's as simple as updating the JSON object and updating the display. However, this in only a static display assigned to a div, and therefore renders the vector layer useless. Has anyone had any success with this in OpenLayers??
On a side note: I've browsed through all the keys in the heatmap JSON string and there doesn't seem to be a way to change the zoom variable.
Figured it out... this isn't advertised in the documentation or anywhere on the internet that I could find.. however, here's how you control the radius in OpenLayers for those of you who need it. Let's keep it simple..
// create our heatmap layer
var heatmap = new OpenLayers.Layer.Heatmap(
"Heatmap Layer", map, osm, {visible: true, radius:50},
{isBaseLayer: false, opacity: 0.3, projection: new OpenLayers.Projection("EPSG:4326")}
);
map.addLayers([heatmap]);
Now add the data:
heatmap.setDataSet(data);
Here's the key. You can do pretty much anything with this list of commands:
this.set("radius",value); //****this one solved my problem
this.set("element",value);
this.set("visible",value); //deceiving! You have to access the canvas subclass to get it to work
this.set("max",value);
this.set("gradient",value);
this.set("opacity",value);
this.set("width",value);
this.set("height",value);
this.set("debug",value);
So, for example, create a zoom event so the radius changes based on your radius function. For me, it was as simple as scaling the radius based on the ratio screen width(pixels)/screen width(meters). So now in your click event:
on zoom change: //pseudocode
canvas = heatmap.heatmap.get('canvas'); //this is the subclass I spoke of above
if the zoom is above a certain value //pseudocode
then
canvas.style.display = 'block'; //show the heatmap... you can also use heatmap.toggle() but this gives you more control
heatmap.heatmap.set('radius',zoomfunction[map.zoom]); //this dynamically updates the radius!!
heatmap.updateLayer();
else
canvas.style.display = 'none';
heatmap.updateLayer();
I hope this helps someone 'cause it drove me crazy!
The Google Maps API for a Polygon does not offer a drag method.
What would be an efficient way of implementing such a feature (i.e., sufficiently optimised so that it would not kill a four year old laptop)?
Thank you!
I found the Google Maps V2 Polygon Implementation to be very limiting for the needs I have had and solved it by creating a custom overlay. My group is currently stuck on IE6 so I have yet to migrate over to Google Maps V3 - but taking a quick look at the API shows that you could probably do a similar thing that I did in V2 with V3.
Essentially the idea is:
Create a Custom Overlay
Populate it with your own SVG/VML Polygons and attach a drag event to this custom polygon object
Custom Overlays:
Here is some information to get you started on making your own custom overlay:
http://code.google.com/apis/maps/documentation/javascript/overlays.html#CustomOverlays
Creating your own "Dragable" Polygon Object:
Once you get that down you'll want to add your own polygons to the custom overlay instead of using GPolygons. I went through the painful process of learning SVG/VML and writing a library to bridge SVG/VML together - you could do that, but I would recommend starting by trying to use another library such as Raphaël.
http://raphaeljs.com/
Using Raphaël will save you a whole lot of time trying to figure out how to get cross-browser Vector Graphic (Polygon) functionality - and best of all it supports drag events already, here is an example from their library:
http://raphaeljs.com/graffle.html
Once you have a custom overlay and you are able to throw some Raphaël objects onto it the last step is to translate the coordinates you want from a Lat/Lng value to a Pixel value. This is available in the MapCanvasProjection of V3:
http://code.google.com/apis/maps/documentation/javascript/reference.html#MapCanvasProjection
You can use fromLatLngToDivPixel to figure out what the actual pixel values are for the points on your Raphael polygon, draw it, then add it to the overlay with a drag event.
Since version 3.11 (dated Jan 22, 2013) it's possible to just set the draggable property onto the google.maps.Polygon instance; see this example.
If you want to programmatically move a polygon, you'll need a custom Google Maps Extension which I wrote, as the API does not provide such a method.
Here's how I do it. Find the approximate center of the polygon, and add a marker, then add a drag listener to the marker. On lat/lng change, subtract the difference from the original marker lat/lng, subtract the difference to each of the paths, then, set the original position to the new position. Make sure that in your javascript api call that you have library=geometry,drawing
google.maps.event.addListener(draw, 'overlaycomplete', function(shape) {
// POLYGON
if (shape.type == 'polygon') {
var bounds = new google.maps.LatLngBounds(); var i;
var path = shape.overlay.getPath();
for (i = 0; i < path.length; i++) { bounds.extend(path.getAt(i)); }
shape.latLng = bounds.getCenter();
marker = getMarker(map,shape);
shape.overlay.marker = marker;
markers.push(marker);
}
google.maps.event.addListener(marker, 'drag', function(event) {
shape.overlay.move(event.latLng, shape, path);
});
google.maps.event.addListener(shape.overlay, 'rightclick', function() {
this.setMap(null);
this.marker.setMap(null);
draw.setDrawingMode('polygon');
});
});
}
google.maps.Polygon.prototype.move = function(latLng, shape, p) {
var lat = latLng.lat();
var lng = latLng.lng();
latDiff = shape.latLng.lat()-lat;
lngDiff = shape.latLng.lng()-lng;
for (i = 0; i < p.length; i++) {
pLat = p.getAt(i).lat();
pLng = p.getAt(i).lng();
p.setAt(i,new google.maps.LatLng(pLat-latDiff,pLng-lngDiff));
}
shape.latLng = latLng;
}
function getMarker(map,shape){
var infowindow = new google.maps.InfoWindow();
if(shape.type=='polygon'){ latLng = shape.latLng; }
marker = new google.maps.Marker({
position: latLng,
map:map,
draggable:true,
clickable: true,
animation: google.maps.Animation.DROP
});
shape.overlay.marker = marker;
shape.overlay.bindTo('center',marker,'position');
google.maps.event.addListener(marker, 'click', (function(marker) {
return function() {
infowindow.setContent('polygon');
infowindow.open(map, marker);
toggleBounce(marker);
}
})(marker));
google.maps.event.addListener(infowindow,'closeclick', (function(marker) {
return function() {
marker.setAnimation(null);
}
})(marker));
return marker;
}
If you have any questions, feel free to contact me.
You could have markers for each point on the polygon, these markers could have drag and at the end of each drag, the polygon could be redrawn.
You could also have a marker in the center of the polygon representing the polygon as a whole, when you move that marker, every marker could be moved by the same amount to maintain the shape.
Okay - so after seeing the website you are trying to implement I started to feel like Raphael may not be necessary because it is a pretty heavy JS Library - and if you are only trying to draw a rectangle polygon I thought, why not just do it with a single lightweight DIV instead?
However I think the Raphael solution would still hold water for many other cases - so I think I'll just post another possible answer.
Here is a working example I threw together:
http://www.johnmick.net/drag-div-v3/
Feel free to take a look at the source:
http://www.johnmick.net/drag-div-v3/js/main.js
Essentially we do the following
Create the Custom Overlay
Create the draggable div polygon and, using jQuery UI, make it draggable
Tie an event that listens to when the dragging has stopped that updates the LatLng position of the rectangle
Add the object to the Custom Overlay
Implement the draw function to redraw the rectangle during zooms and pans
Currently I am only storing one LatLng value for the Rectangle (being the top left corner) - you could easily extend this example to store all 4 points of the rectangle and have the shape dynamically resize itself on zooms. You may want to do that, otherwise as users zoom out they will get a climate report for a larger and larger area.