From 9c56e149ab4c65bd5b542af35c060c50539a6b33 Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Tue, 8 Apr 2014 14:41:16 +0100 Subject: Initial commit --- LICENSE | 22 +++++ README.md | 31 +++++++ leaflet-indoor.js | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 leaflet-indoor.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f7a4ea2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2014 Christopher Baines +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..03a1435 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +Leaflet Indoor +===================== + +Provides basic tools to create indoor maps with +[Leaflet](http://leafletjs.com). + +## Using the plugin + +See the included examples for usage. + +### Basic Usage + +Create a L.IndoorLayer, then add the data to it. + +```javascript +var indoorLayer = new L.IndoorLayer(); + +var levelControl = new L.Control.Level() + +// Connect the level control to the indoor layer +levelControl.addEventListener("levelchange", indoorLayer.setLevel, indoorLayer); +``` + +## Events + +L.Control.Level will fire levelchange events when a level is selected. + +## License + +Leaflet Indoor is free software, and may be redistributed under the +MIT-LICENSE. diff --git a/leaflet-indoor.js b/leaflet-indoor.js new file mode 100644 index 0000000..4daa40c --- /dev/null +++ b/leaflet-indoor.js @@ -0,0 +1,246 @@ +/** + * A layer that will display indoor data + * + * addData takes a GeoJSON feature collection, each feature must have a level + * property that indicates the level. + * + * getLevels can be called to get the array of levels that are present. + */ +L.Indoor = L.Class.extend({ + + options: { + getLevel: function(feature) { + return feature.properties.level; + } + }, + + initialize: function(data, options) { + L.setOptions(this, options); + options = this.options; + + var layers = this._layers = {}; + this._map = null; + + if ("level" in this.options) { + this._level = this.options.level; + } else { + this._level = null; + } + + if ("onEachFeature" in this.options) + var onEachFeature = this.options.onEachFeature; + + this.options.onEachFeature = function(feature, layer) { + + if (onEachFeature) + onEachFeature(feature, layer); + + if ("markerForFeature" in options) { + var marker = options.markerForFeature(feature); + if (typeof(marker) !== 'undefined') { + marker.on('click', function(e) { + layer.fire('click', e); + }); + + layers[feature.properties.level].addLayer(marker); + } + } + }; + + this.addData(data); + }, + addTo: function (map) { + map.addLayer(this); + return this; + }, + onAdd: function (map) { + this._map = map; + + if (this._level === null) { + var levels = this.getLevels(); + + if (levels.length !== 0) { + this._level = levels[0]; + } + } + + if (this._level !== null) { + this._map.addLayer(this._layers[this._level]); + } + }, + onRemove: function (map) { + this._map.removeLayer(this._layers[this._level]); + this._map = null; + }, + addData: function(data) { + var layers = this._layers; + + var options = this.options; + + var features = L.Util.isArray(data) ? data : data.features; + + features.forEach(function (part) { + + var level = options.getLevel(part); + + var layer; + + if (typeof level === 'undefined' || + level === null) + return; + + if (!("geometry" in part)) { + return; + } + + if (L.Util.isArray(level)) { + level.forEach(function(level) { + if (level in layers) { + layer = layers[level]; + } else { + layer = layers[level] = L.geoJson({ type: "FeatureCollection", features: [] }, options); + } + + layer.addData(part); + }); + } else { + if (level in layers) { + layer = layers[level]; + } else { + layer = layers[level] = L.geoJson({ type: "FeatureCollection", features: [] }, options); + } + + layer.addData(part); + } + }); + }, + getLevels: function() { + return Object.keys(this._layers); + }, + getLevel: function() { + return this._level; + }, + setLevel: function(level) { + if (typeof(level) === 'object') { + level = level.newLevel; + } + + if (this._level === level) + return; + + var oldLayer = this._layers[this._level]; + var layer = this._layers[level]; + + if (this._map !== null) { + this._map.removeLayer(oldLayer); + this._map.addLayer(layer); + } + + this._level = level; + } +}); + +L.indoor = function(data, options) { + return new L.Indoor(data, options); +}; + +L.Control.Level = L.Control.extend({ + includes: L.Mixin.Events, + + options: { + position: 'bottomright', + parseLevel: function(level) { + return parseInt(level, 10); + } + }, + + initialize: function(options) { + L.setOptions(this, options); + + this._map = null; + this._buttons = {}; + this._listeners = []; + this._level = options.level; + + this.addEventListener("levelchange", this._levelChange, this); + }, + onAdd: function(map) { + var div = L.DomUtil.create('div', 'leaflet-bar leaflet-control'); + + div.style.font = "18px 'Lucida Console',Monaco,monospace"; + + var buttons = this._buttons; + var activeLevel = this._level; + var self = this; + + var levels = []; + + for (var i=0; i=0; i--) { + var level = levels[i].num; + var originalLevel = levels[i].label; + + var levelBtn = L.DomUtil.create('a', 'leaflet-button-part', div); + + if (level === activeLevel || originalLevel === activeLevel) { + levelBtn.style.backgroundColor = "#b0b0b0"; + } + + levelBtn.appendChild(levelBtn.ownerDocument.createTextNode(originalLevel)); + + (function(level) { + levelBtn.onclick = function() { + self.setLevel(level); + }; + })(level); + + buttons[level] = levelBtn; + } + + return div; + }, + _levelChange: function(e) { + // Probably won't work in some browsers, see + // https://developer.mozilla.org/en-US/docs/Web/API/element.classList + + if (this._map !== null) { + if (typeof e.oldLevel !== "undefined") + this._buttons[e.oldLevel].style.backgroundColor = "#FFFFFF"; + this._buttons[e.newLevel].style.backgroundColor = "#b0b0b0"; + } + }, + setLevel: function(level) { + + if (level === this._level) + return; + + var oldLevel = this._level; + this._level = level; + + this.fireEvent("levelchange", { + oldLevel: oldLevel, + newLevel: level + }); + }, + getLevel: function() { + return this._level; + } +}); + +L.Control.level = function (options) { + return new L.Control.Level(options); +}; -- cgit v1.2.3