<!DOCTYPE html>
<html>
<head>
<link data-require="leaflet@*" data-semver="1.3.4" rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css" />
<link data-require="quill@*" data-semver="1.3.6" rel="stylesheet" href="https://cdn.quilljs.com/1.3.6/quill.snow.css" />
<link data-require="leaflet.draw@*" data-semver="0.2.3" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.2/leaflet.draw.css" />
<link rel="stylesheet" href="style.css" />
<script data-require="jquery@*" data-semver="3.2.1" src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
<script data-require="leaflet@*" data-semver="1.3.4" src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"></script>
<script data-require="leaflet.draw@*" data-semver="0.2.3" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.2/leaflet.draw-src.js"></script>
<script data-require="quill@*" data-semver="1.3.6" src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
</head>
<body>
<div id="map"></div>
<!-- <a href='#' id='export'>Export Features</a> -->
<!-- <a href='#' id='import'>Import Features</a> -->
<script type="text/javascript" src="script.js"></script>
</body>
</html>
// **********************************************
// Globals
// **********************************************
var template = '<div id="popup_{id}" class="popup">\
<div class="popup_section">\
<label for="popup_title_{id}" class="popup_label">Title</label></br>\
<input id="popup_title_{id}" class="popup_input" type="text" value="{title}"></input>\
</div>\
<div class="popup_section">\
<label for="popup_coordinates_{id}" class="popup_label">Coordinates</label>\
<input id="popup_coordinates_{id}" class="popup_input_short" type="text" value="{latlng}" disabled></input>\
</div>\
<div class="popup_section">\
<label for="popup_editor_{id}" class="popup_label">Description</label></br>\
<div id="popup_editor_{id}" class="popup_editor"></div>\
</div>';
// **********************************************
// Map Event Handlers
// **********************************************
// This is used for testing purposes only.
function exportData() {
var data = [];
for(var drawnItem of featureGroup.getLayers()){
data.push(drawnItemToJSON(drawnItem));
}
// Stringify the GeoJson
var convertedData = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data));
// Create export
document.getElementById('export').setAttribute('href', 'data:' + convertedData);
document.getElementById('export').setAttribute('download', 'data.geojson');
}
// This is used for testing purposes only.
function importData() {
// Importing the sample file and recreate drawnitems
jQuery.get('sample.json', function(data) {
for(var drawnitem of data){
var layer;
if (drawnitem.properties.layerType == 'polygon'){
layer = L.polygon(drawnitem.latlngs);
layer.options.color = drawnitem.options.color;
layer.options.weight = drawnitem.options.weight;
layer.options.opacity = drawnitem.options.opacity;
} else if (drawnitem.properties.layerType == 'rectangle'){
layer = L.rectangle(drawnitem.latlngs);
layer.options.color = drawnitem.options.color;
layer.options.weight = drawnitem.options.weight;
layer.options.opacity = drawnitem.options.opacity;
} else if (drawnitem.properties.layerType == 'circle'){
layer = L.circle(drawnitem.latlngs);
layer.options.color = drawnitem.options.color;
layer.options.weight = drawnitem.options.weight;
layer.options.opacity = drawnitem.options.opacity;
} else if (drawnitem.properties.layerType == 'marker'){
layer = L.marker(drawnitem.latlngs);
}
// Create a popup
layer.bindPopup('')
.on('popupclose', popupClose)
.on('popupopen', popupOpen);
// Copy the properties from the saved object
layer.properties = drawnitem.properties;
// Add the layer and the popup to the drawn items group
featureGroup.addLayer(layer);
}
});
}
// Convert the drawnItem to JSON so that it can be persisted
function drawnItemToJSON(layer){
// Calculate feature coordinates
var latlngs;
if (layer instanceof L.Polygon) {
latlngs = layer._defaultShape ? layer._defaultShape() : layer.getLatLngs();
} else {
latlngs = layer.getLatLng();
}
var feature = {
"options": layer.options,
"properties": layer.properties,
"latlngs": latlngs
};
return feature;
}
// **********************************************
// Drawn Items Event Handlers
// **********************************************
// Set the drawnItem's properties and attach a popup
function drawnItemCreated(event){
// Retrieve the drawing layer from the event.
var layer = event.layer;
// Calculate feature coordinates
var latlngs;
if (layer instanceof L.Polygon) {
latlngs = layer._defaultShape ? layer._defaultShape() : layer.getLatLngs();
} else {
latlngs = [layer.getLatLng()];
}
// Format the coordinates
var latlng = latlngs[0];
// Add a properties array to the layer object
layer.properties = {
title: "",
layerType: event.layerType,
latlng: strLatLng(latlng),
id: idLatLng(latlng),
content: ''
};
// Create a popup
layer.bindPopup('')
.on('popupclose', popupClose)
.on('popupopen', popupOpen);
// Add the layer and the popup to the drawn items group
featureGroup.addLayer(layer);
}
// Update the coordinates in the edited drawnItem's properties
function drawnItemEdited(event){
var layers = event.layers;
layers.eachLayer(function(layer) {
// Calculate feature coordinates
var latlngs;
if (layer instanceof L.Polygon) {
latlngs = layer._defaultShape ? layer._defaultShape() : layer.getLatLngs();
} else {
latlngs = [layer.getLatLng()];
}
// Format the coordinates
var latlng = latlngs[0];
// Add a properties array to the layer object
layer.properties.latlng = strLatLng(latlng);
layer.properties.id = idLatLng(latlng);
});
}
// **********************************************
// Popup Event Handlers
// **********************************************
// Function to call when a popup is opened
function popupOpen(e) {
// Close any other open popups
// Calculate the popup contents
var popupContent = L.Util.template(template, e.target.properties);
e.popup.setContent(popupContent);
// Instantiate the popup editor
quill = new Quill("#popup_editor_" + e.target.properties.id, {
theme: 'snow'
});
// Load the editor contents
quill.setContents(e.target.properties.content);
}
// Function to call when a popup is closed
function popupClose(e) {
// Grab form field data
e.target.properties.title = L.DomUtil.get('popup_title_' + e.target.properties.id).value;
// Save the editor contents into the
// the geoJSONMarker's properties
e.target.properties.content = quill.getContents();
quill = null;
// Clear the popup
e.popup.setContent('');
}
// **********************************************
// Utility Functions
// **********************************************
// Truncate value based on number of decimals
var _round = function(num, len) {
return Math.round(num*(Math.pow(10, len)))/(Math.pow(10, len));
};
// Helper method to format LatLng object (x.xxxxxx, y.yyyyyy)
var strLatLng = function(latlng) {
return "("+_round(latlng.lat, 4)+", "+_round(latlng.lng, 4)+")";
};
// Helper method to format LatLng object (x.xxxxxx, y.yyyyyy)
var idLatLng = function(latlng) {
return strLatLng(latlng).replace(/[\s\(\)\.]/g, "").replace(/[,]/g, "_");
};
// **********************************************
// Map Setup
// **********************************************
// Create the map box
var map = new L.Map('map', {
center: new L.LatLng(29.9792, 31.1344),
zoom: 15
});
// Add a feature group to the map to hold drawn items
var featureGroup = L.featureGroup().addTo(map);
// Create tile layer
var tileLayer = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '© <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// Create a draw tool bar and bind it to the feature group
var drawControl = new L.Control.Draw({
draw: {
position: 'topleft',
polygon: true,
polyline: false,
rectangle: true,
circle: true
},
edit: {
featureGroup: featureGroup
}
}).addTo(map);
// Add a listener to the map for items that are added by a user
map.on(L.Draw.Event.CREATED, drawnItemCreated);
// Update all layers with any changes made by a user
map.on(L.Draw.Event.EDITED, drawnItemEdited);
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
#export, #import {
position: absolute;
right:10px;
z-index:400;
background:white;
color:black;
padding:6px;
border-radius:4px;
font-family: 'Helvetica Neue';
cursor: pointer;
font-size:12px;
text-decoration:none;
top:60px;
}
#import {
top:95px;
}
.popup{
width: 300px;
}
.popup_section{
padding: 5px;
padding-bottom: 10px;
background: #f3923b;
margin-bottom: 15px;
}
.popup_label{
font-size: 1.1em;
font-weight: bold;
}
.popup_input{
font-size: 1.1em;
width: 285px;
height: 1.1em;
}
.popup_input_short{
font-size: 1.1em;
width: 195px;
height: 1.1em;
margin-left: 10px;
text-align: center;
}
.popup_title{
font-size: 1.1em;
width: 296px;
height: 1.5em;
margin-bottom: 10px;
}
.popup_editor{
height: 200px;
background: white;
}
[{
"options": {
"stroke": true,
"color": "#f06eaa",
"weight": 4,
"opacity": 0.5,
"fill": true,
"fillColor": null,
"fillOpacity": 0.2,
"clickable": true
},
"properties": {
"title": "Rectangle Title",
"layerType": "rectangle",
"latlng": "(29.97, 31.12)",
"id": "2997_3112",
"content": {
"ops": [{
"insert": "Rectangle Description"
}, {
"attributes": {
"header": 1
},
"insert": "\n"
}, {
"insert": "\n"
}, {
"attributes": {
"underline": true,
"italic": true,
"bold": true
},
"insert": "This is the description."
}, {
"insert": "\n"
}
]
}
},
"latlngs": [{
"lat": 29.974044591534422,
"lng": 31.122508049011234
}, {
"lat": 29.97731598122696,
"lng": 31.122508049011234
}, {
"lat": 29.97731598122696,
"lng": 31.127400398254398
}, {
"lat": 29.974044591534422,
"lng": 31.127400398254398
}
]
}, {
"options": {
"stroke": true,
"color": "#f06eaa",
"weight": 4,
"opacity": 0.5,
"fill": true,
"fillColor": null,
"fillOpacity": 0.2,
"clickable": true
},
"properties": {
"title": "Polygon Title",
"layerType": "polygon",
"latlng": "(29.98, 31.13)",
"id": "2998_3113",
"content": {
"ops": [{
"insert": "Polygon Description"
}, {
"attributes": {
"header": 1
},
"insert": "\n"
}, {
"insert": "\nThis is the description.\n"
}
]
}
},
"latlngs": [{
"lat": 29.98497358578796,
"lng": 31.129674911499027
}, {
"lat": 29.98497358578796,
"lng": 31.129674911499027
}, {
"lat": 29.981033338736204,
"lng": 31.13113403320313
}, {
"lat": 29.981033338736204,
"lng": 31.13113403320313
}, {
"lat": 29.98244589810904,
"lng": 31.141390800476078
}, {
"lat": 29.98244589810904,
"lng": 31.141390800476078
}, {
"lat": 29.989619902644307,
"lng": 31.14036083221436
}, {
"lat": 29.989619902644307,
"lng": 31.14036083221436
}
]
}, {
"options": {
"stroke": true,
"color": "#f06eaa",
"weight": 4,
"opacity": 0.5,
"fill": true,
"fillColor": null,
"fillOpacity": 0.2,
"clickable": true,
"radius": 373.866398252301
},
"properties": {
"title": "Circle Title",
"layerType": "circle",
"latlng": "(29.98, 31.14)",
"id": "2998_3114",
"content": {
"ops": [{
"attributes": {
"link": "https://www.cbc.ca"
},
"insert": "Circle Description"
}, {
"attributes": {
"header": 1
},
"insert": "\n"
}, {
"insert": "\nThis is the description.\n"
}
]
}
},
"latlngs": {
"lat": 29.97545725029985,
"lng": 31.13555431365967
}
}, {
"options": {
"icon": {
"options": {},
"_initHooksCalled": true
}
},
"properties": {
"title": "Marker Title",
"layerType": "marker",
"latlng": "(29.98, 31.14)",
"id": "2998_3114",
"content": {
"ops": [{
"insert": "Marker Description"
}, {
"attributes": {
"header": 1
},
"insert": "\n"
}, {
"insert": "\nThis is the description."
}, {
"attributes": {
"list": "bullet"
},
"insert": "\n"
}, {
"insert": "Another line"
}, {
"attributes": {
"list": "bullet"
},
"insert": "\n"
}
]
}
},
"latlngs": {
"lat": 29.97631227084605,
"lng": 31.143836975097656
}
}
]