forked from Snck3rs/contao-leaflet-libraries
Compare commits
5 Commits
1.0.0-beta
...
1.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86faf4dd48 | ||
|
|
9f2d5d94dc | ||
|
|
5364c0f15e | ||
|
|
4212248b27 | ||
|
|
90a08c29ef |
60
assets/leaflet-extra-markers/.gitignore
vendored
Normal file
60
assets/leaflet-extra-markers/.gitignore
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
### General Ignores ###
|
||||
|
||||
.idea
|
||||
*~
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### Operating System Ignores ###
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
|
||||
# OSX
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
|
||||
###Node###
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Dependency directory
|
||||
# Commenting this out is preferred by some people, see
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
|
||||
node_modules
|
||||
|
||||
# Users Environment Variables
|
||||
.lock-wscript
|
||||
64
assets/leaflet-extra-markers/Gruntfile.js
Normal file
64
assets/leaflet-extra-markers/Gruntfile.js
Normal file
@@ -0,0 +1,64 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
'use strict';
|
||||
|
||||
// require it at the top and pass in the grunt instance
|
||||
require('time-grunt')(grunt);
|
||||
|
||||
/*****************************************************
|
||||
Grunt Init Config:
|
||||
load each task config into grunt via require
|
||||
*****************************************************/
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
|
||||
// Project Config
|
||||
project: require('./build/grunt-config/project'), // Contains paths and banner
|
||||
less: require('./build/grunt-config/less'), // Config to compile and autoprefix less files
|
||||
uglify: require('./build/grunt-config/uglify'),
|
||||
jshint: require('./build/grunt-config/jshint'), // Lint Javascript
|
||||
|
||||
});
|
||||
|
||||
/*****************************************************
|
||||
Dev Tasks - Compile and check files withing the /src/assets/ directory
|
||||
*****************************************************/
|
||||
|
||||
// Default grunt task compiles & checks dev files
|
||||
grunt.registerTask('default', [], function(){
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.task.run('less:dev','js-dev');
|
||||
});
|
||||
|
||||
// Javascript Dev Build - Checks for Errors in Javascript
|
||||
grunt.registerTask('js-dev', [], function(){
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.task.run('jshint:all', 'uglify:dev');
|
||||
});
|
||||
|
||||
/*****************************************************
|
||||
Dist Tasks
|
||||
*****************************************************/
|
||||
|
||||
// 'grunt build' global build command for both less and js files
|
||||
grunt.registerTask('build', 'Compiles all files for live environment', function() {
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.task.run('less:build', 'js-build');
|
||||
});
|
||||
|
||||
// 'grunt js-build' compiles only javascript
|
||||
grunt.registerTask('js-build', [], function(){
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.task.run('jshint:all', 'uglify:build');
|
||||
});
|
||||
|
||||
|
||||
// 'grunt less-build' compiles only less
|
||||
grunt.registerTask('less-build', [], function(){
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
grunt.task.run('less:build');
|
||||
});
|
||||
|
||||
};
|
||||
21
assets/leaflet-extra-markers/LICENSE
Normal file
21
assets/leaflet-extra-markers/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 coryasilva
|
||||
|
||||
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.
|
||||
69
assets/leaflet-extra-markers/README.md
Normal file
69
assets/leaflet-extra-markers/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Leaflet.extra-markers plugin v1.0.4
|
||||
<a href="https://github.com/lvoogdt/Leaflet.awesome-markers">Big Thanks to lvoogdt of Leaflet.awesome-markers</a>
|
||||
|
||||

