//= require augment
//= require leaflet
//= require leaflet.osm
-//= require leaflet.draw
+//= require leaflet.locationfilter
//= require i18n/translations
//= require osm
//= require piwik
});
function startBrowse(sidebarHtml) {
- var browseMode = "auto";
var browseBounds;
var layersById;
var selectedLayer;
return !areasHidden && L.OSM.DataLayer.prototype.isWayArea.apply(this, arguments);
};
- var drawHandler = new L.Rectangle.Draw(map, {title: I18n.t('browse.start_rjs.drag_a_box')});
- map.on('draw:rectangle-created', endDrag);
+ var locationFilter = new L.LocationFilter({
+ enableButton: false,
+ adjustButton: false,
+ onChange: getData
+ }).addTo(map);
$("#sidebar_title").html(I18n.t('browse.start_rjs.data_frame_title'));
$("#sidebar_content").html(sidebarHtml);
map.on("moveend", updateData);
updateData();
- $("#browse_select_view").click(useMap);
+ $("#browse_filter_toggle").toggle(enableFilter, disableFilter);
$("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.hide_areas'));
$("#browse_hide_areas_box").toggle(hideAreas, showAreas);
function updateData() {
- if (browseMode == "auto") {
+ if (!locationFilter.isEnabled()) {
if (map.getZoom() >= 15) {
- useMap();
+ var bounds = map.getBounds();
+ if (!browseBounds || !browseBounds.contains(bounds)) {
+ browseBounds = bounds;
+ getData();
+ }
} else {
setStatus(I18n.t('browse.start_rjs.zoom_or_select'));
}
$("#sidebar").one("closed", function () {
map.removeLayer(dataLayer);
+ map.removeLayer(locationFilter);
map.off("moveend", updateData);
- map.off('draw:rectangle-created', endDrag);
- drawHandler.disable();
- });
-
- $("#browse_select_box").click(function () {
- $("#browse_select_box").html(I18n.t('browse.start_rjs.drag_a_box'));
-
- drawHandler.enable();
-
- return false;
});
- function useMap() {
- var bounds = map.getBounds();
-
- if (!browseBounds || !browseBounds.contains(bounds)) {
- browseBounds = bounds;
- browseMode = "auto";
-
- getData();
-
- $("#browse_select_view").hide();
- }
+ function enableFilter() {
+ $("#browse_filter_toggle").html(I18n.t('browse.start_rjs.view_data'));
+ locationFilter.enable();
+ getData();
+ }
- return false;
+ function disableFilter() {
+ $("#browse_filter_toggle").html(I18n.t('browse.start_rjs.manually_select'));
+ locationFilter.disable();
+ getData();
}
function hideAreas() {
$("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.show_areas'));
-
areasHidden = true;
-
getData();
}
function showAreas() {
$("#browse_hide_areas_box").html(I18n.t('browse.start_rjs.hide_areas'));
-
areasHidden = false;
-
getData();
}
- function endDrag(e) {
- browseBounds = e.rect.getBounds();
- browseMode = "manual";
-
- getData();
-
- $("#browse_select_box").html(I18n.t('browse.start_rjs.manually_select'));
- $("#browse_select_view").show();
-
- drawHandler.disable();
- }
-
function displayFeatureWarning(count, limit, callback) {
clearStatus();
}
function getData() {
- var size = browseBounds.getSize();
+ var bounds = locationFilter.isEnabled() ? locationFilter.getBounds() : map.getBounds();
+ var size = bounds.getSize();
if (size > OSM.MAX_REQUEST_AREA) {
setStatus(I18n.t("browse.start_rjs.unable_to_load_size", { max_bbox_size: OSM.MAX_REQUEST_AREA, bbox_size: size }));
setStatus(I18n.t('browse.start_rjs.loading'));
- var url = "/api/" + OSM.API_VERSION + "/map?bbox=" + browseBounds.toBBOX();
+ var url = "/api/" + OSM.API_VERSION + "/map?bbox=" + bounds.toBBOX();
/*
* Modern browsers are quite happy showing far more than 100 features in
onSelect(layer);
- if (browseMode != "auto") {
+ if (locationFilter.isEnabled()) {
map.panTo(layer.getBounds().getCenter());
}
}
function startExport(sidebarHtml) {
- var marker, rectangle;
+ var marker;
- var drawHandler = new L.Rectangle.Draw(map, {title: I18n.t('export.start_rjs.drag_a_box')});
- map.on('draw:rectangle-created', endDrag);
+ var locationFilter = new L.LocationFilter({
+ enableButton: false,
+ adjustButton: false,
+ onChange: filterChanged
+ }).addTo(map);
map.on("moveend", mapMoved);
map.on("baselayerchange", htmlUrlChanged);
$("#maxlat,#minlon,#maxlon,#minlat").change(boundsChanged);
- $("#drag_box").click(startDrag);
+ $("#drag_box").click(enableFilter);
$("#add_marker").click(startMarker);
$("#sidebar").one("closed", function () {
$("body").removeClass("site-export").addClass("site-index");
- clearBox();
+ map.removeLayer(locationFilter);
clearMarker();
map.off("moveend", mapMoved);
map.off("baselayerchange", htmlUrlChanged);
- map.off('draw:rectangle-created', endDrag);
-
- drawHandler.disable();
});
function getBounds() {
function boundsChanged() {
var bounds = getBounds();
- map.off("moveend", mapMoved);
map.fitBounds(bounds);
- clearBox();
- drawBox(bounds);
+ enableFilter();
+ locationFilter.setBounds(bounds);
validateControls();
mapnikSizeChanged();
}
- function startDrag() {
- $("#drag_box").html(I18n.t('export.start_rjs.drag_a_box'));
-
- clearBox();
- drawHandler.enable();
- }
-
- function endDrag(e) {
- var bounds = e.rect.getBounds();
-
- map.off("moveend", mapMoved);
- setBounds(bounds);
- drawBox(bounds);
- validateControls();
-
- $("#drag_box").html(I18n.t('export.start_rjs.manually_select'));
+ function enableFilter() {
+ $("#drag_box").hide();
+ locationFilter.enable();
}
- function transformComplete(event) {
- setBounds(event.feature.geometry.bounds);
+ function filterChanged() {
+ setBounds(locationFilter.getBounds());
validateControls();
}
}
function mapMoved() {
- setBounds(map.getBounds());
- validateControls();
+ if (!locationFilter.isEnabled()) {
+ setBounds(map.getBounds());
+ validateControls();
+ }
}
function setBounds(bounds) {
htmlUrlChanged();
}
- function clearBox() {
- if (rectangle) {
- map.removeLayer(rectangle);
- }
- rectangle = null;
- }
-
- function drawBox(bounds) {
- rectangle = L.rectangle(bounds);
- rectangle.addTo(map);
- }
-
function validateControls() {
var bounds = getBounds();
<div>
<div style="text-align: center">
<p style="margin-top: 10px; margin-bottom: 20px">
- <a id="browse_select_view" href="#"><%= t'browse.start.view_data' %></a>
+ <a id="browse_filter_toggle" href="#"><%= t'browse.start_rjs.manually_select' %></a>
<br />
- <a id="browse_select_box" href="#"><%= t'browse.start.manually_select' %></a>
- <br />
- <a id="browse_hide_areas_box" href="#"><%= t'browse.start.hide_areas' %></a>
+ <a id="browse_hide_areas_box" href="#"><%= t'browse.start_rjs.hide_areas' %></a>
</p>
</div>
<div id="browse_status" style="text-align: center; display: none">
<%= stylesheet_link_tag "large-#{dir}", :media => "screen and (min-width: 642px)" %>
<%= stylesheet_link_tag "print-#{dir}", :media => "print" %>
<%= stylesheet_link_tag "leaflet" %>
- <%= stylesheet_link_tag "leaflet.draw" %>
+ <%= stylesheet_link_tag "leaflet.locationfilter" %>
<!--[if IE]>
<%= stylesheet_link_tag "leaflet.ie" %>
- <%= stylesheet_link_tag "leaflet.draw.ie" %>
<%= stylesheet_link_tag "large-#{dir}", :media => "screen" %>
<![endif]--> <!-- IE is totally broken with CSS media queries -->
<%= favicon_link_tag "favicon.ico" %>
config.assets.precompile += %w( large-ltr.css small-ltr.css print-ltr.css )
config.assets.precompile += %w( large-rtl.css small-rtl.css print-rtl.css )
config.assets.precompile += %w( browse.css theme/openstreetmap/style.css )
- config.assets.precompile += %w( leaflet.css leaflet.ie.css leaflet.draw.css leaflet.draw.ie.css )
+ config.assets.precompile += %w( leaflet.css leaflet.ie.css leaflet.locationfilter.css )
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
relation_title: "Relation: %{relation_name}"
download_xml: "Download XML"
view_history: "View history"
- start:
- view_data: "View data for current map view"
- manually_select: "Manually select a different area"
start_rjs:
data_layer_name: "Browse Map Data"
data_frame_title: "Data"
zoom_or_select: "Zoom in or select an area of the map to view"
- drag_a_box: "Drag a box on the map to select an area"
+ view_data: "View data for current map view"
manually_select: "Manually select a different area"
hide_areas: "Hide areas"
show_areas: "Show areas"
+++ /dev/null
-/* Leaflet controls */
-
-.leaflet-control-draw {
- background-color: rgba(0, 0, 0, 0.25);
- -webkit-border-radius: 7px;
- -moz-border-radius: 7px;
- border-radius: 7px;
- padding: 5px;
-}
-
-.leaflet-control-draw a {
- background-color: rgba(255, 255, 255, 0.75);
- background-position: 50% 50%;
- background-repeat: no-repeat;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- display: block;
- margin-top: 5px;
- width: 19px;
- height: 19px;
-}
-
-.leaflet-control-draw a:first-child{
- margin-top: 0;
-}
-
-.leaflet-control-draw a:hover {
- background-color: #fff;
-}
-
-.leaflet-touch .leaflet-control-draw a {
- width: 27px;
- height: 27px;
-}
-
-.leaflet-control-draw-polyline {
- background-image: url(images/draw-polyline.png);
-}
-
-.leaflet-control-draw-polygon {
- background-image: url(images/draw-polygon.png);
-}
-
-.leaflet-control-draw-rectangle {
- background-image: url(images/draw-rectangle.png);
-}
-
-.leaflet-control-draw-circle {
- background-image: url(images/draw-circle.png);
-}
-
-.leaflet-control-draw-marker {
- background-image: url(images/draw-marker-icon.png);
-}
-
-.leaflet-mouse-marker {
- background-color: #fff;
- cursor: crosshair;
-}
-
-.leaflet-draw-label {
- background-color: #fff;
- border: 1px solid #ccc;
- color: #222;
- font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
- margin-left: 20px;
- margin-top: -21px;
- padding: 2px 4px;
- position: absolute;
- white-space: nowrap;
- z-index: 6;
-}
-
-.leaflet-error-draw-label {
- background-color: #F2DEDE;
- border-color: #E6B6BD;
- color: #B94A48;
-}
-
-.leaflet-draw-label-single {
- margin-top: -12px
-}
-
-.leaflet-draw-label-subtext {
- color: #999;
-}
-
-.leaflet-draw-guide-dash {
- font-size: 1%;
- opacity: 0.6;
- position: absolute;
- width: 5px;
- height: 5px;
-}
-
-.leaflet-flash-anim {
- -webkit-animation-duration: 0.66s;
- -moz-animation-duration: 0.66s;
- -o-animation-duration: 0.66s;
- animation-duration: 0.66s;
- -webkit-animation-fill-mode: both;
- -moz-animation-fill-mode: both;
- -o-animation-fill-mode: both;
- animation-fill-mode: both;
- -webkit-animation-name: leaflet-flash;
- -moz-animation-name: leaflet-flash;
- -o-animation-name: leaflet-flash;
- animation-name: leaflet-flash;
-}
-
-@-webkit-keyframes leaflet-flash {
- 0%, 50%, 100% { opacity: 1; }
- 25%, 75% { opacity: 0.3; }
-}
-
-@-moz-keyframes leaflet-flash {
- 0%, 50%, 100% { opacity: 1; }
- 25%, 75% { opacity: 0.3; }
-}
-
-@-o-keyframes leaflet-flash {
- 0%, 50%, 100% { opacity: 1; }
- 25%, 75% { opacity: 0.3; }
-}
-
-@keyframes leaflet-flash {
- 0%, 50%, 100% { opacity: 1; }
- 25%, 75% { opacity: 0; }
-}
\ No newline at end of file
+++ /dev/null
-/* Conditional stylesheet for IE. */
-
-.leaflet-control-draw {
- filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#3F000000', EndColorStr='#3F000000');
-}
-
-.leaflet-control-draw a {
- background-color: #eee;
- filter: alpha(opacity=90);
-}
\ No newline at end of file
+++ /dev/null
-/*
- Copyright (c) 2012, Smartrak, Jacob Toye
- Leaflet.draw is an open-source JavaScript library for drawing shapes/markers on leaflet powered maps.
- https://github.com/jacobtoye/Leaflet.draw
-*/
-(function (window, undefined) {
-
-L.drawVersion = '0.1.4';
-
-L.Util.extend(L.LineUtil, {
- // Checks to see if two line segments intersect. Does not handle degenerate cases.
- // http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf
- segmentsIntersect: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2, /*Point*/ p3) {
- return this._checkCounterclockwise(p, p2, p3) !==
- this._checkCounterclockwise(p1, p2, p3) &&
- this._checkCounterclockwise(p, p1, p2) !==
- this._checkCounterclockwise(p, p1, p3);
- },
-
- // check to see if points are in counterclockwise order
- _checkCounterclockwise: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) {
- return (p2.y - p.y) * (p1.x - p.x) > (p1.y - p.y) * (p2.x - p.x);
- }
-});
-
-L.Polyline.include({
- // Check to see if this polyline has any linesegments that intersect.
- // NOTE: does not support detecting intersection for degenerate cases.
- intersects: function () {
- var points = this._originalPoints,
- len = points ? points.length : 0,
- i, j, p, p1, p2, p3;
-
- if (this._tooFewPointsForIntersection()) {
- return false;
- }
-
- for (i = len - 1; i >= 3; i--) {
- p = points[i - 1];
- p1 = points[i];
-
-
- if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {
- return true;
- }
- }
-
- return false;
- },
-
- // Check for intersection if new latlng was added to this polyline.
- // NOTE: does not support detecting intersection for degenerate cases.
- newLatLngIntersects: function (latlng, skipFirst) {
- // Cannot check a polyline for intersecting lats/lngs when not added to the map
- if (!this._map) {
- return false;
- }
-
- return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);
- },
-
- // Check for intersection if new point was added to this polyline.
- // newPoint must be a layer point.
- // NOTE: does not support detecting intersection for degenerate cases.
- newPointIntersects: function (newPoint, skipFirst) {
- var points = this._originalPoints,
- len = points ? points.length : 0,
- lastPoint = points ? points[len - 1] : null,
- // The previous previous line segment. Previous line segement doesn't need testing.
- maxIndex = len - 2;
-
- if (this._tooFewPointsForIntersection(1)) {
- return false;
- }
-
- return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);
- },
-
- // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).
- // Cannot have intersection when < 3 line segments (< 4 points)
- _tooFewPointsForIntersection: function (extraPoints) {
- var points = this._originalPoints,
- len = points ? points.length : 0;
- // Increment length by extraPoints if present
- len += extraPoints || 0;
-
- return !this._originalPoints || len <= 3;
- },
-
- // Checks a line segment intersections with any line segements before its predecessor.
- // Don't need to check the predecessor as will never intersect.
- _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {
- var points = this._originalPoints,
- p2, p3;
-
- minIndex = minIndex || 0;
-
- // Check all previous line segments (beside the immediately previous) for intersections
- for (var j = maxIndex; j > minIndex; j--) {
- p2 = points[j - 1];
- p3 = points[j];
-
- if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {
- return true;
- }
- }
-
- return false;
- }
-});
-
-L.Polygon.include({
- // Checks a polygon for any intersecting line segments. Ignores holes.
- intersects: function () {
- var polylineIntersects,
- points = this._originalPoints,
- len, firstPoint, lastPoint, maxIndex;
-
- if (this._tooFewPointsForIntersection()) {
- return false;
- }
-
- polylineIntersects = L.Polyline.prototype.intersects.call(this);
-
- // If already found an intersection don't need to check for any more.
- if (polylineIntersects) {
- return true;
- }
-
- len = points.length;
- firstPoint = points[0];
- lastPoint = points[len - 1];
- maxIndex = len - 2;
-
- // Check the line segment between last and first point. Don't need to check the first line segment (minIndex = 1)
- return this._lineSegmentsIntersectsRange(lastPoint, firstPoint, maxIndex, 1);
- }
-});
-
-L.Handler.Draw = L.Handler.extend({
- includes: L.Mixin.Events,
-
- initialize: function (map, options) {
- this._map = map;
- this._container = map._container;
- this._overlayPane = map._panes.overlayPane;
- this._popupPane = map._panes.popupPane;
-
- // Merge default shapeOptions options with custom shapeOptions
- if (options && options.shapeOptions) {
- options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
- }
- L.Util.extend(this.options, options);
- },
-
- enable: function () {
- this.fire('activated');
- L.Handler.prototype.enable.call(this);
- },
-
- addHooks: function () {
- if (this._map) {
- L.DomUtil.disableTextSelection();
-
- this._label = L.DomUtil.create('div', 'leaflet-draw-label', this._popupPane);
- this._singleLineLabel = false;
-
- L.DomEvent.addListener(this._container, 'keyup', this._cancelDrawing, this);
- }
- },
-
- removeHooks: function () {
- if (this._map) {
- L.DomUtil.enableTextSelection();
-
- this._popupPane.removeChild(this._label);
- delete this._label;
-
- L.DomEvent.removeListener(this._container, 'keyup', this._cancelDrawing);
- }
- },
-
- _updateLabelText: function (labelText) {
- labelText.subtext = labelText.subtext || '';
-
- // update the vertical position (only if changed)
- if (labelText.subtext.length === 0 && !this._singleLineLabel) {
- L.DomUtil.addClass(this._label, 'leaflet-draw-label-single');
- this._singleLineLabel = true;
- }
- else if (labelText.subtext.length > 0 && this._singleLineLabel) {
- L.DomUtil.removeClass(this._label, 'leaflet-draw-label-single');
- this._singleLineLabel = false;
- }
-
- this._label.innerHTML =
- (labelText.subtext.length > 0 ? '<span class="leaflet-draw-label-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
- '<span>' + labelText.text + '</span>';
- },
-
- _updateLabelPosition: function (pos) {
- L.DomUtil.setPosition(this._label, pos);
- },
-
- // Cancel drawing when the escape key is pressed
- _cancelDrawing: function (e) {
- if (e.keyCode === 27) {
- this.disable();
- }
- }
-});
-
-L.Polyline.Draw = L.Handler.Draw.extend({
- Poly: L.Polyline,
-
- options: {
- allowIntersection: true,
- drawError: {
- color: '#b00b00',
- message: '<strong>Error:</strong> shape edges cannot cross!',
- timeout: 2500
- },
- icon: new L.DivIcon({
- iconSize: new L.Point(8, 8),
- className: 'leaflet-div-icon leaflet-editing-icon'
- }),
- guidelineDistance: 20,
- shapeOptions: {
- stroke: true,
- color: '#f06eaa',
- weight: 4,
- opacity: 0.5,
- fill: false,
- clickable: true
- },
- zIndexOffset: 2000 // This should be > than the highest z-index any map layers
- },
-
- initialize: function (map, options) {
- // Merge default drawError options with custom options
- if (options && options.drawError) {
- options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
- }
- L.Handler.Draw.prototype.initialize.call(this, map, options);
- },
-
- addHooks: function () {
- L.Handler.Draw.prototype.addHooks.call(this);
- if (this._map) {
- this._markers = [];
-
- this._markerGroup = new L.LayerGroup();
- this._map.addLayer(this._markerGroup);
-
- this._poly = new L.Polyline([], this.options.shapeOptions);
-
- this._updateLabelText(this._getLabelText());
-
- // Make a transparent marker that will used to catch click events. These click
- // events will create the vertices. We need to do this so we can ensure that
- // we can create vertices over other map layers (markers, vector layers). We
- // also do not want to trigger any click handlers of objects we are clicking on
- // while drawing.
- if (!this._mouseMarker) {
- this._mouseMarker = L.marker(this._map.getCenter(), {
- icon: L.divIcon({
- className: 'leaflet-mouse-marker',
- iconAnchor: [20, 20],
- iconSize: [40, 40]
- }),
- opacity: 0,
- zIndexOffset: this.options.zIndexOffset
- });
- }
-
- this._mouseMarker
- .on('click', this._onClick, this)
- .addTo(this._map);
-
- this._map.on('mousemove', this._onMouseMove, this);
- }
- },
-
- removeHooks: function () {
- L.Handler.Draw.prototype.removeHooks.call(this);
-
- this._clearHideErrorTimeout();
-
- this._cleanUpShape();
-
- // remove markers from map
- this._map.removeLayer(this._markerGroup);
- delete this._markerGroup;
- delete this._markers;
-
- this._map.removeLayer(this._poly);
- delete this._poly;
-
- this._mouseMarker.off('click', this._onClick);
- this._map.removeLayer(this._mouseMarker);
- delete this._mouseMarker;
-
- // clean up DOM
- this._clearGuides();
-
- this._map.off('mousemove', this._onMouseMove);
- },
-
- _finishShape: function () {
- if (!this.options.allowIntersection && this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], true)) {
- this._showErrorLabel();
- return;
- }
- if (!this._shapeIsValid()) {
- this._showErrorLabel();
- return;
- }
-
- this._map.fire(
- 'draw:poly-created',
- { poly: new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions) }
- );
- this.disable();
- },
-
- //Called to verify the shape is valid when the user tries to finish it
- //Return false if the shape is not valid
- _shapeIsValid: function () {
- return true;
- },
-
- _onMouseMove: function (e) {
- var newPos = e.layerPoint,
- latlng = e.latlng,
- markerCount = this._markers.length;
-
- // Save latlng
- this._currentLatLng = latlng;
-
- // update the label
- this._updateLabelPosition(newPos);
-
- if (markerCount > 0) {
- this._updateLabelText(this._getLabelText());
- // draw the guide line
- this._clearGuides();
- this._drawGuide(
- this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
- newPos
- );
- }
-
- // Update the mouse marker position
- this._mouseMarker.setLatLng(latlng);
-
- L.DomEvent.preventDefault(e.originalEvent);
- },
-
- _onClick: function (e) {
- var latlng = e.target.getLatLng(),
- markerCount = this._markers.length;
-
- if (markerCount > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
- this._showErrorLabel();
- return;
- }
- else if (this._errorShown) {
- this._hideErrorLabel();
- }
-
- this._markers.push(this._createMarker(latlng));
-
- this._poly.addLatLng(latlng);
-
- if (this._poly.getLatLngs().length === 2) {
- this._map.addLayer(this._poly);
- }
-
- this._updateMarkerHandler();
-
- this._vertexAdded(latlng);
- },
-
- _updateMarkerHandler: function () {
- // The last marker shold have a click handler to close the polyline
- if (this._markers.length > 1) {
- this._markers[this._markers.length - 1].on('click', this._finishShape, this);
- }
-
- // Remove the old marker click handler (as only the last point should close the polyline)
- if (this._markers.length > 2) {
- this._markers[this._markers.length - 2].off('click', this._finishShape);
- }
- },
-
- _createMarker: function (latlng) {
- var marker = new L.Marker(latlng, {
- icon: this.options.icon,
- zIndexOffset: this.options.zIndexOffset * 2
- });
-
- this._markerGroup.addLayer(marker);
-
- return marker;
- },
-
- _drawGuide: function (pointA, pointB) {
- var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
- i,
- fraction,
- dashPoint,
- dash;
-
- //create the guides container if we haven't yet (TODO: probaly shouldn't do this every time the user starts to draw?)
- if (!this._guidesContainer) {
- this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
- }
-
- //draw a dash every GuildeLineDistance
- for (i = this.options.guidelineDistance; i < length; i += this.options.guidelineDistance) {
- //work out fraction along line we are
- fraction = i / length;
-
- //calculate new x,y point
- dashPoint = {
- x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
- y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
- };
-
- //add guide dash to guide container
- dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
- dash.style.backgroundColor =
- !this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;
-
- L.DomUtil.setPosition(dash, dashPoint);
- }
- },
-
- _updateGuideColor: function (color) {
- if (this._guidesContainer) {
- for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
- this._guidesContainer.childNodes[i].style.backgroundColor = color;
- }
- }
- },
-
- // removes all child elements (guide dashes) from the guides container
- _clearGuides: function () {
- if (this._guidesContainer) {
- while (this._guidesContainer.firstChild) {
- this._guidesContainer.removeChild(this._guidesContainer.firstChild);
- }
- }
- },
-
- _updateLabelText: function (labelText) {
- if (!this._errorShown) {
- L.Handler.Draw.prototype._updateLabelText.call(this, labelText);
- }
- },
-
- _getLabelText: function () {
- var labelText,
- distance,
- distanceStr;
-
- if (this._markers.length === 0) {
- labelText = {
- text: 'Click to start drawing line.'
- };
- } else {
- // calculate the distance from the last fixed point to the mouse position
- distance = this._measurementRunningTotal + this._currentLatLng.distanceTo(this._markers[this._markers.length - 1].getLatLng());
- // show metres when distance is < 1km, then show km
- distanceStr = distance > 1000 ? (distance / 1000).toFixed(2) + ' km' : Math.ceil(distance) + ' m';
-
- if (this._markers.length === 1) {
- labelText = {
- text: 'Click to continue drawing line.',
- subtext: distanceStr
- };
- } else {
- labelText = {
- text: 'Click last point to finish line.',
- subtext: distanceStr
- };
- }
- }
- return labelText;
- },
-
- _showErrorLabel: function () {
- this._errorShown = true;
-
- // Update label
- L.DomUtil.addClass(this._label, 'leaflet-error-draw-label');
- L.DomUtil.addClass(this._label, 'leaflet-flash-anim');
- L.Handler.Draw.prototype._updateLabelText.call(this, { text: this.options.drawError.message });
-
- // Update shape
- this._updateGuideColor(this.options.drawError.color);
- this._poly.setStyle({ color: this.options.drawError.color });
-
- // Hide the error after 2 seconds
- this._clearHideErrorTimeout();
- this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorLabel, this), this.options.drawError.timeout);
- },
-
- _hideErrorLabel: function () {
- this._errorShown = false;
-
- this._clearHideErrorTimeout();
-
- // Revert label
- L.DomUtil.removeClass(this._label, 'leaflet-error-draw-label');
- L.DomUtil.removeClass(this._label, 'leaflet-flash-anim');
- this._updateLabelText(this._getLabelText());
-
- // Revert shape
- this._updateGuideColor(this.options.shapeOptions.color);
- this._poly.setStyle({ color: this.options.shapeOptions.color });
- },
-
- _clearHideErrorTimeout: function () {
- if (this._hideErrorTimeout) {
- clearTimeout(this._hideErrorTimeout);
- this._hideErrorTimeout = null;
- }
- },
-
- _vertexAdded: function (latlng) {
- if (this._markers.length === 1) {
- this._measurementRunningTotal = 0;
- }
- else {
- this._measurementRunningTotal +=
- latlng.distanceTo(this._markers[this._markers.length - 2].getLatLng());
- }
- },
-
- _cleanUpShape: function () {
- if (this._markers.length > 0) {
- this._markers[this._markers.length - 1].off('click', this._finishShape);
- }
- }
-});
-
-L.Polygon.Draw = L.Polyline.Draw.extend({
- Poly: L.Polygon,
-
- options: {
- shapeOptions: {
- stroke: true,
- color: '#f06eaa',
- weight: 4,
- opacity: 0.5,
- fill: true,
- fillColor: null, //same as color by default
- fillOpacity: 0.2,
- clickable: false
- }
- },
-
- _updateMarkerHandler: function () {
- // The first marker shold have a click handler to close the polygon
- if (this._markers.length === 1) {
- this._markers[0].on('click', this._finishShape, this);
- }
- },
-
- _getLabelText: function () {
- var text;
- if (this._markers.length === 0) {
- text = 'Click to start drawing shape.';
- } else if (this._markers.length < 3) {
- text = 'Click to continue drawing shape.';
- } else {
- text = 'Click first point to close this shape.';
- }
- return {
- text: text
- };
- },
-
- _shapeIsValid: function () {
- return this._markers.length >= 3;
- },
-
- _vertexAdded: function (latlng) {
- //calc area here
- },
-
- _cleanUpShape: function () {
- if (this._markers.length > 0) {
- this._markers[0].off('click', this._finishShape);
- }
- }
-});
-
-L.SimpleShape = {};
-
-L.SimpleShape.Draw = L.Handler.Draw.extend({
- addHooks: function () {
- L.Handler.Draw.prototype.addHooks.call(this);
- if (this._map) {
- this._map.dragging.disable();
- //TODO refactor: move cursor to styles
- this._container.style.cursor = 'crosshair';
-
- this._updateLabelText({ text: this._initialLabelText });
-
- this._map
- .on('mousedown', this._onMouseDown, this)
- .on('mousemove', this._onMouseMove, this);
-
- }
- },
-
- removeHooks: function () {
- L.Handler.Draw.prototype.removeHooks.call(this);
- if (this._map) {
- this._map.dragging.enable();
- //TODO refactor: move cursor to styles
- this._container.style.cursor = '';
-
- this._map
- .off('mousedown', this._onMouseDown, this)
- .off('mousemove', this._onMouseMove, this);
-
- L.DomEvent.off(document, 'mouseup', this._onMouseUp);
-
- // If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
- if (this._shape) {
- this._map.removeLayer(this._shape);
- delete this._shape;
- }
- }
- this._isDrawing = false;
- },
-
- _onMouseDown: function (e) {
- this._isDrawing = true;
- this._startLatLng = e.latlng;
-
- L.DomEvent
- .on(document, 'mouseup', this._onMouseUp, this)
- .preventDefault(e.originalEvent);
- },
-
- _onMouseMove: function (e) {
- var layerPoint = e.layerPoint,
- latlng = e.latlng;
-
- this._updateLabelPosition(layerPoint);
- if (this._isDrawing) {
- this._updateLabelText({ text: 'Release mouse to finish drawing.' });
- this._drawShape(latlng);
- }
- },
-
- _onMouseUp: function (e) {
- if (this._shape) {
- this._fireCreatedEvent();
- }
-
- this.disable();
- }
-});
-
-L.Circle.Draw = L.SimpleShape.Draw.extend({
- options: {
- shapeOptions: {
- stroke: true,
- color: '#f06eaa',
- weight: 4,
- opacity: 0.5,
- fill: true,
- fillColor: null, //same as color by default
- fillOpacity: 0.2,
- clickable: true
- }
- },
-
- _initialLabelText: 'Click and drag to draw circle.',
-
- _drawShape: function (latlng) {
- if (!this._shape) {
- this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(latlng), this.options.shapeOptions);
- this._map.addLayer(this._shape);
- } else {
- this._shape.setRadius(this._startLatLng.distanceTo(latlng));
- }
- },
-
- _fireCreatedEvent: function () {
- this._map.fire(
- 'draw:circle-created',
- { circ: new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions) }
- );
- }
-});
-
-L.Rectangle.Draw = L.SimpleShape.Draw.extend({
- options: {
- shapeOptions: {
- stroke: true,
- color: '#f06eaa',
- weight: 4,
- opacity: 0.5,
- fill: true,
- fillColor: null, //same as color by default
- fillOpacity: 0.2,
- clickable: true
- }
- },
-
- _initialLabelText: 'Click and drag to draw rectangle.',
-
- _drawShape: function (latlng) {
- if (!this._shape) {
- this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
- this._map.addLayer(this._shape);
- } else {
- this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
- }
- },
-
- _fireCreatedEvent: function () {
- this._map.fire(
- 'draw:rectangle-created',
- { rect: new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions) }
- );
- }
-});
-
-L.Marker.Draw = L.Handler.Draw.extend({
- options: {
- icon: new L.Icon.Default(),
- zIndexOffset: 2000 // This should be > than the highest z-index any markers
- },
-
- addHooks: function () {
- L.Handler.Draw.prototype.addHooks.call(this);
-
- if (this._map) {
- this._updateLabelText({ text: 'Click map to place marker.' });
- this._map.on('mousemove', this._onMouseMove, this);
- }
- },
-
- removeHooks: function () {
- L.Handler.Draw.prototype.removeHooks.call(this);
-
- if (this._map) {
- if (this._marker) {
- this._marker.off('click', this._onClick);
- this._map
- .off('click', this._onClick)
- .removeLayer(this._marker);
- delete this._marker;
- }
-
- this._map.off('mousemove', this._onMouseMove);
- }
- },
-
- _onMouseMove: function (e) {
- var newPos = e.layerPoint,
- latlng = e.latlng;
-
- this._updateLabelPosition(newPos);
-
- if (!this._marker) {
- this._marker = new L.Marker(latlng, {
- icon: this.options.icon,
- zIndexOffset: this.options.zIndexOffset
- });
- // Bind to both marker and map to make sure we get the click event.
- this._marker.on('click', this._onClick, this);
- this._map
- .on('click', this._onClick, this)
- .addLayer(this._marker);
- }
- else {
- this._marker.setLatLng(latlng);
- }
- },
-
- _onClick: function (e) {
- this._map.fire(
- 'draw:marker-created',
- { marker: new L.Marker(this._marker.getLatLng(), { icon: this.options.icon }) }
- );
- this.disable();
- }
-});
-
-L.Map.mergeOptions({
- drawControl: false
-});
-
-L.Control.Draw = L.Control.extend({
-
- options: {
- position: 'topleft',
- polyline: {
- title: 'Draw a polyline'
- },
- polygon: {
- title: 'Draw a polygon'
- },
- rectangle: {
- title: 'Draw a rectangle'
- },
- circle: {
- title: 'Draw a circle'
- },
- marker: {
- title: 'Add a marker'
- }
- },
-
- handlers: {},
-
- initialize: function (options) {
- L.Util.extend(this.options, options);
- },
-
- onAdd: function (map) {
- var className = 'leaflet-control-draw',
- container = L.DomUtil.create('div', className);
-
- if (this.options.polyline) {
- this.handlers.polyline = new L.Polyline.Draw(map, this.options.polyline);
- this._createButton(
- this.options.polyline.title,
- className + '-polyline',
- container,
- this.handlers.polyline.enable,
- this.handlers.polyline
- );
- this.handlers.polyline.on('activated', this._disableInactiveModes, this);
- }
-
- if (this.options.polygon) {
- this.handlers.polygon = new L.Polygon.Draw(map, this.options.polygon);
- this._createButton(
- this.options.polygon.title,
- className + '-polygon',
- container,
- this.handlers.polygon.enable,
- this.handlers.polygon
- );
- this.handlers.polygon.on('activated', this._disableInactiveModes, this);
- }
-
- if (this.options.rectangle) {
- this.handlers.rectangle = new L.Rectangle.Draw(map, this.options.rectangle);
- this._createButton(
- this.options.rectangle.title,
- className + '-rectangle',
- container,
- this.handlers.rectangle.enable,
- this.handlers.rectangle
- );
- this.handlers.rectangle.on('activated', this._disableInactiveModes, this);
- }
-
- if (this.options.circle) {
- this.handlers.circle = new L.Circle.Draw(map, this.options.circle);
- this._createButton(
- this.options.circle.title,
- className + '-circle',
- container,
- this.handlers.circle.enable,
- this.handlers.circle
- );
- this.handlers.circle.on('activated', this._disableInactiveModes, this);
- }
-
- if (this.options.marker) {
- this.handlers.marker = new L.Marker.Draw(map, this.options.marker);
- this._createButton(
- this.options.marker.title,
- className + '-marker',
- container,
- this.handlers.marker.enable,
- this.handlers.marker
- );
- this.handlers.marker.on('activated', this._disableInactiveModes, this);
- }
-
- return container;
- },
-
- _createButton: function (title, className, container, fn, context) {
- var link = L.DomUtil.create('a', className, container);
- link.href = '#';
- link.title = title;
-
- L.DomEvent
- .on(link, 'click', L.DomEvent.stopPropagation)
- .on(link, 'mousedown', L.DomEvent.stopPropagation)
- .on(link, 'dblclick', L.DomEvent.stopPropagation)
- .on(link, 'click', L.DomEvent.preventDefault)
- .on(link, 'click', fn, context);
-
- return link;
- },
-
- // Need to disable the drawing modes if user clicks on another without disabling the current mode
- _disableInactiveModes: function () {
- for (var i in this.handlers) {
- // Check if is a property of this object and is enabled
- if (this.handlers.hasOwnProperty(i) && this.handlers[i].enabled()) {
- this.handlers[i].disable();
- }
- }
- }
-});
-
-L.Map.addInitHook(function () {
- if (this.options.drawControl) {
- this.drawControl = new L.Control.Draw();
- this.addControl(this.drawControl);
- }
-});
-
-
-
-}(this));
\ No newline at end of file
--- /dev/null
+div.leaflet-marker-icon.location-filter.resize-marker {
+ background: url( img/resize-handle.png ) no-repeat;
+ cursor: move;
+}
+div.leaflet-marker-icon.location-filter.move-marker {
+ background: url( img/move-handle.png ) no-repeat;
+ cursor: move;
+}
+
+div.location-filter.button-container {
+ background: #bfbfbf;
+ background: rgba(0, 0, 0, 0.25);
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+ border-radius: 7px;
+ padding: 5px;
+}
+.leaflet-container div.location-filter.button-container a {
+ display: inline-block;
+ color: #0F2416;
+ font-size: 11px;
+ font-weight: normal;
+ text-shadow: #A1BB9C 0 1px;
+ padding: 6px 7px;
+ border: 1px solid #9CC5A4;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -webkit-box-shadow: inset rgba(255,255,255,0.75) 0 1px 1px;
+ -moz-box-shadow: inset rgba(255,255,255,0.75) 0 1px 1px;
+ box-shadow: inset rgba(255,255,255,0.75) 0 1px 1px;
+ background: #c4e3b9;
+ background: rgba(218, 252, 205, 0.9);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(218, 252, 205, 0.9)), color-stop(100%, rgba(173, 226, 176, 0.9)));
+ background: -webkit-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background: -moz-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background: -ms-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background: -o-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background: linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+}
+.leaflet-container div.location-filter.button-container a:hover {
+ color: #263F1C;
+ background: #dde6d8;
+ background: rgba(245, 255, 240, 0.9);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(245, 255, 240, 0.9)), color-stop(100%, rgba(203, 228, 205, 0.9)));
+ background: -webkit-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background: -moz-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background: -ms-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background: -o-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background: linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+}
+
+.leaflet-container div.location-filter.button-container a.enable-button {
+ padding: 6px 7px 6px 25px;
+ background-image: url( img/filter-icon.png ), -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(218, 252, 205, 0.9)), color-stop(100%, rgba(173, 226, 176, 0.9)));
+ background-image: url( img/filter-icon.png ), -webkit-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), -moz-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), -ms-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), -o-linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), linear-gradient(top, rgba(218, 252, 205, 0.9) 0%, rgba(173, 226, 176, 0.9) 100%);
+ background-repeat: no-repeat;
+ background-position: left center;
+}
+.leaflet-container div.location-filter.button-container a.enable-button:hover,
+.leaflet-container div.location-filter.button-container.enabled a.enable-button {
+ background-image: url( img/filter-icon.png ), -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(245, 255, 240, 0.9)), color-stop(100%, rgba(203, 228, 205, 0.9)));
+ background-image: url( img/filter-icon.png ), -webkit-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), -moz-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), -ms-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), -o-linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background-image: url( img/filter-icon.png ), linear-gradient(top, rgba(245, 255, 240, 0.9) 0%, rgba(203, 228, 205, 0.9) 100%);
+ background-repeat: no-repeat;
+ background-position: left center;
+}
+
+.leaflet-container div.location-filter.button-container a.adjust-button {
+ margin-left: 2px;
+}
--- /dev/null
+/*
+ * Leaflet.locationfilter - leaflet location filter plugin
+ * Copyright (C) 2012, Tripbirds.com
+ * http://tripbirds.com
+ *
+ * Licensed under the MIT License.
+ *
+ * Date: 2012-09-24
+ * Version: 0.1
+ */
+L.LatLngBounds.prototype.modify = function(map, amount) {
+ var sw = this.getSouthWest(),
+ ne = this.getNorthEast(),
+ swPoint = map.latLngToLayerPoint(sw),
+ nePoint = map.latLngToLayerPoint(ne);
+
+ sw = map.layerPointToLatLng(new L.Point(swPoint.x-amount, swPoint.y+amount));
+ ne = map.layerPointToLatLng(new L.Point(nePoint.x+amount, nePoint.y-amount));
+
+ return new L.LatLngBounds(sw, ne);
+};
+
+L.Control.Button = L.Class.extend({
+ initialize: function(options) {
+ L.Util.setOptions(this, options);
+ },
+
+ addTo: function(container) {
+ container.addButton(this);
+ return this;
+ },
+
+ onAdd: function (buttonContainer) {
+ this._buttonContainer = buttonContainer;
+ this._button = L.DomUtil.create('a', this.options.className, this._buttonContainer.getContainer());
+ this._button.href = '#';
+ this.setText(this.options.text);
+
+ var that = this;
+ this._onClick = function(event) {
+ that.options.onClick.call(that, event);
+ };
+
+ L.DomEvent
+ .on(this._button, 'click', L.DomEvent.stopPropagation)
+ .on(this._button, 'mousedown', L.DomEvent.stopPropagation)
+ .on(this._button, 'dblclick', L.DomEvent.stopPropagation)
+ .on(this._button, 'click', L.DomEvent.preventDefault)
+ .on(this._button, 'click', this._onClick, this);
+ },
+
+ remove: function() {
+ L.DomEvent.off(this._button, "click", this._onClick);
+ this._buttonContainer.getContainer().removeChild(this._button);
+ },
+
+ setText: function(text) {
+ this._button.title = text;
+ this._button.innerHTML = text;
+ }
+});
+
+L.Control.ButtonContainer = L.Control.extend({
+ options: {
+ position: 'topleft'
+ },
+
+ getContainer: function() {
+ if (!this._container) {
+ this._container = L.DomUtil.create('div', this.options.className);
+ }
+ return this._container;
+ },
+
+ onAdd: function (map) {
+ this._map = map;
+ return this.getContainer();
+ },
+
+ addButton: function(button) {
+ button.onAdd(this);
+ },
+
+ addClass: function(className) {
+ L.DomUtil.addClass(this.getContainer(), className);
+ },
+
+ removeClass: function(className) {
+ L.DomUtil.removeClass(this.getContainer(), className);
+ }
+});
+
+L.LocationFilter = L.Class.extend({
+ options: {
+ enableButton: {
+ enableText: "Select area",
+ disableText: "Remove selection"
+ },
+ adjustButton: {
+ text: "Select area within current zoom"
+ }
+ },
+
+ initialize: function(options) {
+ L.Util.setOptions(this, options);
+ },
+
+ addTo: function(map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+ this._layer = new L.LayerGroup();
+ this._initializeButtonContainer();
+
+ if (this.options.enable) {
+ this.enable();
+ }
+ },
+
+ onRemove: function(map) {
+ this.disable();
+ this._buttonContainer.removeFrom(map);
+ },
+
+ /* Get the current filter bounds */
+ getBounds: function() {
+ return new L.LatLngBounds(this._sw, this._ne);
+ },
+
+ setBounds: function(bounds) {
+ this._nw = bounds.getNorthWest();
+ this._ne = bounds.getNorthEast();
+ this._sw = bounds.getSouthWest();
+ this._se = bounds.getSouthEast();
+ this._draw();
+ this._callCallback("onChange");
+ },
+
+ isEnabled: function() {
+ return this._enabled;
+ },
+
+ /* Draw a rectangle */
+ _drawRectangle: function(bounds, options) {
+ options = options || {};
+ var defaultOptions = {
+ stroke: false,
+ fill: true,
+ fillColor: "black",
+ fillOpacity: 0.3,
+ clickable: false
+ };
+ options = L.Util.extend(defaultOptions, options);
+ var rect = new L.Rectangle(bounds, options);
+ rect.addTo(this._layer);
+ return rect;
+ },
+
+ /* Draw a draggable marker */
+ _drawImageMarker: function(point, options) {
+ var marker = new L.Marker(point, {
+ icon: new L.DivIcon({
+ iconAnchor: options.anchor,
+ iconSize: options.size,
+ className: options.className
+ }),
+ draggable: true
+ });
+ marker.addTo(this._layer);
+ return marker;
+ },
+
+ /* Draw a move marker. Sets up drag listener that updates the
+ filter corners and redraws the filter when the move marker is
+ moved */
+ _drawMoveMarker: function(point) {
+ var that = this;
+ this._moveMarker = this._drawImageMarker(point, {
+ "className": "location-filter move-marker",
+ "anchor": [-10, -10],
+ "size": [13,13]
+ });
+ this._moveMarker.on('drag', function(e) {
+ var markerPos = that._moveMarker.getLatLng(),
+ latDelta = markerPos.lat-that._nw.lat,
+ lngDelta = markerPos.lng-that._nw.lng;
+ that._nw = new L.LatLng(that._nw.lat+latDelta, that._nw.lng+lngDelta);
+ that._ne = new L.LatLng(that._ne.lat+latDelta, that._ne.lng+lngDelta);
+ that._sw = new L.LatLng(that._sw.lat+latDelta, that._sw.lng+lngDelta);
+ that._se = new L.LatLng(that._se.lat+latDelta, that._se.lng+lngDelta);
+ that._draw();
+ });
+ this._setupDragendListener(this._moveMarker);
+ return this._moveMarker;
+ },
+
+ /* Draw a resize marker */
+ _drawResizeMarker: function(point, latFollower, lngFollower, otherPos) {
+ return this._drawImageMarker(point, {
+ "className": "location-filter resize-marker",
+ "anchor": [7, 6],
+ "size": [13, 12]
+ });
+ },
+
+ /* Track moving of the given resize marker and update the markers
+ given in options.moveAlong to match the position of the moved
+ marker. Update filter corners and redraw the filter */
+ _setupResizeMarkerTracking: function(marker, options) {
+ var that = this;
+ marker.on('drag', function(e) {
+ var curPosition = marker.getLatLng(),
+ latMarker = options.moveAlong.lat,
+ lngMarker = options.moveAlong.lng;
+ // Move follower markers when this marker is moved
+ latMarker.setLatLng(new L.LatLng(curPosition.lat, latMarker.getLatLng().lng));
+ lngMarker.setLatLng(new L.LatLng(lngMarker.getLatLng().lat, curPosition.lng));
+ // Sort marker positions in nw, ne, sw, se order
+ var corners = [that._nwMarker.getLatLng(),
+ that._neMarker.getLatLng(),
+ that._swMarker.getLatLng(),
+ that._seMarker.getLatLng()];
+ corners.sort(function(a, b) {
+ if (a.lat != b.lat)
+ return b.lat-a.lat;
+ else
+ return a.lng-b.lng;
+ });
+ // Update corner points and redraw everything except the resize markers
+ that._nw = corners[0];
+ that._ne = corners[1];
+ that._sw = corners[2];
+ that._se = corners[3];
+ that._draw({repositionResizeMarkers: false});
+ });
+ this._setupDragendListener(marker);
+ },
+
+ /* Call the callback (given by name) if it was supplied in options */
+ _callCallback: function(callbackName) {
+ if (this.options[callbackName]) {
+ this.options[callbackName](this.getBounds());
+ }
+ },
+
+ /* Call the onChange callback whenever dragend is triggered on the
+ given marker */
+ _setupDragendListener: function(marker) {
+ if (this.options.onChange) {
+ var that = this;
+ marker.on('dragend', function(e) {
+ that._callCallback("onChange");
+ });
+ }
+ },
+
+ /* Create bounds for the mask rectangles and the location
+ filter rectangle */
+ _calculateBounds: function() {
+ var mapBounds = this._map.getBounds(),
+ outerBounds = new L.LatLngBounds(
+ new L.LatLng(mapBounds.getSouthWest().lat-0.1,
+ mapBounds.getSouthWest().lng-0.1),
+ new L.LatLng(mapBounds.getNorthEast().lat+0.1,
+ mapBounds.getNorthEast().lng+0.1)
+ );
+
+ // The south west and north east points of the mask */
+ this._osw = outerBounds.getSouthWest();
+ this._one = outerBounds.getNorthEast();
+
+ // Bounds for the mask rectangles
+ this._northBounds = new L.LatLngBounds(new L.LatLng(this._ne.lat, this._osw.lng), this._one),
+ this._westBounds = new L.LatLngBounds(new L.LatLng(this._sw.lat, this._osw.lng), this._nw),
+ this._eastBounds = new L.LatLngBounds(this._se, new L.LatLng(this._ne.lat, this._one.lng)),
+ this._southBounds = new L.LatLngBounds(this._osw, new L.LatLng(this._sw.lat, this._one.lng));
+ },
+
+ /* Initializes rectangles and markers */
+ _initialDraw: function() {
+ if (this._initialDrawCalled) {
+ return;
+ }
+
+ // Calculate filter bounds
+ this._calculateBounds();
+
+ // Create rectangles
+ this._northRect = this._drawRectangle(this._northBounds);
+ this._westRect = this._drawRectangle(this._westBounds);
+ this._eastRect = this._drawRectangle(this._eastBounds);
+ this._southRect = this._drawRectangle(this._southBounds);
+ this._innerRect = this._drawRectangle(this.getBounds(), {
+ fillColor: "transparent",
+ stroke: true,
+ color: "white",
+ weight: 1,
+ opacity: 0.9
+ });
+
+ // Create resize markers
+ this._nwMarker = this._drawResizeMarker(this._nw);
+ this._neMarker = this._drawResizeMarker(this._ne);
+ this._swMarker = this._drawResizeMarker(this._sw);
+ this._seMarker = this._drawResizeMarker(this._se);
+
+ // Setup tracking of resize markers. Each marker has pair of
+ // follower markers that must be moved whenever the marker is
+ // moved. For example, whenever the north west resize marker
+ // moves, the south west marker must move along on the x-axis
+ // and the north east marker must move on the y axis
+ this._setupResizeMarkerTracking(this._nwMarker, {moveAlong: {lat: this._neMarker, lng: this._swMarker}});
+ this._setupResizeMarkerTracking(this._neMarker, {moveAlong: {lat: this._nwMarker, lng: this._seMarker}});
+ this._setupResizeMarkerTracking(this._swMarker, {moveAlong: {lat: this._seMarker, lng: this._nwMarker}});
+ this._setupResizeMarkerTracking(this._seMarker, {moveAlong: {lat: this._swMarker, lng: this._neMarker}});
+
+ // Create move marker
+ this._moveMarker = this._drawMoveMarker(this._nw);
+
+ this._initialDrawCalled = true;
+ },
+
+ /* Reposition all rectangles and markers to the current filter bounds. */
+ _draw: function(options) {
+ options = L.Util.extend({repositionResizeMarkers: true}, options);
+
+ // Calculate filter bounds
+ this._calculateBounds();
+
+ // Reposition rectangles
+ this._northRect.setBounds(this._northBounds);
+ this._westRect.setBounds(this._westBounds);
+ this._eastRect.setBounds(this._eastBounds);
+ this._southRect.setBounds(this._southBounds);
+ this._innerRect.setBounds(this.getBounds());
+
+ // Reposition resize markers
+ if (options.repositionResizeMarkers) {
+ this._nwMarker.setLatLng(this._nw);
+ this._neMarker.setLatLng(this._ne);
+ this._swMarker.setLatLng(this._sw);
+ this._seMarker.setLatLng(this._se);
+ }
+
+ // Reposition the move marker
+ this._moveMarker.setLatLng(this._nw);
+ },
+
+ /* Adjust the location filter to the current map bounds */
+ _adjustToMap: function() {
+ this.setBounds(this._map.getBounds());
+ this._map.zoomOut();
+ },
+
+ /* Enable the location filter */
+ enable: function() {
+ if (this._enabled) {
+ return;
+ }
+
+ // Initialize corners
+ var bounds;
+ if (this._sw && this._ne) {
+ bounds = new L.LatLngBounds(this._sw, this._ne);
+ } else if (this.options.bounds) {
+ bounds = this.options.bounds;
+ } else {
+ bounds = this._map.getBounds();
+ }
+ this._map.invalidateSize();
+ this._nw = bounds.getNorthWest();
+ this._ne = bounds.getNorthEast();
+ this._sw = bounds.getSouthWest();
+ this._se = bounds.getSouthEast();
+
+
+ // Update buttons
+ this._buttonContainer.addClass("enabled");
+
+ if (this._enableButton) {
+ this._enableButton.setText(this.options.enableButton.disableText);
+ }
+
+ if (this.options.adjustButton) {
+ this._createAdjustButton();
+ }
+
+ // Draw filter
+ this._initialDraw();
+ this._draw();
+
+ // Set up map move event listener
+ var that = this;
+ this._moveHandler = function() {
+ that._draw();
+ };
+ this._map.on("move", this._moveHandler);
+
+ // Add the filter layer to the map
+ this._layer.addTo(this._map);
+
+ // Zoom out the map if necessary
+ var mapBounds = this._map.getBounds();
+ bounds = new L.LatLngBounds(this._sw, this._ne).modify(this._map, 10);
+ if (!mapBounds.contains(bounds.getSouthWest()) || !mapBounds.contains(bounds.getNorthEast())) {
+ this._map.fitBounds(bounds);
+ }
+
+ this._enabled = true;
+
+ // Call the enabled callback
+ this._callCallback("onEnabled");
+ },
+
+ /* Disable the location filter */
+ disable: function() {
+ if (!this._enabled) {
+ return;
+ }
+
+ // Update buttons
+ this._buttonContainer.removeClass("enabled");
+
+ if (this._enableButton) {
+ this._enableButton.setText(this.options.enableButton.enableText);
+ }
+
+ if (this._adjustButton) {
+ this._adjustButton.remove();
+ }
+
+ // Remove event listener
+ this._map.off("move", this._moveHandler);
+
+ // Remove rectangle layer from map
+ this._map.removeLayer(this._layer);
+
+ this._enabled = false;
+
+ // Call the disabled callback
+ this._callCallback("onDisabled");
+ },
+
+ /* Create a button that allows the user to adjust the location
+ filter to the current zoom */
+ _createAdjustButton: function() {
+ var that = this;
+ this._adjustButton = new L.Control.Button({
+ className: "adjust-button",
+ text: this.options.adjustButton.text,
+
+ onClick: function(event) {
+ that._adjustToMap();
+ that._callCallback("onAdjustToZoomClick");
+ }
+ }).addTo(this._buttonContainer);
+ },
+
+ /* Create the location filter button container and the button that
+ toggles the location filter */
+ _initializeButtonContainer: function() {
+ var that = this;
+ this._buttonContainer = new L.Control.ButtonContainer({className: "location-filter button-container"});
+
+ if (this.options.enableButton) {
+ this._enableButton = new L.Control.Button({
+ className: "enable-button",
+ text: this.options.enableButton.enableText,
+
+ onClick: function(event) {
+ if (!that._enabled) {
+ // Enable the location filter
+ that.enable();
+ that._callCallback("onEnableClick");
+ } else {
+ // Disable the location filter
+ that.disable();
+ that._callCallback("onDisableClick");
+ }
+ }
+ }).addTo(this._buttonContainer);
+ }
+
+ this._buttonContainer.addTo(this._map);
+ }
+});
\ No newline at end of file