aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Baines <mail@cbaines.net>2014-04-08 14:41:16 +0100
committerChristopher Baines <mail@cbaines.net>2014-04-08 14:41:44 +0100
commit9c56e149ab4c65bd5b542af35c060c50539a6b33 (patch)
tree9e9d6173bacdde3a410d13fc0d73bfc588572ce8
downloadleaflet-indoor-9c56e149ab4c65bd5b542af35c060c50539a6b33.tar
leaflet-indoor-9c56e149ab4c65bd5b542af35c060c50539a6b33.tar.gz
Initial commit
-rw-r--r--LICENSE22
-rw-r--r--README.md31
-rw-r--r--leaflet-indoor.js246
3 files changed, 299 insertions, 0 deletions
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<this.options.levels.length; i++) {
+ var level = this.options.levels[i];
+
+ var levelNum = self.options.parseLevel(level);
+
+ levels.push({
+ num: levelNum,
+ label: level
+ });
+ }
+
+ levels.sort(function(a, b) {
+ return a.num - b.num;
+ });
+
+ for (i=levels.length-1; 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);
+};