|
||||
### <a href="http://coryasilva.github.io/Leaflet.ExtraMarkers/" target="_blank">Demo</a>
|
||||
|
||||
## Icons
|
||||
Version 1.0 of Leaflet.extra-markers is designed for:
|
||||
- [Bootstrap 3 icons](http://twitter.github.com/bootstrap/)
|
||||
- [Getting Started Guide](http://getbootstrap.com/getting-started/)
|
||||
- [Font Awesome 4.0](http://fortawesome.github.com/Font-Awesome/)
|
||||
- [Getting Started Guide](http://fortawesome.github.io/Font-Awesome/get-started/)
|
||||
- [Semantic UI 0.9.8 icons](http://semantic-ui.com/)
|
||||
- [Ion Icons 2.0.1](http://ionicons.com/)
|
||||
- Leaflet 0.5-Latest
|
||||
|
||||
## Using the plugin
|
||||
|
||||
##### 1. Requirements #####
|
||||
|
||||
Follow the [getting started guide](#icons) for the desired font library and make sure its included in your project.
|
||||
|
||||
##### 2. Installing Leaflet.extra-markers #####
|
||||
|
||||
Next, copy the `dist/img` directory, `/dist/css/leaflet.extra-markers.min.css`, and `/dist/js/leaflet.extra-markers.min.js` to your project and include them:
|
||||
|
||||
````xml
|
||||
<link rel="stylesheet" href="css/leaflet.extra-markers.min.css">
|
||||
````
|
||||
or
|
||||
````less
|
||||
@import 'bower_components/src/assets/less/Leaflet.extra-markers.less
|
||||
````
|
||||
and
|
||||
````xml
|
||||
<script src="js/leaflet.extra-markers.min.js"></script>
|
||||
````
|
||||
|
||||
##### 3. Creating a Marker #####
|
||||
|
||||
Now use the plugin to create a marker like this:
|
||||
````js
|
||||
// Creates a red marker with the coffee icon
|
||||
var redMarker = L.ExtraMarkers.icon({
|
||||
icon: 'fa-coffee',
|
||||
markerColor: 'red',
|
||||
shape: 'square',
|
||||
prefix: 'fa'
|
||||
});
|
||||
|
||||
L.marker([51.941196,4.512291], {icon: redMarker,}).addTo(map);
|
||||
````
|
||||
---
|
||||
|
||||
### Properties
|
||||
|
||||
| Property | Description | Default Value | Possible values |
|
||||
| --------------- | ------------------------------------------- | ------------- | ---------------------------------------------------- |
|
||||
| extraClasses | Additional classes in the created `<i>` tag | `''` | `fa-rotate90 myclass`; space delimited classes to add |
|
||||
| icon | Name of the icon **with** prefix | `''` | `fa-coffee` (see icon library's documentation) |
|
||||
| iconColor | Color of the icon | `'white'` | `'white'`, `'black'` or css code (hex, rgba etc) |
|
||||
| innerHTML | Custom HTML code | `''` | `<svg>`, images, or other HTML; a truthy assignment will override the default html icon creation behavior |
|
||||
| markerColor | Color of the marker (css class) | `'blue'` | `'red'`, `'orange-dark'`, `'orange'`, `'yellow'`, `'blue-dark'`, `'cyan'`, `'purple'`, `'violet'`, `'pink'`, `'green-dark'`, `'green'`, `'green-light'`, `'black'`, or `'white'` |
|
||||
| number | Instead of an icon, define a plain text | `''` | `'1'` or `'A'`, must set `icon: 'fa-number'` |
|
||||
| prefix | The icon library's base class | `'glyphicon'` | `fa` (see icon library's documentation) |
|
||||
| shape | Shape of the marker (css class) | `'circle'` | `'circle'`, `'square'`, `'star'`, or `'penta'` |
|
||||
|
||||
## License
|
||||
- Leaflet.ExtraMarkers and colored markers are licensed under the MIT License - http://opensource.org/licenses/mit-license.html.
|
||||
30
assets/leaflet-extra-markers/bower.json
Normal file
30
assets/leaflet-extra-markers/bower.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "Leaflet.extra-markers",
|
||||
"version": "1.0.6",
|
||||
"homepage": "https://github.com/coryasilva/Leaflet.ExtraMarkers",
|
||||
"authors": [
|
||||
"Cory Silva"
|
||||
],
|
||||
"description": "Custom Markers for Leaflet JS based on Awesome Markers",
|
||||
"main": [
|
||||
"src/assets/css/leaflet.extra-markers.css",
|
||||
"src/assets/less/leaflet.extra-markers.less",
|
||||
"src/assets/js/leaflet.extra-markers.js"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"gh-pages",
|
||||
".bowerrc",
|
||||
".gitignore",
|
||||
".jshintignore",
|
||||
".jshintrc",
|
||||
"bower.json",
|
||||
"gruntfile.js",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
}
|
||||
}
|
||||
1
assets/leaflet-extra-markers/css/leaflet.extra-markers.min.css
vendored
Normal file
1
assets/leaflet-extra-markers/css/leaflet.extra-markers.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.extra-marker{background:url("../img/markers_default.png") no-repeat 0 0;width:35px;height:46px;position:absolute;left:0;top:0;display:block;text-align:center}.extra-marker-shadow{background:url("../img/markers_shadow.png") no-repeat 0 0;width:36px;height:16px}@media (min--moz-device-pixel-ratio:1.5),(-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5),(min-resolution:1.5dppx){.extra-marker{background-image:url("../img/markers_default@2x.png");background-size:540px 184px}.extra-marker-shadow{background-image:url("../img/markers_shadow@2x.png");background-size:35px 16px}}.extra-marker i{color:#fff;margin-top:10px;display:inline-block;font-size:14px}.extra-marker i.icon{margin-right:0;opacity:1}.extra-marker-circle-red{background-position:0 0}.extra-marker-circle-orange-dark{background-position:-36px 0}.extra-marker-circle-orange{background-position:-72px 0}.extra-marker-circle-yellow{background-position:-108px 0}.extra-marker-circle-blue-dark{background-position:-144px 0}.extra-marker-circle-blue{background-position:-180px 0}.extra-marker-circle-cyan{background-position:-216px 0}.extra-marker-circle-purple{background-position:-252px 0}.extra-marker-circle-violet{background-position:-288px 0}.extra-marker-circle-pink{background-position:-324px 0}.extra-marker-circle-green-dark{background-position:-360px 0}.extra-marker-circle-green{background-position:-396px 0}.extra-marker-circle-green-light{background-position:-432px 0}.extra-marker-circle-black{background-position:-468px 0}.extra-marker-circle-white{background-position:-504px 0}.extra-marker-square-red{background-position:0 -46px}.extra-marker-square-orange-dark{background-position:-36px -46px}.extra-marker-square-orange{background-position:-72px -46px}.extra-marker-square-yellow{background-position:-108px -46px}.extra-marker-square-blue-dark{background-position:-144px -46px}.extra-marker-square-blue{background-position:-180px -46px}.extra-marker-square-cyan{background-position:-216px -46px}.extra-marker-square-purple{background-position:-252px -46px}.extra-marker-square-violet{background-position:-288px -46px}.extra-marker-square-pink{background-position:-324px -46px}.extra-marker-square-green-dark{background-position:-360px -46px}.extra-marker-square-green{background-position:-396px -46px}.extra-marker-square-green-light{background-position:-432px -46px}.extra-marker-square-black{background-position:-468px -46px}.extra-marker-square-white{background-position:-504px -46px}.extra-marker-star-red{background-position:0 -92px}.extra-marker-star-orange-dark{background-position:-36px -92px}.extra-marker-star-orange{background-position:-72px -92px}.extra-marker-star-yellow{background-position:-108px -92px}.extra-marker-star-blue-dark{background-position:-144px -92px}.extra-marker-star-blue{background-position:-180px -92px}.extra-marker-star-cyan{background-position:-216px -92px}.extra-marker-star-purple{background-position:-252px -92px}.extra-marker-star-violet{background-position:-288px -92px}.extra-marker-star-pink{background-position:-324px -92px}.extra-marker-star-green-dark{background-position:-360px -92px}.extra-marker-star-green{background-position:-396px -92px}.extra-marker-star-green-light{background-position:-432px -92px}.extra-marker-star-black{background-position:-468px -92px}.extra-marker-star-white{background-position:-504px -92px}.extra-marker-penta-red{background-position:0 -138px}.extra-marker-penta-orange-dark{background-position:-36px -138px}.extra-marker-penta-orange{background-position:-72px -138px}.extra-marker-penta-yellow{background-position:-108px -138px}.extra-marker-penta-blue-dark{background-position:-144px -138px}.extra-marker-penta-blue{background-position:-180px -138px}.extra-marker-penta-cyan{background-position:-216px -138px}.extra-marker-penta-purple{background-position:-252px -138px}.extra-marker-penta-violet{background-position:-288px -138px}.extra-marker-penta-pink{background-position:-324px -138px}.extra-marker-penta-green-dark{background-position:-360px -138px}.extra-marker-penta-green{background-position:-396px -138px}.extra-marker-penta-green-light{background-position:-432px -138px}.extra-marker-penta-black{background-position:-468px -138px}.extra-marker-penta-white{background-position:-504px -138px}.extra-marker .fa-number:before{content:attr(number)}
|
||||
BIN
assets/leaflet-extra-markers/img/markers_default.png
Normal file
BIN
assets/leaflet-extra-markers/img/markers_default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
BIN
assets/leaflet-extra-markers/img/markers_default@2x.png
Normal file
BIN
assets/leaflet-extra-markers/img/markers_default@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 248 KiB |
BIN
assets/leaflet-extra-markers/img/markers_shadow.png
Normal file
BIN
assets/leaflet-extra-markers/img/markers_shadow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 535 B |
BIN
assets/leaflet-extra-markers/img/markers_shadow@2x.png
Normal file
BIN
assets/leaflet-extra-markers/img/markers_shadow@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
1
assets/leaflet-extra-markers/js/leaflet.extra-markers.min.js
vendored
Normal file
1
assets/leaflet-extra-markers/js/leaflet.extra-markers.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(a,b){"use strict";L.ExtraMarkers={},L.ExtraMarkers.version="1.0.1",L.ExtraMarkers.Icon=L.Icon.extend({options:{iconSize:[35,45],iconAnchor:[17,42],popupAnchor:[1,-32],shadowAnchor:[10,12],shadowSize:[36,16],className:"extra-marker",prefix:"",extraClasses:"",shape:"circle",icon:"",innerHTML:"",markerColor:"red",iconColor:"#fff",number:""},initialize:function(a){a=L.Util.setOptions(this,a)},createIcon:function(){var a=b.createElement("div"),c=this.options;return c.icon&&(a.innerHTML=this._createInner()),c.innerHTML&&(a.innerHTML=c.innerHTML),c.bgPos&&(a.style.backgroundPosition=-c.bgPos.x+"px "+-c.bgPos.y+"px"),this._setIconStyles(a,c.shape+"-"+c.markerColor),a},_createInner:function(){var a="",b="",c=this.options;return c.iconColor&&(a="style='color: "+c.iconColor+"' "),c.number&&(b="number='"+c.number+"' "),"<i "+b+a+"class='"+c.extraClasses+" "+c.prefix+" "+c.icon+"'></i>"},_setIconStyles:function(a,b){var c,d,e=this.options,f=L.point(e["shadow"===b?"shadowSize":"iconSize"]);"shadow"===b?(c=L.point(e.shadowAnchor||e.iconAnchor),d="shadow"):(c=L.point(e.iconAnchor),d="icon"),!c&&f&&(c=f.divideBy(2,!0)),a.className="leaflet-marker-"+d+" extra-marker-"+b+" "+e.className,c&&(a.style.marginLeft=-c.x+"px",a.style.marginTop=-c.y+"px"),f&&(a.style.width=f.x+"px",a.style.height=f.y+"px")},createShadow:function(){var a=b.createElement("div");return this._setIconStyles(a,"shadow"),a}}),L.ExtraMarkers.icon=function(a){return new L.ExtraMarkers.Icon(a)}}(window,document);
|
||||
21
assets/leaflet-extra-markers/package.json
Normal file
21
assets/leaflet-extra-markers/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Leaflet.extra-markers",
|
||||
"title": "Leaflet ExtraMarkers",
|
||||
"url": "https://github.com/coryasilva/Leaflet.ExtraMarkers/",
|
||||
"version": "1.0.6",
|
||||
"description": "Custom Markers for Leaflet JS based on Awesome Markers",
|
||||
"author": "coryasilva <https://github.com/coryasilva>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/coryasilva/Leaflet.ExtraMarkers"
|
||||
},
|
||||
"main": "src/assets/js/leaflet.extra-markers.js",
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-contrib-jshint": "^0.11.0",
|
||||
"grunt-contrib-uglify": "^0.8.0",
|
||||
"grunt-contrib-less": "^1.1.0",
|
||||
"less-plugin-autoprefix": "^1.5.1",
|
||||
"time-grunt": "^1.1.0"
|
||||
}
|
||||
}
|
||||
BIN
assets/leaflet-extra-markers/screenshot.png
Normal file
BIN
assets/leaflet-extra-markers/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
13
assets/osmtogeojson/.travis.yml
Normal file
13
assets/osmtogeojson/.travis.yml
Normal file
@@ -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
|
||||
- /.*/
|
||||
117
assets/osmtogeojson/CHANGELOG.md
Normal file
117
assets/osmtogeojson/CHANGELOG.md
Normal file
@@ -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: 
|
||||
|
||||
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 '<osm><node lat="1.23" lon="3.21" id="-1" /></osm>' | 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
|
||||
20
assets/osmtogeojson/LICENSE
Normal file
20
assets/osmtogeojson/LICENSE
Normal file
@@ -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.
|
||||
6
assets/osmtogeojson/Makefile
Normal file
6
assets/osmtogeojson/Makefile
Normal file
@@ -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
|
||||
89
assets/osmtogeojson/README.md
Normal file
89
assets/osmtogeojson/README.md
Normal file
@@ -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.
|
||||
|
||||
[](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 <path-to-osmtogeojson>` 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
|
||||
|
||||
<script src='osmtogeojson.js'></script>
|
||||
|
||||
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.
|
||||
|
||||
26
assets/osmtogeojson/bower.json
Normal file
26
assets/osmtogeojson/bower.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
202
assets/osmtogeojson/index.html
Normal file
202
assets/osmtogeojson/index.html
Normal file
@@ -0,0 +1,202 @@
|
||||
<!--
|
||||
demo page / introduction for osmtogeojson
|
||||
https://tyrasd.github.io/osmtogeojson
|
||||
https://github.com/tyrasd/osmtogeojson
|
||||
this page is based on https://github.com/aaronlidman/osm-and-geojson (c)Aaron Lidman, WTFPL
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>osmtogeojson</title>
|
||||
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.min.js"></script>
|
||||
<script type="text/javascript" src="osmtogeojson.js"></script>
|
||||
<script type="text/javascript">
|
||||
function toGeo(e) {
|
||||
e.preventDefault();
|
||||
var data = document.getElementById('osmxml').value || "<osm></osm>",
|
||||
geojson;
|
||||
try {
|
||||
data = $.parseXML(data);
|
||||
} catch(e) {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
geojson = osmtogeojson(data);
|
||||
document.getElementById('geojson').value = JSON.stringify(geojson, null, 4);
|
||||
console.log(geojson);
|
||||
}
|
||||
$(function() {
|
||||
$("#toGeo").click(toGeo);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-size: 18px;
|
||||
background: white;
|
||||
font-family: "Helvetica Neue", Helvetica, sans-serif;
|
||||
width: 960px;
|
||||
margin: 50px auto;
|
||||
color: #222;
|
||||
line-height: 1.7em
|
||||
}
|
||||
|
||||
a { color: black; }
|
||||
|
||||
li {
|
||||
line-height: 1.7em;
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
textarea, input { outline: none; }
|
||||
|
||||
#left {
|
||||
width: 49%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#osmxml, #geojson {
|
||||
width: 100%;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
height: 320px;
|
||||
overflow: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#right {
|
||||
width: 49%;
|
||||
float: right;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
text-shadow: 0px 0px 1px black;
|
||||
border-radius: 3px;
|
||||
color: white;
|
||||
background: #A33;
|
||||
padding: 10px 15px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #833;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
box-shadow: inset 0 0 10px rgba(0,0,0,0.75);
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-size: 13px;
|
||||
line-height: 1.5em;
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
#top {
|
||||
font-size: 22px;
|
||||
text-align: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
#name {
|
||||
font-size: 44px;
|
||||
letter-spacing: -1px;
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#example {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#about {
|
||||
width: 720px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#download {
|
||||
margin: 50px auto 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#dl {
|
||||
background: #569834;
|
||||
padding: 7px 10px;
|
||||
border-radius: 3px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 32px;
|
||||
}
|
||||
.disc { list-style: disc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://github.com/tyrasd/osmtogeojson"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a>
|
||||
<div id="top">
|
||||
<span id="name">osmtogeojson</span> converts OSM data to GeoJSON.
|
||||
</div>
|
||||
<div id="example">
|
||||
<div id="left">
|
||||
<h3>OSM data</h3>
|
||||
<textarea id="osmxml" contenteditable="true"></textarea>
|
||||
</div>
|
||||
<div id="right">
|
||||
<h3>GeoJSON</h3>
|
||||
<textarea id="geojson" contenteditable="true"></textarea>
|
||||
</div>
|
||||
<a class="button" href="" id="toGeo">convert to GeoJSON →</a>
|
||||
</div>
|
||||
<div id="about">
|
||||
<p>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 <a href="https://github.com/tyrasd/overpass-turbo">overpass-turbo</a> project.</p>
|
||||
<span style="font-weight: bold;">Usage: </span>
|
||||
<ul>
|
||||
<li>In the browser:
|
||||
<ul>
|
||||
<li><code>
|
||||
<script src='osmtogeojson.js'></script>
|
||||
</code></li>
|
||||
<li><code>
|
||||
osmtogeojson(osm_data);
|
||||
</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>As a nodejs library:
|
||||
<ul>
|
||||
<li><code>
|
||||
$ npm install osmtogeojson
|
||||
</code></li>
|
||||
<li><code>
|
||||
var osmtogeojson = require('osmtogeojson');<br>
|
||||
osmtogeojson(xml_data);
|
||||
</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>As a command line tool:
|
||||
<ul>
|
||||
<li><code>
|
||||
$ npm install -g osmtogeojson
|
||||
</code></li>
|
||||
<li><code>
|
||||
$ osmtogeojson file.osm > file.geojson
|
||||
</code></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
Read more about the API in the official <a href="https://github.com/tyrasd/osmtogeojson#usage">documentation</a>.
|
||||
<div id="download">
|
||||
<p><a class="button" href="https://github.com/tyrasd/osmtogeojson/raw/gh-pages/osmtogeojson.js">download osmtogeojson.js</a><br/><br/>
|
||||
github: <a href="https://github.com/tyrasd/osmtogeojson">tyrasd/osmtogeojson</a><br/>
|
||||
npm: <a href="https://npmjs.org/package/osmtogeojson">osmtogeojson</a><br/>
|
||||
license: <a href="https://github.com/tyrasd/osmtogeojson/blob/gh-pages/LICENSE">MIT</a><br/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
945
assets/osmtogeojson/index.js
Normal file
945
assets/osmtogeojson/index.js
Normal file
@@ -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<json.elements.length;i++) {
|
||||
switch (json.elements[i].type) {
|
||||
case "node":
|
||||
var node = json.elements[i];
|
||||
nodes.push(node);
|
||||
break;
|
||||
case "way":
|
||||
var way = _.clone(json.elements[i]);
|
||||
way.nodes = _.clone(way.nodes);
|
||||
ways.push(way);
|
||||
if (way.center)
|
||||
centerGeometry(way);
|
||||
if (way.geometry)
|
||||
fullGeometryWay(way);
|
||||
else if (way.bounds)
|
||||
boundsGeometry(way);
|
||||
break;
|
||||
case "relation":
|
||||
var rel = _.clone(json.elements[i]);
|
||||
rel.members = _.clone(rel.members);
|
||||
rels.push(rel);
|
||||
var has_full_geometry = rel.members && rel.members.some(function (member) {
|
||||
return member.type == "node" && member.lat ||
|
||||
member.type == "way" && member.geometry && member.geometry.length > 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<nodes.length;i++) {
|
||||
if (nodes[i].lat === undefined) {
|
||||
if (options.verbose) console.warn('Node',nodes[i].type+'/'+nodes[i].id,'ignored because it has no coordinates');
|
||||
continue; // ignore nodes without coordinates (e.g. returned by an ids_only query)
|
||||
}
|
||||
nodeids[nodes[i].id] = nodes[i];
|
||||
}
|
||||
var poinids = new Object();
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
if (typeof nodes[i].tags != 'undefined' &&
|
||||
has_interesting_tags(nodes[i].tags)) // this checks if the node has any tags other than "created_by"
|
||||
poinids[nodes[i].id] = true;
|
||||
}
|
||||
for (var i=0;i<rels.length;i++) {
|
||||
if (!_.isArray(rels[i].members)) {
|
||||
if (options.verbose) console.warn('Relation',rels[i].type+'/'+rels[i].id,'ignored because it has no members');
|
||||
continue; // ignore relations without members (e.g. returned by an ids_only query)
|
||||
}
|
||||
for (var j=0;j<rels[i].members.length;j++) {
|
||||
if (rels[i].members[j].type == "node")
|
||||
poinids[rels[i].members[j].ref] = true;
|
||||
}
|
||||
}
|
||||
var wayids = new Object();
|
||||
var waynids = new Object();
|
||||
for (var i=0;i<ways.length;i++) {
|
||||
if (!_.isArray(ways[i].nodes)) {
|
||||
if (options.verbose) console.warn('Way',ways[i].type+'/'+ways[i].id,'ignored because it has no nodes');
|
||||
continue; // ignore ways without nodes (e.g. returned by an ids_only query)
|
||||
}
|
||||
wayids[ways[i].id] = ways[i];
|
||||
for (var j=0;j<ways[i].nodes.length;j++) {
|
||||
waynids[ways[i].nodes[j]] = true;
|
||||
ways[i].nodes[j] = nodeids[ways[i].nodes[j]];
|
||||
}
|
||||
}
|
||||
var pois = new Array();
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
if (((!waynids[nodes[i].id]) ||
|
||||
(poinids[nodes[i].id])) &&
|
||||
!nodes[i].__is_uninteresting)
|
||||
pois.push(nodes[i]);
|
||||
}
|
||||
var relids = new Array();
|
||||
for (var i=0;i<rels.length;i++) {
|
||||
if (!_.isArray(rels[i].members)) {
|
||||
if (options.verbose) console.warn('Relation',rels[i].type+'/'+rels[i].id,'ignored because it has no members');
|
||||
continue; // ignore relations without members (e.g. returned by an ids_only query)
|
||||
}
|
||||
relids[rels[i].id] = rels[i];
|
||||
}
|
||||
var relsmap = {node: {}, way: {}, relation: {}};
|
||||
for (var i=0;i<rels.length;i++) {
|
||||
if (!_.isArray(rels[i].members)) {
|
||||
if (options.verbose) console.warn('Relation',rels[i].type+'/'+rels[i].id,'ignored because it has no members');
|
||||
continue; // ignore relations without members (e.g. returned by an ids_only query)
|
||||
}
|
||||
for (var j=0;j<rels[i].members.length;j++) {
|
||||
var m_type = rels[i].members[j].type;
|
||||
var m_ref = rels[i].members[j].ref;
|
||||
if (typeof m_ref !== "number") {
|
||||
// de-namespace full geometry content
|
||||
m_ref = m_ref.replace("_fullGeom", "");
|
||||
}
|
||||
if (!relsmap[m_type]) {
|
||||
if (options.verbose) console.warn('Relation',rels[i].type+'/'+rels[i].id,'member',m_type+'/'+m_ref,'ignored because it has an invalid type');
|
||||
continue;
|
||||
}
|
||||
if (typeof relsmap[m_type][m_ref] === "undefined")
|
||||
relsmap[m_type][m_ref] = [];
|
||||
relsmap[m_type][m_ref].push({
|
||||
"role" : rels[i].members[j].role,
|
||||
"rel" : rels[i].id,
|
||||
"reltags" : rels[i].tags,
|
||||
});
|
||||
}
|
||||
}
|
||||
// construct geojson
|
||||
var geojson;
|
||||
var geojsonnodes = {
|
||||
"type" : "FeatureCollection",
|
||||
"features" : new Array()};
|
||||
for (i=0;i<pois.length;i++) {
|
||||
if (typeof pois[i].lon == "undefined" || typeof pois[i].lat == "undefined") {
|
||||
if (options.verbose) console.warn('POI',pois[i].type+'/'+pois[i].id,'ignored because it lacks coordinates');
|
||||
continue; // lon and lat are required for showing a point
|
||||
}
|
||||
var feature = {
|
||||
"type" : "Feature",
|
||||
"id" : pois[i].type+"/"+pois[i].id,
|
||||
"properties" : {
|
||||
"type" : pois[i].type,
|
||||
"id" : pois[i].id,
|
||||
"tags" : pois[i].tags || {},
|
||||
"relations" : relsmap["node"][pois[i].id] || [],
|
||||
"meta": build_meta_information(pois[i])
|
||||
},
|
||||
"geometry" : {
|
||||
"type" : "Point",
|
||||
"coordinates" : [+pois[i].lon, +pois[i].lat],
|
||||
}
|
||||
};
|
||||
if (pois[i].__is_center_placeholder)
|
||||
feature.properties["geometry"] = "center";
|
||||
geojsonnodes.features.push(feature);
|
||||
}
|
||||
var geojsonlines = {
|
||||
"type" : "FeatureCollection",
|
||||
"features" : new Array()};
|
||||
var geojsonpolygons = {
|
||||
"type" : "FeatureCollection",
|
||||
"features" : new Array()};
|
||||
// process multipolygons
|
||||
for (var i=0;i<rels.length;i++) {
|
||||
if ((typeof rels[i].tags != "undefined") &&
|
||||
(rels[i].tags["type"] == "multipolygon" || rels[i].tags["type"] == "boundary")) {
|
||||
if (!_.isArray(rels[i].members)) {
|
||||
if (options.verbose) console.warn('Multipolygon',rels[i].type+'/'+rels[i].id,'ignored because it has no members');
|
||||
continue; // ignore relations without members (e.g. returned by an ids_only query)
|
||||
}
|
||||
var outer_count = 0;
|
||||
for (var j=0;j<rels[i].members.length;j++)
|
||||
if (rels[i].members[j].role == "outer")
|
||||
outer_count++;
|
||||
else if (options.verbose && rels[i].members[j].role != "inner")
|
||||
console.warn('Multipolygon',rels[i].type+'/'+rels[i].id,'member',rels[i].members[j].type+'/'+rels[i].members[j].ref,'ignored because it has an invalid role: "' + rels[i].members[j].role + '"');
|
||||
rels[i].members.forEach(function(m) {
|
||||
if (wayids[m.ref]) {
|
||||
// this even works in the following corner case:
|
||||
// a multipolygon amenity=xxx with outer line tagged amenity=yyy
|
||||
// see https://github.com/tyrasd/osmtogeojson/issues/7
|
||||
if (m.role==="outer" && !has_interesting_tags(wayids[m.ref].tags,rels[i].tags))
|
||||
wayids[m.ref].is_multipolygon_outline = true;
|
||||
if (m.role==="inner" && !has_interesting_tags(wayids[m.ref].tags))
|
||||
wayids[m.ref].is_multipolygon_outline = true;
|
||||
}
|
||||
});
|
||||
if (outer_count == 0) {
|
||||
if (options.verbose) console.warn('Multipolygon relation',rels[i].type+'/'+rels[i].id,'ignored because it has no outer ways');
|
||||
continue; // ignore multipolygons without outer ways
|
||||
}
|
||||
var simple_mp = false;
|
||||
var mp_geometry = '';
|
||||
if (outer_count == 1 && !has_interesting_tags(rels[i].tags, {"type":true}))
|
||||
simple_mp = true;
|
||||
var feature = null;
|
||||
if (!simple_mp) {
|
||||
feature = construct_multipolygon(rels[i], rels[i]);
|
||||
} else {
|
||||
// simple multipolygon
|
||||
var outer_way = rels[i].members.filter(function(m) {return m.role === "outer";})[0];
|
||||
outer_way = wayids[outer_way.ref];
|
||||
if (outer_way === undefined) {
|
||||
if (options.verbose) console.warn('Multipolygon relation',rels[i].type+'/'+rels[i].id,'ignored because outer way', outer_way.type+'/'+outer_way.ref,'is missing');
|
||||
continue; // abort if outer way object is not present
|
||||
}
|
||||
outer_way.is_multipolygon_outline = true;
|
||||
feature = construct_multipolygon(outer_way, rels[i]);
|
||||
}
|
||||
if (feature === false) {
|
||||
if (options.verbose) console.warn('Multipolygon relation',rels[i].type+'/'+rels[i].id,'ignored because it has invalid geometry');
|
||||
continue; // abort if feature could not be constructed
|
||||
}
|
||||
geojsonpolygons.features.push(feature);
|
||||
function construct_multipolygon(tag_object, rel) {
|
||||
var is_tainted = false;
|
||||
var mp_geometry = simple_mp ? 'way' : 'relation',
|
||||
mp_id = typeof tag_object.id === "number" ? tag_object.id : +(tag_object.id.replace("_fullGeom", ""));
|
||||
// prepare mp members
|
||||
var members;
|
||||
members = rel.members.filter(function(m) {return m.type === "way";});
|
||||
members = members.map(function(m) {
|
||||
var way = wayids[m.ref];
|
||||
if (way === undefined) { // check for missing ways
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'tainted by a missing way', m.type+'/'+m.ref);
|
||||
is_tainted = true;
|
||||
return;
|
||||
}
|
||||
return { // TODO: this is slow! :(
|
||||
id: m.ref,
|
||||
role: m.role || "outer",
|
||||
way: way,
|
||||
nodes: way.nodes.filter(function(n) {
|
||||
if (n !== undefined)
|
||||
return true;
|
||||
is_tainted = true;
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'tainted by a way', m.type+'/'+m.ref, 'with a missing node');
|
||||
return false;
|
||||
})
|
||||
};
|
||||
});
|
||||
members = _.compact(members);
|
||||
// construct outer and inner rings
|
||||
var outers, inners;
|
||||
function join(ways) {
|
||||
var _first = function(arr) {return arr[0]};
|
||||
var _last = function(arr) {return arr[arr.length-1]};
|
||||
// stolen from iD/relation.js
|
||||
var joined = [], current, first, last, i, how, what;
|
||||
while (ways.length) {
|
||||
current = ways.pop().nodes.slice();
|
||||
joined.push(current);
|
||||
while (ways.length && _first(current) !== _last(current)) {
|
||||
first = _first(current);
|
||||
last = _last(current);
|
||||
for (i = 0; i < ways.length; i++) {
|
||||
what = ways[i].nodes;
|
||||
if (last === _first(what)) {
|
||||
how = current.push;
|
||||
what = what.slice(1);
|
||||
break;
|
||||
} else if (last === _last(what)) {
|
||||
how = current.push;
|
||||
what = what.slice(0, -1).reverse();
|
||||
break;
|
||||
} else if (first == _last(what)) {
|
||||
how = current.unshift;
|
||||
what = what.slice(0, -1);
|
||||
break;
|
||||
} else if (first == _first(what)) {
|
||||
how = current.unshift;
|
||||
what = what.slice(1).reverse();
|
||||
break;
|
||||
} else {
|
||||
what = how = null;
|
||||
}
|
||||
}
|
||||
if (!what) {
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'contains unclosed ring geometry');
|
||||
break; // Invalid geometry (dangling way, unclosed ring)
|
||||
}
|
||||
ways.splice(i, 1);
|
||||
how.apply(current, what);
|
||||
}
|
||||
}
|
||||
return joined;
|
||||
}
|
||||
outers = join(members.filter(function(m) {return m.role==="outer";}));
|
||||
inners = join(members.filter(function(m) {return m.role==="inner";}));
|
||||
// sort rings
|
||||
var mp;
|
||||
function findOuter(inner) {
|
||||
var polygonIntersectsPolygon = function(outer, inner) {
|
||||
for (var i=0; i<inner.length; i++)
|
||||
if (pointInPolygon(inner[i], outer))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
var mapCoordinates = function(from) {
|
||||
return from.map(function(n) {
|
||||
return [+n.lat,+n.lon];
|
||||
});
|
||||
}
|
||||
// stolen from iD/geo.js,
|
||||
// based on https://github.com/substack/point-in-polygon,
|
||||
// ray-casting algorithm based on http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
|
||||
var pointInPolygon = function(point, polygon) {
|
||||
var x = point[0], y = point[1], inside = false;
|
||||
for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
var xi = polygon[i][0], yi = polygon[i][1];
|
||||
var xj = polygon[j][0], yj = polygon[j][1];
|
||||
var intersect = ((yi > 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; j<inners.length; j++) {
|
||||
var o = findOuter(inners[j]);
|
||||
if (o !== undefined)
|
||||
mp[o].push(inners[j]);
|
||||
else
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'contains an inner ring with no containing outer');
|
||||
// so, no outer ring for this inner ring is found.
|
||||
// We're going to ignore holes in empty space.
|
||||
;
|
||||
}
|
||||
// sanitize mp-coordinates (remove empty clusters or rings, {lat,lon,...} to [lon,lat]
|
||||
var mp_coords = [];
|
||||
mp_coords = _.compact(mp.map(function(cluster) {
|
||||
var cl = _.compact(cluster.map(function(ring) {
|
||||
if (ring.length < 4) { // todo: is this correct: ring.length < 4 ?
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'contains a ring with less than four nodes');
|
||||
return;
|
||||
}
|
||||
return _.compact(ring.map(function(node) {
|
||||
return [+node.lon,+node.lat];
|
||||
}));
|
||||
}));
|
||||
if (cl.length == 0) {
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'contains an empty ring cluster');
|
||||
return;
|
||||
}
|
||||
return cl;
|
||||
}));
|
||||
|
||||
if (mp_coords.length == 0) {
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'contains no coordinates');
|
||||
return false; // ignore multipolygons without coordinates
|
||||
}
|
||||
var mp_type = "MultiPolygon";
|
||||
if (mp_coords.length === 1) {
|
||||
mp_type = "Polygon";
|
||||
mp_coords = mp_coords[0];
|
||||
}
|
||||
// mp parsed, now construct the geoJSON
|
||||
var feature = {
|
||||
"type" : "Feature",
|
||||
"id" : tag_object.type+"/"+mp_id,
|
||||
"properties" : {
|
||||
"type" : tag_object.type,
|
||||
"id" : mp_id,
|
||||
"tags" : tag_object.tags || {},
|
||||
"relations" : relsmap[tag_object.type][tag_object.id] || [],
|
||||
"meta": build_meta_information(tag_object)
|
||||
},
|
||||
"geometry" : {
|
||||
"type" : mp_type,
|
||||
"coordinates" : mp_coords,
|
||||
}
|
||||
}
|
||||
if (is_tainted) {
|
||||
if (options.verbose) console.warn('Multipolygon', mp_geometry+'/'+mp_id, 'is tainted');
|
||||
feature.properties["tainted"] = true;
|
||||
}
|
||||
return feature;
|
||||
}
|
||||
}
|
||||
}
|
||||
// process lines and polygons
|
||||
for (var i=0;i<ways.length;i++) {
|
||||
if (!_.isArray(ways[i].nodes)) {
|
||||
if (options.verbose) console.warn('Way',ways[i].type+'/'+ways[i].id,'ignored because it has no nodes');
|
||||
continue; // ignore ways without nodes (e.g. returned by an ids_only query)
|
||||
}
|
||||
if (ways[i].is_multipolygon_outline)
|
||||
continue; // ignore ways which are already rendered as (part of) a multipolygon
|
||||
if (typeof ways[i].id !== "number") {
|
||||
// remove full geometry namespace for output
|
||||
ways[i].id = +ways[i].id.replace("_fullGeom", "");
|
||||
}
|
||||
ways[i].tainted = false;
|
||||
ways[i].hidden = false;
|
||||
var coords = new Array();
|
||||
for (j=0;j<ways[i].nodes.length;j++) {
|
||||
if (typeof ways[i].nodes[j] == "object")
|
||||
coords.push([+ways[i].nodes[j].lon, +ways[i].nodes[j].lat]);
|
||||
else {
|
||||
if (options.verbose) console.warn('Way',ways[i].type+'/'+ways[i].id,'is tainted by an invalid node');
|
||||
ways[i].tainted = true;
|
||||
}
|
||||
}
|
||||
if (coords.length <= 1) { // invalid way geometry
|
||||
if (options.verbose) console.warn('Way',ways[i].type+'/'+ways[i].id,'ignored because it contains too few nodes');
|
||||
continue;
|
||||
}
|
||||
var way_type = "LineString"; // default
|
||||
if (typeof ways[i].nodes[0] != "undefined" && // way has its nodes loaded
|
||||
ways[i].nodes[0] === ways[i].nodes[ways[i].nodes.length-1] && // ... and forms a closed ring
|
||||
(
|
||||
typeof ways[i].tags != "undefined" && // ... and has tags
|
||||
_isPolygonFeature(ways[i].tags) // ... and tags say it is a polygon
|
||||
|| // or is a placeholder for a bounds geometry
|
||||
ways[i].__is_bounds_placeholder
|
||||
)
|
||||
) {
|
||||
way_type = "Polygon";
|
||||
coords = [coords];
|
||||
}
|
||||
var feature = {
|
||||
"type" : "Feature",
|
||||
"id" : ways[i].type+"/"+ways[i].id,
|
||||
"properties" : {
|
||||
"type" : ways[i].type,
|
||||
"id" : ways[i].id,
|
||||
"tags" : ways[i].tags || {},
|
||||
"relations" : relsmap["way"][ways[i].id] || [],
|
||||
"meta": build_meta_information(ways[i])
|
||||
},
|
||||
"geometry" : {
|
||||
"type" : way_type,
|
||||
"coordinates" : coords,
|
||||
}
|
||||
}
|
||||
if (ways[i].tainted) {
|
||||
if (options.verbose) console.warn('Way',ways[i].type+'/'+ways[i].id,'is tainted');
|
||||
feature.properties["tainted"] = true;
|
||||
}
|
||||
if (ways[i].__is_bounds_placeholder)
|
||||
feature.properties["geometry"] = "bounds";
|
||||
if (way_type == "LineString")
|
||||
geojsonlines.features.push(feature);
|
||||
else
|
||||
geojsonpolygons.features.push(feature);
|
||||
}
|
||||
|
||||
geojson = {
|
||||
"type": "FeatureCollection",
|
||||
"features": []
|
||||
};
|
||||
geojson.features = geojson.features.concat(geojsonpolygons.features);
|
||||
geojson.features = geojson.features.concat(geojsonlines.features);
|
||||
geojson.features = geojson.features.concat(geojsonnodes.features);
|
||||
// optionally, flatten properties
|
||||
if (options.flatProperties) {
|
||||
geojson.features.forEach(function(f) {
|
||||
f.properties = _.merge(
|
||||
f.properties.meta,
|
||||
f.properties.tags,
|
||||
{id: f.properties.type+"/"+f.properties.id}
|
||||
);
|
||||
});
|
||||
}
|
||||
// fix polygon winding
|
||||
geojson = rewind(geojson, true /*remove for geojson-rewind >0.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;
|
||||
1794
assets/osmtogeojson/lodash.custom.js
Normal file
1794
assets/osmtogeojson/lodash.custom.js
Normal file
File diff suppressed because it is too large
Load Diff
147
assets/osmtogeojson/osmtogeojson
Executable file
147
assets/osmtogeojson/osmtogeojson
Executable file
@@ -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*</)) // (osm) xml files begin with a "<"
|
||||
format = 'osm';
|
||||
else if (data.match(/^\s*{/)) // osm json files begin with a "{"
|
||||
format = 'json';
|
||||
else {
|
||||
format = 'unknown';
|
||||
}
|
||||
}
|
||||
switch (format) {
|
||||
case 'xmldom':
|
||||
data = xmldom.parseFromString(data);
|
||||
break;
|
||||
case 'json':
|
||||
case 'nativejson':
|
||||
data = JSON.parse(data);
|
||||
break;
|
||||
case 'osm':
|
||||
case 'fastxml':
|
||||
data = osmxmlParser.parseFromString(data);
|
||||
break;
|
||||
default:
|
||||
process.stderr.write('This doesn\'t look like a recognized file format.\n');
|
||||
opt.showHelp();
|
||||
process.exit(1);
|
||||
}
|
||||
convert(data);
|
||||
}
|
||||
|
||||
function convert(data) {
|
||||
var geojson = osmtogeojson(data, {
|
||||
flatProperties: !enhanced_geojson,
|
||||
verbose: argv.v
|
||||
});
|
||||
output(geojson);
|
||||
}
|
||||
|
||||
function output(geojson) {
|
||||
// this is much faster than a simple JSON.stringify of the whole geojson
|
||||
// object. also, this is less memory intensive and output starts right
|
||||
// after the conversion without any additional delay
|
||||
process.stdout.on('error', function() {});
|
||||
|
||||
var separator = argv.m ? '' : '\n';
|
||||
|
||||
process.stdout.write('{'+separator+'"type": "FeatureCollection",'+separator+'"features": ['+separator);
|
||||
geojson.features.forEach(function(f,i) {
|
||||
if (argv.n)
|
||||
f = geojsonNumeric(f,argv.e);
|
||||
process.stdout.write(JSON.stringify(f, null, argv.m ? 0 : 2));
|
||||
if (i != geojson.features.length-1)
|
||||
process.stdout.write(','+separator);
|
||||
});
|
||||
process.stdout.write(separator+']'+separator+'}'+separator);
|
||||
}
|
||||
1
assets/osmtogeojson/osmtogeojson.js
Normal file
1
assets/osmtogeojson/osmtogeojson.js
Normal file
File diff suppressed because one or more lines are too long
47
assets/osmtogeojson/package.json
Normal file
47
assets/osmtogeojson/package.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "osmtogeojson",
|
||||
"version": "2.2.12",
|
||||
"description": "convert OSM to geojson",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"pretest": "npm ls --depth=Infinity > /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"
|
||||
}
|
||||
}
|
||||
91
assets/osmtogeojson/parse_osmxml.js
Normal file
91
assets/osmtogeojson/parse_osmxml.js
Normal file
@@ -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;
|
||||
@@ -29,6 +29,10 @@ var paths = [
|
||||
dest: 'assets/leaflet-fullscreen',
|
||||
css: 'Control.FullScreen.css',
|
||||
js: 'Control.FullScreen.js'
|
||||
},
|
||||
{
|
||||
dest: 'assets/leaflet-layer-overpass',
|
||||
css: 'OverPassLayer.css'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ $GLOBALS['LEAFLET_LIBRARIES']['leaflet'] = array
|
||||
'license' => '<a href="https://github.com/Leaflet/Leaflet/blob/master/LICENSE" target="_blank">BSD-2-Clause</a>',
|
||||
'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
|
||||
@@ -82,6 +82,25 @@ $GLOBALS['LEAFLET_LIBRARIES']['leaflet-control-geocoder'] = array
|
||||
'javascript' => 'assets/leaflet/libs/control-geocoder/Control.Geocoder.min.js'
|
||||
);
|
||||
|
||||
$GLOBALS['LEAFLET_LIBRARIES']['leaflet-extra-markers'] = array
|
||||
(
|
||||
'name' => 'Leaflet Extra Markers',
|
||||
'version' => '1.0.6',
|
||||
'license' => '<a href="https://github.com/coryasilva/Leaflet.ExtraMarkers/blob/master/LICENSE" target="_blank">MIT</a>',
|
||||
'homepage' => 'https://github.com/coryasilva/Leaflet.ExtraMarkers',
|
||||
'css' => 'assets/leaflet/libs/leaflet-extra-markers/css/leaflet.extra-markers.min.css',
|
||||
'javascript' => 'assets/leaflet/libs/leaflet-extra-markers/js/leaflet.extra-markers.min.js'
|
||||
);
|
||||
|
||||
$GLOBALS['LEAFLET_LIBRARIES']['osmtogeojson'] = array
|
||||
(
|
||||
'name' => 'osmtogeojson',
|
||||
'version' => '2.2.12',
|
||||
'license' => '<a href="https://github.com/tyrasd/osmtogeojson/blob/gh-pages/LICENSE" target="_blank">MIT</a>',
|
||||
'homepage' => 'https://github.com/tyrasd/osmtogeojson',
|
||||
'javascript' => 'assets/leaflet/libs/osmtogeojson/osmtogeojson.js'
|
||||
);
|
||||
|
||||
$GLOBALS['LEAFLET_LIBRARIES']['spin.js'] = array
|
||||
(
|
||||
'name' => 'spin.js',
|
||||
|
||||
Reference in New Issue
Block a user