diff options
Diffstat (limited to 'extlib/leaflet/src/layer')
19 files changed, 1579 insertions, 0 deletions
diff --git a/extlib/leaflet/src/layer/FeatureGroup.js b/extlib/leaflet/src/layer/FeatureGroup.js new file mode 100644 index 00000000..6e45d84c --- /dev/null +++ b/extlib/leaflet/src/layer/FeatureGroup.js @@ -0,0 +1,40 @@ +/*
+ * L.FeatureGroup extends L.LayerGroup by introducing mouse events and bindPopup method shared between a group of layers.
+ */
+
+L.FeatureGroup = L.LayerGroup.extend({
+ includes: L.Mixin.Events,
+
+ addLayer: function(layer) {
+ this._initEvents(layer);
+ L.LayerGroup.prototype.addLayer.call(this, layer);
+
+ if (this._popupContent && layer.bindPopup) {
+ layer.bindPopup(this._popupContent);
+ }
+ },
+
+ bindPopup: function(content) {
+ this._popupContent = content;
+
+ for (var i in this._layers) {
+ if (this._layers.hasOwnProperty(i) && this._layers[i].bindPopup) {
+ this._layers[i].bindPopup(content);
+ }
+ }
+ },
+
+ _events: ['click', 'dblclick', 'mouseover', 'mouseout'],
+
+ _initEvents: function(layer) {
+ for (var i = 0, len = this._events.length; i < len; i++) {
+ layer.on(this._events[i], this._propagateEvent, this);
+ }
+ },
+
+ _propagateEvent: function(e) {
+ e.layer = e.target;
+ e.target = this;
+ this.fire(e.type, e);
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/GeoJSON.js b/extlib/leaflet/src/layer/GeoJSON.js new file mode 100644 index 00000000..6cbd4193 --- /dev/null +++ b/extlib/leaflet/src/layer/GeoJSON.js @@ -0,0 +1,106 @@ +
+L.GeoJSON = L.LayerGroup.extend({
+ includes: L.Mixin.Events,
+
+ initialize: function(geojson, options) {
+ L.Util.setOptions(this, options);
+ this._geojson = geojson;
+ this._layers = {};
+
+ if (geojson) {
+ this.addGeoJSON(geojson);
+ }
+ },
+
+ addGeoJSON: function(geojson) {
+ if (geojson.features) {
+ for (var i = 0, len = geojson.features.length; i < len; i++) {
+ this.addGeoJSON(geojson.features[i]);
+ }
+ return;
+ }
+
+ var isFeature = (geojson.type == 'Feature'),
+ geometry = (isFeature ? geojson.geometry : geojson),
+ layer = L.GeoJSON.geometryToLayer(geometry, this.options.pointToLayer);
+
+ this.fire('featureparse', {
+ layer: layer,
+ properties: geojson.properties,
+ geometryType: geometry.type,
+ bbox: geojson.bbox,
+ id: geojson.id
+ });
+
+ this.addLayer(layer);
+ }
+});
+
+L.Util.extend(L.GeoJSON, {
+ geometryToLayer: function(geometry, pointToLayer) {
+ var coords = geometry.coordinates,
+ latlng, latlngs,
+ i, len,
+ layer,
+ layers = [];
+
+ switch (geometry.type) {
+ case 'Point':
+ latlng = this.coordsToLatLng(coords);
+ return pointToLayer ? pointToLayer(latlng) : new L.Marker(latlng);
+
+ case 'MultiPoint':
+ for (i = 0, len = coords.length; i < len; i++) {
+ latlng = this.coordsToLatLng(coords[i]);
+ layer = pointToLayer ? pointToLayer(latlng) : new L.Marker(latlng);
+ layers.push(layer);
+ }
+ return new L.FeatureGroup(layers);
+
+ case 'LineString':
+ latlngs = this.coordsToLatLngs(coords);
+ return new L.Polyline(latlngs);
+
+ case 'Polygon':
+ latlngs = this.coordsToLatLngs(coords, 1);
+ return new L.Polygon(latlngs);
+
+ case 'MultiLineString':
+ latlngs = this.coordsToLatLngs(coords, 1);
+ return new L.MultiPolyline(latlngs);
+
+ case "MultiPolygon":
+ latlngs = this.coordsToLatLngs(coords, 2);
+ return new L.MultiPolygon(latlngs);
+
+ case "GeometryCollection":
+ for (i = 0, len = geometry.geometries.length; i < len; i++) {
+ layer = this.geometryToLayer(geometry.geometries[i]);
+ layers.push(layer);
+ }
+ return new L.FeatureGroup(layers);
+
+ default:
+ throw new Error('Invalid GeoJSON object.');
+ }
+ },
+
+ coordsToLatLng: function(/*Array*/ coords, /*Boolean*/ reverse)/*: LatLng*/ {
+ var lat = parseFloat(coords[reverse ? 0 : 1]),
+ lng = parseFloat(coords[reverse ? 1 : 0]);
+ return new L.LatLng(lat, lng);
+ },
+
+ coordsToLatLngs: function(/*Array*/ coords, /*Number*/ levelsDeep, /*Boolean*/ reverse)/*: Array*/ {
+ var latlng, latlngs = [],
+ i, len = coords.length;
+
+ for (i = 0; i < len; i++) {
+ latlng = levelsDeep ?
+ this.coordsToLatLngs(coords[i], levelsDeep - 1, reverse) :
+ this.coordsToLatLng(coords[i], reverse);
+ latlngs.push(latlng);
+ }
+ return latlngs;
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/ImageOverlay.js b/extlib/leaflet/src/layer/ImageOverlay.js new file mode 100644 index 00000000..4551b2e3 --- /dev/null +++ b/extlib/leaflet/src/layer/ImageOverlay.js @@ -0,0 +1,58 @@ +L.ImageOverlay = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ initialize: function(/*String*/ url, /*LatLngBounds*/ bounds) {
+ this._url = url;
+ this._bounds = bounds;
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+
+ if (!this._image) {
+ this._initImage();
+ }
+
+ map.getPanes().overlayPane.appendChild(this._image);
+
+ map.on('viewreset', this._reset, this);
+ this._reset();
+ },
+
+ onRemove: function(map) {
+ map.getPanes().overlayPane.removeChild(this._image);
+ map.off('viewreset', this._reset, this);
+ },
+
+ _initImage: function() {
+ this._image = L.DomUtil.create('img', 'leaflet-image-layer');
+
+ this._image.style.visibility = 'hidden';
+ //TODO opacity option
+
+ //TODO createImage util method to remove duplication
+ L.Util.extend(this._image, {
+ galleryimg: 'no',
+ onselectstart: L.Util.falseFn,
+ onmousemove: L.Util.falseFn,
+ onload: this._onImageLoad,
+ src: this._url
+ });
+ },
+
+ _reset: function() {
+ var topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
+ bottomRight = this._map.latLngToLayerPoint(this._bounds.getSouthEast()),
+ size = bottomRight.subtract(topLeft);
+
+ L.DomUtil.setPosition(this._image, topLeft);
+
+ this._image.style.width = size.x + 'px';
+ this._image.style.height = size.y + 'px';
+ },
+
+ _onImageLoad: function() {
+ this.style.visibility = '';
+ //TODO fire layerload
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/LayerGroup.js b/extlib/leaflet/src/layer/LayerGroup.js new file mode 100644 index 00000000..58940d40 --- /dev/null +++ b/extlib/leaflet/src/layer/LayerGroup.js @@ -0,0 +1,58 @@ +/*
+ * L.LayerGroup is a class to combine several layers so you can manipulate the group (e.g. add/remove it) as one layer.
+ */
+
+L.LayerGroup = L.Class.extend({
+ initialize: function(layers) {
+ this._layers = {};
+
+ if (layers) {
+ for (var i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ }
+ },
+
+ addLayer: function(layer) {
+ var id = L.Util.stamp(layer);
+ this._layers[id] = layer;
+
+ if (this._map) {
+ this._map.addLayer(layer);
+ }
+ return this;
+ },
+
+ removeLayer: function(layer) {
+ var id = L.Util.stamp(layer);
+ delete this._layers[id];
+
+ if (this._map) {
+ this._map.removeLayer(layer);
+ }
+ return this;
+ },
+
+ clearLayers: function() {
+ this._iterateLayers(this.removeLayer, this);
+ return this;
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+ this._iterateLayers(map.addLayer, map);
+ },
+
+ onRemove: function(map) {
+ this._iterateLayers(map.removeLayer, map);
+ delete this._map;
+ },
+
+ _iterateLayers: function(method, context) {
+ for (var i in this._layers) {
+ if (this._layers.hasOwnProperty(i)) {
+ method.call(context, this._layers[i]);
+ }
+ }
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/Popup.js b/extlib/leaflet/src/layer/Popup.js new file mode 100644 index 00000000..4cb14e3c --- /dev/null +++ b/extlib/leaflet/src/layer/Popup.js @@ -0,0 +1,165 @@ +
+L.Popup = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ maxWidth: 300,
+ autoPan: true,
+ closeButton: true,
+
+ offset: new L.Point(0, 2),
+ autoPanPadding: new L.Point(5, 5)
+ },
+
+ initialize: function(options) {
+ L.Util.setOptions(this, options);
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+ if (!this._container) {
+ this._initLayout();
+ }
+ this._updateContent();
+
+ this._container.style.opacity = '0';
+
+ this._map._panes.popupPane.appendChild(this._container);
+ this._map.on('viewreset', this._updatePosition, this);
+ if (this._map.options.closePopupOnClick) {
+ this._map.on('preclick', this._close, this);
+ }
+ this._update();
+
+ this._container.style.opacity = '1'; //TODO fix ugly opacity hack
+
+ this._opened = true;
+ },
+
+ onRemove: function(map) {
+ map._panes.popupPane.removeChild(this._container);
+ map.off('viewreset', this._updatePosition, this);
+ map.off('click', this._close, this);
+
+ this._container.style.opacity = '0';
+
+ this._opened = false;
+ },
+
+ setLatLng: function(latlng) {
+ this._latlng = latlng;
+ if (this._opened) {
+ this._update();
+ }
+ return this;
+ },
+
+ setContent: function(content) {
+ this._content = content;
+ if (this._opened) {
+ this._update();
+ }
+ return this;
+ },
+
+ _close: function() {
+ if (this._opened) {
+ this._map.removeLayer(this);
+ }
+ },
+
+ _initLayout: function() {
+ this._container = L.DomUtil.create('div', 'leaflet-popup');
+
+ this._closeButton = L.DomUtil.create('a', 'leaflet-popup-close-button', this._container);
+ this._closeButton.href = '#close';
+ this._closeButton.onclick = L.Util.bind(this._onCloseButtonClick, this);
+
+ this._wrapper = L.DomUtil.create('div', 'leaflet-popup-content-wrapper', this._container);
+ L.DomEvent.disableClickPropagation(this._wrapper);
+ this._contentNode = L.DomUtil.create('div', 'leaflet-popup-content', this._wrapper);
+
+ this._tipContainer = L.DomUtil.create('div', 'leaflet-popup-tip-container', this._container);
+ this._tip = L.DomUtil.create('div', 'leaflet-popup-tip', this._tipContainer);
+ },
+
+ _update: function() {
+ this._container.style.visibility = 'hidden';
+
+ this._updateContent();
+ this._updateLayout();
+ this._updatePosition();
+
+ this._container.style.visibility = '';
+
+ this._adjustPan();
+ },
+
+ _updateContent: function() {
+ if (!this._content) return;
+
+ if (typeof this._content == 'string') {
+ this._contentNode.innerHTML = this._content;
+ } else {
+ this._contentNode.innerHTML = '';
+ this._contentNode.appendChild(this._content);
+ }
+ },
+
+ _updateLayout: function() {
+ this._container.style.width = '';
+ this._container.style.whiteSpace = 'nowrap';
+
+ var width = this._container.offsetWidth;
+
+ this._container.style.width = (width > this.options.maxWidth ? this.options.maxWidth : width) + 'px';
+ this._container.style.whiteSpace = '';
+
+ this._containerWidth = this._container.offsetWidth;
+ },
+
+ _updatePosition: function() {
+ var pos = this._map.latLngToLayerPoint(this._latlng);
+
+ this._containerBottom = -pos.y - this.options.offset.y;
+ this._containerLeft = pos.x - Math.round(this._containerWidth/2) + this.options.offset.x;
+
+ this._container.style.bottom = this._containerBottom + 'px';
+ this._container.style.left = this._containerLeft + 'px';
+ },
+
+ _adjustPan: function() {
+ if (!this.options.autoPan) { return; }
+
+ var containerHeight = this._container.offsetHeight,
+ layerPos = new L.Point(
+ this._containerLeft,
+ -containerHeight - this._containerBottom),
+ containerPos = this._map.layerPointToContainerPoint(layerPos),
+ adjustOffset = new L.Point(0, 0),
+ padding = this.options.autoPanPadding,
+ size = this._map.getSize();
+
+ if (containerPos.x < 0) {
+ adjustOffset.x = containerPos.x - padding.x;
+ }
+ if (containerPos.x + this._containerWidth > size.x) {
+ adjustOffset.x = containerPos.x + this._containerWidth - size.x + padding.x;
+ }
+ if (containerPos.y < 0) {
+ adjustOffset.y = containerPos.y - padding.y;
+ }
+ if (containerPos.y + containerHeight > size.y) {
+ adjustOffset.y = containerPos.y + containerHeight - size.y + padding.y;
+ }
+
+ if (adjustOffset.x || adjustOffset.y) {
+ this._map.panBy(adjustOffset);
+ }
+ },
+
+ _onCloseButtonClick: function(e) {
+ this._close();
+ L.DomEvent.stop(e);
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/marker/Icon.js b/extlib/leaflet/src/layer/marker/Icon.js new file mode 100644 index 00000000..6df036e4 --- /dev/null +++ b/extlib/leaflet/src/layer/marker/Icon.js @@ -0,0 +1,56 @@ +L.Icon = L.Class.extend({
+ iconUrl: L.ROOT_URL + 'images/marker.png',
+ shadowUrl: L.ROOT_URL + 'images/marker-shadow.png',
+
+ iconSize: new L.Point(25, 41),
+ shadowSize: new L.Point(41, 41),
+
+ iconAnchor: new L.Point(13, 41),
+ popupAnchor: new L.Point(0, -33),
+
+ initialize: function(iconUrl) {
+ if (iconUrl) {
+ this.iconUrl = iconUrl;
+ }
+ },
+
+ createIcon: function() {
+ return this._createIcon('icon');
+ },
+
+ createShadow: function() {
+ return this._createIcon('shadow');
+ },
+
+ _createIcon: function(name) {
+ var size = this[name + 'Size'],
+ src = this[name + 'Url'],
+ img = this._createImg(src);
+
+ if (!src) { return null; }
+
+ img.className = 'leaflet-marker-' + name;
+
+ img.style.marginLeft = (-this.iconAnchor.x) + 'px';
+ img.style.marginTop = (-this.iconAnchor.y) + 'px';
+
+ if (size) {
+ img.style.width = size.x + 'px';
+ img.style.height = size.y + 'px';
+ }
+
+ return img;
+ },
+
+ _createImg: function(src) {
+ var el;
+ if (!L.Browser.ie6) {
+ el = document.createElement('img');
+ el.src = src;
+ } else {
+ el = document.createElement('div');
+ el.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")';
+ }
+ return el;
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/marker/Marker.Popup.js b/extlib/leaflet/src/layer/marker/Marker.Popup.js new file mode 100644 index 00000000..4c5cad04 --- /dev/null +++ b/extlib/leaflet/src/layer/marker/Marker.Popup.js @@ -0,0 +1,28 @@ +/*
+ * Popup extension to L.Marker, adding openPopup & bindPopup methods.
+ */
+
+L.Marker.include({
+ openPopup: function() {
+ this._popup.setLatLng(this._latlng);
+ this._map.openPopup(this._popup);
+
+ return this;
+ },
+
+ closePopup: function() {
+ if (this._popup) {
+ this._popup._close();
+ }
+ },
+
+ bindPopup: function(content, options) {
+ options = L.Util.extend({offset: this.options.icon.popupAnchor}, options);
+
+ this._popup = new L.Popup(options);
+ this._popup.setContent(content);
+ this.on('click', this.openPopup, this);
+
+ return this;
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/marker/Marker.js b/extlib/leaflet/src/layer/marker/Marker.js new file mode 100644 index 00000000..b98bec4e --- /dev/null +++ b/extlib/leaflet/src/layer/marker/Marker.js @@ -0,0 +1,123 @@ +/*
+ * L.Marker is used to display clickable/draggable icons on the map.
+ */
+
+L.Marker = L.Class.extend({
+
+ includes: L.Mixin.Events,
+
+ options: {
+ icon: new L.Icon(),
+ title: '',
+ clickable: true,
+ draggable: false
+ },
+
+ initialize: function(latlng, options) {
+ L.Util.setOptions(this, options);
+ this._latlng = latlng;
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+
+ this._initIcon();
+
+ map.on('viewreset', this._reset, this);
+ this._reset();
+ },
+
+ onRemove: function(map) {
+ this._removeIcon();
+
+ map.off('viewreset', this._reset, this);
+ },
+
+ getLatLng: function() {
+ return this._latlng;
+ },
+
+ setLatLng: function(latlng) {
+ this._latlng = latlng;
+ this._reset();
+ },
+
+ setIcon: function(icon) {
+ this._removeIcon();
+
+ this._icon = this._shadow = null;
+ this.options.icon = icon;
+
+ this._initIcon();
+ },
+
+ _initIcon: function() {
+ if (!this._icon) {
+ this._icon = this.options.icon.createIcon();
+
+ if (this.options.title) {
+ this._icon.title = this.options.title;
+ }
+
+ this._initInteraction();
+ }
+ if (!this._shadow) {
+ this._shadow = this.options.icon.createShadow();
+ }
+
+ this._map._panes.markerPane.appendChild(this._icon);
+ if (this._shadow) {
+ this._map._panes.shadowPane.appendChild(this._shadow);
+ }
+ },
+
+ _removeIcon: function() {
+ this._map._panes.markerPane.removeChild(this._icon);
+ if (this._shadow) {
+ this._map._panes.shadowPane.removeChild(this._shadow);
+ }
+ },
+
+ _reset: function() {
+ var pos = this._map.latLngToLayerPoint(this._latlng).round();
+
+ L.DomUtil.setPosition(this._icon, pos);
+ if (this._shadow) {
+ L.DomUtil.setPosition(this._shadow, pos);
+ }
+
+ this._icon.style.zIndex = pos.y;
+ },
+
+ _initInteraction: function() {
+ if (this.options.clickable) {
+ this._icon.className += ' leaflet-clickable';
+
+ L.DomEvent.addListener(this._icon, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout'];
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.addListener(this._icon, events[i], this._fireMouseEvent, this);
+ }
+ }
+
+ if (L.Handler.MarkerDrag) {
+ this.dragging = new L.Handler.MarkerDrag(this);
+
+ if (this.options.draggable) {
+ this.dragging.enable();
+ }
+ }
+ },
+
+ _onMouseClick: function(e) {
+ L.DomEvent.stopPropagation(e);
+ if (this.dragging && this.dragging.moved()) { return; }
+ this.fire(e.type);
+ },
+
+ _fireMouseEvent: function(e) {
+ this.fire(e.type);
+ L.DomEvent.stopPropagation(e);
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/tile/TileLayer.Canvas.js b/extlib/leaflet/src/layer/tile/TileLayer.Canvas.js new file mode 100644 index 00000000..08bbaae2 --- /dev/null +++ b/extlib/leaflet/src/layer/tile/TileLayer.Canvas.js @@ -0,0 +1,41 @@ +L.TileLayer.Canvas = L.TileLayer.extend({
+ options: {
+ async: false
+ },
+
+ initialize: function(options) {
+ L.Util.setOptions(this, options);
+ },
+
+ _createTileProto: function() {
+ this._canvasProto = L.DomUtil.create('canvas', 'leaflet-tile');
+
+ var tileSize = this.options.tileSize;
+ this._canvasProto.width = tileSize;
+ this._canvasProto.height = tileSize;
+ },
+
+ _createTile: function() {
+ var tile = this._canvasProto.cloneNode(false);
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+ return tile;
+ },
+
+ _loadTile: function(tile, tilePoint, zoom) {
+ tile._layer = this;
+
+ this.drawTile(tile, tilePoint, zoom);
+
+ if (!this.options.async) {
+ this.tileDrawn(tile);
+ }
+ },
+
+ drawTile: function(tile, tilePoint, zoom) {
+ // override with rendering code
+ },
+
+ tileDrawn: function(tile) {
+ this._tileOnLoad.call(tile);
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/tile/TileLayer.WMS.js b/extlib/leaflet/src/layer/tile/TileLayer.WMS.js new file mode 100644 index 00000000..2f4ad05a --- /dev/null +++ b/extlib/leaflet/src/layer/tile/TileLayer.WMS.js @@ -0,0 +1,47 @@ +L.TileLayer.WMS = L.TileLayer.extend({
+ defaultWmsParams: {
+ service: 'WMS',
+ request: 'GetMap',
+ version: '1.1.1',
+ layers: '',
+ styles: '',
+ format: 'image/jpeg',
+ transparent: false
+ },
+
+ initialize: function(/*String*/ url, /*Object*/ options) {
+ this._url = url;
+
+ this.wmsParams = L.Util.extend({}, this.defaultWmsParams);
+ this.wmsParams.width = this.wmsParams.height = this.options.tileSize;
+
+ for (var i in options) {
+ // all keys that are not TileLayer options go to WMS params
+ if (!this.options.hasOwnProperty(i)) {
+ this.wmsParams[i] = options[i];
+ }
+ }
+
+ L.Util.setOptions(this, options);
+ },
+
+ onAdd: function(map) {
+ var projectionKey = (parseFloat(this.wmsParams.version) >= 1.3 ? 'crs' : 'srs');
+ this.wmsParams[projectionKey] = map.options.crs.code;
+
+ L.TileLayer.prototype.onAdd.call(this, map);
+ },
+
+ getTileUrl: function(/*Point*/ tilePoint, /*Number*/ zoom)/*-> String*/ {
+ var tileSize = this.options.tileSize,
+ nwPoint = tilePoint.multiplyBy(tileSize),
+ sePoint = nwPoint.add(new L.Point(tileSize, tileSize)),
+ nwMap = this._map.unproject(nwPoint, this._zoom, true),
+ seMap = this._map.unproject(sePoint, this._zoom, true),
+ nw = this._map.options.crs.project(nwMap),
+ se = this._map.options.crs.project(seMap),
+ bbox = [nw.x, se.y, se.x, nw.y].join(',');
+
+ return this._url + L.Util.getParamString(this.wmsParams) + "&bbox=" + bbox;
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/tile/TileLayer.js b/extlib/leaflet/src/layer/tile/TileLayer.js new file mode 100644 index 00000000..68072ee9 --- /dev/null +++ b/extlib/leaflet/src/layer/tile/TileLayer.js @@ -0,0 +1,262 @@ +/*
+ * L.TileLayer is used for standard xyz-numbered tile layers.
+ */
+
+L.TileLayer = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ minZoom: 0,
+ maxZoom: 18,
+ tileSize: 256,
+ subdomains: 'abc',
+ errorTileUrl: '',
+ attribution: '',
+ opacity: 1,
+ scheme: 'xyz',
+ noWrap: false,
+
+ unloadInvisibleTiles: L.Browser.mobileWebkit,
+ updateWhenIdle: L.Browser.mobileWebkit
+ },
+
+ initialize: function(url, options) {
+ L.Util.setOptions(this, options);
+
+ this._url = url;
+
+ if (typeof this.options.subdomains == 'string') {
+ this.options.subdomains = this.options.subdomains.split('');
+ }
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+
+ // create a container div for tiles
+ this._initContainer();
+
+ // create an image to clone for tiles
+ this._createTileProto();
+
+ // set up events
+ map.on('viewreset', this._reset, this);
+
+ if (this.options.updateWhenIdle) {
+ map.on('moveend', this._update, this);
+ } else {
+ this._limitedUpdate = L.Util.limitExecByInterval(this._update, 100, this);
+ map.on('move', this._limitedUpdate, this);
+ }
+
+ this._reset();
+ this._update();
+ },
+
+ onRemove: function(map) {
+ this._map.getPanes().tilePane.removeChild(this._container);
+ this._container = null;
+
+ this._map.off('viewreset', this._reset, this);
+
+ if (this.options.updateWhenIdle) {
+ this._map.off('moveend', this._update, this);
+ } else {
+ this._map.off('move', this._limitedUpdate, this);
+ }
+ },
+
+ getAttribution: function() {
+ return this.options.attribution;
+ },
+
+ setOpacity: function(opacity) {
+ this.options.opacity = opacity;
+
+ this._setOpacity(opacity);
+
+ // stupid webkit hack to force redrawing of tiles
+ if (L.Browser.webkit) {
+ for (i in this._tiles) {
+ this._tiles[i].style.webkitTransform += ' translate(0,0)';
+ }
+ }
+ },
+
+ _setOpacity: function(opacity) {
+ if (opacity < 1) {
+ L.DomUtil.setOpacity(this._container, opacity);
+ }
+ },
+
+ _initContainer: function() {
+ var tilePane = this._map.getPanes().tilePane;
+
+ if (!this._container || tilePane.empty) {
+ this._container = L.DomUtil.create('div', 'leaflet-layer', tilePane);
+
+ this._setOpacity(this.options.opacity);
+ }
+ },
+
+ _reset: function() {
+ this._tiles = {};
+ this._initContainer();
+ this._container.innerHTML = '';
+ },
+
+ _update: function() {
+ var bounds = this._map.getPixelBounds(),
+ tileSize = this.options.tileSize;
+
+ var nwTilePoint = new L.Point(
+ Math.floor(bounds.min.x / tileSize),
+ Math.floor(bounds.min.y / tileSize)),
+ seTilePoint = new L.Point(
+ Math.floor(bounds.max.x / tileSize),
+ Math.floor(bounds.max.y / tileSize)),
+ tileBounds = new L.Bounds(nwTilePoint, seTilePoint);
+
+ this._addTilesFromCenterOut(tileBounds);
+
+ if (this.options.unloadInvisibleTiles) {
+ this._removeOtherTiles(tileBounds);
+ }
+ },
+
+ _addTilesFromCenterOut: function(bounds) {
+ var queue = [],
+ center = bounds.getCenter();
+
+ for (var j = bounds.min.y; j <= bounds.max.y; j++) {
+ for (var i = bounds.min.x; i <= bounds.max.x; i++) {
+ if ((i + ':' + j) in this._tiles) { continue; }
+ queue.push(new L.Point(i, j));
+ }
+ }
+
+ // load tiles in order of their distance to center
+ queue.sort(function(a, b) {
+ return a.distanceTo(center) - b.distanceTo(center);
+ });
+
+ this._tilesToLoad = queue.length;
+ for (var k = 0, len = this._tilesToLoad; k < len; k++) {
+ this._addTile(queue[k]);
+ }
+ },
+
+ _removeOtherTiles: function(bounds) {
+ var kArr, x, y, key;
+
+ for (key in this._tiles) {
+ if (this._tiles.hasOwnProperty(key)) {
+ kArr = key.split(':');
+ x = parseInt(kArr[0], 10);
+ y = parseInt(kArr[1], 10);
+
+ // remove tile if it's out of bounds
+ if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {
+ this._tiles[key].src = '';
+ if (this._tiles[key].parentNode == this._container) {
+ this._container.removeChild(this._tiles[key]);
+ }
+ delete this._tiles[key];
+ }
+ }
+ }
+ },
+
+ _addTile: function(tilePoint) {
+ var tilePos = this._getTilePos(tilePoint),
+ zoom = this._map.getZoom(),
+ key = tilePoint.x + ':' + tilePoint.y;
+
+ // wrap tile coordinates
+ var tileLimit = (1 << zoom);
+ if (!this.options.noWrap) {
+ tilePoint.x = ((tilePoint.x % tileLimit) + tileLimit) % tileLimit;
+ }
+ if (tilePoint.y < 0 || tilePoint.y >= tileLimit) { return; }
+
+ // create tile
+ var tile = this._createTile();
+ L.DomUtil.setPosition(tile, tilePos);
+
+ this._tiles[key] = tile;
+
+ if (this.options.scheme == 'tms') {
+ tilePoint.y = tileLimit - tilePoint.y - 1;
+ }
+
+ this._loadTile(tile, tilePoint, zoom);
+
+ this._container.appendChild(tile);
+ },
+
+ _getTilePos: function(tilePoint) {
+ var origin = this._map.getPixelOrigin(),
+ tileSize = this.options.tileSize;
+
+ return tilePoint.multiplyBy(tileSize).subtract(origin);
+ },
+
+ // image-specific code (override to implement e.g. Canvas or SVG tile layer)
+
+ getTileUrl: function(tilePoint, zoom) {
+ var subdomains = this.options.subdomains,
+ s = this.options.subdomains[(tilePoint.x + tilePoint.y) % subdomains.length];
+
+ return this._url
+ .replace('{s}', s)
+ .replace('{z}', zoom)
+ .replace('{x}', tilePoint.x)
+ .replace('{y}', tilePoint.y);
+ },
+
+ _createTileProto: function() {
+ this._tileImg = L.DomUtil.create('img', 'leaflet-tile');
+ this._tileImg.galleryimg = 'no';
+
+ var tileSize = this.options.tileSize;
+ this._tileImg.style.width = tileSize + 'px';
+ this._tileImg.style.height = tileSize + 'px';
+ },
+
+ _createTile: function() {
+ var tile = this._tileImg.cloneNode(false);
+ tile.onselectstart = tile.onmousemove = L.Util.falseFn;
+ return tile;
+ },
+
+ _loadTile: function(tile, tilePoint, zoom) {
+ tile._layer = this;
+ tile.onload = this._tileOnLoad;
+ tile.onerror = this._tileOnError;
+ tile.src = this.getTileUrl(tilePoint, zoom);
+ },
+
+ _tileOnLoad: function(e) {
+ var layer = this._layer;
+
+ this.className += ' leaflet-tile-loaded';
+
+ layer.fire('tileload', {tile: this, url: this.src});
+
+ layer._tilesToLoad--;
+ if (!layer._tilesToLoad) {
+ layer.fire('load');
+ }
+ },
+
+ _tileOnError: function(e) {
+ var layer = this._layer;
+
+ layer.fire('tileerror', {tile: this, url: this.src});
+
+ var newUrl = layer.options.errorTileUrl;
+ if (newUrl) {
+ this.src = newUrl;
+ }
+ }
+});
diff --git a/extlib/leaflet/src/layer/vector/Circle.js b/extlib/leaflet/src/layer/vector/Circle.js new file mode 100644 index 00000000..c737c191 --- /dev/null +++ b/extlib/leaflet/src/layer/vector/Circle.js @@ -0,0 +1,51 @@ +/*
+ * L.Circle is a circle overlay (with a certain radius in meters).
+ */
+
+L.Circle = L.Path.extend({
+ initialize: function(latlng, radius, options) {
+ L.Path.prototype.initialize.call(this, options);
+
+ this._latlng = latlng;
+ this._mRadius = radius;
+ },
+
+ options: {
+ fill: true
+ },
+
+ setLatLng: function(latlng) {
+ this._latlng = latlng;
+ this._redraw();
+ return this;
+ },
+
+ setRadius: function(radius) {
+ this._mRadius = radius;
+ this._redraw();
+ return this;
+ },
+
+ projectLatlngs: function() {
+ var equatorLength = 40075017,
+ scale = this._map.options.scale(this._map._zoom);
+
+ this._point = this._map.latLngToLayerPoint(this._latlng);
+ this._radius = (this._mRadius / equatorLength) * scale;
+ },
+
+ getPathString: function() {
+ var p = this._point,
+ r = this._radius;
+
+ if (L.Path.SVG) {
+ return "M" + p.x + "," + (p.y - r) +
+ "A" + r + "," + r + ",0,1,1," +
+ (p.x - 0.1) + "," + (p.y - r) + " z";
+ } else {
+ p._round();
+ r = Math.round(r);
+ return "AL " + p.x + "," + p.y + " " + r + "," + r + " 0," + (65535 * 360);
+ }
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/vector/CircleMarker.js b/extlib/leaflet/src/layer/vector/CircleMarker.js new file mode 100644 index 00000000..fa4bacf0 --- /dev/null +++ b/extlib/leaflet/src/layer/vector/CircleMarker.js @@ -0,0 +1,25 @@ +/*
+ * L.CircleMarker is a circle overlay with a permanent pixel radius.
+ */
+
+L.CircleMarker = L.Circle.extend({
+ options: {
+ radius: 10,
+ weight: 2
+ },
+
+ initialize: function(latlng, options) {
+ L.Circle.prototype.initialize.call(this, latlng, null, options);
+ this._radius = this.options.radius;
+ },
+
+ projectLatlngs: function() {
+ this._point = this._map.latLngToLayerPoint(this._latlng);
+ },
+
+ setRadius: function(radius) {
+ this._radius = radius;
+ this._redraw();
+ return this;
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/vector/MultiPoly.js b/extlib/leaflet/src/layer/vector/MultiPoly.js new file mode 100644 index 00000000..60d6de68 --- /dev/null +++ b/extlib/leaflet/src/layer/vector/MultiPoly.js @@ -0,0 +1,27 @@ +/*
+ * Contains L.MultiPolyline and L.MultiPolygon layers.
+ */
+
+(function() {
+ function createMulti(klass) {
+ return L.FeatureGroup.extend({
+ initialize: function(latlngs, options) {
+ this._layers = {};
+ for (var i = 0, len = latlngs.length; i < len; i++) {
+ this.addLayer(new klass(latlngs[i], options));
+ }
+ },
+
+ setStyle: function(style) {
+ for (var i in this._layers) {
+ if (this._layers.hasOwnProperty(i) && this._layers[i].setStyle) {
+ this._layers[i].setStyle(style);
+ }
+ }
+ }
+ });
+ }
+
+ L.MultiPolyline = createMulti(L.Polyline);
+ L.MultiPolygon = createMulti(L.Polygon);
+}());
diff --git a/extlib/leaflet/src/layer/vector/Path.Popup.js b/extlib/leaflet/src/layer/vector/Path.Popup.js new file mode 100644 index 00000000..b82a4920 --- /dev/null +++ b/extlib/leaflet/src/layer/vector/Path.Popup.js @@ -0,0 +1,24 @@ +/*
+ * Popup extension to L.Path (polylines, polygons, circles), adding bindPopup method.
+ */
+
+L.Path.include({
+ bindPopup: function(content, options) {
+ if (!this._popup || this._popup.options !== options) {
+ this._popup = new L.Popup(options);
+ }
+ this._popup.setContent(content);
+
+ if (!this._openPopupAdded) {
+ this.on('click', this._openPopup, this);
+ this._openPopupAdded = true;
+ }
+
+ return this;
+ },
+
+ _openPopup: function(e) {
+ this._popup.setLatLng(e.latlng);
+ this._map.openPopup(this._popup);
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/vector/Path.VML.js b/extlib/leaflet/src/layer/vector/Path.VML.js new file mode 100644 index 00000000..8481d994 --- /dev/null +++ b/extlib/leaflet/src/layer/vector/Path.VML.js @@ -0,0 +1,91 @@ +/*
+ * Vector rendering for IE6-8 through VML.
+ * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
+ */
+
+L.Path.VML = (function() {
+ var d = document.createElement('div'), s;
+ d.innerHTML = '<v:shape adj="1"/>';
+ s = d.firstChild;
+ s.style.behavior = 'url(#default#VML)';
+
+ return (s && (typeof s.adj == 'object'));
+})();
+
+L.Path = L.Path.SVG || !L.Path.VML ? L.Path : L.Path.extend({
+ statics: {
+ CLIP_PADDING: 0.02
+ },
+
+ _createElement: (function() {
+ try {
+ document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
+ return function(name) {
+ return document.createElement('<lvml:' + name + ' class="lvml">');
+ };
+ } catch (e) {
+ return function(name) {
+ return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
+ };
+ }
+ })(),
+
+ _initRoot: function() {
+ if (!this._map._pathRoot) {
+ this._map._pathRoot = document.createElement('div');
+ this._map._pathRoot.className = 'leaflet-vml-container';
+ this._map._panes.overlayPane.appendChild(this._map._pathRoot);
+
+ this._map.on('moveend', this._updateViewport, this);
+ this._updateViewport();
+ }
+ },
+
+ _initPath: function() {
+ this._container = this._createElement('shape');
+ this._container.className += ' leaflet-vml-shape' +
+ (this.options.clickable ? ' leaflet-clickable' : '');
+ this._container.coordsize = '1 1';
+
+ this._path = this._createElement('path');
+ this._container.appendChild(this._path);
+
+ this._map._pathRoot.appendChild(this._container);
+ },
+
+ _initStyle: function() {
+ if (this.options.stroke) {
+ this._stroke = this._createElement('stroke');
+ this._stroke.endcap = 'round';
+ this._container.appendChild(this._stroke);
+ } else {
+ this._container.stroked = false;
+ }
+ if (this.options.fill) {
+ this._container.filled = true;
+ this._fill = this._createElement('fill');
+ this._container.appendChild(this._fill);
+ } else {
+ this._container.filled = false;
+ }
+ this._updateStyle();
+ },
+
+ _updateStyle: function() {
+ if (this.options.stroke) {
+ this._stroke.weight = this.options.weight + 'px';
+ this._stroke.color = this.options.color;
+ this._stroke.opacity = this.options.opacity;
+ }
+ if (this.options.fill) {
+ this._fill.color = this.options.fillColor || this.options.color;
+ this._fill.opacity = this.options.fillOpacity;
+ }
+ },
+
+ _updatePath: function() {
+ this._container.style.display = 'none';
+ this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug
+ this._container.style.display = '';
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/vector/Path.js b/extlib/leaflet/src/layer/vector/Path.js new file mode 100644 index 00000000..3d4837cc --- /dev/null +++ b/extlib/leaflet/src/layer/vector/Path.js @@ -0,0 +1,207 @@ +/*
+ * L.Path is a base class for rendering vector paths on a map. It's inherited by Polyline, Circle, etc.
+ */
+
+L.Path = L.Class.extend({
+ includes: [L.Mixin.Events],
+
+ statics: (function() {
+ var svgns = 'http://www.w3.org/2000/svg',
+ ce = 'createElementNS';
+
+ return {
+ SVG_NS: svgns,
+ SVG: !!(document[ce] && document[ce](svgns, 'svg').createSVGRect),
+
+ // how much to extend the clip area around the map view
+ // (relative to its size, e.g. 0.5 is half the screen in each direction)
+ CLIP_PADDING: 0.5
+ };
+ })(),
+
+ options: {
+ stroke: true,
+ color: '#0033ff',
+ weight: 5,
+ opacity: 0.5,
+
+ fill: false,
+ fillColor: null, //same as color by default
+ fillOpacity: 0.2,
+
+ clickable: true,
+
+ updateOnMoveEnd: false
+ },
+
+ initialize: function(options) {
+ L.Util.setOptions(this, options);
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+
+ this._initElements();
+ this._initEvents();
+ this.projectLatlngs();
+ this._updatePath();
+
+ map.on('viewreset', this.projectLatlngs, this);
+
+ this._updateTrigger = this.options.updateOnMoveEnd ? 'moveend' : 'viewreset';
+ map.on(this._updateTrigger, this._updatePath, this);
+ },
+
+ onRemove: function(map) {
+ map._pathRoot.removeChild(this._container);
+ map.off('viewreset', this._projectLatlngs, this);
+ map.off(this._updateTrigger, this._updatePath, this);
+ },
+
+ projectLatlngs: function() {
+ // do all projection stuff here
+ },
+
+ getPathString: function() {
+ // form path string here
+ },
+
+ setStyle: function(style) {
+ L.Util.setOptions(this, style);
+ if (this._path) {
+ this._updateStyle();
+ }
+ },
+
+ _initElements: function() {
+ this._initRoot();
+ this._initPath();
+ this._initStyle();
+ },
+
+ _initRoot: function() {
+ if (!this._map._pathRoot) {
+ this._map._pathRoot = this._createElement('svg');
+ this._map._panes.overlayPane.appendChild(this._map._pathRoot);
+
+ this._map.on('moveend', this._updateSvgViewport, this);
+ this._updateSvgViewport();
+ }
+ },
+
+ _updateSvgViewport: function() {
+ this._updateViewport();
+
+ var vp = this._map._pathViewport,
+ min = vp.min,
+ max = vp.max,
+ width = max.x - min.x,
+ height = max.y - min.y,
+ root = this._map._pathRoot,
+ pane = this._map._panes.overlayPane;
+
+ // Hack to make flicker on drag end on mobile webkit less irritating
+ // Unfortunately I haven't found a good workaround for this yet
+ if (L.Browser.mobileWebkit) { pane.removeChild(root); }
+
+ L.DomUtil.setPosition(root, min);
+ root.setAttribute('width', width);
+ root.setAttribute('height', height);
+ root.setAttribute('viewBox', [min.x, min.y, width, height].join(' '));
+
+ if (L.Browser.mobileWebkit) { pane.appendChild(root); }
+ },
+
+ _updateViewport: function() {
+ var p = L.Path.CLIP_PADDING,
+ size = this._map.getSize(),
+ //TODO this._map._getMapPanePos()
+ panePos = L.DomUtil.getPosition(this._map._mapPane),
+ min = panePos.multiplyBy(-1).subtract(size.multiplyBy(p)),
+ max = min.add(size.multiplyBy(1 + p * 2));
+
+ this._map._pathViewport = new L.Bounds(min, max);
+ },
+
+ _initPath: function() {
+ this._container = this._createElement('g');
+
+ this._path = this._createElement('path');
+ this._container.appendChild(this._path);
+
+ this._map._pathRoot.appendChild(this._container);
+ },
+
+ _initStyle: function() {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke-linejoin', 'round');
+ this._path.setAttribute('stroke-linecap', 'round');
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill-rule', 'evenodd');
+ } else {
+ this._path.setAttribute('fill', 'none');
+ }
+ this._updateStyle();
+ },
+
+ _updateStyle: function() {
+ if (this.options.stroke) {
+ this._path.setAttribute('stroke', this.options.color);
+ this._path.setAttribute('stroke-opacity', this.options.opacity);
+ this._path.setAttribute('stroke-width', this.options.weight);
+ }
+ if (this.options.fill) {
+ this._path.setAttribute('fill', this.options.fillColor || this.options.color);
+ this._path.setAttribute('fill-opacity', this.options.fillOpacity);
+ }
+ },
+
+ _updatePath: function() {
+ var str = this.getPathString();
+ if (!str) {
+ // fix webkit empty string parsing bug
+ str = 'M0 0';
+ }
+ this._path.setAttribute('d', str);
+ },
+
+ _createElement: function(name) {
+ return document.createElementNS(L.Path.SVG_NS, name);
+ },
+
+ // TODO remove duplication with L.Map
+ _initEvents: function() {
+ if (this.options.clickable) {
+ if (!L.Path.VML) {
+ this._path.setAttribute('class', 'leaflet-clickable');
+ }
+
+ L.DomEvent.addListener(this._container, 'click', this._onMouseClick, this);
+
+ var events = ['dblclick', 'mousedown', 'mouseover', 'mouseout'];
+ for (var i = 0; i < events.length; i++) {
+ L.DomEvent.addListener(this._container, events[i], this._fireMouseEvent, this);
+ }
+ }
+ },
+
+ _onMouseClick: function(e) {
+ if (this._map.dragging && this._map.dragging.moved()) { return; }
+ this._fireMouseEvent(e);
+ },
+
+ _fireMouseEvent: function(e) {
+ if (!this.hasEventListeners(e.type)) { return; }
+ this.fire(e.type, {
+ latlng: this._map.mouseEventToLatLng(e),
+ layerPoint: this._map.mouseEventToLayerPoint(e)
+ });
+ L.DomEvent.stopPropagation(e);
+ },
+
+ _redraw: function() {
+ this.projectLatlngs();
+ this._updatePath();
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/vector/Polygon.js b/extlib/leaflet/src/layer/vector/Polygon.js new file mode 100644 index 00000000..52bf2d6b --- /dev/null +++ b/extlib/leaflet/src/layer/vector/Polygon.js @@ -0,0 +1,58 @@ +/*
+ * L.Polygon is used to display polygons on a map.
+ */
+
+L.Polygon = L.Polyline.extend({
+ options: {
+ fill: true
+ },
+
+ initialize: function(latlngs, options) {
+ L.Polyline.prototype.initialize.call(this, latlngs, options);
+
+ if (latlngs[0] instanceof Array) {
+ this._latlngs = latlngs[0];
+ this._holes = latlngs.slice(1);
+ }
+ },
+
+ projectLatlngs: function() {
+ L.Polyline.prototype.projectLatlngs.call(this);
+
+ // project polygon holes points
+ // TODO move this logic to Polyline to get rid of duplication
+ this._holePoints = [];
+
+ if (!this._holes) return;
+
+ for (var i = 0, len = this._holes.length, hole; i < len; i++) {
+ this._holePoints[i] = [];
+
+ for(var j = 0, len2 = this._holes[i].length; j < len2; j++) {
+ this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]);
+ }
+ }
+ },
+
+ _clipPoints: function() {
+ var points = this._originalPoints,
+ newParts = [];
+
+ this._parts = [points].concat(this._holePoints);
+
+ if (this.options.noClip) return;
+
+ for (var i = 0, len = this._parts.length; i < len; i++) {
+ var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport);
+ if (!clipped.length) continue;
+ newParts.push(clipped);
+ }
+
+ this._parts = newParts;
+ },
+
+ _getPathPartStr: function(points) {
+ var str = L.Polyline.prototype._getPathPartStr.call(this, points);
+ return str + (L.Path.SVG ? 'z' : 'x');
+ }
+});
\ No newline at end of file diff --git a/extlib/leaflet/src/layer/vector/Polyline.js b/extlib/leaflet/src/layer/vector/Polyline.js new file mode 100644 index 00000000..606d7d71 --- /dev/null +++ b/extlib/leaflet/src/layer/vector/Polyline.js @@ -0,0 +1,112 @@ +
+L.Polyline = L.Path.extend({
+ initialize: function(latlngs, options) {
+ L.Path.prototype.initialize.call(this, options);
+ this._latlngs = latlngs;
+ },
+
+ options: {
+ // how much to simplify the polyline on each zoom level
+ // more = better performance and smoother look, less = more accurate
+ smoothFactor: 1.0,
+ noClip: false,
+
+ updateOnMoveEnd: true
+ },
+
+ projectLatlngs: function() {
+ this._originalPoints = [];
+
+ for (var i = 0, len = this._latlngs.length; i < len; i++) {
+ this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
+ }
+ },
+
+ getPathString: function() {
+ for (var i = 0, len = this._parts.length, str = ''; i < len; i++) {
+ str += this._getPathPartStr(this._parts[i]);
+ }
+ return str;
+ },
+
+ getLatLngs: function() {
+ return this._latlngs;
+ },
+
+ setLatLngs: function(latlngs) {
+ this._latlngs = latlngs;
+ this._redraw();
+ return this;
+ },
+
+ addLatLng: function(latlng) {
+ this._latlngs.push(latlng);
+ this._redraw();
+ return this;
+ },
+
+ spliceLatLngs: function(index, howMany) {
+ var removed = [].splice.apply(this._latlngs, arguments);
+ this._redraw();
+ return removed;
+ },
+
+ _getPathPartStr: function(points) {
+ var round = L.Path.VML;
+
+ for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) {
+ p = points[j];
+ if (round) p._round();
+ str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
+ }
+ return str;
+ },
+
+ _clipPoints: function() {
+ var points = this._originalPoints,
+ len = points.length,
+ i, k, segment;
+
+ if (this.options.noClip) {
+ this._parts = [points];
+ return;
+ }
+
+ this._parts = [];
+
+ var parts = this._parts,
+ vp = this._map._pathViewport,
+ lu = L.LineUtil;
+
+ for (i = 0, k = 0; i < len - 1; i++) {
+ segment = lu.clipSegment(points[i], points[i+1], vp, i);
+ if (!segment) continue;
+
+ parts[k] = parts[k] || [];
+ parts[k].push(segment[0]);
+
+ // if segment goes out of screen, or it's the last one, it's the end of the line part
+ if ((segment[1] != points[i+1]) || (i == len - 2)) {
+ parts[k].push(segment[1]);
+ k++;
+ }
+ }
+ },
+
+ // simplify each clipped part of the polyline
+ _simplifyPoints: function() {
+ var parts = this._parts,
+ lu = L.LineUtil;
+
+ for (var i = 0, len = parts.length; i < len; i++) {
+ parts[i] = lu.simplify(parts[i], this.options.smoothFactor);
+ }
+ },
+
+ _updatePath: function() {
+ this._clipPoints();
+ this._simplifyPoints();
+
+ L.Path.prototype._updatePath.call(this);
+ }
+});
\ No newline at end of file |