diff --git a/assets/leaflet-layer-overpass/LICENSE b/assets/leaflet-layer-overpass/LICENSE deleted file mode 100644 index 93b5069..0000000 --- a/assets/leaflet-layer-overpass/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) 2013 Karsten Hinz - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Please note that this software includes other libraries or parts of libraries which have their own license file or license annotation in the source code and may be released under different licences. diff --git a/assets/leaflet-layer-overpass/OverPassLayer.css b/assets/leaflet-layer-overpass/OverPassLayer.css deleted file mode 100644 index 9a644e8..0000000 --- a/assets/leaflet-layer-overpass/OverPassLayer.css +++ /dev/null @@ -1,8 +0,0 @@ -.leaflet-control-minZoomIndicator { - font-size: 2em; - background: #ffffff; - background-color: rgba(255,255,255,0.7); - border-radius: 10px; - padding: 1px 15px; - opacity: 0.5; -} diff --git a/assets/leaflet-layer-overpass/OverPassLayer.js b/assets/leaflet-layer-overpass/OverPassLayer.js deleted file mode 100644 index 3f8f0e8..0000000 --- a/assets/leaflet-layer-overpass/OverPassLayer.js +++ /dev/null @@ -1,333 +0,0 @@ -L.Control.MinZoomIndicator = L.Control.extend({ - options: { - position: 'bottomleft', - }, - - /** - * map: layerId -> zoomlevel - */ - _layers: {}, - - /** TODO check if nessesary - */ - initialize: function (options) { - L.Util.setOptions(this, options); - this._layers = new Object(); - }, - - /** - * adds a layer with minzoom information to this._layers - */ - _addLayer: function(layer) { - var minzoom = 15; - if (layer.options.minzoom) { - minzoom = layer.options.minzoom; - } - this._layers[layer._leaflet_id] = minzoom; - this._updateBox(null); - }, - - /** - * removes a layer from this._layers - */ - _removeLayer: function(layer) { - this._layers[layer._leaflet_id] = null; - this._updateBox(null); - }, - - _getMinZoomLevel: function() { - var minZoomlevel=-1; - for(var key in this._layers) { - if ((this._layers[key] != null)&&(this._layers[key] > minZoomlevel)) { - minZoomlevel = this._layers[key]; - } - } - return minZoomlevel; - }, - - onAdd: function (map) { - this._map = map; - map.zoomIndicator = this; - - var className = this.className; - var container = this._container = L.DomUtil.create('div', className); - map.on('moveend', this._updateBox, this); - this._updateBox(null); - - // L.DomEvent.disableClickPropagation(container); - return container; - }, - - onRemove: function(map) { - L.Control.prototype.onRemove.call(this, map); - map.off({ - 'moveend': this._updateBox - }, this); - - this._map = null; - }, - - _updateBox: function (event) { - //console.log("map moved -> update Container..."); - if (event != null) { - L.DomEvent.preventDefault(event); - } - var minzoomlevel = this._getMinZoomLevel(); - if (minzoomlevel == -1) { - this._container.innerHTML = this.options.minZoomMessageNoLayer; - }else{ - this._container.innerHTML = this.options.minZoomMessage - .replace(/CURRENTZOOM/, this._map.getZoom()) - .replace(/MINZOOMLEVEL/, minzoomlevel); - } - - if (this._map.getZoom() >= minzoomlevel) { - this._container.style.display = 'none'; - }else{ - this._container.style.display = 'block'; - } - }, - - className : 'leaflet-control-minZoomIndicator' -}); - -L.LatLngBounds.prototype.toOverpassBBoxString = function (){ - var a = this._southWest, - b = this._northEast; - return [a.lat, a.lng, b.lat, b.lng].join(","); -} - -L.OverPassLayer = L.FeatureGroup.extend({ - options: { - debug: false, - minzoom: 15, - endpoint: "http://overpass-api.de/api/", - query: "(node(BBOX)[organic];node(BBOX)[second_hand];);out qt;", - callback: function(data) { - for(var i = 0; i < data.elements.length; i++) { - var e = data.elements[i]; - - if (e.id in this.instance._ids) return; - this.instance._ids[e.id] = true; - var pos; - if (e.type == "node") { - pos = new L.LatLng(e.lat, e.lon); - } else { - pos = new L.LatLng(e.center.lat, e.center.lon); - } - var popup = this.instance._poiInfo(e.tags,e.id); - var circle = L.circle(pos, 50, { - color: 'green', - fillColor: '#3f0', - fillOpacity: 0.5 - }) - .bindPopup(popup); - this.instance.addLayer(circle); - } - }, - beforeRequest: function() { - if (this.options.debug) { - console.debug('about to query the OverPassAPI'); - } - }, - afterRequest: function() { - if (this.options.debug) { - console.debug('all queries have finished!'); - } - }, - minZoomIndicatorOptions: { - position: 'bottomleft', - minZoomMessageNoLayer: "no layer assigned", - minZoomMessage: "current Zoom-Level: CURRENTZOOM all data at Level: MINZOOMLEVEL" - }, - }, - - initialize: function (options) { - L.Util.setOptions(this, options); - this._layers = {}; - // save position of the layer or any options from the constructor - this._ids = {}; - this._requested = {}; - }, - - _poiInfo: function(tags,id) { - var link = document.createElement("a"); - link.href = "http://www.openstreetmap.org/edit?editor=id&node=" + id; - link.appendChild(document.createTextNode("Edit this entry in iD")); - var table = document.createElement('table'); - for (var key in tags){ - var row = table.insertRow(0); - row.insertCell(0).appendChild(document.createTextNode(key)); - row.insertCell(1).appendChild(document.createTextNode(tags[key])); - } - var div = document.createElement("div") - div.appendChild(link); - div.appendChild(table); - return div; - }, - - /** - * splits the current view in uniform bboxes to allow caching - */ - long2tile: function (lon,zoom) { return (Math.floor((lon+180)/360*Math.pow(2,zoom))); }, - lat2tile: function (lat,zoom) { - return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom))); - }, - tile2long: function (x,z) { - return (x/Math.pow(2,z)*360-180); - }, - tile2lat: function (y,z) { - var n=Math.PI-2*Math.PI*y/Math.pow(2,z); - return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n)))); - }, - _view2BBoxes: function(l,b,r,t) { - //console.log(l+"\t"+b+"\t"+r+"\t"+t); - //this.addBBox(l,b,r,t); - //console.log("calc bboxes"); - var requestZoomLevel= 14; - //get left tile index - var lidx = this.long2tile(l,requestZoomLevel); - var ridx = this.long2tile(r,requestZoomLevel); - var tidx = this.lat2tile(t,requestZoomLevel); - var bidx = this.lat2tile(b,requestZoomLevel); - - //var result; - var result = new Array(); - for (var x=lidx; x<=ridx; x++) { - for (var y=tidx; y<=bidx; y++) {//in tiles tidx<=bidx - var left = Math.round(this.tile2long(x,requestZoomLevel)*1000000)/1000000; - var right = Math.round(this.tile2long(x+1,requestZoomLevel)*1000000)/1000000; - var top = Math.round(this.tile2lat(y,requestZoomLevel)*1000000)/1000000; - var bottom = Math.round(this.tile2lat(y+1,requestZoomLevel)*1000000)/1000000; - //console.log(left+"\t"+bottom+"\t"+right+"\t"+top); - //this.addBBox(left,bottom,right,top); - //console.log("http://osm.org?bbox="+left+","+bottom+","+right+","+top); - result.push( new L.LatLngBounds(new L.LatLng(bottom, left),new L.LatLng(top, right))); - } - } - //console.log(result); - return result; - }, - - addBBox: function (l,b,r,t) { - var polygon = L.polygon([ - [t, l], - [b, l], - [b, r], - [t, r] - ]).addTo(this._map); - }, - - onMoveEnd: function () { - if (this.options.debug) { - console.debug("load Pois"); - } - //console.log(this._map.getBounds()); - if (this._map.getZoom() >= this.options.minzoom) { - //var bboxList = new Array(this._map.getBounds()); - var bboxList = this._view2BBoxes( - this._map.getBounds()._southWest.lng, - this._map.getBounds()._southWest.lat, - this._map.getBounds()._northEast.lng, - this._map.getBounds()._northEast.lat); - - // controls the after/before (Request) callbacks - var finishedCount = 0; - var queryCount = bboxList.length; - var beforeRequest = true; - - for (var i = 0; i < bboxList.length; i++) { - var bbox = bboxList[i]; - var x = bbox._southWest.lng; - var y = bbox._northEast.lat; - if ((x in this._requested) && (y in this._requested[x]) && (this._requested[x][y] == true)) { - queryCount--; - continue; - } - if (!(x in this._requested)) { - this._requested[x] = {}; - } - this._requested[x][y] = true; - - - var queryWithMapCoordinates = this.options.query.replace(/(BBOX)/g, bbox.toOverpassBBoxString()); - var url = this.options.endpoint + "interpreter?data=[out:json];" + queryWithMapCoordinates; - - if (beforeRequest) { - this.options.beforeRequest.call(this); - beforeRequest = false; - } - - var self = this; - var request = new XMLHttpRequest(); - request.open("GET", url, true); - - request.onload = function() { - if (this.status >= 200 && this.status < 400) { - var reference = {instance: self}; - self.options.callback.call(reference, JSON.parse(this.response)); - if (self.options.debug) { - console.debug('queryCount: ' + queryCount + ' - finishedCount: ' + finishedCount); - } - if (++finishedCount == queryCount) { - self.options.afterRequest.call(self); - } - } - }; - - request.send(); - - - } - } - }, - - onAdd: function (map) { - this._map = map; - if (map.zoomIndicator) { - this._zoomControl = map.zoomIndicator; - this._zoomControl._addLayer(this); - }else{ - this._zoomControl = new L.Control.MinZoomIndicator(this.options.minZoomIndicatorOptions); - map.addControl(this._zoomControl); - this._zoomControl._addLayer(this); - } - - this.onMoveEnd(); - if (this.options.query.indexOf("(BBOX)") != -1) { - map.on('moveend', this.onMoveEnd, this); - } - if (this.options.debug) { - console.debug("add layer"); - } - }, - - onRemove: function (map) { - if (this.options.debug) { - console.debug("remove layer"); - } - L.LayerGroup.prototype.onRemove.call(this, map); - this._ids = {}; - this._requested = {}; - this._zoomControl._removeLayer(this); - - map.off({ - 'moveend': this.onMoveEnd - }, this); - - this._map = null; - }, - - getData: function () { - if (this.options.debug) { - console.debug(this._data); - } - return this._data; - } - -}); - -//FIXME no idea why the browser crashes with this code -//L.OverPassLayer = function (options) { -// return new L.OverPassLayer(options); -//}; diff --git a/assets/leaflet-layer-overpass/OverPassLayer.min.css b/assets/leaflet-layer-overpass/OverPassLayer.min.css deleted file mode 100644 index 5db48fa..0000000 --- a/assets/leaflet-layer-overpass/OverPassLayer.min.css +++ /dev/null @@ -1 +0,0 @@ -.leaflet-control-minZoomIndicator{font-size:2em;background:#fff;background-color:rgba(255,255,255,.7);border-radius:10px;padding:1px 15px;opacity:.5} \ No newline at end of file diff --git a/assets/leaflet-layer-overpass/OverPassLayer.min.js b/assets/leaflet-layer-overpass/OverPassLayer.min.js deleted file mode 100644 index 6fadba9..0000000 --- a/assets/leaflet-layer-overpass/OverPassLayer.min.js +++ /dev/null @@ -1 +0,0 @@ -L.Control.MinZoomIndicator=L.Control.extend({options:{position:"bottomleft"},_layers:{},initialize:function(t){L.Util.setOptions(this,t),this._layers=new Object},_addLayer:function(t){var e=15;t.options.minzoom&&(e=t.options.minzoom),this._layers[t._leaflet_id]=e,this._updateBox(null)},_removeLayer:function(t){this._layers[t._leaflet_id]=null,this._updateBox(null)},_getMinZoomLevel:function(){var t=-1;for(var e in this._layers)null!=this._layers[e]&&this._layers[e]>t&&(t=this._layers[e]);return t},onAdd:function(t){this._map=t,t.zoomIndicator=this;var e=this.className,o=this._container=L.DomUtil.create("div",e);return t.on("moveend",this._updateBox,this),this._updateBox(null),o},onRemove:function(t){L.Control.prototype.onRemove.call(this,t),t.off({moveend:this._updateBox},this),this._map=null},_updateBox:function(t){null!=t&&L.DomEvent.preventDefault(t);var e=this._getMinZoomLevel();this._container.innerHTML=-1==e?this.options.minZoomMessageNoLayer:this.options.minZoomMessage.replace(/CURRENTZOOM/,this._map.getZoom()).replace(/MINZOOMLEVEL/,e),this._container.style.display=this._map.getZoom()>=e?"none":"block"},className:"leaflet-control-minZoomIndicator"}),L.LatLngBounds.prototype.toOverpassBBoxString=function(){var t=this._southWest,e=this._northEast;return[t.lat,t.lng,e.lat,e.lng].join(",")},L.OverPassLayer=L.FeatureGroup.extend({options:{debug:!1,minzoom:15,endpoint:"http://overpass-api.de/api/",query:"(node(BBOX)[organic];node(BBOX)[second_hand];);out qt;",callback:function(t){for(var e=0;e=d;d++)for(var u=r;l>=u;u++){var p=Math.round(1e6*this.tile2long(d,i))/1e6,c=Math.round(1e6*this.tile2long(d+1,i))/1e6,m=Math.round(1e6*this.tile2lat(u,i))/1e6,_=Math.round(1e6*this.tile2lat(u+1,i))/1e6;h.push(new L.LatLngBounds(new L.LatLng(_,p),new L.LatLng(m,c)))}return h},addBBox:function(t,e,o,n){L.polygon([[n,t],[e,t],[e,o],[n,o]]).addTo(this._map)},onMoveEnd:function(){if(this.options.debug&&console.debug("load Pois"),this._map.getZoom()>=this.options.minzoom)for(var t=this._view2BBoxes(this._map.getBounds()._southWest.lng,this._map.getBounds()._southWest.lat,this._map.getBounds()._northEast.lng,this._map.getBounds()._northEast.lat),e=0,o=t.length,n=!0,i=0;i=200&&this.status<400){var t={instance:d};d.options.callback.call(t,JSON.parse(this.response)),d.options.debug&&console.debug("queryCount: "+o+" - finishedCount: "+e),++e==o&&d.options.afterRequest.call(d)}},u.send()}}},onAdd:function(t){this._map=t,t.zoomIndicator?(this._zoomControl=t.zoomIndicator,this._zoomControl._addLayer(this)):(this._zoomControl=new L.Control.MinZoomIndicator(this.options.minZoomIndicatorOptions),t.addControl(this._zoomControl),this._zoomControl._addLayer(this)),this.onMoveEnd(),-1!=this.options.query.indexOf("(BBOX)")&&t.on("moveend",this.onMoveEnd,this),this.options.debug&&console.debug("add layer")},onRemove:function(t){this.options.debug&&console.debug("remove layer"),L.LayerGroup.prototype.onRemove.call(this,t),this._ids={},this._requested={},this._zoomControl._removeLayer(this),t.off({moveend:this.onMoveEnd},this),this._map=null},getData:function(){return this.options.debug&&console.debug(this._data),this._data}}); \ No newline at end of file diff --git a/assets/leaflet-layer-overpass/README.md b/assets/leaflet-layer-overpass/README.md deleted file mode 100644 index 6435f2e..0000000 --- a/assets/leaflet-layer-overpass/README.md +++ /dev/null @@ -1,101 +0,0 @@ -Leaflet Layer OverPass -============================= -[![Bower version](https://badge.fury.io/bo/leaflet-layer-overpass.svg)](http://badge.fury.io/bo/leaflet-layer-overpass) - -## What is it? -A [Leaflet](http://leafletjs.com/) plugin to create a custom POI overlay - thanks to the [OSM](http://www.openstreetmap.org/) dataset and the [Overpass API](http://overpass-api.de/) - -checkout the [demo](http://kartenkarsten.github.io/leaflet-layer-overpass/demo/) - - -## Installation -You can use bower to install leaflet-layer-overpass. - -Simply run -```bash -$ bower install --save leaflet-layer-overpass -``` -After that you can include and use the `OverpassLayer.css` and `OverpassLayer.js` files (or `OverPassLayer.min.js` if you want the minified version) from the `dist` folder in your html. - -## How to use it? -```javascript -var attr_osm = 'Map data © OpenStreetMap contributors', -attr_overpass = 'POI via Overpass API'; -var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {opacity: 0.7, attribution: [attr_osm, attr_overpass].join(', ')}); - -var map = new L.Map('map').addLayer(osm).setView(new L.LatLng(52.265, 10.524), 14); - -//OverPassAPI overlay -var opl = new L.OverPassLayer({ - query: "node(BBOX)['amenity'='post_box'];out;", -}); - -map.addLayer(opl); -``` -In order to get a valid query the [Overpass-turbo IDE](http://overpass-turbo.eu/) might help. - -## What are the options? -You can specify an options object as an argument of L.OverPassLayer. -```javascript -options: { - endpoint: "http://overpass.osm.rambler.ru/cgi/", - query: "node(BBOX)['amenity'='post_box'];out;", - debug: false, - callback: function(data) { - for(var i=0;i geoJSON to -> Leaflet Layer to support ways and areas as well (see also [PoiMap](https://github.com/simon04/POImap/blob/master/railway.html), [OverPassTurbo](https://github.com/tyrasd/overpass-ide/blob/gh-pages/js/overpass.js)) -- improve popup text. use links, format addresses and contact details (compare with [OpenLinkMap](http://www.openlinkmap.org/)) -- improve caching - allow to store data for some days in browser - diff --git a/assets/leaflet-layer-overpass/bower.json b/assets/leaflet-layer-overpass/bower.json deleted file mode 100644 index fbceb00..0000000 --- a/assets/leaflet-layer-overpass/bower.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "leaflet-layer-overpass", - "version": "1.0.2", - "homepage": "https://github.com/kartenkarsten/leaflet-layer-overpass", - "authors": [ - "Karsten Hinz ", - "Knut Hühne " - ], - "description": "simply add an overpass layer to a leafleat map", - "main": "OverPassLayer.js", - "keywords": [ - "leaflet", - "overpass", - "openstreetmap", - "osm", - "features", - "layer" - ], - "license": "MIT", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "demo", - "test", - "tests" - ], - "dependencies": { - "leaflet": "~0.7.3" - } -} diff --git a/assets/leaflet-layer-overpass/gulpfile.js b/assets/leaflet-layer-overpass/gulpfile.js deleted file mode 100644 index 5a19c45..0000000 --- a/assets/leaflet-layer-overpass/gulpfile.js +++ /dev/null @@ -1,14 +0,0 @@ -var gulp = require('gulp'); -var concat = require('gulp-concat'); -var uglify = require('gulp-uglify'); -var rename = require('gulp-rename'); - -gulp.task('default', function() { - return gulp.src('./src/*.js') - .pipe(concat('OverPassLayer.js')) - .pipe(gulp.dest('./dist/')) - .pipe(uglify()) - .pipe(rename({ extname: '.min.js' })) - .pipe(gulp.dest('./dist/')); -}); - diff --git a/assets/leaflet-layer-overpass/package.json b/assets/leaflet-layer-overpass/package.json deleted file mode 100644 index 19acaa0..0000000 --- a/assets/leaflet-layer-overpass/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "leaflet-layer-overpass", - "version": "1.0.1", - "main": "gulpfile.js", - "devDependencies": { - "gulp-concat": "^2.5.0", - "gulp-rename": "^1.2.0", - "gulp": "^3.8.11", - "gulp-uglify": "^1.1.0" - }, - "dependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com/kartenkarsten/leaflet-layer-overpass/" - }, - "author": "Karsten Hinz", - "license": "MIT" -} diff --git a/assets/osmtogeojson/.travis.yml b/assets/osmtogeojson/.travis.yml new file mode 100644 index 0000000..ab2b61f --- /dev/null +++ b/assets/osmtogeojson/.travis.yml @@ -0,0 +1,13 @@ +sudo: false +language: node_js +node_js: + - "iojs" + - "4.1" + - "4.0" + - "0.12" + - "0.10" +# whitelist +branches: + only: + - gh-pages + - /.*/ diff --git a/assets/osmtogeojson/CHANGELOG.md b/assets/osmtogeojson/CHANGELOG.md new file mode 100644 index 0000000..c4cddf4 --- /dev/null +++ b/assets/osmtogeojson/CHANGELOG.md @@ -0,0 +1,117 @@ +2.2.12 +------ +backported from 3.0.0-dev: de-namespace full geometry content in output (internal state leak) + +2.2.11 +------ +revert "use strict" because of some issues on older (0.…) nodejs + +2.2.10 +------ +* fix another undeclared variable breaking the module in strict mode +* enable "use strict"; + +2.2.9 +----- +split off polygon detection data: https://github.com/tyrasd/osm-polygon-features + +2.2.8 +----- +fix variable leaking into global scope + +2.2.7 +----- +fix a bug where loading certain complex `out geom` content resulted in invalid polygon geometries + +2.2.6 +----- +add bower support #45 + +2.2.5 +----- +add `-m` parameter that minifies output json + +2.2.4 +----- +fixed a bug where full geometry information caused additional Point features in the output + +2.2.3 +----- +* updates to polygon detection: natural=cliff is not automatically an area, golf=* is rendered as polygons +* speed optimizations for xml input in CLI mode #34 + +2.2.1 +----- +* fix bug with ref-less, clipped full geometry ways in JSON mode + +2.2.0 +----- +* support for Overpass API "full" geometry + +2.1.0 +----- +* implemented support for Overpass API geometry types "center" and "bounds" +* new command line option `-n` to make properties numeric +* added verbose option/mode that displays some debug info to the console/stderr + +2.0.5 +----- +* support input files larger than 256 MB. #17 + +2.0.4 +----- +* fix unresolved xml entities in command line mode + +2.0.2 +----- +* fix a dangling dependency (which led fresh installations to crash prematurely) + +2.0.0 +----- +* simpler API (module exports as a plain function), old `.toGeojson` still available as a fallback +* output (multi)polygons with consistent winding orders +* improve handling of simple multipolygons +* use browserify for browser library (comes bundeled with dependencies) +* some minor speed improvements + +1.4.0 +----- +* fix various speed bottlenecks (xml parsing, geojson construction, …) +* better performance when handling large xml files with the cli tool: ![](https://f.cloud.github.com/assets/1927298/1461813/4a1ce53e-44ce-11e3-9a96-d600eb3aba9b.png) + +1.3.1 +----- +* add --help and --version parameters to cli tool +* fix bug with not automatically detected file formats in pipe mode (cli tool) + +1.3.0 +----- +* more versatile cli tool (can read osm json, optionally enhanced output) +* many more unit tests +* fixed bugs with some partially incomplete data + +1.2.1 +----- +* fix wrong (inverse) logic in uninterestingTags callback evaluation (backported from master) + +1.2.0 +----- +* add [demo](http://tyrasd.github.io/osmtogeojson/) page +* support for pipes in cli tool: `echo '' | osmtogeojson` +* add flat-properties output mode (default for the cli tool) +* add options to override default `uninterestingTags` and `polygonFeatures` +* better documentation +* more test cases +* further improvements in polygon feature detection + +1.1.1 +----- +* bugfix: remove unneeded debugging code + +1.1.0 +----- +* command line tool added + +1.0.0 +----- +* initial release diff --git a/assets/osmtogeojson/LICENSE b/assets/osmtogeojson/LICENSE new file mode 100644 index 0000000..0cbe986 --- /dev/null +++ b/assets/osmtogeojson/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Martin Raifer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/assets/osmtogeojson/Makefile b/assets/osmtogeojson/Makefile new file mode 100644 index 0000000..e41e2f1 --- /dev/null +++ b/assets/osmtogeojson/Makefile @@ -0,0 +1,6 @@ +osmtogeojson.js: index.js package.json lodash.custom.js node_modules + browserify -s osmtogeojson index.js | uglifyjs -c -m -o osmtogeojson.js +coverage: . + istanbul cover _mocha -x lodash.custom.js -- -R spec +lodash: . + lodash exports=node include=clone,merge,isEmpty,isArray,compact,each -d diff --git a/assets/osmtogeojson/README.md b/assets/osmtogeojson/README.md new file mode 100644 index 0000000..abe9407 --- /dev/null +++ b/assets/osmtogeojson/README.md @@ -0,0 +1,89 @@ +osmtogeojson +============ + +Converts [OSM](http://openstreetmap.org) [data](http://wiki.openstreetmap.org/wiki/OSM_XML) to [GeoJSON](http://www.geojson.org/). Try the [demo](http://tyrasd.github.io/osmtogeojson/)! + +* stable +* real OSM [polygon detection](https://wiki.openstreetmap.org/wiki/Overpass_turbo/Polygon_Features) +* proper OSM multipolygon support +* full support for extended Overpass API [geometry modes](http://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Print_.28out.29) +* well [tested](https://github.com/tyrasd/osmtogeojson/tree/gh-pages/test/) and proven +* fast + +This code is used in and maintained by the [overpass turbo](http://github.com/tyrasd/overpass-ide) project. + +[![Build Status](https://secure.travis-ci.org/tyrasd/osmtogeojson.png)](https://travis-ci.org/tyrasd/osmtogeojson) + +Usage +----- + +### command line tool + +Installation: + + $ npm install -g osmtogeojson + +Usage: + + $ osmtogeojson file.osm > file.geojson + +Supported command line options are shown with: + + $ osmtogeojson --help + +When working with extra large data files (≳ 100 MB) it is recommended to run the programm with a little extra memory to avoid *process out of memory* errors. The easiest way to do this is by running the command as `node ` and setting the `--max_old_space_size=…` parameter to the available memory size in MB (osmtogeojson typically needs about 4-5 times the input data size): + + $ node --max_old_space_size=8192 `which osmtogeojson` large.osm > large.geojson + +### nodejs library + +Installation: + + $ npm install osmtogeojson + +Usage: + + var osmtogeojson = require('osmtogeojson'); + osmtogeojson(osm_data); + +### browser library + + + + osmtogeojson(osm_data); + +API +--- + +### `osmtogeojson( data, options )` + +Converts OSM data into GeoJSON. + +* `data`: the OSM data. Either as a XML DOM or in [OSM JSON](http://overpass-api.de/output_formats.html#json). +* `options`: optional. The following options can be used: + * `flatProperties`: If true, the resulting GeoJSON feature's properties will be a simple key-value list instead of a structured json object (with separate tags and metadata). default: false + * `uninterestingTags`: Either a [blacklist](https://github.com/tyrasd/osmtogeojson/blob/2.0.0/index.js#L14-L24) of tag keys or a callback function. Will be used to decide if a feature is *interesting* enough for its own GeoJSON feature. + * `polygonFeatures`: Either a [json object](https://github.com/tyrasd/osmtogeojson/blob/2.0.0/polygon_features.json) or callback function that is used to determine if a closed way should be treated as a Polygon or LineString. [read more](https://wiki.openstreetmap.org/wiki/Overpass_turbo/Polygon_Features) + +The result is a javascript object of GeoJSON data: + +GeoJSON +------- + +The GeoJSON produced by this library will include exactly one GeoJSON-feature for each of the following OSM objects (that is everything that is also visible in overpass turbo's map view): + +* all unconnected or [*interesting*](#api) tagged nodes (POIs) +* all ways (except [*uninteresting*](#api) multipolygon outlines) +* all multipolygons (simple multipolygons with exactly one closed outer way are present via their outer way) + +All data is given as a FeatureCollection. Each Feature in the collection has an `id` property that is formed from the type and id of the original OSM object (e.g. `node/123`) and has the member `properties` containing the following data: + +* `type`: the OSM data type +* `id`: the OSM id +* `tags`: a collection of all tags +* `meta`: metainformaton about the feature (e.g. version, timestamp, user, etc.) +* `relations`: an array of relations the feature is member of. Each relation is encoded as an object literal containing the following properties: `role` (membership role), `rel` (the relation's id) and `reltags` (contains all tags of the relation) +* `tainted`: this flag is set when the feature's geometry is incomplete (e.g. missing nodes of a way or missing ways of a multipolygon) + +If the [option](#api) `flatProperties` is set to true, the `properties` object will not contain any nested object literals, but directly provide a concise id, meta data and the tags of the respective OSM object. + diff --git a/assets/osmtogeojson/bower.json b/assets/osmtogeojson/bower.json new file mode 100644 index 0000000..2de94c3 --- /dev/null +++ b/assets/osmtogeojson/bower.json @@ -0,0 +1,26 @@ +{ + "name": "osmtogeojson", + "main": "osmtogeojson.js", + "version": "2.2.12", + "homepage": "http://tyrasd.github.io/osmtogeojson", + "authors": [ + "Martin Raifer" + ], + "description": "convert OSM to geojson", + "moduleType": [ + "node" + ], + "keywords": [ + "OSM", + "geoJSON", + "GIS" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/assets/osmtogeojson/index.html b/assets/osmtogeojson/index.html new file mode 100644 index 0000000..d7f943d --- /dev/null +++ b/assets/osmtogeojson/index.html @@ -0,0 +1,202 @@ + + + + osmtogeojson + + + + + + + + Fork me on GitHub +
+ osmtogeojson converts OSM data to GeoJSON. +
+
+
+

OSM data

+ +
+ + convert to GeoJSON → +
+
+

osmtogeojson is a Javascript module for converting OSM data (OSM XML or Overpass JSON) to GeoJSON. It works in the browser, nodejs and can also be used as a command line tool. This OSM conversion code was written for and is maintained by the overpass-turbo project.

+ Usage: +
    +
  • In the browser: +
      +
    • + <script src='osmtogeojson.js'></script> +
    • +
    • + osmtogeojson(osm_data); +
    • +
    +
  • +
  • As a nodejs library: +
      +
    • + $ npm install osmtogeojson +
    • +
    • + var osmtogeojson = require('osmtogeojson');
      + osmtogeojson(xml_data); +
    • +
    +
  • +
  • As a command line tool: +
      +
    • + $ npm install -g osmtogeojson +
    • +
    • + $ osmtogeojson file.osm > file.geojson +
    • +
    +
  • +
+ Read more about the API in the official documentation. +
+

download osmtogeojson.js

+ github: tyrasd/osmtogeojson
+ npm: osmtogeojson
+ license: MIT
+

+
+
+ + + diff --git a/assets/osmtogeojson/index.js b/assets/osmtogeojson/index.js new file mode 100644 index 0000000..4f9cf2d --- /dev/null +++ b/assets/osmtogeojson/index.js @@ -0,0 +1,945 @@ +var _ = require("./lodash.custom.js"); +var rewind = require("geojson-rewind"); + +// see https://wiki.openstreetmap.org/wiki/Overpass_turbo/Polygon_Features +var polygonFeatures = {}; +require("osm-polygon-features").forEach(function(tags) { + if (tags.polygon === "all") + polygonFeatures[tags.key] = true; + else { + var list = (tags.polygon === "whitelist") ? "included_values" : "excluded_values", + tagValuesObj = {}; + tags.values.forEach(function(value) { tagValuesObj[value] = true; }); + polygonFeatures[tags.key] = {}; + polygonFeatures[tags.key][list] = tagValuesObj; + } +}); + +var osmtogeojson = {}; + +osmtogeojson = function( data, options ) { + + options = _.merge( + { + verbose: false, + flatProperties: false, + uninterestingTags: { + "source": true, + "source_ref": true, + "source:ref": true, + "history": true, + "attribution": true, + "created_by": true, + "tiger:county": true, + "tiger:tlid": true, + "tiger:upload_uuid": true + }, + polygonFeatures: polygonFeatures, + }, + options + ); + + var result; + if ( ((typeof XMLDocument !== "undefined") && data instanceof XMLDocument || + (typeof XMLDocument === "undefined") && data.childNodes) ) + result = _osmXML2geoJSON(data); + else + result = _overpassJSON2geoJSON(data); + return result; + + function _overpassJSON2geoJSON(json) { + // sort elements + var nodes = new Array(); + var ways = new Array(); + var rels = new Array(); + // helper functions + function centerGeometry(object) { + var pseudoNode = _.clone(object); + pseudoNode.lat = object.center.lat; + pseudoNode.lon = object.center.lon; + pseudoNode.__is_center_placeholder = true; + nodes.push(pseudoNode); + } + function boundsGeometry(object) { + var pseudoWay = _.clone(object); + pseudoWay.nodes = []; + function addPseudoNode(lat,lon,i) { + var pseudoNode = { + type:"node", + id: "_"+pseudoWay.type+"/"+pseudoWay.id+"bounds"+i, + lat: lat, + lon: lon + } + pseudoWay.nodes.push(pseudoNode.id); + nodes.push(pseudoNode); + } + addPseudoNode(pseudoWay.bounds.minlat,pseudoWay.bounds.minlon,1); + addPseudoNode(pseudoWay.bounds.maxlat,pseudoWay.bounds.minlon,2); + addPseudoNode(pseudoWay.bounds.maxlat,pseudoWay.bounds.maxlon,3); + addPseudoNode(pseudoWay.bounds.minlat,pseudoWay.bounds.maxlon,4); + pseudoWay.nodes.push(pseudoWay.nodes[0]); + pseudoWay.__is_bounds_placeholder = true; + ways.push(pseudoWay); + } + function fullGeometryWay(way) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon, + __is_uninteresting: true + } + nodes.push(geometryNode); + } + if (!_.isArray(way.nodes)) { + way.nodes = way.geometry.map(function(nd) { + if (nd !== null) // have to skip ref-less nodes + return "_anonymous@"+nd.lat+"/"+nd.lon; + else + return "_anonymous@unknown_location"; + }); + } + way.geometry.forEach(function(nd, i) { + if (nd) { + addFullGeometryNode( + nd.lat, + nd.lon, + way.nodes[i] + ); + } + }); + } + function fullGeometryRelation(rel) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon + } + nodes.push(geometryNode); + } + function addFullGeometryWay(geometry,id) { + // shared multipolygon ways cannot be defined multiple times with the same id. + if (ways.some(function (way) { // todo: this is slow :( + return way.type == "way" && way.id == id; + })) return; + var geometryWay = { + type: "way", + id: id, + nodes:[] + } + function addFullGeometryWayPseudoNode(lat,lon) { + // todo? do not save the same pseudo node multiple times + var geometryPseudoNode = { + type:"node", + id: "_anonymous@"+lat+"/"+lon, + lat: lat, + lon: lon, + __is_uninteresting: true + } + geometryWay.nodes.push(geometryPseudoNode.id); + nodes.push(geometryPseudoNode); + } + geometry.forEach(function(nd) { + if (nd) { + addFullGeometryWayPseudoNode( + nd.lat, + nd.lon + ); + } else { + geometryWay.nodes.push(undefined); + } + }); + ways.push(geometryWay); + } + rel.members.forEach(function(member, i) { + if (member.type == "node") { + if (member.lat) { + addFullGeometryNode( + member.lat, + member.lon, + member.ref + ); + } + } else if (member.type == "way") { + if (member.geometry) { + member.ref = "_fullGeom"+member.ref; + addFullGeometryWay( + member.geometry, + member.ref + ); + } + } + }); + } + // create copies of individual json objects to make sure the original data doesn't get altered + // todo: cloning is slow: see if this can be done differently! + for (var i=0;i 0 + }); + if (rel.center) + centerGeometry(rel); + if (has_full_geometry) + fullGeometryRelation(rel); + else if (rel.bounds) + boundsGeometry(rel); + break; + default: + // type=area (from coord-query) is an example for this case. + } + } + return _convert2geoJSON(nodes,ways,rels); + } + function _osmXML2geoJSON(xml) { + // sort elements + var nodes = new Array(); + var ways = new Array(); + var rels = new Array(); + // helper function + function copy_attribute( x, o, attr ) { + if (x.hasAttribute(attr)) + o[attr] = x.getAttribute(attr); + } + function centerGeometry(object, centroid) { + var pseudoNode = _.clone(object); + copy_attribute(centroid, pseudoNode, 'lat'); + copy_attribute(centroid, pseudoNode, 'lon'); + pseudoNode.__is_center_placeholder = true; + nodes.push(pseudoNode); + } + function boundsGeometry(object, bounds) { + var pseudoWay = _.clone(object); + pseudoWay.nodes = []; + function addPseudoNode(lat,lon,i) { + var pseudoNode = { + type:"node", + id: "_"+pseudoWay.type+"/"+pseudoWay.id+"bounds"+i, + lat: lat, + lon: lon + } + pseudoWay.nodes.push(pseudoNode.id); + nodes.push(pseudoNode); + } + addPseudoNode(bounds.getAttribute('minlat'),bounds.getAttribute('minlon'),1); + addPseudoNode(bounds.getAttribute('maxlat'),bounds.getAttribute('minlon'),2); + addPseudoNode(bounds.getAttribute('maxlat'),bounds.getAttribute('maxlon'),3); + addPseudoNode(bounds.getAttribute('minlat'),bounds.getAttribute('maxlon'),4); + pseudoWay.nodes.push(pseudoWay.nodes[0]); + pseudoWay.__is_bounds_placeholder = true; + ways.push(pseudoWay); + } + function fullGeometryWay(way, nds) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon, + __is_uninteresting: true + } + nodes.push(geometryNode); + return geometryNode.id; + } + if (!_.isArray(way.nodes)) { + way.nodes = []; + _.each( nds, function( nd, i ) { + way.nodes.push("_anonymous@"+nd.getAttribute('lat')+"/"+nd.getAttribute('lon')); + }); + } + _.each( nds, function( nd, i ) { + if (nd.getAttribute('lat')) { + addFullGeometryNode( + nd.getAttribute('lat'), + nd.getAttribute('lon'), + way.nodes[i] + ); + } + }); + } + function fullGeometryRelation(rel, members) { + function addFullGeometryNode(lat,lon,id) { + var geometryNode = { + type:"node", + id: id, + lat: lat, + lon: lon + } + nodes.push(geometryNode); + } + function addFullGeometryWay(nds,id) { + // shared multipolygon ways cannot be defined multiple times with the same id. + if (ways.some(function (way) { // todo: this is slow :( + return way.type == "way" && way.id == id; + })) return; + var geometryWay = { + type: "way", + id: id, + nodes:[] + } + function addFullGeometryWayPseudoNode(lat,lon) { + // todo? do not save the same pseudo node multiple times + var geometryPseudoNode = { + type:"node", + id: "_anonymous@"+lat+"/"+lon, + lat: lat, + lon: lon, + __is_uninteresting: true + } + geometryWay.nodes.push(geometryPseudoNode.id); + nodes.push(geometryPseudoNode); + } + _.each(nds, function(nd) { + if (nd.getAttribute('lat')) { + addFullGeometryWayPseudoNode( + nd.getAttribute('lat'), + nd.getAttribute('lon') + ); + } else { + geometryWay.nodes.push(undefined); + } + }); + ways.push(geometryWay); + } + _.each( members, function( member, i ) { + if (rel.members[i].type == "node") { + if (member.getAttribute('lat')) { + addFullGeometryNode( + member.getAttribute('lat'), + member.getAttribute('lon'), + rel.members[i].ref + ); + } + } else if (rel.members[i].type == "way") { + if (member.getElementsByTagName('nd').length > 0) { + rel.members[i].ref = "_fullGeom"+rel.members[i].ref; + addFullGeometryWay( + member.getElementsByTagName('nd'), + rel.members[i].ref + ); + } + } + }); + } + // nodes + _.each( xml.getElementsByTagName('node'), function( node, i ) { + var tags = {}; + _.each( node.getElementsByTagName('tag'), function( tag ) { + tags[tag.getAttribute('k')] = tag.getAttribute('v'); + }); + var nodeObject = { + 'type': 'node' + }; + copy_attribute( node, nodeObject, 'id' ); + copy_attribute( node, nodeObject, 'lat' ); + copy_attribute( node, nodeObject, 'lon' ); + copy_attribute( node, nodeObject, 'version' ); + copy_attribute( node, nodeObject, 'timestamp' ); + copy_attribute( node, nodeObject, 'changeset' ); + copy_attribute( node, nodeObject, 'uid' ); + copy_attribute( node, nodeObject, 'user' ); + if (!_.isEmpty(tags)) + nodeObject.tags = tags; + nodes.push(nodeObject); + }); + // ways + var centroid,bounds; + _.each( xml.getElementsByTagName('way'), function( way, i ) { + var tags = {}; + var wnodes = []; + _.each( way.getElementsByTagName('tag'), function( tag ) { + tags[tag.getAttribute('k')] = tag.getAttribute('v'); + }); + var has_full_geometry = false; + _.each( way.getElementsByTagName('nd'), function( nd, i ) { + var id; + if (id = nd.getAttribute('ref')) + wnodes[i] = id; + if (!has_full_geometry && nd.getAttribute('lat')) + has_full_geometry = true; + }); + var wayObject = { + "type": "way" + }; + copy_attribute( way, wayObject, 'id' ); + copy_attribute( way, wayObject, 'version' ); + copy_attribute( way, wayObject, 'timestamp' ); + copy_attribute( way, wayObject, 'changeset' ); + copy_attribute( way, wayObject, 'uid' ); + copy_attribute( way, wayObject, 'user' ); + if (wnodes.length > 0) + wayObject.nodes = wnodes; + if (!_.isEmpty(tags)) + wayObject.tags = tags; + if (centroid = way.getElementsByTagName('center')[0]) + centerGeometry(wayObject,centroid); + if (has_full_geometry) + fullGeometryWay(wayObject, way.getElementsByTagName('nd')); + else if (bounds = way.getElementsByTagName('bounds')[0]) + boundsGeometry(wayObject,bounds); + ways.push(wayObject); + }); + // relations + _.each( xml.getElementsByTagName('relation'), function( relation, i ) { + var tags = {}; + var members = []; + _.each( relation.getElementsByTagName('tag'), function( tag ) { + tags[tag.getAttribute('k')] = tag.getAttribute('v'); + }); + var has_full_geometry = false; + _.each( relation.getElementsByTagName('member'), function( member, i ) { + members[i] = {}; + copy_attribute( member, members[i], 'ref' ); + copy_attribute( member, members[i], 'role' ); + copy_attribute( member, members[i], 'type' ); + if (!has_full_geometry && + (members[i].type == 'node' && member.getAttribute('lat')) || + (members[i].type == 'way' && member.getElementsByTagName('nd').length>0) ) + has_full_geometry = true; + }); + var relObject = { + "type": "relation" + } + copy_attribute( relation, relObject, 'id' ); + copy_attribute( relation, relObject, 'version' ); + copy_attribute( relation, relObject, 'timestamp' ); + copy_attribute( relation, relObject, 'changeset' ); + copy_attribute( relation, relObject, 'uid' ); + copy_attribute( relation, relObject, 'user' ); + if (members.length > 0) + relObject.members = members; + if (!_.isEmpty(tags)) + relObject.tags = tags; + if (centroid = relation.getElementsByTagName('center')[0]) + centerGeometry(relObject,centroid); + if (has_full_geometry) + fullGeometryRelation(relObject, relation.getElementsByTagName('member')); + else if (bounds = relation.getElementsByTagName('bounds')[0]) + boundsGeometry(relObject,bounds); + rels.push(relObject); + }); + return _convert2geoJSON(nodes,ways,rels); + } + function _convert2geoJSON(nodes,ways,rels) { + + // helper function that checks if there are any tags other than "created_by", "source", etc. or any tag provided in ignore_tags + function has_interesting_tags(t, ignore_tags) { + if (typeof ignore_tags !== "object") + ignore_tags={}; + if (typeof options.uninterestingTags === "function") + return !options.uninterestingTags(t, ignore_tags); + for (var k in t) + if (!(options.uninterestingTags[k]===true) && + !(ignore_tags[k]===true || ignore_tags[k]===t[k])) + return true; + return false; + }; + // helper function to extract meta information + function build_meta_information(object) { + var res = { + "timestamp": object.timestamp, + "version": object.version, + "changeset": object.changeset, + "user": object.user, + "uid": object.uid + }; + for (var k in res) + if (res[k] === undefined) + delete res[k]; + return res; + } + + // some data processing (e.g. filter nodes only used for ways) + var nodeids = new Object(); + for (var i=0;i y) != (yj > y)) && + (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + if (intersect) inside = !inside; + } + return inside; + }; + // stolen from iD/relation.js + var o, outer; + // todo: all this coordinate mapping makes this unneccesarily slow. + // see the "todo: this is slow! :(" above. + inner = mapCoordinates(inner); + /*for (o = 0; o < outers.length; o++) { + outer = mapCoordinates(outers[o]); + if (polygonContainsPolygon(outer, inner)) + return o; + }*/ + for (o = 0; o < outers.length; o++) { + outer = mapCoordinates(outers[o]); + if (polygonIntersectsPolygon(outer, inner)) + return o; + } + } + mp = outers.map(function(o) {return [o];}); + for (var j=0; j0.1.0*/); + return geojson; + } + function _isPolygonFeature( tags ) { + var polygonFeatures = options.polygonFeatures; + if (typeof polygonFeatures === "function") + return polygonFeatures(tags); + // explicitely tagged non-areas + if ( tags['area'] === 'no' ) + return false; + // assuming that a typical OSM way has in average less tags than + // the polygonFeatures list, this way around should be faster + for ( var key in tags ) { + var val = tags[key]; + var pfk = polygonFeatures[key]; + // continue with next if tag is unknown or not "categorizing" + if ( typeof pfk === 'undefined' ) + continue; + // continue with next if tag is explicitely un-set ("building=no") + if ( val === 'no' ) + continue; + // check polygon features for: general acceptance, included or excluded values + if ( pfk === true ) + return true; + if ( pfk.included_values && pfk.included_values[val] === true ) + return true; + if ( pfk.excluded_values && pfk.excluded_values[val] !== true ) + return true; + } + // if no tags matched, this ain't no area. + return false; + } +}; + +// for backwards compatibility +osmtogeojson.toGeojson = osmtogeojson; + +module.exports = osmtogeojson; diff --git a/assets/osmtogeojson/lodash.custom.js b/assets/osmtogeojson/lodash.custom.js new file mode 100644 index 0000000..43d71b6 --- /dev/null +++ b/assets/osmtogeojson/lodash.custom.js @@ -0,0 +1,1794 @@ +/** + * @license + * Lo-Dash 2.4.1 (Custom Build) + * Build: `lodash exports="node" include="clone,merge,isEmpty,isArray,compact,each" -d` + * Copyright 2012-2013 The Dojo Foundation + * Based on Underscore.js 1.5.2 + * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + * Available under MIT license + */ +;(function() { + + /** Used to pool arrays and objects used internally */ + var arrayPool = []; + + /** Used internally to indicate various things */ + var indicatorObject = {}; + + /** Used as the max size of the `arrayPool` and `objectPool` */ + var maxPoolSize = 40; + + /** Used to match regexp flags from their coerced string values */ + var reFlags = /\w*$/; + + /** Used to detected named functions */ + var reFuncName = /^\s*function[ \n\r\t]+\w/; + + /** Used to detect functions containing a `this` reference */ + var reThis = /\bthis\b/; + + /** Used to fix the JScript [[DontEnum]] bug */ + var shadowedProps = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' + ]; + + /** `Object#toString` result shortcuts */ + var argsClass = '[object Arguments]', + arrayClass = '[object Array]', + boolClass = '[object Boolean]', + dateClass = '[object Date]', + errorClass = '[object Error]', + funcClass = '[object Function]', + numberClass = '[object Number]', + objectClass = '[object Object]', + regexpClass = '[object RegExp]', + stringClass = '[object String]'; + + /** Used to identify object classifications that `_.clone` supports */ + var cloneableClasses = {}; + cloneableClasses[funcClass] = false; + cloneableClasses[argsClass] = cloneableClasses[arrayClass] = + cloneableClasses[boolClass] = cloneableClasses[dateClass] = + cloneableClasses[numberClass] = cloneableClasses[objectClass] = + cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; + + /** Used as the property descriptor for `__bindData__` */ + var descriptor = { + 'configurable': false, + 'enumerable': false, + 'value': null, + 'writable': false + }; + + /** Used as the data object for `iteratorTemplate` */ + var iteratorData = { + 'args': '', + 'array': null, + 'bottom': '', + 'firstArg': '', + 'init': '', + 'keys': null, + 'loop': '', + 'shadowedProps': null, + 'support': null, + 'top': '', + 'useHas': false + }; + + /** Used to determine if values are of the language type Object */ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + /** Used as a reference to the global object */ + var root = (objectTypes[typeof window] && window) || this; + + /** Detect free variable `exports` */ + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + + /** Detect free variable `module` */ + var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports` */ + var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; + + /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */ + var freeGlobal = objectTypes[typeof global] && global; + if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Gets an array from the array pool or creates a new one if the pool is empty. + * + * @private + * @returns {Array} The array from the pool. + */ + function getArray() { + return arrayPool.pop() || []; + } + + /** + * Checks if `value` is a DOM node in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`. + */ + function isNode(value) { + // IE < 9 presents DOM nodes as `Object` objects except they have `toString` + // methods that are `typeof` "string" and still can coerce nodes to strings + return typeof value.toString != 'function' && typeof (value + '') == 'string'; + } + + /** + * Releases the given array back to the array pool. + * + * @private + * @param {Array} [array] The array to release. + */ + function releaseArray(array) { + array.length = 0; + if (arrayPool.length < maxPoolSize) { + arrayPool.push(array); + } + } + + /** + * Slices the `collection` from the `start` index up to, but not including, + * the `end` index. + * + * Note: This function is used instead of `Array#slice` to support node lists + * in IE < 9 and to ensure dense arrays are returned. + * + * @private + * @param {Array|Object|string} collection The collection to slice. + * @param {number} start The start index. + * @param {number} end The end index. + * @returns {Array} Returns the new array. + */ + function slice(array, start, end) { + start || (start = 0); + if (typeof end == 'undefined') { + end = array ? array.length : 0; + } + var index = -1, + length = end - start || 0, + result = Array(length < 0 ? 0 : length); + + while (++index < length) { + result[index] = array[start + index]; + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Used for `Array` method references. + * + * Normally `Array.prototype` would suffice, however, using an array literal + * avoids issues in Narwhal. + */ + var arrayRef = []; + + /** Used for native method references */ + var errorProto = Error.prototype, + objectProto = Object.prototype, + stringProto = String.prototype; + + /** Used to resolve the internal [[Class]] of values */ + var toString = objectProto.toString; + + /** Used to detect if a method is native */ + var reNative = RegExp('^' + + String(toString) + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + .replace(/toString| for [^\]]+/g, '.*?') + '$' + ); + + /** Native method shortcuts */ + var fnToString = Function.prototype.toString, + getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, + hasOwnProperty = objectProto.hasOwnProperty, + push = arrayRef.push, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + unshift = arrayRef.unshift; + + /** Used to set meta data on functions */ + var defineProperty = (function() { + // IE 8 only accepts DOM elements + try { + var o = {}, + func = isNative(func = Object.defineProperty) && func, + result = func(o, o, o) && func; + } catch(e) { } + return result; + }()); + + /* Native method shortcuts for methods with the same name as other `lodash` methods */ + var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, + nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, + nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys; + + /** Used to lookup a built-in constructor by [[Class]] */ + var ctorByClass = {}; + ctorByClass[arrayClass] = Array; + ctorByClass[boolClass] = Boolean; + ctorByClass[dateClass] = Date; + ctorByClass[funcClass] = Function; + ctorByClass[objectClass] = Object; + ctorByClass[numberClass] = Number; + ctorByClass[regexpClass] = RegExp; + ctorByClass[stringClass] = String; + + /** Used to avoid iterating non-enumerable properties in IE < 9 */ + var nonEnumProps = {}; + nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true }; + nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true }; + nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true }; + nonEnumProps[objectClass] = { 'constructor': true }; + + (function() { + var length = shadowedProps.length; + while (length--) { + var key = shadowedProps[length]; + for (var className in nonEnumProps) { + if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) { + nonEnumProps[className][key] = false; + } + } + } + }()); + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps the given value to enable intuitive + * method chaining. + * + * In addition to Lo-Dash methods, wrappers also have the following `Array` methods: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, + * and `unshift` + * + * Chaining is supported in custom builds as long as the `value` method is + * implicitly or explicitly included in the build. + * + * The chainable wrapper functions are: + * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, + * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`, + * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, + * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, + * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, + * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, + * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, + * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, + * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, + * and `zip` + * + * The non-chainable wrapper functions are: + * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`, + * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`, + * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, + * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, + * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, + * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`, + * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`, + * `template`, `unescape`, `uniqueId`, and `value` + * + * The wrapper functions `first` and `last` return wrapped values when `n` is + * provided, otherwise they return unwrapped values. + * + * Explicit chaining can be enabled by using the `_.chain` method. + * + * @name _ + * @constructor + * @category Chaining + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns a `lodash` instance. + * @example + * + * var wrapped = _([1, 2, 3]); + * + * // returns an unwrapped value + * wrapped.reduce(function(sum, num) { + * return sum + num; + * }); + * // => 6 + * + * // returns a wrapped value + * var squares = wrapped.map(function(num) { + * return num * num; + * }); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash() { + // no operation performed + } + + /** + * An object used to flag environments features. + * + * @static + * @memberOf _ + * @type Object + */ + var support = lodash.support = {}; + + (function() { + var ctor = function() { this.x = 1; }, + object = { '0': 1, 'length': 1 }, + props = []; + + ctor.prototype = { 'valueOf': 1, 'y': 1 }; + for (var key in new ctor) { props.push(key); } + for (key in arguments) { } + + /** + * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9). + * + * @memberOf _.support + * @type boolean + */ + support.argsClass = toString.call(arguments) == argsClass; + + /** + * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5). + * + * @memberOf _.support + * @type boolean + */ + support.argsObject = arguments.constructor == Object && !(arguments instanceof Array); + + /** + * Detect if `name` or `message` properties of `Error.prototype` are + * enumerable by default. (IE < 9, Safari < 5.1) + * + * @memberOf _.support + * @type boolean + */ + support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); + + /** + * Detect if `prototype` properties are enumerable by default. + * + * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 + * (if the prototype or a property on the prototype has been set) + * incorrectly sets a function's `prototype` property [[Enumerable]] + * value to `true`. + * + * @memberOf _.support + * @type boolean + */ + support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype'); + + /** + * Detect if functions can be decompiled by `Function#toString` + * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps). + * + * @memberOf _.support + * @type boolean + */ + support.funcDecomp = !isNative(root.WinRTError) && reThis.test(function() { return this; }); + + /** + * Detect if `Function#name` is supported (all but IE). + * + * @memberOf _.support + * @type boolean + */ + support.funcNames = typeof Function.name == 'string'; + + /** + * Detect if `arguments` object indexes are non-enumerable + * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1). + * + * @memberOf _.support + * @type boolean + */ + support.nonEnumArgs = key != 0; + + /** + * Detect if properties shadowing those on `Object.prototype` are non-enumerable. + * + * In IE < 9 an objects own properties, shadowing non-enumerable ones, are + * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug). + * + * @memberOf _.support + * @type boolean + */ + support.nonEnumShadows = !/valueOf/.test(props); + + /** + * Detect if own properties are iterated after inherited properties (all but IE < 9). + * + * @memberOf _.support + * @type boolean + */ + support.ownLast = props[0] != 'x'; + + /** + * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly. + * + * Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()` + * and `splice()` functions that fail to remove the last element, `value[0]`, + * of array-like objects even though the `length` property is set to `0`. + * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` + * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9. + * + * @memberOf _.support + * @type boolean + */ + support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]); + + /** + * Detect lack of support for accessing string characters by index. + * + * IE < 8 can't access characters by index and IE 8 can only access + * characters by index on string literals. + * + * @memberOf _.support + * @type boolean + */ + support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx'; + + /** + * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9) + * and that the JS engine errors when attempting to coerce an object to + * a string without a `toString` function. + * + * @memberOf _.support + * @type boolean + */ + try { + support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + '')); + } catch(e) { + support.nodeClass = true; + } + }(1)); + + /*--------------------------------------------------------------------------*/ + + /** + * The template used to create iterator functions. + * + * @private + * @param {Object} data The data object used to populate the text. + * @returns {string} Returns the interpolated text. + */ + var iteratorTemplate = function(obj) { + + var __p = 'var index, iterable = ' + + (obj.firstArg) + + ', result = ' + + (obj.init) + + ';\nif (!iterable) return result;\n' + + (obj.top) + + ';'; + if (obj.array) { + __p += '\nvar length = iterable.length; index = -1;\nif (' + + (obj.array) + + ') { '; + if (support.unindexedChars) { + __p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } '; + } + __p += '\n while (++index < length) {\n ' + + (obj.loop) + + ';\n }\n}\nelse { '; + } else if (support.nonEnumArgs) { + __p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' + + (obj.loop) + + ';\n }\n } else { '; + } + + if (support.enumPrototypes) { + __p += '\n var skipProto = typeof iterable == \'function\';\n '; + } + + if (support.enumErrorProps) { + __p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n '; + } + + var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); } + + if (obj.useHas && obj.keys) { + __p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n'; + if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; + } + __p += + (obj.loop) + + '; '; + if (conditions.length) { + __p += '\n }'; + } + __p += '\n } '; + } else { + __p += '\n for (index in iterable) {\n'; + if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) { + __p += ' if (' + + (conditions.join(' && ')) + + ') {\n '; + } + __p += + (obj.loop) + + '; '; + if (conditions.length) { + __p += '\n }'; + } + __p += '\n } '; + if (support.nonEnumShadows) { + __p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n '; + for (k = 0; k < 7; k++) { + __p += '\n index = \'' + + (obj.shadowedProps[k]) + + '\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))'; + if (!obj.useHas) { + __p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])'; + } + __p += ') {\n ' + + (obj.loop) + + ';\n } '; + } + __p += '\n } '; + } + + } + + if (obj.array || support.nonEnumArgs) { + __p += '\n}'; + } + __p += + (obj.bottom) + + ';\nreturn result'; + + return __p + }; + + /*--------------------------------------------------------------------------*/ + + /** + * The base implementation of `_.bind` that creates the bound function and + * sets its meta data. + * + * @private + * @param {Array} bindData The bind data array. + * @returns {Function} Returns the new bound function. + */ + function baseBind(bindData) { + var func = bindData[0], + partialArgs = bindData[2], + thisArg = bindData[4]; + + function bound() { + // `Function#bind` spec + // http://es5.github.io/#x15.3.4.5 + if (partialArgs) { + // avoid `arguments` object deoptimizations by using `slice` instead + // of `Array.prototype.slice.call` and not assigning `arguments` to a + // variable as a ternary expression + var args = slice(partialArgs); + push.apply(args, arguments); + } + // mimic the constructor's `return` behavior + // http://es5.github.io/#x13.2.2 + if (this instanceof bound) { + // ensure `new bound` is an instance of `func` + var thisBinding = baseCreate(func.prototype), + result = func.apply(thisBinding, args || arguments); + return isObject(result) ? result : thisBinding; + } + return func.apply(thisArg, args || arguments); + } + setBindData(bound, bindData); + return bound; + } + + /** + * The base implementation of `_.clone` without argument juggling or support + * for `thisArg` binding. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} [isDeep=false] Specify a deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates clones with source counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, isDeep, callback, stackA, stackB) { + if (callback) { + var result = callback(value); + if (typeof result != 'undefined') { + return result; + } + } + // inspect [[Class]] + var isObj = isObject(value); + if (isObj) { + var className = toString.call(value); + if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) { + return value; + } + var ctor = ctorByClass[className]; + switch (className) { + case boolClass: + case dateClass: + return new ctor(+value); + + case numberClass: + case stringClass: + return new ctor(value); + + case regexpClass: + result = ctor(value.source, reFlags.exec(value)); + result.lastIndex = value.lastIndex; + return result; + } + } else { + return value; + } + var isArr = isArray(value); + if (isDeep) { + // check for circular references and return corresponding clone + var initedStack = !stackA; + stackA || (stackA = getArray()); + stackB || (stackB = getArray()); + + var length = stackA.length; + while (length--) { + if (stackA[length] == value) { + return stackB[length]; + } + } + result = isArr ? ctor(value.length) : {}; + } + else { + result = isArr ? slice(value) : assign({}, value); + } + // add array properties assigned by `RegExp#exec` + if (isArr) { + if (hasOwnProperty.call(value, 'index')) { + result.index = value.index; + } + if (hasOwnProperty.call(value, 'input')) { + result.input = value.input; + } + } + // exit for shallow clone + if (!isDeep) { + return result; + } + // add the source value to the stack of traversed objects + // and associate it with its clone + stackA.push(value); + stackB.push(result); + + // recursively populate clone (susceptible to call stack limits) + (isArr ? baseEach : forOwn)(value, function(objValue, key) { + result[key] = baseClone(objValue, isDeep, callback, stackA, stackB); + }); + + if (initedStack) { + releaseArray(stackA); + releaseArray(stackB); + } + return result; + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} prototype The object to inherit from. + * @returns {Object} Returns the new object. + */ + function baseCreate(prototype, properties) { + return isObject(prototype) ? nativeCreate(prototype) : {}; + } + // fallback for browsers without `Object.create` + if (!nativeCreate) { + baseCreate = (function() { + function Object() {} + return function(prototype) { + if (isObject(prototype)) { + Object.prototype = prototype; + var result = new Object; + Object.prototype = null; + } + return result || root.Object(); + }; + }()); + } + + /** + * The base implementation of `_.createCallback` without support for creating + * "_.pluck" or "_.where" style callbacks. + * + * @private + * @param {*} [func=identity] The value to convert to a callback. + * @param {*} [thisArg] The `this` binding of the created callback. + * @param {number} [argCount] The number of arguments the callback accepts. + * @returns {Function} Returns a callback function. + */ + function baseCreateCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + // exit early for no `thisArg` or already bound by `Function#bind` + if (typeof thisArg == 'undefined' || !('prototype' in func)) { + return func; + } + var bindData = func.__bindData__; + if (typeof bindData == 'undefined') { + if (support.funcNames) { + bindData = !func.name; + } + bindData = bindData || !support.funcDecomp; + if (!bindData) { + var source = fnToString.call(func); + if (!support.funcNames) { + bindData = !reFuncName.test(source); + } + if (!bindData) { + // checks if `func` references the `this` keyword and stores the result + bindData = reThis.test(source); + setBindData(func, bindData); + } + } + } + // exit early if there are no `this` references or `func` is bound + if (bindData === false || (bindData !== true && bindData[1] & 1)) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 2: return function(a, b) { + return func.call(thisArg, a, b); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + } + return bind(func, thisArg); + } + + /** + * The base implementation of `createWrapper` that creates the wrapper and + * sets its meta data. + * + * @private + * @param {Array} bindData The bind data array. + * @returns {Function} Returns the new function. + */ + function baseCreateWrapper(bindData) { + var func = bindData[0], + bitmask = bindData[1], + partialArgs = bindData[2], + partialRightArgs = bindData[3], + thisArg = bindData[4], + arity = bindData[5]; + + var isBind = bitmask & 1, + isBindKey = bitmask & 2, + isCurry = bitmask & 4, + isCurryBound = bitmask & 8, + key = func; + + function bound() { + var thisBinding = isBind ? thisArg : this; + if (partialArgs) { + var args = slice(partialArgs); + push.apply(args, arguments); + } + if (partialRightArgs || isCurry) { + args || (args = slice(arguments)); + if (partialRightArgs) { + push.apply(args, partialRightArgs); + } + if (isCurry && args.length < arity) { + bitmask |= 16 & ~32; + return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); + } + } + args || (args = arguments); + if (isBindKey) { + func = thisBinding[key]; + } + if (this instanceof bound) { + thisBinding = baseCreate(func.prototype); + var result = func.apply(thisBinding, args); + return isObject(result) ? result : thisBinding; + } + return func.apply(thisBinding, args); + } + setBindData(bound, bindData); + return bound; + } + + /** + * The base implementation of `_.merge` without argument juggling or support + * for `thisArg` binding. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} [callback] The function to customize merging properties. + * @param {Array} [stackA=[]] Tracks traversed source objects. + * @param {Array} [stackB=[]] Associates values with source counterparts. + */ + function baseMerge(object, source, callback, stackA, stackB) { + (isArray(source) ? forEach : forOwn)(source, function(source, key) { + var found, + isArr, + result = source, + value = object[key]; + + if (source && ((isArr = isArray(source)) || isPlainObject(source))) { + // avoid merging previously merged cyclic sources + var stackLength = stackA.length; + while (stackLength--) { + if ((found = stackA[stackLength] == source)) { + value = stackB[stackLength]; + break; + } + } + if (!found) { + var isShallow; + if (callback) { + result = callback(value, source); + if ((isShallow = typeof result != 'undefined')) { + value = result; + } + } + if (!isShallow) { + value = isArr + ? (isArray(value) ? value : []) + : (isPlainObject(value) ? value : {}); + } + // add `source` and associated `value` to the stack of traversed objects + stackA.push(source); + stackB.push(value); + + // recursively merge objects and arrays (susceptible to call stack limits) + if (!isShallow) { + baseMerge(value, source, callback, stackA, stackB); + } + } + } + else { + if (callback) { + result = callback(value, source); + if (typeof result == 'undefined') { + result = source; + } + } + if (typeof result != 'undefined') { + value = result; + } + } + object[key] = value; + }); + } + + /** + * Creates a function that, when called, either curries or invokes `func` + * with an optional `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to reference. + * @param {number} bitmask The bitmask of method flags to compose. + * The bitmask may be composed of the following flags: + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` + * 8 - `_.curry` (bound) + * 16 - `_.partial` + * 32 - `_.partialRight` + * @param {Array} [partialArgs] An array of arguments to prepend to those + * provided to the new function. + * @param {Array} [partialRightArgs] An array of arguments to append to those + * provided to the new function. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new function. + */ + function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { + var isBind = bitmask & 1, + isBindKey = bitmask & 2, + isCurry = bitmask & 4, + isCurryBound = bitmask & 8, + isPartial = bitmask & 16, + isPartialRight = bitmask & 32; + + if (!isBindKey && !isFunction(func)) { + throw new TypeError; + } + if (isPartial && !partialArgs.length) { + bitmask &= ~16; + isPartial = partialArgs = false; + } + if (isPartialRight && !partialRightArgs.length) { + bitmask &= ~32; + isPartialRight = partialRightArgs = false; + } + var bindData = func && func.__bindData__; + if (bindData && bindData !== true) { + // clone `bindData` + bindData = slice(bindData); + if (bindData[2]) { + bindData[2] = slice(bindData[2]); + } + if (bindData[3]) { + bindData[3] = slice(bindData[3]); + } + // set `thisBinding` is not previously bound + if (isBind && !(bindData[1] & 1)) { + bindData[4] = thisArg; + } + // set if previously bound but not currently (subsequent curried functions) + if (!isBind && bindData[1] & 1) { + bitmask |= 8; + } + // set curried arity if not yet set + if (isCurry && !(bindData[1] & 4)) { + bindData[5] = arity; + } + // append partial left arguments + if (isPartial) { + push.apply(bindData[2] || (bindData[2] = []), partialArgs); + } + // append partial right arguments + if (isPartialRight) { + unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); + } + // merge flags + bindData[1] |= bitmask; + return createWrapper.apply(null, bindData); + } + // fast path for `_.bind` + var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; + return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); + } + + /** + * Creates compiled iteration functions. + * + * @private + * @param {...Object} [options] The compile options object(s). + * @param {string} [options.array] Code to determine if the iterable is an array or array-like. + * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop. + * @param {Function} [options.keys] A reference to `_.keys` for use in own property iteration. + * @param {string} [options.args] A comma separated string of iteration function arguments. + * @param {string} [options.top] Code to execute before the iteration branches. + * @param {string} [options.loop] Code to execute in the object loop. + * @param {string} [options.bottom] Code to execute after the iteration branches. + * @returns {Function} Returns the compiled function. + */ + function createIterator() { + // data properties + iteratorData.shadowedProps = shadowedProps; + + // iterator options + iteratorData.array = iteratorData.bottom = iteratorData.loop = iteratorData.top = ''; + iteratorData.init = 'iterable'; + iteratorData.useHas = true; + + // merge options into a template data object + for (var object, index = 0; object = arguments[index]; index++) { + for (var key in object) { + iteratorData[key] = object[key]; + } + } + var args = iteratorData.args; + iteratorData.firstArg = /^[^,]+/.exec(args)[0]; + + // create the function factory + var factory = Function( + 'baseCreateCallback, errorClass, errorProto, hasOwnProperty, ' + + 'indicatorObject, isArguments, isArray, isString, keys, objectProto, ' + + 'objectTypes, nonEnumProps, stringClass, stringProto, toString', + 'return function(' + args + ') {\n' + iteratorTemplate(iteratorData) + '\n}' + ); + + // return the compiled function + return factory( + baseCreateCallback, errorClass, errorProto, hasOwnProperty, + indicatorObject, isArguments, isArray, isString, iteratorData.keys, objectProto, + objectTypes, nonEnumProps, stringClass, stringProto, toString + ); + } + + /** + * Checks if `value` is a native function. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. + */ + function isNative(value) { + return typeof value == 'function' && reNative.test(value); + } + + /** + * Sets `this` binding data on a given function. + * + * @private + * @param {Function} func The function to set data on. + * @param {Array} value The data array to set. + */ + var setBindData = !defineProperty ? noop : function(func, value) { + descriptor.value = value; + defineProperty(func, '__bindData__', descriptor); + }; + + /** + * A fallback implementation of `isPlainObject` which checks if a given value + * is an object created by the `Object` constructor, assuming objects created + * by the `Object` constructor have no inherited enumerable properties and that + * there are no `Object.prototype` extensions. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + */ + function shimIsPlainObject(value) { + var ctor, + result; + + // avoid non Object objects, `arguments` objects, and DOM elements + if (!(value && toString.call(value) == objectClass) || + (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor)) || + (!support.argsClass && isArguments(value)) || + (!support.nodeClass && isNode(value))) { + return false; + } + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + if (support.ownLast) { + forIn(value, function(value, key, object) { + result = hasOwnProperty.call(object, key); + return false; + }); + return result !== false; + } + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + forIn(value, function(value, key) { + result = key; + }); + return typeof result == 'undefined' || hasOwnProperty.call(value, result); + } + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if `value` is an `arguments` object. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @example + * + * (function() { return _.isArguments(arguments); })(1, 2, 3); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == argsClass || false; + } + // fallback for browsers that can't detect `arguments` objects by [[Class]] + if (!support.argsClass) { + isArguments = function(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false; + }; + } + + /** + * Checks if `value` is an array. + * + * @static + * @memberOf _ + * @type Function + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an array, else `false`. + * @example + * + * (function() { return _.isArray(arguments); })(); + * // => false + * + * _.isArray([1, 2, 3]); + * // => true + */ + var isArray = nativeIsArray || function(value) { + return value && typeof value == 'object' && typeof value.length == 'number' && + toString.call(value) == arrayClass || false; + }; + + /** + * A fallback implementation of `Object.keys` which produces an array of the + * given object's own enumerable property names. + * + * @private + * @type Function + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names. + */ + var shimKeys = createIterator({ + 'args': 'object', + 'init': '[]', + 'top': 'if (!(objectTypes[typeof object])) return result', + 'loop': 'result.push(index)' + }); + + /** + * Creates an array composed of the own enumerable property names of an object. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns an array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (property order is not guaranteed across environments) + */ + var keys = !nativeKeys ? shimKeys : function(object) { + if (!isObject(object)) { + return []; + } + if ((support.enumPrototypes && typeof object == 'function') || + (support.nonEnumArgs && object.length && isArguments(object))) { + return shimKeys(object); + } + return nativeKeys(object); + }; + + /** Reusable iterator options shared by `each`, `forIn`, and `forOwn` */ + var eachIteratorOptions = { + 'args': 'collection, callback, thisArg', + 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)", + 'array': "typeof length == 'number'", + 'keys': keys, + 'loop': 'if (callback(iterable[index], index, collection) === false) return result' + }; + + /** Reusable iterator options for `assign` and `defaults` */ + var defaultsIteratorOptions = { + 'args': 'object, source, guard', + 'top': + 'var args = arguments,\n' + + ' argsIndex = 0,\n' + + " argsLength = typeof guard == 'number' ? 2 : args.length;\n" + + 'while (++argsIndex < argsLength) {\n' + + ' iterable = args[argsIndex];\n' + + ' if (iterable && objectTypes[typeof iterable]) {', + 'keys': keys, + 'loop': "if (typeof result[index] == 'undefined') result[index] = iterable[index]", + 'bottom': ' }\n}' + }; + + /** Reusable iterator options for `forIn` and `forOwn` */ + var forOwnIteratorOptions = { + 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top, + 'array': false + }; + + /** + * A function compiled to iterate `arguments` objects, arrays, objects, and + * strings consistenly across environments, executing the callback for each + * element in the collection. The callback is bound to `thisArg` and invoked + * with three arguments; (value, index|key, collection). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @private + * @type Function + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|string} Returns `collection`. + */ + var baseEach = createIterator(eachIteratorOptions); + + /*--------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources will overwrite property assignments of previous + * sources. If a callback is provided it will be executed to produce the + * assigned values. The callback is bound to `thisArg` and invoked with two + * arguments; (objectValue, sourceValue). + * + * @static + * @memberOf _ + * @type Function + * @alias extend + * @category Objects + * @param {Object} object The destination object. + * @param {...Object} [source] The source objects. + * @param {Function} [callback] The function to customize assigning values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the destination object. + * @example + * + * _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); + * // => { 'name': 'fred', 'employer': 'slate' } + * + * var defaults = _.partialRight(_.assign, function(a, b) { + * return typeof a == 'undefined' ? b : a; + * }); + * + * var object = { 'name': 'barney' }; + * defaults(object, { 'name': 'fred', 'employer': 'slate' }); + * // => { 'name': 'barney', 'employer': 'slate' } + */ + var assign = createIterator(defaultsIteratorOptions, { + 'top': + defaultsIteratorOptions.top.replace(';', + ';\n' + + "if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n" + + ' var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n' + + "} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n" + + ' callback = args[--argsLength];\n' + + '}' + ), + 'loop': 'result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]' + }); + + /** + * Creates a clone of `value`. If `isDeep` is `true` nested objects will also + * be cloned, otherwise they will be assigned by reference. If a callback + * is provided it will be executed to produce the cloned values. If the + * callback returns `undefined` cloning will be handled by the method instead. + * The callback is bound to `thisArg` and invoked with one argument; (value). + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to clone. + * @param {boolean} [isDeep=false] Specify a deep clone. + * @param {Function} [callback] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {*} Returns the cloned value. + * @example + * + * var characters = [ + * { 'name': 'barney', 'age': 36 }, + * { 'name': 'fred', 'age': 40 } + * ]; + * + * var shallow = _.clone(characters); + * shallow[0] === characters[0]; + * // => true + * + * var deep = _.clone(characters, true); + * deep[0] === characters[0]; + * // => false + * + * _.mixin({ + * 'clone': _.partialRight(_.clone, function(value) { + * return _.isElement(value) ? value.cloneNode(false) : undefined; + * }) + * }); + * + * var clone = _.clone(document.body); + * clone.childNodes.length; + * // => 0 + */ + function clone(value, isDeep, callback, thisArg) { + // allows working with "Collections" methods without using their `index` + // and `collection` arguments for `isDeep` and `callback` + if (typeof isDeep != 'boolean' && isDeep != null) { + thisArg = callback; + callback = isDeep; + isDeep = false; + } + return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); + } + + /** + * Iterates over own and inherited enumerable properties of an object, + * executing the callback for each property. The callback is bound to `thisArg` + * and invoked with three arguments; (value, key, object). Callbacks may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @type Function + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * Shape.prototype.move = function(x, y) { + * this.x += x; + * this.y += y; + * }; + * + * _.forIn(new Shape, function(value, key) { + * console.log(key); + * }); + * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) + */ + var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); + + /** + * Iterates over own enumerable properties of an object, executing the callback + * for each property. The callback is bound to `thisArg` and invoked with three + * arguments; (value, key, object). Callbacks may exit iteration early by + * explicitly returning `false`. + * + * @static + * @memberOf _ + * @type Function + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * console.log(key); + * }); + * // => logs '0', '1', and 'length' (property order is not guaranteed across environments) + */ + var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); + + /** + * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a + * length of `0` and objects with no own enumerable properties are considered + * "empty". + * + * @static + * @memberOf _ + * @category Objects + * @param {Array|Object|string} value The value to inspect. + * @returns {boolean} Returns `true` if the `value` is empty, else `false`. + * @example + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({}); + * // => true + * + * _.isEmpty(''); + * // => true + */ + function isEmpty(value) { + var result = true; + if (!value) { + return result; + } + var className = toString.call(value), + length = value.length; + + if ((className == arrayClass || className == stringClass || + (support.argsClass ? className == argsClass : isArguments(value))) || + (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { + return !length; + } + forOwn(value, function() { + return (result = false); + }); + return result; + } + + /** + * Checks if `value` is a function. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + */ + function isFunction(value) { + return typeof value == 'function'; + } + // fallback for older versions of Chrome and Safari + if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value == 'function' && toString.call(value) == funcClass; + }; + } + + /** + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.io/#x8 + // and avoid a V8 bug + // http://code.google.com/p/v8/issues/detail?id=2291 + return !!(value && objectTypes[typeof value]); + } + + /** + * Checks if `value` is an object created by the `Object` constructor. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * _.isPlainObject(new Shape); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + */ + var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { + if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) { + return false; + } + var valueOf = value.valueOf, + objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); + + return objProto + ? (value == objProto || getPrototypeOf(value) == objProto) + : shimIsPlainObject(value); + }; + + /** + * Checks if `value` is a string. + * + * @static + * @memberOf _ + * @category Objects + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if the `value` is a string, else `false`. + * @example + * + * _.isString('fred'); + * // => true + */ + function isString(value) { + return typeof value == 'string' || + value && typeof value == 'object' && toString.call(value) == stringClass || false; + } + + /** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * will overwrite property assignments of previous sources. If a callback is + * provided it will be executed to produce the merged values of the destination + * and source properties. If the callback returns `undefined` merging will + * be handled by the method instead. The callback is bound to `thisArg` and + * invoked with two arguments; (objectValue, sourceValue). + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The destination object. + * @param {...Object} [source] The source objects. + * @param {Function} [callback] The function to customize merging properties. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns the destination object. + * @example + * + * var names = { + * 'characters': [ + * { 'name': 'barney' }, + * { 'name': 'fred' } + * ] + * }; + * + * var ages = { + * 'characters': [ + * { 'age': 36 }, + * { 'age': 40 } + * ] + * }; + * + * _.merge(names, ages); + * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] } + * + * var food = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var otherFood = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(food, otherFood, function(a, b) { + * return _.isArray(a) ? a.concat(b) : undefined; + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] } + */ + function merge(object) { + var args = arguments, + length = 2; + + if (!isObject(object)) { + return object; + } + // allows working with `_.reduce` and `_.reduceRight` without using + // their `index` and `collection` arguments + if (typeof args[2] != 'number') { + length = args.length; + } + if (length > 3 && typeof args[length - 2] == 'function') { + var callback = baseCreateCallback(args[--length - 1], args[length--], 2); + } else if (length > 2 && typeof args[length - 1] == 'function') { + callback = args[--length]; + } + var sources = slice(arguments, 1, length), + index = -1, + stackA = getArray(), + stackB = getArray(); + + while (++index < length) { + baseMerge(object, sources[index], callback, stackA, stackB); + } + releaseArray(stackA); + releaseArray(stackB); + return object; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Iterates over elements of a collection, executing the callback for each + * element. The callback is bound to `thisArg` and invoked with three arguments; + * (value, index|key, collection). Callbacks may exit iteration early by + * explicitly returning `false`. + * + * Note: As with other "Collections" methods, objects with a `length` property + * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` + * may be used for object iteration. + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {*} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); + * // => logs each number and returns '1,2,3' + * + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); + * // => logs each number and returns the object (property order is not guaranteed across environments) + */ + function forEach(collection, callback, thisArg) { + if (callback && typeof thisArg == 'undefined' && isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + if (callback(collection[index], index, collection) === false) { + break; + } + } + } else { + baseEach(collection, callback, thisArg); + } + return collection; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are all falsey. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array ? array.length : 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result.push(value); + } + } + return result; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * provided to the bound function. + * + * @static + * @memberOf _ + * @category Functions + * @param {Function} func The function to bind. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {...*} [arg] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'fred' }, 'hi'); + * func(); + * // => 'hi fred' + */ + function bind(func, thisArg) { + return arguments.length > 2 + ? createWrapper(func, 17, slice(arguments, 2), null, thisArg) + : createWrapper(func, 1, null, null, thisArg); + } + + /*--------------------------------------------------------------------------*/ + + /** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utilities + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'name': 'fred' }; + * _.identity(object) === object; + * // => true + */ + function identity(value) { + return value; + } + + /** + * A no-operation function. + * + * @static + * @memberOf _ + * @category Utilities + * @example + * + * var object = { 'name': 'fred' }; + * _.noop(object) === undefined; + * // => true + */ + function noop() { + // no operation performed + } + + /*--------------------------------------------------------------------------*/ + + lodash.assign = assign; + lodash.bind = bind; + lodash.compact = compact; + lodash.forEach = forEach; + lodash.forIn = forIn; + lodash.forOwn = forOwn; + lodash.keys = keys; + lodash.merge = merge; + + lodash.each = forEach; + lodash.extend = assign; + + /*--------------------------------------------------------------------------*/ + + // add functions that return unwrapped values when chaining + lodash.clone = clone; + lodash.identity = identity; + lodash.isArguments = isArguments; + lodash.isArray = isArray; + lodash.isEmpty = isEmpty; + lodash.isFunction = isFunction; + lodash.isObject = isObject; + lodash.isPlainObject = isPlainObject; + lodash.isString = isString; + lodash.noop = noop; + + /*--------------------------------------------------------------------------*/ + + /** + * The semantic version number. + * + * @static + * @memberOf _ + * @type string + */ + lodash.VERSION = '2.4.1'; + + /*--------------------------------------------------------------------------*/ + + if (freeExports && freeModule) { + // in Node.js or RingoJS + if (moduleExports) { + (freeModule.exports = lodash)._ = lodash; + } + + } + +}.call(this)); diff --git a/assets/osmtogeojson/osmtogeojson b/assets/osmtogeojson/osmtogeojson new file mode 100755 index 0000000..d149bec --- /dev/null +++ b/assets/osmtogeojson/osmtogeojson @@ -0,0 +1,147 @@ +#!/usr/bin/env node + +var osmtogeojson = require('./'), + opt = require('optimist') + .usage('Usage: $0 [-f format] [-e] [-v] FILE') + .string('f').describe('f', 'file format. if not given, will be detected from filename. supported values: osm, json') + .boolean('e').describe('e', 'enhanced properties. if set, the resulting GeoJSON feature\'s properties will contain more structured information') + .boolean('n').describe('n', 'numeric properties. if set, the resulting GeoJSON feature\'s properties will be numbers if possible') + .boolean('v').describe('v', 'verbose mode. output diagnostic information during processing') + .boolean('m').describe('m', 'minify output json (no identation and linebreaks)') + .boolean('version').describe('version','display software version') + .boolean('help').describe('help','print this help message'), + argv = opt.argv, + fs = require('fs'), + concat = require('concat-stream'), + xmldom = new (require('xmldom').DOMParser)(), + osmxmlParser = require('./parse_osmxml.js'), + JSONStream = require('JSONStream'), + geojsonNumeric = require('geojson-numeric'), + pack = require('./package.json'); + +if (argv.help) { + return opt.showHelp(); +} +if (argv.version) { + process.stdout.write(pack.version+'\n'); + return; +} + +var filename = argv._[0] || ''; + +var enhanced_geojson = argv.e; +var format = argv.f; + +if (format === 'xml') format = 'osm'; +// detect file format from filename +if (!format) { + if (filename.match(/\.osm$/i)) format = 'osm'; + if (filename.match(/\.xml$/i)) format = 'osm'; + if (filename.match(/\.json$/i)) format = 'json'; +} +// fall back to the native JSON parser if the file is small enough +// (unfortunately, the streaming JSON parser isn't very fast) +if (format === 'json' && filename) { + if (fs.statSync(filename).size < 268435577) + format = 'nativejson'; +} +// fall back to autodetection if still no format +if (!format) format = 'auto'; + +var datastream = (filename ? fs.createReadStream(filename) : process.stdin); + +// use streaming parsers if format is already known +switch(format) { +case 'json': +case 'streamjson': + datastream.pipe(JSONStream.parse()) + .on('root', function(data) { + // iron out some nasty floating point rounding errors + if (data.version) data.version = Math.round(data.version*1000)/1000; + data.elements.forEach(function(element) { + if (element.lat) element.lat = Math.round(element.lat*1E12)/1E12; + if (element.lon) element.lon = Math.round(element.lon*1E12)/1E12; + }); + // convert to geojson + convert(data); + }) + .on('error', function(err) { + process.stderr.write("ERROR: JSON input stream could not be parsed.\n"); + process.exit(1); + }); +break; +case 'osm': +case 'streamxml': + datastream + .on('data', function(chunk) { + osmxmlParser.write(chunk); + }) + .on('end', function() { + osmxmlParser.end(); + data = osmxmlParser.getJSON(); + convert(data); + }); + datastream.resume(); +break; +default: + // otherwise use leagacy non-streaming parsers + datastream.pipe(concat(legacyParsers)); +} + +function legacyParsers(data) { + if (!data) data = ''; else data = data.toString(); + if (format === 'auto') { + if (data.match(/^\s*0});y.center&&n(y),g?a(y):y.bounds&&t(y)}return s(i,l,u)}function i(e){function n(e,n,t){e.hasAttribute(t)&&(n[t]=e.getAttribute(t))}function t(e,t){var o=r.clone(e);n(t,o,"lat"),n(t,o,"lon"),o.__is_center_placeholder=!0,l.push(o)}function o(e,n){function t(e,n,t){var r={type:"node",id:"_"+o.type+"/"+o.id+"bounds"+t,lat:e,lon:n};o.nodes.push(r.id),l.push(r)}var o=r.clone(e);o.nodes=[],t(n.getAttribute("minlat"),n.getAttribute("minlon"),1),t(n.getAttribute("maxlat"),n.getAttribute("minlon"),2),t(n.getAttribute("maxlat"),n.getAttribute("maxlon"),3),t(n.getAttribute("minlat"),n.getAttribute("maxlon"),4),o.nodes.push(o.nodes[0]),o.__is_bounds_placeholder=!0,u.push(o)}function a(e,n){function t(e,n,t){var r={type:"node",id:t,lat:e,lon:n,__is_uninteresting:!0};return l.push(r),r.id}r.isArray(e.nodes)||(e.nodes=[],r.each(n,function(n,t){e.nodes.push("_anonymous@"+n.getAttribute("lat")+"/"+n.getAttribute("lon"))})),r.each(n,function(n,r){n.getAttribute("lat")&&t(n.getAttribute("lat"),n.getAttribute("lon"),e.nodes[r])})}function i(e,n){function t(e,n,t){var r={type:"node",id:t,lat:e,lon:n};l.push(r)}function o(e,n){function t(e,n){var t={type:"node",id:"_anonymous@"+e+"/"+n,lat:e,lon:n,__is_uninteresting:!0};o.nodes.push(t.id),l.push(t)}if(!u.some(function(e){return"way"==e.type&&e.id==n})){var o={type:"way",id:n,nodes:[]};r.each(e,function(e){e.getAttribute("lat")?t(e.getAttribute("lat"),e.getAttribute("lon")):o.nodes.push(void 0)}),u.push(o)}}r.each(n,function(n,r){"node"==e.members[r].type?n.getAttribute("lat")&&t(n.getAttribute("lat"),n.getAttribute("lon"),e.members[r].ref):"way"==e.members[r].type&&n.getElementsByTagName("nd").length>0&&(e.members[r].ref="_fullGeom"+e.members[r].ref,o(n.getElementsByTagName("nd"),e.members[r].ref))})}var l=new Array,u=new Array,c=new Array;r.each(e.getElementsByTagName("node"),function(e,t){var o={};r.each(e.getElementsByTagName("tag"),function(e){o[e.getAttribute("k")]=e.getAttribute("v")});var a={type:"node"};n(e,a,"id"),n(e,a,"lat"),n(e,a,"lon"),n(e,a,"version"),n(e,a,"timestamp"),n(e,a,"changeset"),n(e,a,"uid"),n(e,a,"user"),r.isEmpty(o)||(a.tags=o),l.push(a)});var f,p;return r.each(e.getElementsByTagName("way"),function(e,i){var s={},l=[];r.each(e.getElementsByTagName("tag"),function(e){s[e.getAttribute("k")]=e.getAttribute("v")});var c=!1;r.each(e.getElementsByTagName("nd"),function(e,n){var t;(t=e.getAttribute("ref"))&&(l[n]=t),!c&&e.getAttribute("lat")&&(c=!0)});var y={type:"way"};n(e,y,"id"),n(e,y,"version"),n(e,y,"timestamp"),n(e,y,"changeset"),n(e,y,"uid"),n(e,y,"user"),l.length>0&&(y.nodes=l),r.isEmpty(s)||(y.tags=s),(f=e.getElementsByTagName("center")[0])&&t(y,f),c?a(y,e.getElementsByTagName("nd")):(p=e.getElementsByTagName("bounds")[0])&&o(y,p),u.push(y)}),r.each(e.getElementsByTagName("relation"),function(e,a){var s={},l=[];r.each(e.getElementsByTagName("tag"),function(e){s[e.getAttribute("k")]=e.getAttribute("v")});var u=!1;r.each(e.getElementsByTagName("member"),function(e,t){l[t]={},n(e,l[t],"ref"),n(e,l[t],"role"),n(e,l[t],"type"),(!u&&"node"==l[t].type&&e.getAttribute("lat")||"way"==l[t].type&&e.getElementsByTagName("nd").length>0)&&(u=!0)});var y={type:"relation"};n(e,y,"id"),n(e,y,"version"),n(e,y,"timestamp"),n(e,y,"changeset"),n(e,y,"uid"),n(e,y,"user"),l.length>0&&(y.members=l),r.isEmpty(s)||(y.tags=s),(f=e.getElementsByTagName("center")[0])&&t(y,f),u?i(y,e.getElementsByTagName("member")):(p=e.getElementsByTagName("bounds")[0])&&o(y,p),c.push(y)}),s(l,u,c)}function s(e,t,a){function i(e,t){if("object"!=typeof t&&(t={}),"function"==typeof n.uninterestingTags)return!n.uninterestingTags(e,t);for(var r in e)if(n.uninterestingTags[r]!==!0&&t[r]!==!0&&t[r]!==e[r])return!0;return!1}function s(e){var n={timestamp:e.timestamp,version:e.version,changeset:e.changeset,user:e.user,uid:e.uid};for(var t in n)void 0===n[t]&&delete n[t];return n}function u(e,t){function o(e){for(var t,r,o,a,i,s,l=function(e){return e[0]},f=function(e){return e[e.length-1]},p=[];e.length;)for(t=e.pop().nodes.slice(),p.push(t);e.length&&l(t)!==f(t);){for(r=l(t),o=f(t),a=0;ar!=c>r&&t<(u-s)*(r-l)/(c-l)+s;f&&(o=!o)}return o};for(e=o(e),n=0;n3&&"function"==typeof n[t-2])var o=f(n[--t-1],n[t--],2);else t>2&&"function"==typeof n[t-1]&&(o=n[--t]);for(var s=i(arguments,1,t),l=-1,u=r(),c=r();++l2?g(e,17,i(arguments,2),null,n):g(e,1,null,null,n)}function T(e){return e}function N(){}var S=[],C={},M=40,L=/\w*$/,B=/^\s*function[ \n\r\t]+\w/,F=/\bthis\b/,I=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],D="[object Arguments]",R="[object Array]",G="[object Boolean]",H="[object Date]",U="[object Error]",W="[object Function]",q="[object Number]",$="[object Object]",X="[object RegExp]",V="[object String]",z={};z[W]=!1,z[D]=z[R]=z[G]=z[H]=z[q]=z[$]=z[X]=z[V]=!0;var J={configurable:!1,enumerable:!1,value:null,writable:!1},K={args:"",array:null,bottom:"",firstArg:"",init:"",keys:null,loop:"",shadowedProps:null,support:null,top:"",useHas:!1},Q={boolean:!1,function:!0,object:!0,number:!1,string:!1,undefined:!1},Y=Q[typeof window]&&window||this,Z=Q[typeof t]&&t&&!t.nodeType&&t,ee=Q[typeof n]&&n&&!n.nodeType&&n,ne=ee&&ee.exports===Z&&Z,te=Q[typeof e]&&e;!te||te.global!==te&&te.window!==te||(Y=te);var re=[],oe=Error.prototype,ae=Object.prototype,ie=String.prototype,se=ae.toString,le=RegExp("^"+String(se).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),ue=Function.prototype.toString,ce=b(ce=Object.getPrototypeOf)&&ce,fe=ae.hasOwnProperty,pe=re.push,ye=ae.propertyIsEnumerable,ge=re.unshift,de=function(){try{var e={},n=b(n=Object.defineProperty)&&n,t=n(e,e,e)&&n}catch(e){}return t}(),be=b(be=Object.create)&&be,me=b(me=Array.isArray)&&me,he=b(he=Object.keys)&&he,ve={};ve[R]=Array,ve[G]=Boolean,ve[H]=Date,ve[W]=Function,ve[$]=Object,ve[q]=Number,ve[X]=RegExp,ve[V]=String;var we={};we[R]=we[H]=we[q]={constructor:!0,toLocaleString:!0,toString:!0,valueOf:!0},we[G]=we[V]={constructor:!0,toString:!0,valueOf:!0},we[U]=we[W]=we[X]={constructor:!0,toString:!0},we[$]={constructor:!0},function(){for(var e=I.length;e--;){var n=I[e];for(var t in we)fe.call(we,t)&&!fe.call(we[t],n)&&(we[t][n]=!1)}}();var _e=s.support={};!function(){var e=function(){this.x=1},n={0:1,length:1},t=[];e.prototype={valueOf:1,y:1};for(var r in new e)t.push(r);for(r in arguments);_e.argsClass=se.call(arguments)==D,_e.argsObject=arguments.constructor==Object&&!(arguments instanceof Array),_e.enumErrorProps=ye.call(oe,"message")||ye.call(oe,"name"),_e.enumPrototypes=ye.call(e,"prototype"),_e.funcDecomp=!b(Y.WinRTError)&&F.test(function(){return this}),_e.funcNames="string"==typeof Function.name,_e.nonEnumArgs=0!=r,_e.nonEnumShadows=!/valueOf/.test(t),_e.ownLast="x"!=t[0],_e.spliceObjects=(re.splice.call(n,0,1),!n[0]),_e.unindexedChars="x"[0]+Object("x")[0]!="xx";try{_e.nodeClass=!(se.call(document)==$&&!({toString:0}+""))}catch(e){_e.nodeClass=!0}}(1);var xe=function(e){var n="var index, iterable = "+e.firstArg+", result = "+e.init+";\nif (!iterable) return result;\n"+e.top+";";e.array?(n+="\nvar length = iterable.length; index = -1;\nif ("+e.array+") { ",_e.unindexedChars&&(n+="\n if (isString(iterable)) {\n iterable = iterable.split('')\n } "),n+="\n while (++index < length) {\n "+e.loop+";\n }\n}\nelse { "):_e.nonEnumArgs&&(n+="\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += '';\n "+e.loop+";\n }\n } else { "),_e.enumPrototypes&&(n+="\n var skipProto = typeof iterable == 'function';\n "),_e.enumErrorProps&&(n+="\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n ");var t=[];if(_e.enumPrototypes&&t.push('!(skipProto && index == "prototype")'),_e.enumErrorProps&&t.push('!(skipErrorProps && (index == "message" || index == "name"))'),e.useHas&&e.keys)n+="\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n",t.length&&(n+=" if ("+t.join(" && ")+") {\n "),n+=e.loop+"; ",t.length&&(n+="\n }"),n+="\n } ";else if(n+="\n for (index in iterable) {\n",e.useHas&&t.push("hasOwnProperty.call(iterable, index)"),t.length&&(n+=" if ("+t.join(" && ")+") {\n "),n+=e.loop+"; ",t.length&&(n+="\n }"),n+="\n } ",_e.nonEnumShadows){for(n+="\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n ",k=0;k<7;k++)n+="\n index = '"+e.shadowedProps[k]+"';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))",e.useHas||(n+=" || (!nonEnum[index] && iterable[index] !== objectProto[index])"),n+=") {\n "+e.loop+";\n } ";n+="\n } "}return(e.array||_e.nonEnumArgs)&&(n+="\n}"),n+=e.bottom+";\nreturn result"};be||(c=function(){function e(){}return function(n){if(x(n)){e.prototype=n;var t=new e;e.prototype=null}return t||Y.Object()}}());var ke=de?function(e,n){J.value=n,de(e,"__bindData__",J)}:N;_e.argsClass||(h=function(e){return e&&"object"==typeof e&&"number"==typeof e.length&&fe.call(e,"callee")&&!ye.call(e,"callee")||!1});var Ae=me||function(e){return e&&"object"==typeof e&&"number"==typeof e.length&&se.call(e)==R||!1},je=d({args:"object",init:"[]",top:"if (!(objectTypes[typeof object])) return result",loop:"result.push(index)"}),Ee=he?function(e){return x(e)?_e.enumPrototypes&&"function"==typeof e||_e.nonEnumArgs&&e.length&&h(e)?je(e):he(e):[]}:je,Pe={args:"collection, callback, thisArg",top:"callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3)",array:"typeof length == 'number'",keys:Ee,loop:"if (callback(iterable[index], index, collection) === false) return result"},Oe={args:"object, source, guard",top:"var args = arguments,\n argsIndex = 0,\n argsLength = typeof guard == 'number' ? 2 : args.length;\nwhile (++argsIndex < argsLength) {\n iterable = args[argsIndex];\n if (iterable && objectTypes[typeof iterable]) {",keys:Ee,loop:"if (typeof result[index] == 'undefined') result[index] = iterable[index]",bottom:" }\n}"},Te={top:"if (!objectTypes[typeof iterable]) return result;\n"+Pe.top,array:!1},Ne=d(Pe),Se=d(Oe,{top:Oe.top.replace(";",";\nif (argsLength > 3 && typeof args[argsLength - 2] == 'function') {\n var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);\n} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {\n callback = args[--argsLength];\n}"),loop:"result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]"}),Ce=d(Pe,Te,{useHas:!1}),Me=d(Pe,Te);_(/x/)&&(_=function(e){return"function"==typeof e&&se.call(e)==W});var Le=ce?function(e){if(!e||se.call(e)!=$||!_e.argsClass&&h(e))return!1;var n=e.valueOf,t=b(n)&&(t=ce(n))&&ce(t);return t?e==t||ce(e)==t:m(e)}:m;s.assign=Se,s.bind=O,s.compact=P,s.forEach=E,s.forIn=Ce,s.forOwn=Me,s.keys=Ee,s.merge=j,s.each=E,s.extend=Se,s.clone=v,s.identity=T,s.isArguments=h,s.isArray=Ae,s.isEmpty=w,s.isFunction=_,s.isObject=x,s.isPlainObject=Le,s.isString=A,s.noop=N,s.VERSION="2.4.1",Z&&ee&&ne&&((ee.exports=s)._=s)}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(e,n,t){function r(e,n){switch(e&&e.type||null){case"FeatureCollection":return e.features=e.features.map(o(r,n)),e;case"Feature":return e.geometry=r(e.geometry,n),e;case"Polygon":case"MultiPolygon":return a(e,n);default:return e}}function o(e,n){return function(t){return e(t,n)}}function a(e,n){return"Polygon"===e.type?e.coordinates=i(e.coordinates,n):"MultiPolygon"===e.type&&(e.coordinates=e.coordinates.map(o(i,n))),e}function i(e,n){n=!!n,e[0]=s(e[0],!n);for(var t=1;t=0}var u=e("geojson-area");n.exports=r},{"geojson-area":4}],4:[function(e,n,t){function r(e){if("Polygon"===e.type)return o(e.coordinates);if("MultiPolygon"===e.type){for(var n=0,t=0;t0){n+=Math.abs(a(e[0]));for(var t=1;t2){for(var t,r,o=0;o /dev/null", + "test": "npm run test-lib && npm run test-cli", + "test-lib": "mocha -R spec", + "test-cli": "node test-cli/cli.test.js | faucet" + }, + "bin": { + "osmtogeojson": "osmtogeojson" + }, + "repository": { + "type": "git", + "url": "https://github.com/tyrasd/osmtogeojson.git" + }, + "keywords": [ + "openstreetmap", + "geojson" + ], + "author": "Martin Raifer", + "license": "MIT", + "bugs": { + "url": "https://github.com/tyrasd/osmtogeojson/issues" + }, + "dependencies": { + "JSONStream": "0.8.0", + "concat-stream": "~1.0.1", + "geojson-numeric": "0.2.0", + "geojson-rewind": "0.1.0", + "htmlparser2": "3.5.1", + "optimist": "~0.3.5", + "osm-polygon-features": "^0.9.1", + "xmldom": "~0.1.16" + }, + "devDependencies": { + "expect.js": "~0.2.0", + "mocha": "~1.12.0", + "tape": "~2.10.2", + "faucet": "~0.0.1" + }, + "engines": { + "node": ">=0.5" + } +} diff --git a/assets/osmtogeojson/parse_osmxml.js b/assets/osmtogeojson/parse_osmxml.js new file mode 100644 index 0000000..54e8cbf --- /dev/null +++ b/assets/osmtogeojson/parse_osmxml.js @@ -0,0 +1,91 @@ +/* converts OSM XML to OSM JSON using a fast streaming parser */ +var htmlparser = require('htmlparser2'); +var _ = require("./lodash.custom.js"); + +var json = { + "version": 0.6, + "elements": [] +}; +var buffer = {}; +var p = new htmlparser.Parser({ + onopentag: function(name, attr) { + switch (name) { + case "node": + case "way": + case "relation": + buffer = { + type: name, + tags: {} + } + _.merge(buffer, attr); + if (name === "way") { + buffer.nodes = []; + buffer.geometry = []; + } + if (name === "relation") { + buffer.members = []; + buffer.nodes = []; + buffer.geometry = []; + } + break; + case "tag": + buffer.tags[attr.k] = attr.v; + break; + case "nd": + buffer.nodes.push(attr.ref); + if (attr.lat) { + buffer.geometry.push({ + lat: attr.lat, + lon: attr.lon + }); + } else { + buffer.geometry.push(null); + } + break; + case "member": + buffer.members.push(attr); + break; + case "center": + buffer.center = { + lat: attr.lat, + lon: attr.lon + }; + break; + case "bounds": + buffer.bounds = { + minlat: attr.minlat, + minlon: attr.minlon, + maxlat: attr.maxlat, + maxlon: attr.maxlon + }; + } + }, + ontext: function(text) { + }, + onclosetag: function(name) { + if (name === "node" || name === "way" || name === "relation" || name === "area") { + // remove empty geometry or nodes arrays + if (buffer.geometry && buffer.geometry.every(function(g) {return g===null;})) + delete buffer.geometry; + if (name === "relation") + delete buffer.nodes; + json.elements.push(buffer); + } + if (name === "member") { + if (buffer.geometry) { + buffer.members[buffer.members.length-1].geometry = buffer.geometry; + buffer.geometry = []; + } + } + } +}, { + decodeEntities: true, + xmlMode: true +}); + +p.parseFromString = function(xml_str) { p.write(xml_str); p.end(); return json; } +p.getJSON = function() { + return json; +} + +module.exports = p; diff --git a/module/config/config.php b/module/config/config.php index ffd1cc8..49fe500 100644 --- a/module/config/config.php +++ b/module/config/config.php @@ -21,7 +21,7 @@ $GLOBALS['LEAFLET_LIBRARIES']['leaflet'] = array 'license' => 'BSD-2-Clause', 'homepage' => 'http://leafletjs.com', 'css' => 'assets/leaflet/libs/leaflet/leaflet.min.css', - 'javascript' => 'assets/leaflet/libs/leaflet/leaflet.js', + 'javascript' => 'assets/leaflet/libs/leaflet/leaflet-src.js', ); $GLOBALS['LEAFLET_LIBRARIES']['leaflet-providers'] = array @@ -72,16 +72,6 @@ $GLOBALS['LEAFLET_LIBRARIES']['leaflet-fullscreen'] = array 'javascript' => 'assets/leaflet/libs/leaflet-fullscreen/Control.FullScreen.min.js' ); -$GLOBALS['LEAFLET_LIBRARIES']['leaflet-layer-overpass'] = array -( - 'name' => 'Leaflet Layer OverPass', - 'version' => '1.0.2', - 'license' => 'License', - 'homepage' => 'https://github.com/kartenkarsten/leaflet-layer-overpass', - 'css' => 'assets/leaflet/libs/leaflet-layer-overpass/OverPassLayer.min.css', - 'javascript' => 'assets/leaflet/libs/leaflet-layer-overpass/OverPassLayer.min.js' -); - $GLOBALS['LEAFLET_LIBRARIES']['leaflet-control-geocoder'] = array ( 'name' => 'Leaflet Control Geocoder', @@ -92,6 +82,15 @@ $GLOBALS['LEAFLET_LIBRARIES']['leaflet-control-geocoder'] = array 'javascript' => 'assets/leaflet/libs/control-geocoder/Control.Geocoder.min.js' ); +$GLOBALS['LEAFLET_LIBRARIES']['osmtogeojson'] = array +( + 'name' => 'osmtogeojson', + 'version' => '2.2.12', + 'license' => 'MIT', + 'homepage' => 'https://github.com/tyrasd/osmtogeojson', + 'javascript' => 'assets/leaflet/libs/osmtogeojson/osmtogeojson.js' +); + $GLOBALS['LEAFLET_LIBRARIES']['spin.js'] = array ( 'name' => 'spin.js',