The following post is a portion of the Leaflet Tips and Tricks book which is free to download. To use this post in context, consider it with the others in this blog or just download the the book as a pdf / epub or mobi .
----------------------------------------------------------
Leaflet.draw
Leaflet.draw adds support for drawing and editing vectors and markers overlaid onto Leaflet maps. Its driving force is Jacob Toye (a good Hamilton lad, so he gets a special shout-out :-)).
It is a comprehensive plugin that can add polylines, polygons, rectangles, circles and markers to a map and then edit or delete those objects as desired. It has an extensive range of options for configuring the drawing objects ‘look and feel’. It’s code is supported on GitHub and it can be downloaded from there. There is also some great documentation on function and use for the plugin that should be the authority for use.
Leaflet.draw is less of an endpoint, than an enabler of additional functionality. I say this because while it gives us the ability to draw to our hearts content on a map, it also provides the framework to take those drawn objects and push them to a database or similar (which we won’t cover in this overview sorry).
What we will go over though is how to add Leaflet.draw to our simple base map and how to configure some of the wide range of options.
Here’s what we are aiming to show in terms of the range of controls and options on the left hand side of our map.
Leaflet.draw toolbar |
And here’s some example objects produced with the plugin.
Leaflet.draw sample objects |
The colours are configurable as we shall see in the coming pages.
Leaflet.draw code description
The following code listing is the bare minimum that should be considered for use with Leaflet.draw. I even hesitate to say that, because the following is really only suitable for demonstrating that you have it running correctly. The configuration options that we will work through in the coming pages will add considerable functionality and will be captured in a separate example that will be available in the Appendices and online on GitHub.
<!DOCTYPE html>
<html>
<head>
<title>
Simple Leaflet Map</title>
<meta
charset=
"utf-8"
/>
<link
rel=
"stylesheet"
href=
"http://cdn.leafletjs.com/leaflet-0.7/leaflet.css"
/>
<link
rel=
"stylesheet"
href=
"http://leaflet.github.io/Leaflet.draw/leaflet.draw.css"
/>
</head>
<body>
<div
id=
"map"
style=
"width: 600px; height: 400px"
></div>
<script
src=
"http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"
>
</script>
<script
src=
"http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"
>
</script>
<script>
var
map
=
L
.
map
(
'map'
).
setView
([
-
41.2858
,
174.78682
],
14
);
mapLink
=
'<a href="http://openstreetmap.org">OpenStreetMap</a>'
;
L
.
tileLayer
(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
,
{
attribution
:
'© '
+
mapLink
+
' Contributors'
,
maxZoom
:
18
,
}).
addTo
(
map
);
var
drawnItems
=
new
L
.
FeatureGroup
();
map
.
addLayer
(
drawnItems
);
var
drawControl
=
new
L
.
Control
.
Draw
({
edit
:
{
featureGroup
:
drawnItems
}
});
map
.
addControl
(
drawControl
);
map
.
on
(
'draw:created'
,
function
(
e
)
{
var
type
=
e
.
layerType
,
layer
=
e
.
layer
;
drawnItems
.
addLayer
(
layer
);
});
</script>
</body>
</html>
There are only three ‘blocks’ that have changed in the code from our simple map example.
The first is an additional link to load more CSS code;
<link
rel=
"stylesheet"
href=
"http://leaflet.github.io/Leaflet.draw/leaflet.draw.css"
/>
(As with the
leaflet.css
file which is loaded before hand, I have taken some small formatting liberties to make the code appear more readable on the page.)
This loads the file directly from the Leaflet.draw repository on GitHub, so if you are loading from a local file you will need to adjust the path appropriately.
The second is the block that loads the leaflet.draw.js script.
<script
src=
"http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"
>
</script>
Leaflet.draw exists as a separate block of JavaScript code and again, here we are loading the file directly from the Leaflet.draw repository on GitHub (as per the earlier advice, if you are loading from a local file you will need to adjust the path appropriately).
The last change to the file is the block of code that runs and configures Leaflet.draw.
var
drawnItems
=
new
L
.
FeatureGroup
();
map
.
addLayer
(
drawnItems
);
var
drawControl
=
new
L
.
Control
.
Draw
({
edit
:
{
featureGroup
:
drawnItems
}
});
map
.
addControl
(
drawControl
);
map
.
on
(
'draw:created'
,
function
(
e
)
{
var
type
=
e
.
layerType
,
layer
=
e
.
layer
;
drawnItems
.
addLayer
(
layer
);
});
The
var drawnItems = new L.FeatureGroup();
line adds a new extended layer group to the map called drawnItems
. This is the layer that the elements we create will be stored on.
Then the
map.addLayer(drawnItems);
line adds the layer with our drawn items to the map.
Next we get to the first of the true Leaflet.draw commands when we initialize the draw control and pass it the feature group of editable layers;
var
drawControl
=
new
L
.
Control
.
Draw
({
edit
:
{
featureGroup
:
drawnItems
}
});
map
.
addControl
(
drawControl
);
This is required when adding the edit toolbar and tells the Leaflet.draw plugin which layer (
drawnItems
) should be editable. Then the controls are added to the map (map.addControl(drawControl);
).
Finally when we add a new vector or marker we need prompt a trigger that captures the type of item we have created (polyline, rectangle etc) and adds it to the drawn items layer on the map.
map
.
on
(
'draw:created'
,
function
(
e
)
{
var
type
=
e
.
layerType
,
layer
=
e
.
layer
;
drawnItems
.
addLayer
(
layer
);
});
This is alto the part of the code where you could store the information that described the element in a database or similar.
Leaflet.draw configuration options
As I mentioned earlier, the sample code described above is extremely cut down and should be extended using the wide range of options available to Leaflet.draw.
OBJECT COLOURS
As our first change, if we use the simple example, all of the elements we generate have the same colour, so lets change that first.
Leaflet.draw map with common colours |
Changing the options is a simple matter of declaring them when we initialize the draw controls by adding them as required by the documentation on the Leaflet.draw GitHub page. For example in the following code snippet we have added in the
draw:
option which in turn has options for each of the shapes. We have entered thepolygon:
option which has it’s own options of which we have added shapeOptions:
as an option. And as if that wasn’t enough we select the option for color:
from this and finally declare it as purple
. var
drawControl
=
new
L
.
Control
.
Draw
({
draw
:
{
polygon
:
{
shapeOptions
:
{
color
:
'purple'
},
},
},
edit
:
{
featureGroup
:
drawnItems
}
});
map
.
addControl
(
drawControl
);
This might seem slightly confusing, but it’s just a simple hierarchy which we can flesh out by doing the same thing for each of the remaining shapes (polyline, rectangle and circle). The code snippet would then look as follows;
var
drawControl
=
new
L
.
Control
.
Draw
({
draw
:
{
polygon
:
{
shapeOptions
:
{
color
:
'purple'
},
},
polyline
:
{
shapeOptions
:
{
color
:
'red'
},
},
rect
:
{
shapeOptions
:
{
color
:
'green'
},
},
circle
:
{
shapeOptions
:
{
color
:
'steelblue'
},
},
},
edit
:
{
featureGroup
:
drawnItems
}
});
map
.
addControl
(
drawControl
);
And our new colours look like this…
Leaflet.draw map with various colours |
POLYGON LINE INTERSECTION
When drawing a polygon it is very easy to cross the lines when describing our object on the screen, and while this may be a desired action, in general it is probably not. However as our code stands, if we tell the script to cross the lines and draw a polygon it will do it with a (perhaps unintended) result looking something like the following…
Leaflet.draw polygon with crossed lines |
Luckily there is an option that will provide a warning that this is happening while drawing and will allow you to correct and carry on. The following screen shot shows the results when trying to place a point that allows boundary lines to cross;
Leaflet.draw polygon with crossed lines |
We can see that not only is a warning raised, but the shape colour changes.
This is accomplished by alteration of the
polygon
options as follows; polygon
:
{
shapeOptions
:
{
color
:
'purple'
},
allowIntersection
:
false
,
drawError
:
{
color
:
'orange'
,
timeout
:
1000
},
},
This has introduced the
allowIntersection
option and set it to false and provided the drawError
option with the instructions to change the colour of the object to orange for 1000 milliseconds.
This option will also work with
polyline
objects.SHOW AND MEASURE AN AREA
While tracing a polygon we can get Leaflet.draw to report the total area spanned by the shape by setting the
showArea
option to true
.Leaflet.draw polygon showing area |
You can see from the screen shot that the area is in hectares, but we can set the measurement units to not be metric (to show acres instead) by setting the
metric
option to false
. The code for the polygon now looks like this; polygon
:
{
shapeOptions
:
{
color
:
'purple'
},
allowIntersection
:
false
,
drawError
:
{
color
:
'orange'
,
timeout
:
1000
},
showArea
:
true
,
metric
:
false
},
REPEATING A DRAWING OPTION AUTOMATICALLY
By default once we have finished drawing a polygon, if we wanted to draw another, we would need to click on the polygon tool on the toolbar to start again. But we can use the
repeatMode
set to true
to continue to dray polygons until we select another object to draw or until we press the escape key.
Our
polygon
option code will now look like this; polygon
:
{
shapeOptions
:
{
color
:
'purple'
},
allowIntersection
:
false
,
drawError
:
{
color
:
'orange'
,
timeout
:
1000
},
showArea
:
true
,
metric
:
false
,
repeatMode
:
true
},
This option will work with all the other drawing objects.
PLACE AN ALTERNATIVE MARKER
If an alternative marker has been declared (see section on setting up different markers) it can be specified under the
marker
option as an alternative icon
.
The code to set up an alternative icon duplicates is covered elsewhere, but consists of the following;
var
LeafIcon
=
L
.
Icon
.
extend
({
options
:
{
shadowUrl
:
'http://leafletjs.com/docs/images/leaf-shadow.png'
,
iconSize
:
[
38
,
95
],
shadowSize
:
[
50
,
64
],
iconAnchor
:
[
22
,
94
],
shadowAnchor
:
[
4
,
62
],
popupAnchor
:
[
-
3
,
-
76
]
}
});
var
greenIcon
=
new
LeafIcon
({
iconUrl
:
'http://leafletjs.com/docs/images/leaf-green.png'
});
Here we are using one of the markers (the green leaf) set up as part of the custom icons tutorial on GitHub.
And the option code to be added to include an alternative marker is;
marker
:
{
icon
:
greenIcon
},
And here’s a pretty picture of the green marker.
Add a custom marker |
PLACE THE LEAFLET.DRAW TOOLBAR IN ANOTHER POSITION
The toolbar position can also be changed. This is configured via an option that is quite high up the hierarchy (in parallel with the
draw
and edit
options). So this should be added directly under the drawControl
declaration per the following code snippet; var
drawControl
=
new
L
.
Control
.
Draw
({
position
:
'topright'
,
This will place the toolbar in the top right hand corner of the map as follows;
Moving the Leaflet.draw toolbar |
The other options for positioning are
bottomright
, bottomleft
and the default of topleft
.
The full code and a live example of the use of the Leaflet.draw plugin with the options described here in the appendices and is available online at bl.ocks.org or GitHub. A copy of all the files that appear in the book can be downloaded (in a zip file) when you download the book from Leanpub.
The description above (and heaps of other stuff) is in the Leaflet Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).
" Leaflet.draw is less of an endpoint, than an enabler of additional functionality. I say this because while it gives us the ability to draw to our hearts content on a map, it also provides the framework to take those drawn objects and push them to a database or similar (which we won’t cover in this overview sorry)."
ReplyDeleteI would truly appreciate a link where i can find more information on how to push objects to database or json. thank you
Hi Damian, That sort of function is a little more complex than I have explained in any of my books so far. However, the way I learned how to do it was simply an extension of the chapter on using MySQL in D3 Tips and Tricks (https://leanpub.com/D3-Tips-and-Tricks). The only problem is that it will require a bit of a learning curve on your part that I don't have examples for sorry. If it's any consolation that is an idea that would make a great new section for the book. Thanks for the question and good luck.
DeleteThanks - I found this page very helpful for getting the leaflet.draw controls set up. Sometimes the basics are deemed so "simple" that they get glossed over elsewhere!
ReplyDeleteHow to get the lat n long value of selected polygon
ReplyDeletesorry, I'm not sure. It's not something that I've tried before.
DeleteHi. Great tutorial, I am really excited to try this out. But I am really noob with Leaflet and when I use your sample code, my map doesn't have any drawing options? Just a zoom in and out.
ReplyDeleteCheers. I think that we're probably the victim of the various parts of the code examples being really old (over four years). If I go to a live example that I did a few years ago I can see the same result http://bl.ocks.org/d3noob/7730264. So there's good news and bad news. The good news is that if you're using the code examples correctly, it's not you, it's the code. The bad news is that it doesn't work :-(. The good news is that Leaflet continues to be totally awesome and if you go to this page there are a range of the latest leaflet plugins including some new draw ones http://leafletjs.com/plugins.html#edit-geometries. I can't make the promise that they will just work automagically, but have a play and don't be afraid of trying lots of different things before it works. Start simple till you get it going and then build from there. Good luck and thanks for the comment.
DeleteThanks for the advice! I have come pretty far since my original post and it's been an awesome experience. I can even export the drawn items as geoJSONs now which is exciting :) I really want these to be converted to .kml though but I am having no luck with the "tokml" plugin.
DeleteGreat tutorial, you have saved me a lot of time.
ReplyDelete