<!DOCTYPE html>
<html data-ng-app="Leaflet">
<head>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<!-- Font Awesome -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" />
<!-- Leaflet-->
<link rel="stylesheet" href="//cdn.jsdelivr.net/leaflet/0.7.3/leaflet.css" />
<script src="//cdn.jsdelivr.net/leaflet/0.7.3/leaflet.js"></script>
<!-- Leaflet Draw -->
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.draw/leaflet.draw.css">
<script src="http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"></script>
<!-- ESRI Leaflet -->
<script src="http://cdn-geoweb.s3.amazonaws.com/esri-leaflet/1.0.0-rc.5/esri-leaflet.js"></script>
<!-- Marker Cluster -->
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.css" />
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.Default.css" />
<script src="http://leaflet.github.io/Leaflet.markercluster/dist/leaflet.markercluster-src.js"></script>
<!-- Fullscreen -->
<link href='https://rawgithub.com/brunob/leaflet.fullscreen/master/Control.FullScreen.css' rel='stylesheet' />
<script src="https://rawgithub.com/brunob/leaflet.fullscreen/master/Control.FullScreen.js"></script>
<!-- Context Menu -->
<link rel="stylesheet" href="https://cdn.rawgit.com/aratcliffe/Leaflet.contextmenu/master/dist/leaflet.contextmenu.css">
<script type="text/javascript" src="https://cdn.rawgit.com/aratcliffe/Leaflet.contextmenu/master/dist/leaflet.contextmenu-src.js"></script>
<!-- Bookmarks -->
<link rel="stylesheet" type="text/css" href="https://rawgit.com/w8r/Leaflet.Bookmarks/master/dist/leaflet.bookmarks.css">
<script type="text/javascript" src="https://rawgit.com/w8r/Leaflet.Bookmarks/master/dist/Leaflet.Bookmarks.js"></script>
<!-- Zoom Box -->
<link rel="stylesheet" href="http://consbio.github.io/Leaflet.ZoomBox/L.Control.ZoomBox.css">
<script src="http://consbio.github.io/Leaflet.ZoomBox/L.Control.ZoomBox.js"></script>
<!-- Coordinates Control -->
<link rel="stylesheet" href="http://mrmufflon.github.io/Leaflet.Coordinates/dist/Leaflet.Coordinates-0.1.3.css">
<script type="text/javascript" src="http://mrmufflon.github.io/Leaflet.Coordinates/dist/Leaflet.Coordinates-0.1.3.min.js"></script>
<!-- Measure Control -->
<script src="http://makinacorpus.github.io/Leaflet.MeasureControl/leaflet.measurecontrol.js"></script>
<link rel="stylesheet" href="http://makinacorpus.github.io/Leaflet.MeasureControl/leaflet.measurecontrol.css" />
<!-- Loading Control -->
<link rel="stylesheet" href="https://rawgithub.com/ebrelsford/Leaflet.loading/master/src/Control.Loading.css">
<script src="https://rawgithub.com/ebrelsford/Leaflet.loading/master/src/Control.Loading.js"></script>
<!-- Angular -->
<script data-require="angular.js@1.2.15" data-semver="1.2.15" src="http://code.angularjs.org/1.2.15/angular.js"></script>
<!-- Demo -->
<link rel="stylesheet" href="style.css" />
<script src="_declaration.js"></script>
<script src="service.js"></script>
<script src="directive.js"></script>
<script src="example.js"></script>
</head>
<body data-ng-controller="MyCtrl as vm">
<div leaflet="" options="vm.mapOptions" style="height:640px;width:800px;position:relative;"></div>
</body>
</html>
/* Leaflet Overrides */
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
}
.leaflet-container {
height:100%;
}
.leaflet-control a:hover {
background-color: #f6f6f6;
}
.leaflet-control-zoom,
.leaflet-control-layers,
.leaflet-bar {
border-radius: 0;
box-shadow: 0 2px 6px rgba(50,50,50,.75);
}
.leaflet-bar a,
.leaflet-bar a:hover {
width: 32px;
height: 32px;
line-height: 32px;
}
.leaflet-bar a:first-child,
.leaflet-bar a:last-child {
border-radius:0;
}
/* Bookmark Overrides */
.leaflet-right .leaflet-bookmarks-control {
margin-top: 10px;
margin-right: 10px;
}
.leaflet-bookmarks-control.expanded .bookmarks-icon-wrapper {
background: transparent;
padding: 4px 3px 0.25em 7px;
border-radius: 0 0 0 4px;
position: relative;
}
.leaflet-bookmarks-control .bookmarks-icon {
font-size: 14px;
}
.leaflet-bookmarks-control .bookmarks-header {
height: 1.4em;
}
.leaflet-bookmarks-form .leaflet-bookmarks-form-submit {
margin: 0;
height: 23px;
width: 26px;
cursor: pointer;
}
Because angular-leaflet is just too complex...
{
"records":[
{"id":"1", "name": "Kramer", "geometry":{"type":"Point", "coordinates":[33.71, -117.81]}},
{"id":"2", "name": "Jerry", "geometry":{"type":"Point", "coordinates":[33.75, -117.82]}},
{"id":"3", "name": "George", "geometry":{"type":"Point", "coordinates":[33.70, -117.79]}},
{"id":"4", "name": "Elaine", "geometry":{"type":"Point", "coordinates":[33.78, -117.8]}}
]
}
angular.module('Leaflet', []);
angular.module('Leaflet')
.controller('MyCtrl', function MyCtrl($scope, RecordService) {
var vm = this;
vm.mapOptions= {
basemaps:{},
layers: {},
map: {
scrollWheelZoom: false,
attributionControl: false
},
// Control true | falsy | Object
controls: {
zoom: true,
fullscreen: true,
layers: true,
scale: true,
measure: true,
loading: true,
coordinate: true,
zoomBox: true,
bookmarks: true,
draw: false
}
};
})
.factory('RecordService', function RecordService($http) {
var service = {
getPartials = getPartials
};
return service;
function getPartials() {
return $http.get('partials.json').then(function(value) {
return value.data.records;
});
};
})
;
angular.module('Leaflet')
.controller('LeafletCtrl', function LeafletCtrl($scope, $attrs, $element, $parse, $window, $location, $log, RecordService, MapService) {
// TODO: Configuration:
// Set Base Layers
// Set Layers
// Set Plugins
// Set Symbolization http://colorbrewer2.org/
// Allow for filters
// TODO:
// Leaflet Plugins - Permalink
// Google basemaps vs esri vs mapbox.... all have different pricing models
// Determine what geocoding service to use -- leaning towards google or opencage
/* Aliases */
var vm = this;
var L = $window.L;
/* Expose Controller API to Link Function */
vm.init = init;
/* Options */
var options = $parse($attrs.options)($scope) || {controls:{}, map:{}};
var mapOptions = MapService.mapDefaults();
// TODO: Move to settings
mapOptions.contextmenu = true;
mapOptions.contextmenuItems = [{
text: 'Bookmark this position',
callback: function(evt) {
this.fire('bookmark:new', {
latlng: evt.latlng
});
}
}];
angular.extend(mapOptions, options.map);
var controlOptions = MapService.controlDefaults();
angular.extend(controlOptions, options.controls);
/* Internal Configurations */
var icons = {};
var dataModel = {
data: [],
markers: []
};
var basemaps = {
"Topographic": L.esri.basemapLayer('Topographic'),
"Streets": L.esri.basemapLayer('Streets'),
"National Geographic": L.esri.basemapLayer('NationalGeographic'),
"Oceans": L.esri.basemapLayer('Oceans'),
"Gray": L.esri.basemapLayer('Gray'),
"DarkGray": L.esri.basemapLayer('DarkGray'),
"Imagery": L.esri.basemapLayer('Imagery'),
"Shaded Relief": L.esri.basemapLayer('ShadedRelief')
};
var layers = {};
layers.markers = L.markerClusterGroup();
layers.drawnItems = new L.FeatureGroup();
/* Control Options */
var drawOptions = {
draw: {
position: 'topleft',
polygon: {
title: 'Draw a sexy polygon!',
allowIntersection: false,
drawError: {
color: '#b00b00',
timeout: 1000
},
shapeOptions: {
color: '#bada55'
},
showArea: true
},
polyline: {
metric: false
},
circle: {
shapeOptions: {
color: '#662d91'
}
}
},
edit: {
featureGroup: layers.drawnItems
}
};
/* Watchers */
$scope.$watch(RecordService.partials, function(newValue, oldValue) {
var data = newValue || [];
refreshMarkers(data, dataModel.markers, layers.markers);
});
/* Events */
$scope.$on('$destroy', function() {
// Consider destroying Controls & layers first... .removeFrom(vm.map)
// Consider tearing down event listeners
vm.map.remove();
});
////////////////////
/**
* @function init
*/
function init(id) {
// Get Data
RecordService.getPartials();
// Create map
vm.map = L.map(id, mapOptions);
// Set Map Options/Properties
var view = MapService.getView(); // TODO: consider moving to mapOptions extend function
vm.map.setView(L.latLng(view.lat, view.lng), view.zoom, true);
// Setup Controls (in order of position top-right, top-left, bottom-left, bottom-right)
if (controlOptions.layers) {
var layersControl = L.control.layers(basemaps, layers).addTo(vm.map);
}
if (controlOptions.bookmarks) {
var bookmarksControl = new L.Control.Bookmarks().addTo(vm.map);
}
if (controlOptions.zoom) {
var zoomControl = L.control.zoom().addTo(vm.map);
}
if (controlOptions.zoomBox) {
var zoomBoxControl = L.control.zoomBox().addTo(vm.map);
}
if (controlOptions.fullscreen) {
var fullscreenControl = new L.Control.FullScreen().addTo(vm.map);
}
if (controlOptions.draw) {
var drawControl = new L.Control.Draw(drawOptions).addTo(vm.map);
}
if (controlOptions.measure) {
var measureControl = L.Control.measureControl().addTo(vm.map);
}
if (controlOptions.loading) {
var loadingControl = L.Control.loading().addTo(vm.map);
}
if (controlOptions.scale) {
var scaleControl = L.control.scale().addTo(vm.map);
}
if (controlOptions.coordinate) {
var coordinateControl = L.control.coordinates().addTo(vm.map);
}
// Add Layers
vm.map.addLayer(basemaps.Topographic);
// Register Event Listeners
vm.map.on("load", function(event) {
$log.info('load', event);
//drawMarkers();
});
vm.map.on("click", function(event) {
$log.info('click', event);
});
vm.map.on("moveend", function(event) {
$log.info('moveend', event);
if (!this._loaded) {
return;
}
var view = {
lat: this.getCenter().lat,
lng: this.getCenter().lng,
zoom: this.getZoom()
};
MapService.setView(view);
});
vm.map.on('draw:created', function(event) {
$log.info('draw:created', event);
var type = event.layerType;
var layer = event.layer;
if (type === 'marker') {
layer.bindPopup('A popup!');
}
layers.drawnItems.addLayer(layer);
});
}
/**
* @function drawMarkers
*/
function drawMarkers(markersArray, layer) {
angular.forEach(markersArray, function(value, key) {
layer.addLayer(value);
});
}
/**
* @function eraseMarkers
*/
function eraseMarkers(markersArray, layer) {
angular.forEach(markersArray, function(value, key) {
layer.removeLayer(v);
});
}
/**
* @function createMarkers
*/
function createMarkers(data) {
var arr = [];
angular.forEach(data, function(value, key) {
var marker = L.marker(value.geometry.coordinates, {
title: value.id,
riseOnHover: true,
showOnMouseOver: true
});
marker.bindPopup('<div>' + value.name + '<div>');
arr.push(marker);
});
return arr;
}
/**
* @function refreshMarkers
*/
function refreshMarkers(data, markersArray, layer) {
if (markersArray.length > 0) {
eraseMarkers(markersArray, layer);
}
markersArray = createMarkers(data);
drawMarkers(markersArray, layer);
}
})
.directive('leaflet', function leaflet() {
var _id = 'leaflet-' + new Date().getTime();
return {
restrict: 'AE',
controller: 'LeafletCtrl',
template: function(element, attributes) {
var id = attributes.leaflet || _id;
return '<div id="' + id + '" style="position:absolute;top:0;bottom:0;right:0;left:0;"></div>';
},
controllerAs: 'vm',
link: function(scope, element, attributes, controller) {
var id = attributes.leaflet || _id;
controller.init(id);
}
};
});
angular.module('Leaflet')
.factory('MapService', function MapService($window) {
var localStorage = $window.localStorage || {};
var service = {
getView: getView,
setView: setView,
mapDefaults: mapDefaults,
controlDefaults: controlDefaults
};
return service;
////////////////////
function getView() {
var view = localStorage.mapView;
if (typeof view != 'undefined') {
view = $window.JSON.parse(view || '');
return view;
} else {
return {
lat: 33.7,
lng: -117.8,
zoom: 10
};
}
}
function setView(view) {
localStorage.mapView = $window.JSON.stringify(view);
}
function controlDefaults() {
return {
zoom: true,
fullscreen: true,
layers: true,
scale: true,
measure: false,
loading: true,
coordinate: false,
zoomBox: false,
bookmarks: false,
draw: false
};
}
function mapDefaults() {
return {
// Default
center: [33.7, -117.8],
zoom: 10,
//layers: layers
minZoom: undefined,
maxZoom: undefined,
maxBounds: undefined,
dragging: true,
touchZoom: true,
scrollWheelZoom: true,
doubleClickZoom: true,
boxZoom: true,
trackResize: true,
closePopupOnClick: true,
zoomControl: false,
attributionControl: false
};
}
})
;