├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── debug ├── css │ ├── debug.css │ └── performance.css ├── index.html ├── kothic-include.js ├── styles │ ├── mapnik.js │ ├── mapnik.png │ ├── osmosnimki.js │ ├── osmosnimki.png │ └── surface.js ├── test-tile.json └── tile.html ├── dist └── kothic-leaflet.js ├── package.json └── src ├── kothic.js ├── renderer ├── line.js ├── path.js ├── polygon.js ├── shields.js ├── text.js └── texticons.js ├── style ├── mapcss.js └── style.js └── utils ├── collisions.js ├── geom.js └── rbush.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist/kothic*.js 2 | node_modules 3 | .idea 4 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function (grunt) { 3 | 'use strict'; 4 | 5 | grunt.initConfig({ 6 | jshint: { 7 | options: { 8 | strict: false, 9 | 10 | bitwise: false, 11 | curly: false, 12 | eqeqeq: true, 13 | immed: true, 14 | latedef: true, 15 | newcap: true, 16 | noarg: true, 17 | noempty: true, 18 | nonew: true, 19 | sub: true, 20 | undef: false, 21 | unused: true, 22 | 23 | globals: { 24 | MapCSS: true, 25 | L: true, 26 | Kothic: true, 27 | console: true, 28 | rbush: true 29 | }, 30 | 31 | // camelcase: true, 32 | trailing: true, 33 | indent: 4, 34 | // quotmark: 'single', 35 | // maxlen: 120, 36 | 37 | // force breaking complex functions into smaller ones for readability 38 | // maxstatements: 10, 39 | // maxcomplexity: 5, 40 | 41 | browser: true 42 | }, 43 | all: { 44 | src: ['Gruntfile.js', 'src/**/*.js', 'dist/kothic-leaflet.js', '!src/utils/rbush.js'] 45 | } 46 | }, 47 | 48 | concat: { 49 | options: { 50 | separator: ';' 51 | }, 52 | dist: { 53 | src: ['src/**/*.js'], 54 | dest: 'dist/kothic.js' 55 | } 56 | }, 57 | 58 | uglify: { 59 | options: { 60 | banner: '/*\n (c) 2011-2019, Darafei Praliaskouski, Vladimir Agafonkin, Maksim Gurtovenko, Stephan Brandt\n' + 61 | ' Kothic JS is a full-featured JavaScript map rendering engine using HTML5 Canvas.\n' + 62 | ' Built on <%= grunt.template.today("dd-mm-yyyy") %> |' + 63 | ' https://github.com/kothic/kothic-js\n*/\n' 64 | }, 65 | dist: { 66 | files: { 67 | 'dist/kothic.min.js': ['<%= concat.dist.dest %>'] 68 | } 69 | } 70 | }, 71 | }); 72 | 73 | grunt.loadNpmTasks('grunt-contrib-jshint'); 74 | grunt.loadNpmTasks('grunt-contrib-concat'); 75 | grunt.loadNpmTasks('grunt-contrib-uglify'); 76 | 77 | grunt.registerTask('default', ['jshint', 'concat', 'uglify']); 78 | }; 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2013, Darafei Praliaskouski, Vladimir Agafonkin, Maksim Gurtovenko 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are 5 | permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of 8 | conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | of conditions and the following disclaimer in the documentation and/or other materials 12 | provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 17 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 21 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Kothic JS** is a full-featured JavaScript map rendering engine using HTML5 Canvas. 2 | It was initially developed as a JavaScript port of [Kothic](http://wiki.openstreetmap.org/wiki/Kothic) rendering engine written in Python. 3 | 4 | Check out the demo: http://kothic.org/ 5 | 6 | ### Features 7 | 8 | * Rendering [OpenStreetMap](http://openstreetmap.org) data visually on par with [Mapnik](http://mapnik.org) 9 | * [MapCSS](http://wiki.openstreetmap.org/wiki/MapCSS/0.2) support (see [How to Prepare a Map Style](https://github.com/kothic/kothic-js/wiki/How-to-prepare-map-style)) 10 | * rendering from lightweight GeoJSON-like tiles (see [Tiles Format](https://github.com/kothic/kothic-js/wiki/Tiles-format)) 11 | * easy integration with [Leaflet](http://leaflet.cloudmade.com) (interactive maps library) 12 | 13 | ### Building Kothic 14 | 15 | Install Node.js, then run: 16 | 17 | ``` 18 | npm install 19 | npm install -g grunt-cli 20 | grunt 21 | ``` 22 | 23 | Minified Kothic source will be generated in the `dist` folder. 24 | 25 | ### Basic usage 26 | 27 | Include `kothic.js` from the `dist` folder on your page. Now you can call: 28 | 29 | ```javascript 30 | Kothic.render( 31 | canvas, // canvas element (or its id) to render on 32 | data, // JSON data to render 33 | zoom, // zoom level 34 | { 35 | onRenderComplete: callback, // (optional) callback to call when rendering is done 36 | styles: ['osmosnimki-maps', 'surface'], // (optional) only specified styles will be rendered, if any 37 | locales: ['be', 'ru', 'en'] // (optional) map languages, see below 38 | }); 39 | ``` 40 | 41 | `locales` Kothic-JS supports map localization based on name:*lang* tags. Renderer will check all mentioned languages in order of persence. If object doesn't have localized name, *name* tag will be used. 42 | 43 | ### Contributing to Kothic JS 44 | 45 | Kothic JS is licensed under a BSD license, and we'll be glad to accept your contributions! 46 | 47 | #### Core contributors: 48 | 49 | * Darafei Praliaskouski ([@Komzpa](https://github.com/Komzpa)) 50 | * Vladimir Agafonkin ([@mourner](https://github.com/mourner), creator of [Leaflet](http://leafletjs.com)) 51 | * Maksim Gurtovenko ([@Miroff](https://github.com/Miroff)) 52 | 53 | * Leaflet 1.x compatibility, Stephan Brandt ([@braandl](https://github.com/braandl)) -------------------------------------------------------------------------------- /debug/css/debug.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | html, body, #map { 6 | height: 100%; 7 | } 8 | #debug { 9 | position: absolute; 10 | top: 0; 11 | right: 0; 12 | background: rgba(255,255,255,0.7); 13 | border: 1px solid #ddd; 14 | padding: 10px; 15 | z-index: 10000; 16 | font: 12px/1.4 Verdana, sans-serif; 17 | width: 170px; 18 | } 19 | #trace { 20 | margin-top: 10px; 21 | font: 10px/1 Verdana, sans-serif; 22 | } 23 | #trace table { 24 | margin-top: 3px; 25 | } 26 | #mapnik { 27 | margin-bottom: 10px; 28 | } 29 | table { 30 | border-collapse: collapse; 31 | } 32 | table td { 33 | padding-right: 10px; 34 | } 35 | 36 | #trace .tileIndex { 37 | font-weight: bold; 38 | } 39 | 40 | td.time { 41 | text-align: right; 42 | } 43 | -------------------------------------------------------------------------------- /debug/css/performance.css: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /debug/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kothic debug page 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |

OpenStreetMap data rendered on the browser using Kothic JS

36 | 41 | Surface overlay 42 |
Rendering...
43 |
44 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /debug/kothic-include.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var scripts = [ 3 | 'kothic.js', 4 | 'renderer/path.js', 5 | 'renderer/line.js', 6 | 'renderer/polygon.js', 7 | 'renderer/shields.js', 8 | 'renderer/texticons.js', 9 | 'renderer/text.js', 10 | 'style/mapcss.js', 11 | 'style/style.js', 12 | 'utils/collisions.js', 13 | 'utils/geom.js', 14 | 'utils/rbush.js' 15 | ]; 16 | 17 | function getSrcUrl() { 18 | var scripts = document.getElementsByTagName('script'); 19 | for (var i = 0; i < scripts.length; i++) { 20 | var src = scripts[i].src; 21 | if (src) { 22 | var res = src.match(/^(.*)kothic-include\.js$/); 23 | if (res) { 24 | return res[1] + '../src/'; 25 | } 26 | } 27 | } 28 | 29 | return ""; 30 | } 31 | 32 | var path = getSrcUrl(); 33 | for (var i = 0; i < scripts.length; i++) { 34 | document.writeln(""); 35 | } 36 | })(); 37 | -------------------------------------------------------------------------------- /debug/styles/mapnik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kothic/kothic-js/4edc827f5f59b680ce2c6936d98d8b8283b6620e/debug/styles/mapnik.png -------------------------------------------------------------------------------- /debug/styles/osmosnimki.js: -------------------------------------------------------------------------------- 1 | 2 | (function (MapCSS) { 3 | 'use strict'; 4 | 5 | function restyle(style, tags, zoom, type, selector) { 6 | var s_default = {}, s_centerline = {}, s_ticks = {}, s_label = {}; 7 | 8 | if ((selector === 'canvas')) { 9 | s_default['fill-color'] = '#C4D4F5'; 10 | } 11 | 12 | if (((selector === 'area' && tags['natural'] === 'coastline'))) { 13 | s_default['fill-color'] = '#fcf8e4'; 14 | s_default['-x-mapnik-layer'] = 'bottom'; 15 | } 16 | 17 | if (((selector === 'area' && tags['natural'] === 'glacier') && zoom >= 3)) { 18 | s_default['fill-color'] = '#fcfeff'; 19 | s_default['fill-image'] = 'glacier.png'; 20 | } 21 | 22 | if (((selector === 'area' && tags['place'] === 'city') && zoom >= 10) || ((selector === 'area' && tags['place'] === 'town') && zoom >= 10)) { 23 | s_default['fill-color'] = '#FAF7F7'; 24 | s_default['fill-opacity'] = 0.6; 25 | s_default['z-index'] = 1; 26 | } 27 | 28 | if (((selector === 'area' && tags['place'] === 'hamlet') && zoom >= 10) || ((selector === 'area' && tags['place'] === 'village') && zoom >= 10) || ((selector === 'area' && tags['place'] === 'locality') && zoom >= 10)) { 29 | s_default['fill-color'] = '#f3eceb'; 30 | s_default['fill-opacity'] = 0.6; 31 | s_default['z-index'] = 1; 32 | } 33 | 34 | if (((selector === 'area' && tags['landuse'] === 'residential') && zoom >= 10) || ((selector === 'area' && tags['residential'] === 'urban') && zoom >= 10)) { 35 | s_default['fill-color'] = '#F7EFEB'; 36 | s_default['z-index'] = 2; 37 | } 38 | 39 | if (((selector === 'area' && tags['residential'] === 'rural') && zoom >= 10)) { 40 | s_default['fill-color'] = '#f4d7c7'; 41 | s_default['z-index'] = 2; 42 | } 43 | 44 | if (((selector === 'area' && tags['landuse'] === 'residential') && zoom >= 16)) { 45 | s_default['width'] = 0.3; 46 | s_default['color'] = '#cb8904'; 47 | s_default['z-index'] = 2; 48 | } 49 | 50 | if (((selector === 'area' && tags['landuse'] === 'allotments') && zoom >= 10) || ((selector === 'area' && tags['leisure'] === 'garden') && zoom >= 10 && zoom <= 15) || ((selector === 'area' && tags['landuse'] === 'orchard') && zoom >= 10 && zoom <= 15)) { 51 | s_default['fill-color'] = '#edf2c1'; 52 | s_default['z-index'] = 3; 53 | } 54 | 55 | if (((selector === 'area' && tags['leisure'] === 'park') && zoom >= 10)) { 56 | s_default['fill-color'] = '#c4e9a4'; 57 | s_default['z-index'] = 3; 58 | s_default['fill-image'] = 'parks2.png'; 59 | } 60 | 61 | if (((selector === 'area' && tags['leisure'] === 'garden') && zoom >= 16) || ((selector === 'area' && tags['landuse'] === 'orchard') && zoom >= 16)) { 62 | s_default['fill-image'] = 'sady10.png'; 63 | s_default['z-index'] = 3; 64 | } 65 | 66 | if (((selector === 'area' && tags['natural'] === 'scrub') && zoom >= 12)) { 67 | s_default['fill-color'] = '#e5f5dc'; 68 | s_default['fill-image'] = 'kust1.png'; 69 | s_default['z-index'] = 3; 70 | } 71 | 72 | if (((selector === 'area' && tags['natural'] === 'heath') && zoom >= 12)) { 73 | s_default['fill-color'] = '#ecffe5'; 74 | s_default['z-index'] = 3; 75 | } 76 | 77 | if (((selector === 'area' && tags['landuse'] === 'industrial') && zoom >= 10) || ((selector === 'area' && tags['landuse'] === 'military') && zoom >= 10)) { 78 | s_default['fill-color'] = '#ddd8da'; 79 | s_default['z-index'] = 3; 80 | } 81 | 82 | if (((selector === 'area' && tags['amenity'] === 'parking') && zoom >= 15)) { 83 | s_default['fill-color'] = '#ecedf4'; 84 | s_default['z-index'] = 3; 85 | } 86 | 87 | if (((selector === 'area' && tags['natural'] === 'desert') && zoom >= 4)) { 88 | s_default['fill-image'] = 'desert22.png'; 89 | } 90 | 91 | if (((selector === 'area' && tags['natural'] === 'forest') && zoom >= 4) || ((selector === 'area' && tags['natural'] === 'wood') && zoom >= 4) || ((selector === 'area' && tags['landuse'] === 'forest') && zoom >= 4) || ((selector === 'area' && tags['landuse'] === 'wood') && zoom >= 4)) { 92 | s_default['fill-color'] = '#d6f4c6'; 93 | s_default['z-index'] = 3; 94 | } 95 | 96 | if (((selector === 'area' && tags['landuse'] === 'garages') && zoom >= 10)) { 97 | s_default['fill-color'] = '#ddd8da'; 98 | s_default['z-index'] = 3; 99 | } 100 | 101 | if (((selector === 'area' && tags['natural'] === 'forest') && zoom >= 10) || ((selector === 'area' && tags['natural'] === 'wood') && zoom >= 10) || ((selector === 'area' && tags['landuse'] === 'forest') && zoom >= 10) || ((selector === 'area' && tags['landuse'] === 'wood') && zoom >= 10)) { 102 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 103 | s_default['text-offset'] = 0; 104 | s_default['font-size'] = '10'; 105 | s_default['font-family'] = 'DejaVu Serif Italic'; 106 | s_default['text-color'] = 'green'; 107 | s_default['text-allow-overlap'] = 'false'; 108 | s_default['text-halo-radius'] = 1; 109 | s_default['text-halo-color'] = '#ffffff'; 110 | s_default['-x-mapnik-min-distance'] = '0 '; 111 | } 112 | 113 | if (((selector === 'area' && tags['landuse'] === 'grass') && zoom >= 12) || ((selector === 'area' && tags['natural'] === 'grass') && zoom >= 12) || ((selector === 'area' && tags['natural'] === 'meadow') && zoom >= 12) || ((selector === 'area' && tags['landuse'] === 'meadow') && zoom >= 12) || ((selector === 'area' && tags['landuse'] === 'recreation_ground') && zoom >= 12)) { 114 | s_default['fill-color'] = '#f4ffe5'; 115 | s_default['z-index'] = 4; 116 | } 117 | 118 | if (((selector === 'area' && tags['natural'] === 'wetland') && zoom >= 10)) { 119 | s_default['fill-image'] = 'swamp_world2.png'; 120 | s_default['z-index'] = 4; 121 | } 122 | 123 | if (((selector === 'area' && tags['landuse'] === 'farmland') && zoom >= 10) || ((selector === 'area' && tags['landuse'] === 'farm') && zoom >= 10) || ((selector === 'area' && tags['landuse'] === 'field') && zoom >= 10)) { 124 | s_default['fill-color'] = '#fff5c4'; 125 | s_default['z-index'] = 5; 126 | } 127 | 128 | if (((selector === 'area' && tags['place'] === 'city') && zoom >= 6 && zoom <= 9) || ((selector === 'area' && tags['place'] === 'town') && zoom >= 6 && zoom <= 9)) { 129 | s_default['fill-color'] = '#ffe1d0'; 130 | s_default['fill-opacity'] = 0.6; 131 | s_default['z-index'] = 5; 132 | } 133 | 134 | if (((selector === 'area' && tags['landuse'] === 'cemetery') && zoom >= 10)) { 135 | s_default['fill-color'] = '#e5f5dc'; 136 | s_default['z-index'] = 5; 137 | s_default['fill-image'] = 'cemetry7_2.png'; 138 | } 139 | 140 | if (((selector === 'area' && tags['aeroway'] === 'aerodrome') && zoom >= 13)) { 141 | s_default['color'] = '#008ac6'; 142 | s_default['width'] = 0.8; 143 | s_default['z-index'] = 5; 144 | s_default['fill-image'] = 'bull2.png'; 145 | } 146 | 147 | if (((selector === 'area' && tags['leisure'] === 'stadium') && zoom >= 12) || ((selector === 'area' && tags['leisure'] === 'pitch') && zoom >= 12)) { 148 | s_default['fill-color'] = '#e3deb1'; 149 | s_default['z-index'] = 5; 150 | } 151 | 152 | if (((type === 'way' && tags['waterway'] === 'river') && zoom >= 7 && zoom <= 10)) { 153 | s_default['color'] = '#C4D4F5'; 154 | s_default['width'] = 0.6; 155 | s_default['z-index'] = 9; 156 | } 157 | 158 | if (((type === 'way' && tags['waterway'] === 'stream') && zoom >= 9 && zoom <= 10)) { 159 | s_default['color'] = '#C4D4F5'; 160 | s_default['width'] = 0.3; 161 | s_default['z-index'] = 9; 162 | } 163 | 164 | if (((type === 'way' && tags['waterway'] === 'river') && zoom >= 10 && zoom <= 14)) { 165 | s_default['color'] = '#C4D4F5'; 166 | s_default['width'] = 0.7; 167 | s_default['z-index'] = 9; 168 | } 169 | 170 | if (((type === 'way' && tags['waterway'] === 'river') && zoom >= 15)) { 171 | s_default['color'] = '#C4D4F5'; 172 | s_default['width'] = 0.9; 173 | s_default['z-index'] = 9; 174 | } 175 | 176 | if (((type === 'way' && tags['waterway'] === 'stream') && zoom >= 10)) { 177 | s_default['color'] = '#C4D4F5'; 178 | s_default['width'] = 0.5; 179 | s_default['z-index'] = 9; 180 | } 181 | 182 | if (((type === 'way' && tags['waterway'] === 'canal') && zoom >= 10)) { 183 | s_default['color'] = '#abc4f5'; 184 | s_default['width'] = 0.6; 185 | s_default['z-index'] = 9; 186 | } 187 | 188 | if (((selector === 'area' && tags['waterway'] === 'riverbank') && zoom >= 5) || ((selector === 'area' && tags['natural'] === 'water') && zoom >= 5) || ((selector === 'area' && tags['landuse'] === 'reservoir') && zoom >= 10)) { 189 | s_default['fill-color'] = '#C4D4F5'; 190 | s_default['color'] = '#C4D4F5'; 191 | s_default['width'] = 0.1; 192 | s_default['z-index'] = 9; 193 | } 194 | 195 | if (((selector === 'area' && tags['natural'] === 'water') && zoom >= 9)) { 196 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 197 | s_default['text-offset'] = 1; 198 | s_default['font-size'] = '10'; 199 | s_default['font-family'] = 'DejaVu Serif Italic'; 200 | s_default['text-color'] = '#285fd1'; 201 | s_default['text-allow-overlap'] = 'false'; 202 | s_default['text-halo-radius'] = 1; 203 | s_default['text-halo-color'] = '#ffffff'; 204 | } 205 | 206 | if (((type === 'way' && tags['highway'] === 'construction') && zoom >= 15 && zoom <= 16)) { 207 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 208 | s_default['text-position'] = 'line'; 209 | s_default['text-color'] = '#404040'; 210 | s_default['font-family'] = 'DejaVu Sans Book'; 211 | s_default['font-size'] = '9'; 212 | s_default['text-halo-radius'] = 1; 213 | s_default['text-halo-color'] = '#ffffff'; 214 | s_default['text-halo-radius'] = 1; 215 | s_default['text-halo-color'] = '#ffffff'; 216 | s_default['casing-width'] = 0.5; 217 | s_default['casing-color'] = '#996703'; 218 | s_default['width'] = 2; 219 | s_default['color'] = '#ffffff'; 220 | s_default['z-index'] = 10; 221 | s_default['dashes'] = [9, 9]; 222 | } 223 | 224 | if (((type === 'way' && tags['highway'] === 'construction') && zoom >= 17)) { 225 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 226 | s_default['text-position'] = 'line'; 227 | s_default['text-color'] = '#404040'; 228 | s_default['font-family'] = 'DejaVu Sans Book'; 229 | s_default['font-size'] = '9'; 230 | s_default['text-halo-radius'] = 1; 231 | s_default['text-halo-color'] = '#ffffff'; 232 | s_default['text-halo-radius'] = 1; 233 | s_default['text-halo-color'] = '#ffffff'; 234 | s_default['casing-width'] = 0.5; 235 | s_default['casing-color'] = '#996703'; 236 | s_default['width'] = 3; 237 | s_default['color'] = '#ffffff'; 238 | s_default['z-index'] = 10; 239 | s_default['dashes'] = [9, 9]; 240 | } 241 | 242 | if (((type === 'way' && tags['highway'] === 'footway') && zoom >= 15) || ((type === 'way' && tags['highway'] === 'path') && zoom >= 15) || ((type === 'way' && tags['highway'] === 'cycleway') && zoom >= 15) || ((type === 'way' && tags['highway'] === 'pedestrian') && zoom >= 15)) { 243 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 244 | s_default['text-position'] = 'line'; 245 | s_default['text-color'] = '#404040'; 246 | s_default['font-family'] = 'DejaVu Sans Book'; 247 | s_default['font-size'] = '9'; 248 | s_default['text-halo-radius'] = 1; 249 | s_default['text-halo-color'] = '#ffffff'; 250 | s_default['text-halo-radius'] = 1; 251 | s_default['text-halo-color'] = '#ffffff'; 252 | s_default['casing-width'] = 0.3; 253 | s_default['casing-color'] = '#bf96ce'; 254 | s_default['width'] = 0.2; 255 | s_default['color'] = '#ffffff'; 256 | s_default['z-index'] = 10; 257 | s_default['dashes'] = [2, 2]; 258 | } 259 | 260 | if (((type === 'way' && tags['highway'] === 'steps') && zoom >= 15)) { 261 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 262 | s_default['text-position'] = 'line'; 263 | s_default['text-color'] = '#404040'; 264 | s_default['font-family'] = 'DejaVu Sans Book'; 265 | s_default['font-size'] = '9'; 266 | s_default['text-halo-radius'] = 1; 267 | s_default['text-halo-color'] = '#ffffff'; 268 | s_default['text-halo-radius'] = 1; 269 | s_default['text-halo-color'] = '#ffffff'; 270 | s_default['casing-width'] = 0.3; 271 | s_default['casing-color'] = '#ffffff'; 272 | s_default['width'] = 3; 273 | s_default['color'] = '#bf96ce'; 274 | s_default['z-index'] = 10; 275 | s_default['dashes'] = [1, 1]; 276 | s_default['linecap'] = 'butt'; 277 | } 278 | 279 | if (((type === 'way' && tags['highway'] === 'road') && zoom === 12) || ((type === 'way' && tags['highway'] === 'track') && zoom === 12) || ((type === 'way' && tags['highway'] === 'residential') && zoom === 12) || ((type === 'way' && tags['highway'] === 'secondary') && zoom === 9) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom >= 9 && zoom <= 10) || ((type === 'way' && tags['highway'] === 'service' && (tags['living_street'] === '-1' || tags['living_street'] === 'false' || tags['living_street'] === 'no') && tags['service'] !== 'parking_aisle') && zoom === 14)) { 280 | s_default['width'] = 0.3; 281 | s_default['opacity'] = 0.6; 282 | s_default['color'] = '#996703'; 283 | s_default['z-index'] = 10; 284 | } 285 | 286 | if (((type === 'way' && tags['highway'] === 'road') && zoom === 13) || ((type === 'way' && tags['highway'] === 'track') && zoom === 13)) { 287 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 288 | s_default['text-position'] = 'line'; 289 | s_default['text-color'] = '#404040'; 290 | s_default['font-family'] = 'DejaVu Sans Book'; 291 | s_default['font-size'] = '9'; 292 | s_default['text-halo-radius'] = 1; 293 | s_default['text-halo-color'] = '#ffffff'; 294 | s_default['width'] = 0.6; 295 | s_default['opacity'] = 0.5; 296 | s_default['color'] = '#996703'; 297 | s_default['z-index'] = 10; 298 | } 299 | 300 | if (((type === 'way' && tags['highway'] === 'road') && zoom >= 14 && zoom <= 16) || ((type === 'way' && tags['highway'] === 'track') && zoom >= 14 && zoom <= 16)) { 301 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 302 | s_default['text-position'] = 'line'; 303 | s_default['text-color'] = '#404040'; 304 | s_default['font-family'] = 'DejaVu Sans Book'; 305 | s_default['font-size'] = '9'; 306 | s_default['text-halo-radius'] = 1; 307 | s_default['text-halo-color'] = '#ffffff'; 308 | s_default['width'] = 1.5; 309 | s_default['color'] = '#ffffff'; 310 | s_default['casing-width'] = 0.5; 311 | s_default['casing-color'] = '#996703'; 312 | s_default['z-index'] = 9; 313 | } 314 | 315 | if (((type === 'way' && tags['highway'] === 'road') && zoom >= 16) || ((type === 'way' && tags['highway'] === 'track') && zoom >= 16)) { 316 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 317 | s_default['text-position'] = 'line'; 318 | s_default['text-color'] = '#404040'; 319 | s_default['font-family'] = 'DejaVu Sans Book'; 320 | s_default['font-size'] = '9'; 321 | s_default['text-halo-radius'] = 1; 322 | s_default['text-halo-color'] = '#ffffff'; 323 | s_default['width'] = 2.5; 324 | s_default['color'] = '#ffffff'; 325 | s_default['casing-width'] = 0.5; 326 | s_default['casing-color'] = '#996703'; 327 | s_default['z-index'] = 9; 328 | } 329 | 330 | if (((type === 'way' && tags['highway'] === 'residential') && zoom === 13)) { 331 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 332 | s_default['text-position'] = 'line'; 333 | s_default['text-color'] = '#404040'; 334 | s_default['font-family'] = 'DejaVu Sans Book'; 335 | s_default['font-size'] = '9'; 336 | s_default['text-halo-radius'] = 1; 337 | s_default['text-halo-color'] = '#ffffff'; 338 | s_default['width'] = 1.2; 339 | s_default['color'] = '#ffffff'; 340 | s_default['casing-width'] = 0.3; 341 | s_default['casing-color'] = '#996703'; 342 | s_default['z-index'] = 10; 343 | } 344 | 345 | if (((type === 'way' && tags['highway'] === 'service' && (tags['living_street'] === '1' || tags['living_street'] === 'true' || tags['living_street'] === 'yes')) && zoom === 15) || ((type === 'way' && tags['highway'] === 'service' && tags['service'] === 'parking_aisle') && zoom === 15)) { 346 | s_default['width'] = 0.2; 347 | s_default['opacity'] = 0.5; 348 | s_default['color'] = '#996703'; 349 | s_default['z-index'] = 10; 350 | } 351 | 352 | if (((type === 'way' && tags['highway'] === 'service' && (tags['living_street'] === '1' || tags['living_street'] === 'true' || tags['living_street'] === 'yes')) && zoom >= 16) || ((type === 'way' && tags['highway'] === 'service' && tags['service'] === 'parking_aisle') && zoom >= 16)) { 353 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 354 | s_default['text-position'] = 'line'; 355 | s_default['text-color'] = '#404040'; 356 | s_default['font-family'] = 'DejaVu Sans Book'; 357 | s_default['font-size'] = '9'; 358 | s_default['text-halo-radius'] = 1; 359 | s_default['text-halo-color'] = '#ffffff'; 360 | s_default['width'] = 1.2; 361 | s_default['color'] = '#ffffff'; 362 | s_default['casing-width'] = 0.3; 363 | s_default['casing-color'] = '#996703'; 364 | s_default['z-index'] = 10; 365 | } 366 | 367 | if (((type === 'way' && tags['highway'] === 'residential') && zoom >= 14 && zoom <= 15) || ((type === 'way' && tags['highway'] === 'unclassified') && zoom >= 14 && zoom <= 15) || ((type === 'way' && tags['highway'] === 'service' && (tags['living_street'] === '-1' || tags['living_street'] === 'false' || tags['living_street'] === 'no') && tags['service'] !== 'parking_aisle') && zoom === 15)) { 368 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 369 | s_default['text-position'] = 'line'; 370 | s_default['text-color'] = '#404040'; 371 | s_default['font-family'] = 'DejaVu Sans Book'; 372 | s_default['font-size'] = '9'; 373 | s_default['text-halo-radius'] = 1; 374 | s_default['text-halo-color'] = '#ffffff'; 375 | s_default['width'] = 2.5; 376 | s_default['color'] = '#ffffff'; 377 | s_default['casing-width'] = 0.5; 378 | s_default['casing-color'] = '#996703'; 379 | s_default['z-index'] = 10; 380 | } 381 | 382 | if (((type === 'way' && tags['highway'] === 'residential') && zoom === 16) || ((type === 'way' && tags['highway'] === 'unclassified') && zoom === 16) || ((type === 'way' && tags['highway'] === 'living_street') && zoom === 16) || ((type === 'way' && tags['highway'] === 'service' && (tags['living_street'] === '-1' || tags['living_street'] === 'false' || tags['living_street'] === 'no') && tags['service'] !== 'parking_aisle') && zoom === 16)) { 383 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 384 | s_default['text-position'] = 'line'; 385 | s_default['text-color'] = '#404040'; 386 | s_default['font-family'] = 'DejaVu Sans Book'; 387 | s_default['font-size'] = '9'; 388 | s_default['text-halo-radius'] = 1; 389 | s_default['text-halo-color'] = '#ffffff'; 390 | s_default['width'] = 3.5; 391 | s_default['color'] = '#ffffff'; 392 | s_default['casing-width'] = 0.5; 393 | s_default['casing-color'] = '#996703'; 394 | s_default['z-index'] = 10; 395 | } 396 | 397 | if (((type === 'way' && tags['highway'] === 'residential') && zoom >= 17) || ((type === 'way' && tags['highway'] === 'unclassified') && zoom >= 17) || ((type === 'way' && tags['highway'] === 'living_street') && zoom >= 17) || ((type === 'way' && tags['highway'] === 'service' && (tags['living_street'] === '-1' || tags['living_street'] === 'false' || tags['living_street'] === 'no') && tags['service'] !== 'parking_aisle') && zoom >= 17)) { 398 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 399 | s_default['text-position'] = 'line'; 400 | s_default['text-color'] = '#404040'; 401 | s_default['font-family'] = 'DejaVu Sans Book'; 402 | s_default['font-size'] = '9'; 403 | s_default['text-halo-radius'] = 1; 404 | s_default['text-halo-color'] = '#ffffff'; 405 | s_default['width'] = 4.5; 406 | s_default['color'] = '#ffffff'; 407 | s_default['casing-width'] = 0.5; 408 | s_default['casing-color'] = '#996703'; 409 | s_default['z-index'] = 10; 410 | } 411 | 412 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 10)) { 413 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 414 | s_default['text-position'] = 'line'; 415 | s_default['width'] = 1.2; 416 | s_default['color'] = '#fcffd1'; 417 | s_default['casing-width'] = 0.35; 418 | s_default['casing-color'] = '#996703'; 419 | s_default['z-index'] = 11; 420 | } 421 | 422 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 11) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 11)) { 423 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 424 | s_default['text-position'] = 'line'; 425 | s_default['text-color'] = '#404040'; 426 | s_default['font-family'] = 'DejaVu Sans Book'; 427 | s_default['font-size'] = '9'; 428 | s_default['text-halo-radius'] = 1; 429 | s_default['text-halo-color'] = '#ffffff'; 430 | s_default['text-halo-radius'] = 1; 431 | s_default['text-halo-color'] = '#ffffff'; 432 | s_default['width'] = 1.4; 433 | s_default['color'] = '#fcffd1'; 434 | s_default['casing-width'] = 0.35; 435 | s_default['casing-color'] = '#996703'; 436 | s_default['z-index'] = 11; 437 | } 438 | 439 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 12) || ((type === 'way' && tags['highway'] === 'secondary_link') && zoom === 12) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 12) || ((type === 'way' && tags['highway'] === 'tertiary_link') && zoom === 12)) { 440 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 441 | s_default['text-position'] = 'line'; 442 | s_default['text-color'] = '#404040'; 443 | s_default['font-family'] = 'DejaVu Sans Book'; 444 | s_default['font-size'] = '9'; 445 | s_default['text-halo-radius'] = 1; 446 | s_default['text-halo-color'] = '#ffffff'; 447 | s_default['text-halo-radius'] = 1; 448 | s_default['text-halo-color'] = '#ffffff'; 449 | s_default['width'] = 3; 450 | s_default['color'] = '#fcffd1'; 451 | s_default['casing-width'] = 0.35; 452 | s_default['casing-color'] = '#996703'; 453 | s_default['z-index'] = 11; 454 | } 455 | 456 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 13) || ((type === 'way' && tags['highway'] === 'secondary_link') && zoom === 13) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 13) || ((type === 'way' && tags['highway'] === 'tertiary_link') && zoom === 13)) { 457 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 458 | s_default['text-position'] = 'line'; 459 | s_default['text-color'] = '#404040'; 460 | s_default['font-family'] = 'DejaVu Sans Book'; 461 | s_default['font-size'] = '9'; 462 | s_default['text-halo-radius'] = 1; 463 | s_default['text-halo-color'] = '#ffffff'; 464 | s_default['width'] = 4; 465 | s_default['color'] = '#fcffd1'; 466 | s_default['casing-width'] = 0.35; 467 | s_default['casing-color'] = '#996703'; 468 | s_default['z-index'] = 11; 469 | } 470 | 471 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 14) || ((type === 'way' && tags['highway'] === 'secondary_link') && zoom === 14) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 14) || ((type === 'way' && tags['highway'] === 'tertiary_link') && zoom === 14)) { 472 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 473 | s_default['text-position'] = 'line'; 474 | s_default['text-color'] = '#404040'; 475 | s_default['font-family'] = 'DejaVu Sans Bold'; 476 | s_default['font-size'] = '9'; 477 | s_default['text-halo-radius'] = 1; 478 | s_default['text-halo-color'] = '#ffffff'; 479 | s_default['width'] = 5; 480 | s_default['color'] = '#fcffd1'; 481 | s_default['casing-width'] = 0.5; 482 | s_default['casing-color'] = '#996703'; 483 | s_default['z-index'] = 11; 484 | } 485 | 486 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 15) || ((type === 'way' && tags['highway'] === 'secondary_link') && zoom === 15) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 15) || ((type === 'way' && tags['highway'] === 'tertiary_link') && zoom === 15)) { 487 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 488 | s_default['text-position'] = 'line'; 489 | s_default['text-color'] = '#404040'; 490 | s_default['font-family'] = 'DejaVu Sans Bold'; 491 | s_default['font-size'] = '9'; 492 | s_default['text-halo-radius'] = 1; 493 | s_default['text-halo-color'] = '#ffffff'; 494 | s_default['width'] = 6; 495 | s_default['color'] = '#fcffd1'; 496 | s_default['casing-width'] = 0.5; 497 | s_default['casing-color'] = '#996703'; 498 | s_default['z-index'] = 11; 499 | } 500 | 501 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 16) || ((type === 'way' && tags['highway'] === 'secondary_link') && zoom === 16) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 16) || ((type === 'way' && tags['highway'] === 'tertiary_link') && zoom === 16)) { 502 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 503 | s_default['text-position'] = 'line'; 504 | s_default['text-color'] = '#404040'; 505 | s_default['font-family'] = 'DejaVu Sans Bold'; 506 | s_default['font-size'] = '9'; 507 | s_default['text-halo-radius'] = 1; 508 | s_default['text-halo-color'] = '#ffffff'; 509 | s_default['width'] = 7; 510 | s_default['color'] = '#fcffd1'; 511 | s_default['casing-width'] = 0.5; 512 | s_default['casing-color'] = '#996703'; 513 | s_default['z-index'] = 11; 514 | } 515 | 516 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 17) || ((type === 'way' && tags['highway'] === 'secondary_link') && zoom === 17) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 17) || ((type === 'way' && tags['highway'] === 'tertiary_link') && zoom === 17)) { 517 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 518 | s_default['text-position'] = 'line'; 519 | s_default['text-color'] = '#404040'; 520 | s_default['font-family'] = 'DejaVu Sans Bold'; 521 | s_default['font-size'] = '9'; 522 | s_default['text-halo-radius'] = 1; 523 | s_default['text-halo-color'] = '#ffffff'; 524 | s_default['width'] = 8; 525 | s_default['color'] = '#fcffd1'; 526 | s_default['casing-width'] = 0.5; 527 | s_default['casing-color'] = '#996703'; 528 | s_default['z-index'] = 11; 529 | } 530 | 531 | if (((type === 'way' && tags['highway'] === 'secondary') && zoom === 18) || ((type === 'way' && tags['highway'] === 'secondary_link') && zoom === 18) || ((type === 'way' && tags['highway'] === 'tertiary') && zoom === 18) || ((type === 'way' && tags['highway'] === 'tertiary_link') && zoom === 18)) { 532 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 533 | s_default['text-position'] = 'line'; 534 | s_default['text-color'] = '#404040'; 535 | s_default['font-family'] = 'DejaVu Sans Bold'; 536 | s_default['font-size'] = '9'; 537 | s_default['text-halo-radius'] = 1; 538 | s_default['text-halo-color'] = '#ffffff'; 539 | s_default['width'] = 9; 540 | s_default['color'] = '#fcffd1'; 541 | s_default['casing-width'] = 0.5; 542 | s_default['casing-color'] = '#996703'; 543 | s_default['z-index'] = 11; 544 | } 545 | 546 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 7)) { 547 | s_default['width'] = 1; 548 | s_default['color'] = '#fcea97'; 549 | s_default['z-index'] = 12; 550 | } 551 | 552 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 8)) { 553 | s_default['width'] = 2; 554 | s_default['color'] = '#fcea97'; 555 | s_default['z-index'] = 12; 556 | } 557 | 558 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 9) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 9)) { 559 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 560 | s_default['text-position'] = 'line'; 561 | s_default['text-color'] = '#404040'; 562 | s_default['font-family'] = 'DejaVu Sans Bold'; 563 | s_default['font-size'] = '9'; 564 | s_default['text-halo-radius'] = 1; 565 | s_default['text-halo-color'] = '#ffffff'; 566 | s_default['width'] = 2; 567 | s_default['color'] = '#fcea97'; 568 | s_default['casing-width'] = 0.5; 569 | s_default['casing-color'] = '#996703'; 570 | s_default['z-index'] = 12; 571 | } 572 | 573 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 10) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 10)) { 574 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 575 | s_default['text-position'] = 'line'; 576 | s_default['text-color'] = '#404040'; 577 | s_default['font-family'] = 'DejaVu Sans Bold'; 578 | s_default['font-size'] = '9'; 579 | s_default['text-halo-radius'] = 1; 580 | s_default['text-halo-color'] = '#ffffff'; 581 | s_default['width'] = 3; 582 | s_default['color'] = '#fcea97'; 583 | s_default['casing-width'] = 0.5; 584 | s_default['casing-color'] = '#996703'; 585 | s_default['z-index'] = 12; 586 | } 587 | 588 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 11) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 11)) { 589 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 590 | s_default['text-position'] = 'line'; 591 | s_default['text-color'] = '#404040'; 592 | s_default['font-family'] = 'DejaVu Sans Bold'; 593 | s_default['font-size'] = '9'; 594 | s_default['text-halo-radius'] = 1; 595 | s_default['text-halo-color'] = '#ffffff'; 596 | s_default['width'] = 4; 597 | s_default['color'] = '#fcea97'; 598 | s_default['casing-width'] = 0.5; 599 | s_default['casing-color'] = '#996703'; 600 | s_default['z-index'] = 12; 601 | } 602 | 603 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 12) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 12)) { 604 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 605 | s_default['text-position'] = 'line'; 606 | s_default['text-color'] = '#404040'; 607 | s_default['font-family'] = 'DejaVu Sans Bold'; 608 | s_default['font-size'] = '9'; 609 | s_default['text-halo-radius'] = 1; 610 | s_default['text-halo-color'] = '#ffffff'; 611 | s_default['width'] = 5; 612 | s_default['color'] = '#fcea97'; 613 | s_default['casing-width'] = 0.5; 614 | s_default['casing-color'] = '#996703'; 615 | s_default['z-index'] = 12; 616 | } 617 | 618 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 13) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 13)) { 619 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 620 | s_default['text-position'] = 'line'; 621 | s_default['text-color'] = '#404040'; 622 | s_default['font-family'] = 'DejaVu Sans Bold'; 623 | s_default['font-size'] = '9'; 624 | s_default['text-halo-radius'] = 1; 625 | s_default['text-halo-color'] = '#ffffff'; 626 | s_default['width'] = 6; 627 | s_default['color'] = '#fcea97'; 628 | s_default['casing-width'] = 0.5; 629 | s_default['casing-color'] = '#996703'; 630 | s_default['z-index'] = 12; 631 | } 632 | 633 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 14) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 14)) { 634 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 635 | s_default['text-position'] = 'line'; 636 | s_default['text-color'] = '#404040'; 637 | s_default['font-family'] = 'DejaVu Sans Bold'; 638 | s_default['font-size'] = '9'; 639 | s_default['text-halo-radius'] = 1; 640 | s_default['text-halo-color'] = '#ffffff'; 641 | s_default['width'] = 7; 642 | s_default['color'] = '#fcea97'; 643 | s_default['casing-width'] = 0.5; 644 | s_default['casing-color'] = '#996703'; 645 | s_default['z-index'] = 12; 646 | } 647 | 648 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 15) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 15)) { 649 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 650 | s_default['text-position'] = 'line'; 651 | s_default['text-color'] = '#404040'; 652 | s_default['font-family'] = 'DejaVu Sans Bold'; 653 | s_default['font-size'] = '9'; 654 | s_default['text-halo-radius'] = 1; 655 | s_default['text-halo-color'] = '#ffffff'; 656 | s_default['width'] = 8; 657 | s_default['color'] = '#fcea97'; 658 | s_default['casing-width'] = 0.5; 659 | s_default['casing-color'] = '#996703'; 660 | s_default['z-index'] = 12; 661 | } 662 | 663 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 16) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 16)) { 664 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 665 | s_default['text-position'] = 'line'; 666 | s_default['text-color'] = '#404040'; 667 | s_default['font-family'] = 'DejaVu Sans Bold'; 668 | s_default['font-size'] = '9'; 669 | s_default['text-halo-radius'] = 1; 670 | s_default['text-halo-color'] = '#ffffff'; 671 | s_default['width'] = 9; 672 | s_default['color'] = '#fcea97'; 673 | s_default['casing-width'] = 0.5; 674 | s_default['casing-color'] = '#996703'; 675 | s_default['z-index'] = 12; 676 | } 677 | 678 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 17) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 17)) { 679 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 680 | s_default['text-position'] = 'line'; 681 | s_default['text-color'] = '#404040'; 682 | s_default['font-family'] = 'DejaVu Sans Bold'; 683 | s_default['font-size'] = '9'; 684 | s_default['text-halo-radius'] = 1; 685 | s_default['text-halo-color'] = '#ffffff'; 686 | s_default['width'] = 10; 687 | s_default['color'] = '#fcea97'; 688 | s_default['casing-width'] = 0.5; 689 | s_default['casing-color'] = '#996703'; 690 | s_default['z-index'] = 12; 691 | } 692 | 693 | if (((type === 'way' && tags['highway'] === 'primary') && zoom === 18) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom === 18)) { 694 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 695 | s_default['text-position'] = 'line'; 696 | s_default['text-color'] = '#404040'; 697 | s_default['font-family'] = 'DejaVu Sans Bold'; 698 | s_default['font-size'] = '9'; 699 | s_default['text-halo-radius'] = 1; 700 | s_default['text-halo-color'] = '#ffffff'; 701 | s_default['width'] = 11; 702 | s_default['color'] = '#fcea97'; 703 | s_default['casing-width'] = 0.5; 704 | s_default['casing-color'] = '#996703'; 705 | s_default['z-index'] = 12; 706 | } 707 | 708 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 6)) { 709 | s_default['width'] = 0.9; 710 | s_default['color'] = '#fbcd40'; 711 | s_default['z-index'] = 13; 712 | } 713 | 714 | if (((type === 'way' && tags['highway'] === 'motorway') && zoom === 6)) { 715 | s_default['width'] = 1; 716 | s_default['color'] = '#fc9265'; 717 | s_default['z-index'] = 13; 718 | } 719 | 720 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 7)) { 721 | s_default['width'] = 1; 722 | s_default['color'] = '#fbcd40'; 723 | s_default['z-index'] = 13; 724 | } 725 | 726 | if (((type === 'way' && tags['highway'] === 'motorway') && zoom === 7)) { 727 | s_default['width'] = 1.2; 728 | s_default['color'] = '#fc9265'; 729 | s_default['z-index'] = 13; 730 | } 731 | 732 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 8)) { 733 | s_default['width'] = 2; 734 | s_default['color'] = '#fbcd40'; 735 | s_default['z-index'] = 13; 736 | } 737 | 738 | if (((type === 'way' && tags['highway'] === 'motorway') && zoom === 8)) { 739 | s_default['width'] = 2; 740 | s_default['color'] = '#fc9265'; 741 | s_default['z-index'] = 13; 742 | } 743 | 744 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 9) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 9)) { 745 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 746 | s_default['text-position'] = 'line'; 747 | s_default['text-color'] = '#404040'; 748 | s_default['font-family'] = 'DejaVu Sans Bold'; 749 | s_default['font-size'] = '9'; 750 | s_default['text-halo-radius'] = 1; 751 | s_default['text-halo-color'] = '#ffffff'; 752 | s_default['width'] = 3; 753 | s_default['color'] = '#ffd780'; 754 | s_default['casing-width'] = 1; 755 | s_default['casing-color'] = '#996703'; 756 | s_default['z-index'] = 13; 757 | } 758 | 759 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 10) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 10)) { 760 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 761 | s_default['text-position'] = 'line'; 762 | s_default['text-color'] = '#404040'; 763 | s_default['font-family'] = 'DejaVu Sans Bold'; 764 | s_default['font-size'] = '9'; 765 | s_default['text-halo-radius'] = 1; 766 | s_default['text-halo-color'] = '#ffffff'; 767 | s_default['width'] = 4; 768 | s_default['color'] = '#ffd780'; 769 | s_default['casing-width'] = 1; 770 | s_default['casing-color'] = '#996703'; 771 | s_default['z-index'] = 13; 772 | } 773 | 774 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 11) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 11)) { 775 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 776 | s_default['text-position'] = 'line'; 777 | s_default['text-color'] = '#404040'; 778 | s_default['font-family'] = 'DejaVu Sans Bold'; 779 | s_default['font-size'] = '9'; 780 | s_default['text-halo-radius'] = 1; 781 | s_default['text-halo-color'] = '#ffffff'; 782 | s_default['width'] = 5; 783 | s_default['color'] = '#ffd780'; 784 | s_default['casing-width'] = 1; 785 | s_default['casing-color'] = '#996703'; 786 | s_default['z-index'] = 13; 787 | } 788 | 789 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 12) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 12)) { 790 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 791 | s_default['text-position'] = 'line'; 792 | s_default['text-color'] = '#404040'; 793 | s_default['font-family'] = 'DejaVu Sans Bold'; 794 | s_default['font-size'] = '9'; 795 | s_default['text-halo-radius'] = 1; 796 | s_default['text-halo-color'] = '#ffffff'; 797 | s_default['width'] = 7; 798 | s_default['color'] = '#ffd780'; 799 | s_default['casing-width'] = 1; 800 | s_default['casing-color'] = '#996703'; 801 | s_default['z-index'] = 13; 802 | } 803 | 804 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 13) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 13)) { 805 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 806 | s_default['text-position'] = 'line'; 807 | s_default['text-color'] = '#404040'; 808 | s_default['font-family'] = 'DejaVu Sans Bold'; 809 | s_default['font-size'] = '9'; 810 | s_default['text-halo-radius'] = 1; 811 | s_default['text-halo-color'] = '#ffffff'; 812 | s_default['width'] = 8; 813 | s_default['color'] = '#ffd780'; 814 | s_default['casing-width'] = 1; 815 | s_default['casing-color'] = '#996703'; 816 | s_default['z-index'] = 13; 817 | } 818 | 819 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 14) || ((type === 'way' && tags['highway'] === 'trunk_link') && zoom === 14) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 14) || ((type === 'way' && tags['highway'] === 'motorway_link') && zoom === 14)) { 820 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 821 | s_default['text-position'] = 'line'; 822 | s_default['text-color'] = '#404040'; 823 | s_default['font-family'] = 'DejaVu Sans Bold'; 824 | s_default['font-size'] = '9'; 825 | s_default['text-halo-radius'] = 1; 826 | s_default['text-halo-color'] = '#ffffff'; 827 | s_default['width'] = 9; 828 | s_default['color'] = '#ffd780'; 829 | s_default['casing-width'] = 1; 830 | s_default['casing-color'] = '#996703'; 831 | s_default['z-index'] = 13; 832 | } 833 | 834 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 15) || ((type === 'way' && tags['highway'] === 'trunk_link') && zoom === 15) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 15) || ((type === 'way' && tags['highway'] === 'motorway_link') && zoom === 15)) { 835 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 836 | s_default['text-position'] = 'line'; 837 | s_default['text-color'] = '#404040'; 838 | s_default['font-family'] = 'DejaVu Sans Bold'; 839 | s_default['font-size'] = '9'; 840 | s_default['text-halo-radius'] = 1; 841 | s_default['text-halo-color'] = '#ffffff'; 842 | s_default['width'] = 10; 843 | s_default['color'] = '#ffd780'; 844 | s_default['casing-width'] = 1; 845 | s_default['casing-color'] = '#996703'; 846 | s_default['z-index'] = 13; 847 | } 848 | 849 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 16) || ((type === 'way' && tags['highway'] === 'trunk_link') && zoom === 16) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 16) || ((type === 'way' && tags['highway'] === 'motorway_link') && zoom === 16)) { 850 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 851 | s_default['text-position'] = 'line'; 852 | s_default['text-color'] = '#404040'; 853 | s_default['font-family'] = 'DejaVu Sans Bold'; 854 | s_default['font-size'] = '9'; 855 | s_default['text-halo-radius'] = 1; 856 | s_default['text-halo-color'] = '#ffffff'; 857 | s_default['width'] = 11; 858 | s_default['color'] = '#ffd780'; 859 | s_default['casing-width'] = 1; 860 | s_default['casing-color'] = '#996703'; 861 | s_default['z-index'] = 13; 862 | } 863 | 864 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 17) || ((type === 'way' && tags['highway'] === 'trunk_link') && zoom === 17) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 17) || ((type === 'way' && tags['highway'] === 'motorway_link') && zoom === 17)) { 865 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 866 | s_default['text-position'] = 'line'; 867 | s_default['text-color'] = '#404040'; 868 | s_default['font-family'] = 'DejaVu Sans Bold'; 869 | s_default['font-size'] = '9'; 870 | s_default['text-halo-radius'] = 1; 871 | s_default['text-halo-color'] = '#ffffff'; 872 | s_default['width'] = 12; 873 | s_default['color'] = '#ffd780'; 874 | s_default['casing-width'] = 1; 875 | s_default['casing-color'] = '#996703'; 876 | s_default['z-index'] = 13; 877 | } 878 | 879 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom === 18) || ((type === 'way' && tags['highway'] === 'trunk_link') && zoom === 18) || ((type === 'way' && tags['highway'] === 'motorway') && zoom === 18) || ((type === 'way' && tags['highway'] === 'motorway_link') && zoom === 18)) { 880 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 881 | s_default['text-position'] = 'line'; 882 | s_default['text-color'] = '#404040'; 883 | s_default['font-family'] = 'DejaVu Sans Bold'; 884 | s_default['font-size'] = '9'; 885 | s_default['text-halo-radius'] = 1; 886 | s_default['text-halo-color'] = '#ffffff'; 887 | s_default['width'] = 13; 888 | s_default['color'] = '#ffd780'; 889 | s_default['casing-width'] = 1; 890 | s_default['casing-color'] = '#996703'; 891 | s_default['z-index'] = 13; 892 | } 893 | 894 | if (((type === 'way' && tags['highway'] === 'trunk') && zoom >= 9) || ((type === 'way' && tags['highway'] === 'trunk_link') && zoom >= 9) || ((type === 'way' && tags['highway'] === 'motorway') && zoom >= 9) || ((type === 'way' && tags['highway'] === 'motorway_link') && zoom >= 9) || ((type === 'way' && tags['highway'] === 'primary') && zoom >= 13) || ((type === 'way' && tags['highway'] === 'primary_link') && zoom >= 13)) { 895 | s_centerline['width'] = 0.3; 896 | s_centerline['color'] = '#fa6478'; 897 | s_centerline['z-index'] = 14; 898 | s_centerline['-x-mapnik-layer'] = 'top'; 899 | } 900 | 901 | if (((type === 'way' && (tags['oneway'] === '1' || tags['oneway'] === 'true' || tags['oneway'] === 'yes')) && zoom >= 17)) { 902 | s_default['line-style'] = 'arrows'; 903 | s_default['z-index'] = 15; 904 | s_default['-x-mapnik-layer'] = 'top'; 905 | } 906 | 907 | if (((selector === 'line' && tags['railway'] === 'rail') && zoom === 7)) { 908 | s_default['width'] = 0.5; 909 | s_default['color'] = '#303030'; 910 | s_default['z-index'] = 15; 911 | } 912 | 913 | if (((selector === 'line' && tags['railway'] === 'rail') && zoom === 7)) { 914 | s_ticks['width'] = 0.3; 915 | s_ticks['color'] = '#ffffff'; 916 | s_ticks['dashes'] = [3, 3]; 917 | s_ticks['z-index'] = 16; 918 | } 919 | 920 | if (((selector === 'line' && tags['railway'] === 'rail') && zoom === 8)) { 921 | s_default['width'] = 0.6; 922 | s_default['color'] = '#303030'; 923 | s_default['z-index'] = 15; 924 | } 925 | 926 | if (((selector === 'line' && tags['railway'] === 'rail') && zoom === 8)) { 927 | s_ticks['width'] = 0.35; 928 | s_ticks['color'] = '#ffffff'; 929 | s_ticks['dashes'] = [3, 3]; 930 | s_ticks['z-index'] = 16; 931 | } 932 | 933 | if (((selector === 'line' && tags['railway'] === 'rail') && zoom >= 9)) { 934 | s_default['width'] = 1.4; 935 | s_default['color'] = '#606060'; 936 | s_default['z-index'] = 15; 937 | } 938 | 939 | if (((selector === 'line' && tags['railway'] === 'rail') && zoom >= 9)) { 940 | s_ticks['width'] = 1; 941 | s_ticks['color'] = '#ffffff'; 942 | s_ticks['dashes'] = [6, 6]; 943 | s_ticks['z-index'] = 16; 944 | } 945 | 946 | if (((type === 'way' && tags['railway'] === 'subway') && zoom >= 12)) { 947 | s_default['width'] = 3; 948 | s_default['color'] = '#072889'; 949 | s_default['z-index'] = 15; 950 | s_default['dashes'] = [3, 3]; 951 | s_default['opacity'] = 0.3; 952 | s_default['linecap'] = 'butt'; 953 | s_default['-x-mapnik-layer'] = 'top'; 954 | } 955 | 956 | if (((type === 'way' && tags['barrier'] === 'fence') && zoom >= 16)) { 957 | s_default['width'] = 0.3; 958 | s_default['color'] = 'black'; 959 | s_default['z-index'] = 16; 960 | s_default['-x-mapnik-layer'] = 'top'; 961 | } 962 | 963 | if (((type === 'way' && tags['barrier'] === 'wall') && zoom >= 16)) { 964 | s_default['width'] = 0.5; 965 | s_default['color'] = 'black'; 966 | s_default['z-index'] = 16; 967 | s_default['-x-mapnik-layer'] = 'top'; 968 | } 969 | 970 | if (((type === 'way' && tags['marking'] === 'sport' && (!tags.hasOwnProperty('colour')) && (!tags.hasOwnProperty('color'))) && zoom >= 15)) { 971 | s_default['width'] = 0.5; 972 | s_default['color'] = '#a0a0a0'; 973 | s_default['z-index'] = 16; 974 | s_default['-x-mapnik-layer'] = 'top'; 975 | } 976 | 977 | if (((type === 'way' && tags['marking'] === 'sport' && tags['colour'] === 'white') && zoom >= 15) || ((type === 'way' && tags['marking'] === 'sport' && tags['color'] === 'white') && zoom >= 15)) { 978 | s_default['width'] = 1; 979 | s_default['color'] = 'white'; 980 | s_default['z-index'] = 16; 981 | s_default['-x-mapnik-layer'] = 'top'; 982 | } 983 | 984 | if (((type === 'way' && tags['marking'] === 'sport' && tags['colour'] === 'red') && zoom >= 15) || ((type === 'way' && tags['marking'] === 'sport' && tags['color'] === 'red') && zoom >= 15)) { 985 | s_default['width'] = 1; 986 | s_default['color'] = '#c00000'; 987 | s_default['z-index'] = 16; 988 | s_default['-x-mapnik-layer'] = 'top'; 989 | } 990 | 991 | if (((type === 'way' && tags['marking'] === 'sport' && tags['colour'] === 'black') && zoom >= 15) || ((type === 'way' && tags['marking'] === 'sport' && tags['color'] === 'black') && zoom >= 15)) { 992 | s_default['width'] = 1; 993 | s_default['color'] = 'black'; 994 | s_default['z-index'] = 16; 995 | s_default['-x-mapnik-layer'] = 'top'; 996 | } 997 | 998 | if (((type === 'node' && tags['amenity'] === 'bus_station') && zoom >= 15)) { 999 | s_default['icon-image'] = 'aut2_16x16_park.png'; 1000 | } 1001 | 1002 | if (((type === 'node' && tags['highway'] === 'bus_stop') && zoom >= 16)) { 1003 | s_default['icon-image'] = 'autobus_stop_14x10.png'; 1004 | } 1005 | 1006 | if (((type === 'node' && tags['railway'] === 'tram_stop') && zoom >= 16)) { 1007 | s_default['icon-image'] = 'tramway_14x13.png'; 1008 | } 1009 | 1010 | if (((type === 'node' && tags['amenity'] === 'fuel') && zoom >= 15)) { 1011 | s_default['icon-image'] = 'tankstelle1_10x11.png'; 1012 | } 1013 | 1014 | if (((type === 'node' && tags['amenity'] === 'pharmacy') && zoom >= 16)) { 1015 | s_default['icon-image'] = 'med1_11x14.png'; 1016 | } 1017 | 1018 | if (((type === 'node' && tags['amenity'] === 'cinema') && zoom >= 16)) { 1019 | s_default['icon-image'] = 'cinema_14x14.png'; 1020 | } 1021 | 1022 | if (((type === 'node' && tags['amenity'] === 'museum') && zoom >= 15)) { 1023 | s_default['icon-image'] = 'mus_13x12.png'; 1024 | } 1025 | 1026 | if (((type === 'node' && tags['tourism'] === 'zoo') && zoom >= 16)) { 1027 | s_default['icon-image'] = 'zoo4_14x14.png'; 1028 | } 1029 | 1030 | if (((type === 'node' && tags['amenity'] === 'courthouse') && zoom >= 16)) { 1031 | s_default['icon-image'] = 'sud_14x13.png'; 1032 | } 1033 | 1034 | if (((type === 'node' && tags['amenity'] === 'theatre') && zoom >= 16)) { 1035 | s_default['icon-image'] = 'teater_14x14.png'; 1036 | } 1037 | 1038 | if (((type === 'node' && tags['amenity'] === 'university') && zoom >= 16)) { 1039 | s_default['icon-image'] = 'univer_15x11.png'; 1040 | } 1041 | 1042 | if (((type === 'node' && tags['amenity'] === 'toilets') && zoom >= 16)) { 1043 | s_default['icon-image'] = 'wc-3_13x13.png'; 1044 | } 1045 | 1046 | if (((type === 'node' && tags['amenity'] === 'place_of_worship' && tags['religion'] === 'christian') && zoom >= 16)) { 1047 | s_default['icon-image'] = 'pravosl_kupol_11x15.png'; 1048 | } 1049 | 1050 | if (((selector === 'area' && tags['amenity'] === 'place_of_worship' && tags['religion'] === 'christian') && zoom >= 16)) { 1051 | s_default['icon-image'] = 'pravosl_kupol_11x15.png'; 1052 | } 1053 | 1054 | if (((type === 'node' && tags['amenity'] === 'place_of_worship') && zoom >= 14)) { 1055 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1056 | s_default['text-color'] = '#623f00'; 1057 | s_default['font-family'] = 'DejaVu Serif Italic'; 1058 | s_default['font-size'] = '9'; 1059 | s_default['text-halo-radius'] = 1; 1060 | s_default['text-halo-color'] = '#ffffff'; 1061 | s_default['text-offset'] = 3; 1062 | s_default['max-width'] = 70; 1063 | } 1064 | 1065 | if (((selector === 'area' && tags['amenity'] === 'place_of_worship') && zoom >= 14)) { 1066 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1067 | s_default['text-color'] = '#623f00'; 1068 | s_default['font-family'] = 'DejaVu Serif Italic'; 1069 | s_default['font-size'] = '9'; 1070 | s_default['text-halo-radius'] = 1; 1071 | s_default['text-halo-color'] = '#ffffff'; 1072 | s_default['text-offset'] = 3; 1073 | s_default['max-width'] = 70; 1074 | s_default['z-index'] = 16; 1075 | s_default['width'] = 0.1; 1076 | s_default['color'] = '#111111'; 1077 | s_default['text-opacity'] = '1'; 1078 | s_default['fill-color'] = '#777777'; 1079 | s_default['fill-opacity'] = 0.5; 1080 | } 1081 | 1082 | if (((type === 'node' && tags['amenity'] === 'kindergarten') && zoom >= 17)) { 1083 | s_default['icon-image'] = 'kindergarten_14x14.png'; 1084 | } 1085 | 1086 | if (((type === 'node' && tags['amenity'] === 'school') && zoom >= 17)) { 1087 | s_default['icon-image'] = 'school_13x13.png'; 1088 | } 1089 | 1090 | if (((type === 'node' && tags['amenity'] === 'library') && zoom >= 17)) { 1091 | s_default['icon-image'] = 'lib_13x14.png'; 1092 | } 1093 | 1094 | if (((type === 'node' && tags['tourism'] === 'hotel') && zoom >= 17)) { 1095 | s_default['icon-image'] = 'hotell_14x14.png'; 1096 | } 1097 | 1098 | if (((type === 'node' && tags['amenity'] === 'post_office') && zoom >= 17)) { 1099 | s_default['icon-image'] = 'post_14x11.png'; 1100 | } 1101 | 1102 | if (((type === 'node' && tags['amenity'] === 'restaurant') && zoom >= 17)) { 1103 | s_default['icon-image'] = 'rest_14x14.png'; 1104 | } 1105 | 1106 | if (((type === 'node' && (tags.hasOwnProperty('shop'))) && zoom >= 17)) { 1107 | s_default['icon-image'] = 'superm_12x12.png'; 1108 | } 1109 | 1110 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '2'))) { 1111 | s_default['width'] = 0.5; 1112 | s_default['color'] = '#202020'; 1113 | s_default['dashes'] = [6, 4]; 1114 | s_default['opacity'] = 0.7; 1115 | s_default['z-index'] = 16; 1116 | } 1117 | 1118 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '3') && zoom === 3)) { 1119 | s_default['width'] = 0.4; 1120 | s_default['color'] = '#7e0156'; 1121 | s_default['dashes'] = [3, 3]; 1122 | s_default['opacity'] = 0.5; 1123 | s_default['z-index'] = 16; 1124 | } 1125 | 1126 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '3') && zoom >= 4)) { 1127 | s_default['width'] = 1.3; 1128 | s_default['color'] = '#ff99cc'; 1129 | s_default['opacity'] = 0.5; 1130 | s_default['z-index'] = 16; 1131 | } 1132 | 1133 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '6') && zoom >= 10)) { 1134 | s_default['width'] = 0.5; 1135 | s_default['color'] = '#101010'; 1136 | s_default['dashes'] = [1, 2]; 1137 | s_default['opacity'] = 0.6; 1138 | s_default['z-index'] = 16.1; 1139 | } 1140 | 1141 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '4') && zoom >= 4 && zoom <= 5)) { 1142 | s_default['width'] = 0.3; 1143 | s_default['color'] = '#000000'; 1144 | s_default['dashes'] = [1, 2]; 1145 | s_default['opacity'] = 0.8; 1146 | s_default['z-index'] = 16.3; 1147 | } 1148 | 1149 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '4') && zoom >= 6)) { 1150 | s_default['width'] = 0.7; 1151 | s_default['color'] = '#000000'; 1152 | s_default['dashes'] = [1, 2]; 1153 | s_default['opacity'] = 0.8; 1154 | s_default['z-index'] = 16.3; 1155 | } 1156 | 1157 | if (((type === 'way' && tags['railway'] === 'tram') && zoom >= 12)) { 1158 | s_default['line-style'] = 'rway44.png'; 1159 | s_default['z-index'] = 17; 1160 | } 1161 | 1162 | if (((type === 'node' && tags['railway'] === 'station' && tags['transport'] !== 'subway') && zoom >= 9)) { 1163 | s_default['icon-image'] = 'rw_stat_stanzii_2_blue.png'; 1164 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1165 | s_default['text-offset'] = 7; 1166 | s_default['font-size'] = '9'; 1167 | s_default['font-family'] = 'DejaVu Sans Mono Book'; 1168 | s_default['text-halo-radius'] = 1; 1169 | s_default['text-color'] = '#000d6c'; 1170 | s_default['text-halo-color'] = '#ffffff'; 1171 | s_default['text-allow-overlap'] = 'false'; 1172 | s_default['-x-mapnik-min-distance'] = '0'; 1173 | } 1174 | 1175 | if (((type === 'node' && tags['railway'] === 'station' && tags['transport'] === 'subway') && zoom >= 12 && zoom <= 15)) { 1176 | s_default['icon-image'] = 'metro_others6.png'; 1177 | s_default['z-index'] = 17; 1178 | } 1179 | 1180 | if (((type === 'node' && tags['railway'] === 'station' && tags['transport'] === 'subway') && zoom >= 12 && zoom <= 15)) { 1181 | s_label['text'] = MapCSS.e_localize(tags, 'name'); 1182 | s_label['text-offset'] = 11; 1183 | s_label['font-size'] = '9'; 1184 | s_label['font-family'] = 'DejaVu Sans Book'; 1185 | s_label['text-halo-radius'] = 2; 1186 | s_label['text-color'] = '#1300bb'; 1187 | s_label['text-halo-color'] = '#ffffff'; 1188 | s_label['text-allow-overlap'] = 'false'; 1189 | s_label['-x-mapnik-min-distance'] = '0'; 1190 | } 1191 | 1192 | if (((type === 'node' && tags['railway'] === 'subway_entrance') && zoom >= 16)) { 1193 | s_default['icon-image'] = 'metro_others6.png'; 1194 | s_default['z-index'] = 17; 1195 | } 1196 | 1197 | if (((type === 'node' && tags['railway'] === 'subway_entrance' && (tags.hasOwnProperty('name'))) && zoom >= 16)) { 1198 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1199 | s_default['text-offset'] = 11; 1200 | s_default['font-size'] = '9'; 1201 | s_default['font-family'] = 'DejaVu Sans Book'; 1202 | s_default['text-halo-radius'] = 2; 1203 | s_default['text-color'] = '#1300bb'; 1204 | s_default['text-halo-color'] = '#ffffff'; 1205 | s_default['text-allow-overlap'] = 'false'; 1206 | s_default['-x-mapnik-min-distance'] = '0'; 1207 | } 1208 | 1209 | if (((type === 'node' && tags['aeroway'] === 'aerodrome') && zoom >= 10)) { 1210 | s_default['icon-image'] = 'airport_world.png'; 1211 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1212 | s_default['text-offset'] = 12; 1213 | s_default['font-size'] = '9'; 1214 | s_default['font-family'] = 'DejaVu Sans Condensed Bold'; 1215 | s_default['text-halo-radius'] = 1; 1216 | s_default['text-color'] = '#1e7ca5'; 1217 | s_default['text-halo-color'] = '#ffffff'; 1218 | s_default['text-allow-overlap'] = 'false'; 1219 | s_default['z-index'] = 17; 1220 | } 1221 | 1222 | if (((type === 'node' && (tags['capital'] === '1' || tags['capital'] === 'true' || tags['capital'] === 'yes') && tags['population'] > '5000000') && zoom >= 3 && zoom <= 6)) { 1223 | s_default['icon-image'] = 'adm_5.png'; 1224 | s_default['allow-overlap'] = 'true'; 1225 | } 1226 | 1227 | if (((type === 'node' && (tags['capital'] === '1' || tags['capital'] === 'true' || tags['capital'] === 'yes') && tags['population'] > '5000000') && zoom === 3)) { 1228 | s_default['text-offset'] = 4; 1229 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1230 | s_default['font-size'] = '8'; 1231 | s_default['font-family'] = 'DejaVu Sans Bold'; 1232 | s_default['text-halo-radius'] = 1; 1233 | s_default['text-color'] = '#505050'; 1234 | s_default['text-halo-color'] = '#ffffff'; 1235 | s_default['text-allow-overlap'] = 'false'; 1236 | s_default['-x-mapnik-min-distance'] = '0'; 1237 | s_default['text-align'] = 'left'; 1238 | } 1239 | 1240 | if (((type === 'node' && (tags['capital'] === '1' || tags['capital'] === 'true' || tags['capital'] === 'yes') && tags['population'] > '5000000') && zoom >= 4 && zoom <= 6)) { 1241 | s_default['text-offset'] = 6; 1242 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1243 | s_default['font-size'] = '10'; 1244 | s_default['font-family'] = 'DejaVu Sans Bold'; 1245 | s_default['text-halo-radius'] = 1; 1246 | s_default['text-color'] = '#303030'; 1247 | s_default['text-halo-color'] = '#ffffff'; 1248 | s_default['text-allow-overlap'] = 'false'; 1249 | s_default['-x-mapnik-min-distance'] = '0'; 1250 | s_default['text-align'] = 'left'; 1251 | } 1252 | 1253 | if (((type === 'node' && (tags.hasOwnProperty('place')) && tags['population'] < '100000' && (tags.hasOwnProperty('capital')) && tags['admin_level'] < '5') && zoom >= 4 && zoom <= 5)) { 1254 | s_default['icon-image'] = 'adm_4.png'; 1255 | s_default['text-offset'] = 5; 1256 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1257 | s_default['font-size'] = '7'; 1258 | s_default['font-family'] = 'DejaVu Sans Bold'; 1259 | s_default['text-halo-radius'] = 1; 1260 | s_default['text-color'] = '#404040'; 1261 | s_default['text-halo-color'] = '#ffffff'; 1262 | s_default['text-allow-overlap'] = 'false'; 1263 | s_default['-x-mapnik-min-distance'] = '0'; 1264 | } 1265 | 1266 | if (((type === 'node' && (tags.hasOwnProperty('place')) && tags['population'] >= '100000' && tags['population'] <= '5000000' && (tags.hasOwnProperty('capital')) && tags['admin_level'] < '5') && zoom >= 4 && zoom <= 5)) { 1267 | s_default['icon-image'] = 'adm_5.png'; 1268 | s_default['text-offset'] = 5; 1269 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1270 | s_default['font-size'] = '8'; 1271 | s_default['font-family'] = 'DejaVu Sans Bold'; 1272 | s_default['text-halo-radius'] = 1; 1273 | s_default['text-color'] = '#404040'; 1274 | s_default['text-halo-color'] = '#ffffff'; 1275 | s_default['text-allow-overlap'] = 'false'; 1276 | s_default['-x-mapnik-min-distance'] = '0'; 1277 | s_default['z-index'] = 1; 1278 | } 1279 | 1280 | if (((type === 'node' && tags['place'] === 'town' && (tags.hasOwnProperty('capital'))) && zoom === 5)) { 1281 | s_default['icon-image'] = 'town_4.png'; 1282 | } 1283 | 1284 | if (((type === 'node' && tags['place'] === 'city' && tags['population'] < '100000') && zoom === 6) || ((type === 'node' && tags['place'] === 'town' && tags['population'] < '100000' && (tags.hasOwnProperty('admin_level'))) && zoom === 6)) { 1285 | s_default['icon-image'] = 'adm1_4_6.png'; 1286 | s_default['text-offset'] = 5; 1287 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1288 | s_default['font-size'] = '8'; 1289 | s_default['font-family'] = 'DejaVu Sans Bold'; 1290 | s_default['text-halo-radius'] = 1; 1291 | s_default['text-color'] = '#202020'; 1292 | s_default['text-halo-color'] = '#ffffff'; 1293 | s_default['text-allow-overlap'] = 'false'; 1294 | s_default['-x-mapnik-min-distance'] = '0'; 1295 | } 1296 | 1297 | if (((type === 'node' && tags['place'] === 'city' && tags['population'] < '100000') && zoom === 7) || ((type === 'node' && tags['place'] === 'town' && tags['population'] < '100000') && zoom === 7)) { 1298 | s_default['icon-image'] = 'town_6.png'; 1299 | s_default['text-offset'] = 5; 1300 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1301 | s_default['font-size'] = '9'; 1302 | s_default['font-family'] = 'DejaVu Sans Bold'; 1303 | s_default['text-halo-radius'] = 1; 1304 | s_default['text-color'] = '#202020'; 1305 | s_default['text-halo-color'] = '#ffffff'; 1306 | s_default['text-allow-overlap'] = 'false'; 1307 | s_default['-x-mapnik-min-distance'] = '0'; 1308 | } 1309 | 1310 | if (((type === 'node' && tags['place'] === 'town' && (!tags.hasOwnProperty('population'))) && zoom === 7) || ((type === 'node' && tags['place'] === 'city' && (!tags.hasOwnProperty('population'))) && zoom === 7)) { 1311 | s_default['icon-image'] = 'town_6.png'; 1312 | s_default['text-offset'] = 5; 1313 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1314 | s_default['font-size'] = '8'; 1315 | s_default['font-family'] = 'DejaVu Sans Bold'; 1316 | s_default['text-halo-radius'] = 1; 1317 | s_default['text-color'] = '#202020'; 1318 | s_default['text-halo-color'] = '#ffffff'; 1319 | s_default['text-allow-overlap'] = 'false'; 1320 | s_default['-x-mapnik-min-distance'] = '0'; 1321 | } 1322 | 1323 | if (((type === 'node' && tags['place'] === 'town') && zoom === 8)) { 1324 | s_default['icon-image'] = 'town_6.png'; 1325 | s_default['text-offset'] = 5; 1326 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1327 | s_default['font-size'] = '8'; 1328 | s_default['font-family'] = 'DejaVu Sans Bold'; 1329 | s_default['text-halo-radius'] = 1; 1330 | s_default['text-color'] = '#202020'; 1331 | s_default['text-halo-color'] = '#ffffff'; 1332 | s_default['text-allow-overlap'] = 'false'; 1333 | s_default['-x-mapnik-min-distance'] = '0'; 1334 | } 1335 | 1336 | if (((type === 'node' && tags['place'] === 'city' && tags['population'] >= '100000' && tags['population'] <= '1000000') && zoom >= 6 && zoom <= 8) || ((type === 'node' && tags['place'] === 'town' && tags['population'] >= '100000' && tags['population'] <= '1000000' && (tags.hasOwnProperty('admin_level'))) && zoom === 6)) { 1337 | s_default['icon-image'] = 'adm1_5.png'; 1338 | s_default['text-offset'] = 5; 1339 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1340 | s_default['font-size'] = '9'; 1341 | s_default['font-family'] = 'DejaVu Sans Bold'; 1342 | s_default['text-halo-radius'] = 1; 1343 | s_default['text-color'] = '#303030'; 1344 | s_default['text-halo-color'] = '#ffffff'; 1345 | s_default['text-allow-overlap'] = 'false'; 1346 | s_default['-x-mapnik-min-distance'] = '0'; 1347 | } 1348 | 1349 | if (((type === 'node' && tags['place'] === 'city' && tags['population'] >= '100000' && tags['population'] <= '1000000') && zoom >= 7 && zoom <= 8) || ((type === 'node' && tags['place'] === 'town' && tags['population'] >= '100000' && tags['population'] <= '1000000') && zoom === 7)) { 1350 | s_default['icon-image'] = 'adm1_5.png'; 1351 | s_default['text-offset'] = 5; 1352 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1353 | s_default['font-size'] = '10'; 1354 | s_default['font-family'] = 'DejaVu Sans Bold'; 1355 | s_default['text-halo-radius'] = 1; 1356 | s_default['text-color'] = '#303030'; 1357 | s_default['text-halo-color'] = '#ffffff'; 1358 | s_default['text-allow-overlap'] = 'false'; 1359 | s_default['-x-mapnik-min-distance'] = '0'; 1360 | } 1361 | 1362 | if (((type === 'node' && tags['place'] === 'city' && tags['population'] > '1000000') && zoom === 6)) { 1363 | s_default['icon-image'] = 'adm1_6_test2.png'; 1364 | s_default['text-offset'] = 5; 1365 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1366 | s_default['font-size'] = '10'; 1367 | s_default['font-family'] = 'DejaVu Sans Bold'; 1368 | s_default['text-halo-radius'] = 1; 1369 | s_default['text-color'] = '#404040'; 1370 | s_default['text-halo-color'] = '#ffffff'; 1371 | s_default['text-allow-overlap'] = 'false'; 1372 | s_default['-x-mapnik-min-distance'] = '0'; 1373 | s_default['z-index'] = 1; 1374 | } 1375 | 1376 | if (((type === 'node' && tags['place'] === 'city' && tags['population'] > '1000000' && tags['population'] < '5000000') && zoom >= 7 && zoom <= 8)) { 1377 | s_default['icon-image'] = 'adm1_6_test2.png'; 1378 | s_default['text-offset'] = 5; 1379 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1380 | s_default['font-size'] = '11'; 1381 | s_default['font-family'] = 'DejaVu Sans Bold'; 1382 | s_default['text-halo-radius'] = 1; 1383 | s_default['text-color'] = '#404040'; 1384 | s_default['text-halo-color'] = '#ffffff'; 1385 | s_default['text-allow-overlap'] = 'false'; 1386 | s_default['-x-mapnik-min-distance'] = '0'; 1387 | s_default['z-index'] = 2; 1388 | } 1389 | 1390 | if (((type === 'node' && tags['place'] === 'city' && tags['population'] >= '5000000') && zoom >= 7 && zoom <= 8)) { 1391 | s_default['icon-image'] = 'adm_6.png'; 1392 | s_default['text-offset'] = 5; 1393 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1394 | s_default['font-size'] = '12'; 1395 | s_default['font-family'] = 'DejaVu Sans Bold'; 1396 | s_default['text-halo-radius'] = 1; 1397 | s_default['text-color'] = '#404040'; 1398 | s_default['text-halo-color'] = '#ffffff'; 1399 | s_default['text-allow-overlap'] = 'false'; 1400 | s_default['-x-mapnik-min-distance'] = '0'; 1401 | s_default['z-index'] = 3; 1402 | } 1403 | 1404 | if (((type === 'node' && tags['place'] === 'city' && (tags['capital'] === '1' || tags['capital'] === 'true' || tags['capital'] === 'yes')) && zoom >= 9 && zoom <= 11)) { 1405 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1406 | s_default['text-offset'] = -20; 1407 | s_default['font-size'] = '14'; 1408 | s_default['font-family'] = 'DejaVu Sans Bold'; 1409 | s_default['text-halo-radius'] = 4; 1410 | s_default['text-color'] = '#101010'; 1411 | s_default['text-halo-color'] = '#ffffff'; 1412 | s_default['text-allow-overlap'] = 'false'; 1413 | s_default['-x-mapnik-min-distance'] = '50'; 1414 | s_default['z-index'] = 20; 1415 | } 1416 | 1417 | if (((type === 'node' && tags['place'] === 'city' && (tags['capital'] === '-1' || tags['capital'] === 'false' || tags['capital'] === 'no')) && zoom >= 9 && zoom <= 11)) { 1418 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1419 | s_default['text-offset'] = -20; 1420 | s_default['font-size'] = '14'; 1421 | s_default['font-family'] = 'DejaVu Sans Bold'; 1422 | s_default['text-halo-radius'] = 2; 1423 | s_default['text-color'] = '#101010'; 1424 | s_default['text-halo-color'] = '#ffffff'; 1425 | s_default['text-allow-overlap'] = 'false'; 1426 | s_default['-x-mapnik-min-distance'] = '0'; 1427 | s_default['z-index'] = 1; 1428 | } 1429 | 1430 | if (((type === 'node' && tags['place'] === 'town') && zoom === 11)) { 1431 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1432 | s_default['font-size'] = '12'; 1433 | s_default['font-family'] = 'DejaVu Sans Book'; 1434 | s_default['text-color'] = '#101010'; 1435 | s_default['text-halo-radius'] = 1; 1436 | s_default['text-halo-color'] = '#ffffff'; 1437 | s_default['z-index'] = 20; 1438 | } 1439 | 1440 | if (((type === 'node' && tags['place'] === 'town') && zoom === 12)) { 1441 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1442 | s_default['font-size'] = '20'; 1443 | s_default['font-family'] = 'DejaVu Sans Book'; 1444 | s_default['text-color'] = '#101010'; 1445 | s_default['text-opacity'] = '0.2'; 1446 | s_default['text-allow-overlap'] = 'true'; 1447 | s_default['z-index'] = 20; 1448 | } 1449 | 1450 | if (((type === 'node' && tags['place'] === 'city') && zoom === 12)) { 1451 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1452 | s_default['font-size'] = '25'; 1453 | s_default['font-family'] = 'DejaVu Sans Book'; 1454 | s_default['text-color'] = '#101010'; 1455 | s_default['text-opacity'] = '0.3'; 1456 | s_default['text-allow-overlap'] = 'true'; 1457 | s_default['z-index'] = 20; 1458 | } 1459 | 1460 | if (((type === 'node' && tags['place'] === 'town') && zoom === 13)) { 1461 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1462 | s_default['font-size'] = '40'; 1463 | s_default['font-family'] = 'DejaVu Sans Book'; 1464 | s_default['text-color'] = '#101010'; 1465 | s_default['text-opacity'] = '0.2'; 1466 | s_default['text-allow-overlap'] = 'true'; 1467 | s_default['z-index'] = 20; 1468 | } 1469 | 1470 | if (((type === 'node' && tags['place'] === 'city') && zoom === 13)) { 1471 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1472 | s_default['font-size'] = '50'; 1473 | s_default['font-family'] = 'DejaVu Sans Book'; 1474 | s_default['text-color'] = '#101010'; 1475 | s_default['text-opacity'] = '0.3'; 1476 | s_default['text-allow-overlap'] = 'true'; 1477 | s_default['z-index'] = 20; 1478 | } 1479 | 1480 | if (((type === 'node' && tags['place'] === 'town') && zoom >= 14)) { 1481 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1482 | s_default['font-size'] = '80'; 1483 | s_default['font-family'] = 'DejaVu Sans Book'; 1484 | s_default['text-color'] = '#101010'; 1485 | s_default['text-opacity'] = '0.2'; 1486 | s_default['text-allow-overlap'] = 'true'; 1487 | s_default['z-index'] = 20; 1488 | } 1489 | 1490 | if (((type === 'node' && tags['place'] === 'city') && zoom >= 14)) { 1491 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1492 | s_default['font-size'] = '100'; 1493 | s_default['font-family'] = 'DejaVu Sans Book'; 1494 | s_default['text-color'] = '#101010'; 1495 | s_default['text-opacity'] = '0.3'; 1496 | s_default['text-allow-overlap'] = 'true'; 1497 | s_default['z-index'] = 20; 1498 | } 1499 | 1500 | if (((type === 'node' && tags['place'] === 'village') && zoom >= 9)) { 1501 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1502 | s_default['text-offset'] = 1; 1503 | s_default['font-size'] = '9'; 1504 | s_default['font-family'] = 'DejaVu Sans Book'; 1505 | s_default['text-halo-radius'] = 1; 1506 | s_default['text-color'] = '#606060'; 1507 | s_default['text-halo-color'] = '#ffffff'; 1508 | s_default['text-allow-overlap'] = 'false'; 1509 | } 1510 | 1511 | if (((type === 'node' && tags['place'] === 'hamlet') && zoom >= 9)) { 1512 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1513 | s_default['text-offset'] = 1; 1514 | s_default['font-size'] = '8'; 1515 | s_default['font-family'] = 'DejaVu Sans Book'; 1516 | s_default['text-halo-radius'] = 1; 1517 | s_default['text-color'] = '#505050'; 1518 | s_default['text-halo-color'] = '#ffffff'; 1519 | s_default['text-allow-overlap'] = 'false'; 1520 | } 1521 | 1522 | if (((selector === 'area' && tags['landuse'] === 'nature_reserve') && zoom >= 9) || ((selector === 'area' && tags['leisure'] === 'park') && zoom >= 11)) { 1523 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1524 | s_default['text-offset'] = 1; 1525 | s_default['font-size'] = '10'; 1526 | s_default['font-family'] = 'DejaVu Serif Italic'; 1527 | s_default['text-halo-radius'] = 0; 1528 | s_default['text-color'] = '#3c8000'; 1529 | s_default['text-halo-color'] = '#ffffff'; 1530 | s_default['text-allow-overlap'] = 'false'; 1531 | } 1532 | 1533 | if (((type === 'way' && tags['waterway'] === 'stream') && zoom >= 10) || ((type === 'way' && tags['waterway'] === 'river') && zoom >= 9) || ((type === 'way' && tags['waterway'] === 'canal') && zoom >= 13)) { 1534 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1535 | s_default['font-size'] = '9'; 1536 | s_default['font-family'] = 'DejaVu Sans Oblique'; 1537 | s_default['text-color'] = '#547bd1'; 1538 | s_default['text-halo-radius'] = 1; 1539 | s_default['text-halo-color'] = '#ffffff'; 1540 | s_default['text-position'] = 'line'; 1541 | } 1542 | 1543 | if (((type === 'node' && tags['place'] === 'continent') && zoom <= 3)) { 1544 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1545 | s_default['text-offset'] = -10; 1546 | s_default['font-size'] = '10'; 1547 | s_default['font-family'] = 'DejaVu Sans ExtraLight'; 1548 | s_default['text-halo-radius'] = 1; 1549 | s_default['text-color'] = '#202020'; 1550 | s_default['text-halo-color'] = '#ffffff'; 1551 | s_default['z-index'] = -1; 1552 | s_default['-x-mapnik-min-distance'] = '0'; 1553 | } 1554 | 1555 | if (((type === 'node' && tags['place'] === 'continent') && zoom >= 2 && zoom <= 3)) { 1556 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1557 | s_default['text-offset'] = -10; 1558 | s_default['font-size'] = '8'; 1559 | s_default['font-family'] = 'DejaVu Sans ExtraLight'; 1560 | s_default['text-halo-radius'] = 1; 1561 | s_default['text-color'] = '#202020'; 1562 | s_default['text-halo-color'] = '#ffffff'; 1563 | s_default['z-index'] = -1; 1564 | s_default['-x-mapnik-min-distance'] = '0'; 1565 | } 1566 | 1567 | if (((type === 'node' && tags['place'] === 'ocean') && zoom <= 6)) { 1568 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1569 | s_default['text-offset'] = 0; 1570 | s_default['font-size'] = '8'; 1571 | s_default['font-family'] = 'DejaVu Sans Oblique'; 1572 | s_default['text-halo-radius'] = 1; 1573 | s_default['text-color'] = '#202020'; 1574 | s_default['text-halo-color'] = '#ffffff'; 1575 | s_default['z-index'] = -1; 1576 | s_default['-x-mapnik-min-distance'] = '0'; 1577 | } 1578 | 1579 | if (((type === 'node' && tags['place'] === 'ocean') && zoom >= 7)) { 1580 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1581 | s_default['text-offset'] = 0; 1582 | s_default['font-size'] = '11'; 1583 | s_default['font-family'] = 'DejaVu Sans Oblique'; 1584 | s_default['text-halo-radius'] = 1; 1585 | s_default['text-color'] = '#202020'; 1586 | s_default['text-halo-color'] = '#ffffff'; 1587 | s_default['z-index'] = -1; 1588 | s_default['-x-mapnik-min-distance'] = '0'; 1589 | } 1590 | 1591 | if (((type === 'node' && tags['place'] === 'sea') && zoom <= 6)) { 1592 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1593 | s_default['text-offset'] = 0; 1594 | s_default['font-size'] = '8'; 1595 | s_default['font-family'] = 'DejaVu Sans Oblique'; 1596 | s_default['text-halo-radius'] = 1; 1597 | s_default['text-color'] = '#4976d1'; 1598 | s_default['text-halo-color'] = '#ffffff'; 1599 | s_default['-x-mapnik-min-distance'] = '0'; 1600 | } 1601 | 1602 | if (((type === 'node' && tags['place'] === 'sea') && zoom >= 7)) { 1603 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1604 | s_default['text-offset'] = 0; 1605 | s_default['font-size'] = '10'; 1606 | s_default['font-family'] = 'DejaVu Sans Oblique'; 1607 | s_default['text-halo-radius'] = 1; 1608 | s_default['text-color'] = '#4976d1'; 1609 | s_default['text-halo-color'] = '#ffffff'; 1610 | s_default['-x-mapnik-min-distance'] = '0'; 1611 | } 1612 | 1613 | if (((type === 'node' && tags['natural'] === 'peak' && tags['ele'] > '4500') && zoom >= 3 && zoom <= 4)) { 1614 | s_default['icon-image'] = 'mountain_peak6.png'; 1615 | s_default['text'] = MapCSS.e_localize(tags, 'ele'); 1616 | s_default['text-offset'] = 3; 1617 | s_default['font-size'] = '7'; 1618 | s_default['font-family'] = 'DejaVu Sans Mono Book'; 1619 | s_default['text-halo-radius'] = 0; 1620 | s_default['text-color'] = '#664229'; 1621 | s_default['text-halo-color'] = '#ffffff'; 1622 | s_default['-x-mapnik-min-distance'] = '0'; 1623 | } 1624 | 1625 | if (((type === 'node' && tags['natural'] === 'peak' && tags['ele'] > '3500') && zoom >= 5 && zoom <= 6)) { 1626 | s_default['icon-image'] = 'mountain_peak6.png'; 1627 | s_default['text'] = MapCSS.e_localize(tags, 'ele'); 1628 | s_default['text-offset'] = 3; 1629 | s_default['font-size'] = '7'; 1630 | s_default['font-family'] = 'DejaVu Sans Mono Book'; 1631 | s_default['text-halo-radius'] = 0; 1632 | s_default['text-color'] = '#664229'; 1633 | s_default['text-halo-color'] = '#ffffff'; 1634 | s_default['-x-mapnik-min-distance'] = '0'; 1635 | } 1636 | 1637 | if (((type === 'node' && tags['natural'] === 'peak' && tags['ele'] > '2500') && zoom >= 7 && zoom <= 12)) { 1638 | s_default['icon-image'] = 'mountain_peak6.png'; 1639 | s_default['text'] = MapCSS.e_localize(tags, 'ele'); 1640 | s_default['text-offset'] = 3; 1641 | s_default['font-size'] = '7'; 1642 | s_default['font-family'] = 'DejaVu Sans Mono Book'; 1643 | s_default['text-halo-radius'] = 0; 1644 | s_default['text-color'] = '#664229'; 1645 | s_default['text-halo-color'] = '#ffffff'; 1646 | s_default['-x-mapnik-min-distance'] = '0'; 1647 | } 1648 | 1649 | if (((type === 'node' && tags['natural'] === 'peak') && zoom >= 12)) { 1650 | s_default['icon-image'] = 'mountain_peak6.png'; 1651 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1652 | s_default['text-offset'] = 3; 1653 | s_default['font-size'] = '7'; 1654 | s_default['font-family'] = 'DejaVu Sans Mono Book'; 1655 | s_default['text-halo-radius'] = 0; 1656 | s_default['text-color'] = '#664229'; 1657 | s_default['text-halo-color'] = '#ffffff'; 1658 | s_default['-x-mapnik-min-distance'] = '0'; 1659 | } 1660 | 1661 | if (((type === 'node' && tags['place'] === 'country') && zoom >= 2 && zoom <= 3)) { 1662 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1663 | s_default['text-offset'] = 0; 1664 | s_default['font-size'] = '10'; 1665 | s_default['font-family'] = 'DejaVu Sans Book'; 1666 | s_default['text-halo-radius'] = 1; 1667 | s_default['text-color'] = '#dd5875'; 1668 | s_default['text-halo-color'] = '#ffffff'; 1669 | s_default['z-index'] = 1; 1670 | s_default['-x-mapnik-min-distance'] = '0'; 1671 | } 1672 | 1673 | if (((type === 'node' && tags['place'] === 'country') && zoom >= 4 && zoom <= 8)) { 1674 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1675 | s_default['text-offset'] = 0; 1676 | s_default['font-size'] = '13'; 1677 | s_default['font-family'] = 'DejaVu Sans Book'; 1678 | s_default['text-halo-radius'] = 1; 1679 | s_default['text-color'] = 'red'; 1680 | s_default['text-halo-color'] = '#ffffff'; 1681 | s_default['z-index'] = 1; 1682 | s_default['-x-mapnik-min-distance'] = '0'; 1683 | } 1684 | 1685 | if (((type === 'node' && tags['place'] === 'country') && zoom >= 8 && zoom <= 10)) { 1686 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1687 | s_default['text-offset'] = 0; 1688 | s_default['font-size'] = '16'; 1689 | s_default['font-family'] = 'DejaVu Sans Book'; 1690 | s_default['text-halo-radius'] = 1; 1691 | s_default['text-color'] = 'red'; 1692 | s_default['text-halo-color'] = '#ffffff'; 1693 | s_default['z-index'] = 1; 1694 | s_default['-x-mapnik-min-distance'] = '0'; 1695 | } 1696 | 1697 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '3') && zoom >= 3 && zoom <= 5)) { 1698 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1699 | s_default['text-offset'] = -5; 1700 | s_default['font-size'] = '8'; 1701 | s_default['font-family'] = 'DejaVu Sans ExtraLight'; 1702 | s_default['text-halo-radius'] = 0; 1703 | s_default['text-color'] = '#101010'; 1704 | s_default['text-halo-color'] = '#ffffff'; 1705 | s_default['-x-mapnik-min-distance'] = '0'; 1706 | s_default['max-width'] = 50; 1707 | } 1708 | 1709 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '4') && zoom >= 6 && zoom <= 10)) { 1710 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1711 | s_default['text-offset'] = 17; 1712 | s_default['font-size'] = '14'; 1713 | s_default['font-family'] = 'DejaVu Sans ExtraLight'; 1714 | s_default['text-halo-radius'] = 1; 1715 | s_default['text-color'] = '#606060'; 1716 | s_default['text-halo-color'] = '#ffffff'; 1717 | s_default['-x-mapnik-min-distance'] = '0'; 1718 | } 1719 | 1720 | if (((selector === 'area' && tags['boundary'] === 'administrative' && tags['admin_level'] === '6') && zoom >= 10)) { 1721 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1722 | s_default['text-offset'] = -10; 1723 | s_default['font-size'] = '12'; 1724 | s_default['font-family'] = 'DejaVu Sans ExtraLight'; 1725 | s_default['text-halo-radius'] = 1; 1726 | s_default['text-color'] = '#7848a0'; 1727 | s_default['text-halo-color'] = '#ffffff'; 1728 | } 1729 | 1730 | if (((type === 'node' && tags['place'] === 'suburb') && zoom >= 12)) { 1731 | s_default['text'] = MapCSS.e_localize(tags, 'name'); 1732 | s_default['font-size'] = '12'; 1733 | s_default['font-family'] = 'DejaVu Sans ExtraLight'; 1734 | s_default['text-color'] = '#7848a0'; 1735 | s_default['z-index'] = 20; 1736 | } 1737 | 1738 | if (((selector === 'area' && (tags.hasOwnProperty('building'))) && zoom >= 13)) { 1739 | s_default['width'] = 0.3; 1740 | s_default['color'] = '#cca352'; 1741 | s_default['z-index'] = 17; 1742 | } 1743 | 1744 | if (((selector === 'area' && (tags['building'] === '1' || tags['building'] === 'true' || tags['building'] === 'yes')) && zoom >= 15)) { 1745 | s_default['fill-color'] = '#E7CCB4'; 1746 | s_default['z-index'] = 17; 1747 | } 1748 | 1749 | if (((selector === 'area' && tags['building'] === 'public') && zoom >= 15)) { 1750 | s_default['fill-color'] = '#edc2ba'; 1751 | s_default['z-index'] = 17; 1752 | } 1753 | 1754 | if (((selector === 'area' && (tags.hasOwnProperty('building')) && (tags['building '] === '-1' || tags['building '] === 'false' || tags['building '] === 'no') && tags['building'] !== 'public') && zoom >= 15)) { 1755 | s_default['fill-color'] = '#D8D1D1'; 1756 | s_default['z-index'] = 17; 1757 | } 1758 | 1759 | if (((selector === 'area' && (tags.hasOwnProperty('building'))) && zoom >= 15 && zoom <= 16)) { 1760 | s_default['text'] = MapCSS.e_localize(tags, 'addr:housenumber'); 1761 | s_default['text-halo-radius'] = 1; 1762 | s_default['text-position'] = 'center'; 1763 | s_default['font-size'] = '7'; 1764 | s_default['-x-mapnik-min-distance'] = '10'; 1765 | s_default['opacity'] = 0.8; 1766 | } 1767 | 1768 | if (((selector === 'area' && (tags.hasOwnProperty('building'))) && zoom >= 17)) { 1769 | s_default['text'] = MapCSS.e_localize(tags, 'addr:housenumber'); 1770 | s_default['text-halo-radius'] = 1; 1771 | s_default['text-position'] = 'center'; 1772 | s_default['font-size'] = '8'; 1773 | s_default['-x-mapnik-min-distance'] = '10'; 1774 | s_default['opacity'] = 0.8; 1775 | } 1776 | 1777 | if (((type === 'node' && tags['highway'] === 'milestone' && (tags.hasOwnProperty('pk'))) && zoom >= 13)) { 1778 | s_default['text'] = MapCSS.e_localize(tags, 'pk'); 1779 | s_default['font-size'] = '7'; 1780 | s_default['text-halo-radius'] = 5; 1781 | s_default['-x-mapnik-min-distance'] = '0'; 1782 | } 1783 | 1784 | if (Object.keys(s_default).length) { 1785 | style['default'] = s_default; 1786 | } 1787 | if (Object.keys(s_centerline).length) { 1788 | style['centerline'] = s_centerline; 1789 | } 1790 | if (Object.keys(s_ticks).length) { 1791 | style['ticks'] = s_ticks; 1792 | } 1793 | if (Object.keys(s_label).length) { 1794 | style['label'] = s_label; 1795 | } 1796 | return style; 1797 | } 1798 | 1799 | var sprite_images = { 1800 | 'adm1_4_6.png': { 1801 | width: 4, 1802 | height: 4, 1803 | offset: 0 1804 | }, 1805 | 'adm1_5.png': { 1806 | width: 5, 1807 | height: 5, 1808 | offset: 4 1809 | }, 1810 | 'adm1_6_test2.png': { 1811 | width: 6, 1812 | height: 6, 1813 | offset: 9 1814 | }, 1815 | 'adm_4.png': { 1816 | width: 4, 1817 | height: 4, 1818 | offset: 15 1819 | }, 1820 | 'adm_5.png': { 1821 | width: 5, 1822 | height: 5, 1823 | offset: 19 1824 | }, 1825 | 'adm_6.png': { 1826 | width: 6, 1827 | height: 6, 1828 | offset: 24 1829 | }, 1830 | 'airport_world.png': { 1831 | width: 11, 1832 | height: 14, 1833 | offset: 30 1834 | }, 1835 | 'aut2_16x16_park.png': { 1836 | width: 16, 1837 | height: 16, 1838 | offset: 44 1839 | }, 1840 | 'autobus_stop_14x10.png': { 1841 | width: 14, 1842 | height: 10, 1843 | offset: 60 1844 | }, 1845 | 'bull2.png': { 1846 | width: 12, 1847 | height: 12, 1848 | offset: 70 1849 | }, 1850 | 'cemetry7_2.png': { 1851 | width: 14, 1852 | height: 14, 1853 | offset: 82 1854 | }, 1855 | 'cinema_14x14.png': { 1856 | width: 14, 1857 | height: 14, 1858 | offset: 96 1859 | }, 1860 | 'desert22.png': { 1861 | width: 16, 1862 | height: 8, 1863 | offset: 110 1864 | }, 1865 | 'glacier.png': { 1866 | width: 10, 1867 | height: 10, 1868 | offset: 118 1869 | }, 1870 | 'hotell_14x14.png': { 1871 | width: 14, 1872 | height: 14, 1873 | offset: 128 1874 | }, 1875 | 'kindergarten_14x14.png': { 1876 | width: 14, 1877 | height: 14, 1878 | offset: 142 1879 | }, 1880 | 'kust1.png': { 1881 | width: 14, 1882 | height: 14, 1883 | offset: 156 1884 | }, 1885 | 'lib_13x14.png': { 1886 | width: 13, 1887 | height: 12, 1888 | offset: 170 1889 | }, 1890 | 'med1_11x14.png': { 1891 | width: 11, 1892 | height: 14, 1893 | offset: 182 1894 | }, 1895 | 'metro_others6.png': { 1896 | width: 16, 1897 | height: 16, 1898 | offset: 196 1899 | }, 1900 | 'mountain_peak6.png': { 1901 | width: 3, 1902 | height: 3, 1903 | offset: 212 1904 | }, 1905 | 'mus_13x12.png': { 1906 | width: 13, 1907 | height: 12, 1908 | offset: 215 1909 | }, 1910 | 'parks2.png': { 1911 | width: 12, 1912 | height: 12, 1913 | offset: 227 1914 | }, 1915 | 'post_14x11.png': { 1916 | width: 14, 1917 | height: 11, 1918 | offset: 239 1919 | }, 1920 | 'pravosl_kupol_11x15.png': { 1921 | width: 11, 1922 | height: 15, 1923 | offset: 250 1924 | }, 1925 | 'rest_14x14.png': { 1926 | width: 14, 1927 | height: 14, 1928 | offset: 265 1929 | }, 1930 | 'rw_stat_stanzii_2_blue.png': { 1931 | width: 9, 1932 | height: 5, 1933 | offset: 279 1934 | }, 1935 | 'sady10.png': { 1936 | width: 16, 1937 | height: 16, 1938 | offset: 284 1939 | }, 1940 | 'school_13x13.png': { 1941 | width: 13, 1942 | height: 13, 1943 | offset: 300 1944 | }, 1945 | 'sud_14x13.png': { 1946 | width: 14, 1947 | height: 13, 1948 | offset: 313 1949 | }, 1950 | 'superm_12x12.png': { 1951 | width: 12, 1952 | height: 12, 1953 | offset: 326 1954 | }, 1955 | 'swamp_world2.png': { 1956 | width: 23, 1957 | height: 24, 1958 | offset: 338 1959 | }, 1960 | 'tankstelle1_10x11.png': { 1961 | width: 10, 1962 | height: 11, 1963 | offset: 362 1964 | }, 1965 | 'teater_14x14.png': { 1966 | width: 14, 1967 | height: 14, 1968 | offset: 373 1969 | }, 1970 | 'town_4.png': { 1971 | width: 4, 1972 | height: 4, 1973 | offset: 387 1974 | }, 1975 | 'town_6.png': { 1976 | width: 6, 1977 | height: 6, 1978 | offset: 391 1979 | }, 1980 | 'tramway_14x13.png': { 1981 | width: 14, 1982 | height: 13, 1983 | offset: 397 1984 | }, 1985 | 'univer_15x11.png': { 1986 | width: 15, 1987 | height: 11, 1988 | offset: 410 1989 | }, 1990 | 'wc-3_13x13.png': { 1991 | width: 13, 1992 | height: 13, 1993 | offset: 421 1994 | }, 1995 | 'zoo4_14x14.png': { 1996 | width: 14, 1997 | height: 14, 1998 | offset: 434 1999 | } 2000 | }, external_images = [], presence_tags = ['shop'], value_tags = ['color', 'amenity', 'pk', 'building ', 'marking', 'service', 'addr:housenumber', 'population', 'leisure', 'waterway', 'aeroway', 'landuse', 'barrier', 'colour', 'railway', 'oneway', 'religion', 'tourism', 'admin_level', 'transport', 'name', 'building', 'place', 'residential', 'highway', 'ele', 'living_street', 'natural', 'boundary', 'capital']; 2001 | 2002 | MapCSS.loadStyle('osmosnimki', restyle, sprite_images, external_images, presence_tags, value_tags); 2003 | MapCSS.preloadExternalImages('osmosnimki'); 2004 | })(MapCSS); 2005 | 2006 | -------------------------------------------------------------------------------- /debug/styles/osmosnimki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kothic/kothic-js/4edc827f5f59b680ce2c6936d98d8b8283b6620e/debug/styles/osmosnimki.png -------------------------------------------------------------------------------- /debug/styles/surface.js: -------------------------------------------------------------------------------- 1 | 2 | (function (MapCSS) { 3 | 'use strict'; 4 | 5 | function restyle(style, tags, zoom, type, selector) { 6 | var s_default = {}, s_overlay = {}; 7 | 8 | if (((type == 'way' && tags['highway'] == 'primary' && (!tags.hasOwnProperty('surface'))))) { 9 | s_overlay['color'] = '#f00'; 10 | s_overlay['width'] = 1; 11 | s_overlay['z-index'] = 100; 12 | } 13 | 14 | if (Object.keys(s_default).length) { 15 | style['default'] = s_default; 16 | } 17 | if (Object.keys(s_overlay).length) { 18 | style['overlay'] = s_overlay; 19 | } 20 | return style; 21 | } 22 | 23 | var sprite_images = { 24 | }, external_images = [], presence_tags = ['surface'], value_tags = ['highway']; 25 | 26 | MapCSS.loadStyle('surface', restyle, sprite_images, external_images, presence_tags, value_tags); 27 | MapCSS.preloadExternalImages('surface'); 28 | })(MapCSS); 29 | 30 | -------------------------------------------------------------------------------- /debug/tile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kothic JS tile test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /dist/kothic-leaflet.js: -------------------------------------------------------------------------------- 1 | L.TileLayer.Kothic = L.GridLayer.extend({ 2 | options: { 3 | tileSize: 256, 4 | zoomOffset: 0, 5 | minZoom: 2, 6 | maxZoom: 22, 7 | updateWhenIdle: true, 8 | unloadInvisibleTiles: true, 9 | attribution: 'Map data © 2019 OpenStreetMap contributors,' + 10 | ' Rendering by Kothic JS', 11 | async: true, 12 | buffered: false, 13 | styles: MapCSS.availableStyles 14 | }, 15 | 16 | initialize: function(url,options) { 17 | L.Util.setOptions(this, options); 18 | 19 | this._url = url; 20 | this._canvases = {}; 21 | this._debugMessages = []; 22 | 23 | window.onKothicDataResponse = L.Util.bind(this._onKothicDataResponse, this); 24 | }, 25 | 26 | _onKothicDataResponse: function(data, zoom, x, y, done) { 27 | var error; 28 | var key = [zoom, x, y].join('/'), 29 | canvas = this._canvases[key], 30 | zoomOffset = this.options.zoomOffset; 31 | 32 | if (!canvas) { 33 | return; 34 | } 35 | 36 | function onRenderComplete() { 37 | done(error, canvas); 38 | } 39 | 40 | this._invertYAxe(data); 41 | 42 | var styles = this.options.styles; 43 | 44 | Kothic.render(canvas, data, zoom + zoomOffset, { 45 | styles: styles, 46 | locales: ['be', 'ru', 'en'], 47 | onRenderComplete: onRenderComplete 48 | }); 49 | 50 | delete this._canvases[key]; 51 | }, 52 | 53 | getDebugMessages: function() { 54 | return this._debugMessages; 55 | }, 56 | 57 | createTile: function(tilePoint, done) { 58 | // create a element for drawing 59 | var tile = L.DomUtil.create('canvas', 'leaflet-tile'); 60 | // setup tile width and height according to the options 61 | var size = this.getTileSize(); 62 | tile.width = size.x; 63 | tile.height = size.y; 64 | 65 | tile._layer = this; 66 | tile.onerror = this._tileOnError; 67 | 68 | var tileUrl = this._getTileUrl(tilePoint); 69 | this.fire('tileloadstart', { 70 | tile: tile, 71 | url: tileUrl 72 | }); 73 | 74 | // Fall back to standard behaviour 75 | tile.onload = this._tileOnLoad; 76 | tile.src = tileUrl; 77 | this.drawTile(tile, tilePoint, tilePoint.z, done); 78 | 79 | return tile; 80 | }, 81 | 82 | _getTileUrl: function(tilePoint) { 83 | return this._url.replace('{x}',tilePoint.x). 84 | replace('{y}',tilePoint.y). 85 | replace('{z}',tilePoint.z); 86 | }, 87 | 88 | drawTile: function(canvas, tilePoint, zoom, done) { 89 | var zoomOffset = this.options.zoomOffset, 90 | rzoom = zoom - zoomOffset, 91 | key = [rzoom, tilePoint.x, tilePoint.y].join('/'), 92 | url=this._url.replace('{x}',tilePoint.x). 93 | replace('{y}',tilePoint.y). 94 | replace('{z}',rzoom); 95 | this._canvases[key] = canvas; 96 | this._loadJSON(url, rzoom, tilePoint.x, tilePoint.y, done); 97 | }, 98 | 99 | enableStyle: function(name) { 100 | if (MapCSS.availableStyles.indexOf(name) >= 0 && this.options.styles.indexOf(name) < 0) { 101 | this.options.styles.push(name); 102 | this.redraw(); 103 | } 104 | }, 105 | 106 | disableStyle: function(name) { 107 | if (this.options.styles.indexOf(name) >= 0) { 108 | var i = this.options.styles.indexOf(name); 109 | this.options.styles.splice(i, 1); 110 | this.redraw(); 111 | } 112 | }, 113 | 114 | redraw: function() { 115 | // TODO implement layer.redraw() in Leaflet 116 | if (this._map && this._map._container) { 117 | MapCSS.invalidateCache(); 118 | this._invalidateAll(); 119 | this._update(); 120 | } 121 | }, 122 | 123 | _invertYAxe: function(data) { 124 | var type, coordinates, tileSize = data.granularity, i, j, k, l, feature; 125 | for (i = 0; i < data.features.length; i++) { 126 | feature = data.features[i]; 127 | coordinates = feature.coordinates; 128 | type = data.features[i].type; 129 | 130 | if (type === 'Point') { 131 | coordinates[1] = tileSize - coordinates[1]; 132 | } else if (type === 'MultiPoint' || type === 'LineString') { 133 | for (j = 0; j < coordinates.length; j++) { 134 | coordinates[j][1] = tileSize - coordinates[j][1]; 135 | } 136 | } else if (type === 'MultiLineString' || type === 'Polygon') { 137 | for (k = 0; k < coordinates.length; k++) { 138 | for (j = 0; j < coordinates[k].length; j++) { 139 | coordinates[k][j][1] = tileSize - coordinates[k][j][1]; 140 | } 141 | } 142 | } else if (type === 'MultiPolygon') { 143 | for (l = 0; l < coordinates.length; l++) { 144 | for (k = 0; k < coordinates[l].length; k++) { 145 | for (j = 0; j < coordinates[l][k].length; j++) { 146 | coordinates[l][k][j][1] = tileSize - coordinates[l][k][j][1]; 147 | } 148 | } 149 | } 150 | } else { 151 | throw "Unexpected GeoJSON type: " + type; 152 | } 153 | 154 | if (feature.hasOwnProperty('reprpoint')) { 155 | feature.reprpoint[1] = tileSize - feature.reprpoint[1]; 156 | } 157 | } 158 | }, 159 | 160 | _loadJSON: function(url, zoom, x, y, done) { 161 | var xhr = new XMLHttpRequest(); 162 | xhr.onreadystatechange = function() { 163 | if (xhr.readyState === XMLHttpRequest.DONE) { 164 | if (xhr.status === 200) { 165 | window.onKothicDataResponse(JSON.parse(xhr.responseText), zoom, x, y, done); 166 | } else { 167 | console.debug("failed:", url, xhr.status); 168 | } 169 | } 170 | }; 171 | xhr.open("GET", url, true); 172 | xhr.send(null); 173 | } 174 | }); 175 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kothic-js", 3 | "version": "0.3.0", 4 | "devDependencies": { 5 | "grunt": "^1.0.4", 6 | "grunt-contrib-jshint": "^2.1.0", 7 | "grunt-contrib-concat": "^1.0.1", 8 | "grunt-contrib-uglify": "^4.0.1" 9 | }, 10 | "scripts": { 11 | "prepublish": "grunt" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/kothic.js: -------------------------------------------------------------------------------- 1 | /* 2 | (c) 2013 - 2019, Darafei Praliaskouski, Vladimir Agafonkin, Maksim Gurtovenko, Stephan Brandt 3 | Kothic JS is a full-featured JavaScript map rendering engine using HTML5 Canvas. 4 | http://github.com/kothic/kothic-js 5 | */ 6 | 7 | var Kothic = { 8 | 9 | render: function (canvas, data, zoom, options) { 10 | 11 | if(!MapCSS.imagesLoaded) { 12 | MapCSS.renderQueue.push(Kothic.render.bind(this, canvas, data, zoom, options)); 13 | return; 14 | } 15 | 16 | if (typeof canvas === 'string') { 17 | canvas = document.getElementById(canvas); 18 | } 19 | 20 | var styles = (options && options.styles) || []; 21 | 22 | MapCSS.locales = (options && options.locales) || []; 23 | 24 | var devicePixelRatio = Math.max(window.devicePixelRatio || 1, 2); 25 | 26 | var width = canvas.width, 27 | height = canvas.height; 28 | 29 | if (devicePixelRatio !== 1) { 30 | canvas.style.width = width + 'px'; 31 | canvas.style.height = height + 'px'; 32 | canvas.width = canvas.width * devicePixelRatio; 33 | canvas.height = canvas.height * devicePixelRatio; 34 | } 35 | 36 | var ctx = canvas.getContext('2d'); 37 | ctx.scale(devicePixelRatio, devicePixelRatio); 38 | 39 | var granularity = data.granularity, 40 | ws = width / granularity, hs = height / granularity, 41 | collisionBuffer = new Kothic.CollisionBuffer(height, width); 42 | 43 | console.time('styles'); 44 | 45 | // setup layer styles 46 | var layers = Kothic.style.populateLayers(data.features, zoom, styles), 47 | layerIds = Kothic.getLayerIds(layers); 48 | 49 | // render the map 50 | Kothic.style.setStyles(ctx, Kothic.style.defaultCanvasStyles); 51 | 52 | console.timeEnd('styles'); 53 | 54 | Kothic.getFrame(function () { 55 | console.time('geometry'); 56 | 57 | Kothic._renderBackground(ctx, width, height, zoom, styles); 58 | Kothic._renderGeometryFeatures(layerIds, layers, ctx, ws, hs, granularity); 59 | 60 | if (options && options.onRenderComplete) { 61 | options.onRenderComplete(); 62 | } 63 | 64 | console.timeEnd('geometry'); 65 | 66 | Kothic.getFrame(function () { 67 | console.time('text/icons'); 68 | Kothic._renderTextAndIcons(layerIds, layers, ctx, ws, hs, collisionBuffer); 69 | console.timeEnd('text/icons'); 70 | 71 | //Kothic._renderCollisions(ctx, collisionBuffer.buffer.data); 72 | }); 73 | }); 74 | }, 75 | 76 | _renderCollisions: function (ctx, node) { 77 | var i, len, a; 78 | if (node.leaf) { 79 | for (i = 0, len = node.children.length; i < len; i++) { 80 | ctx.strokeStyle = 'red'; 81 | ctx.lineWidth = 1; 82 | a = node.children[i]; 83 | ctx.strokeRect(Math.round(a[0]), Math.round(a[1]), Math.round(a[2] - a[0]), Math.round(a[3] - a[1])); 84 | } 85 | } else { 86 | for (i = 0, len = node.children.length; i < len; i++) { 87 | this._renderCollisions(ctx, node.children[i]); 88 | } 89 | } 90 | }, 91 | 92 | getLayerIds: function (layers) { 93 | return Object.keys(layers).sort(function (a, b) { 94 | return parseInt(a, 10) - parseInt(b, 10); 95 | }); 96 | }, 97 | 98 | getFrame: function (fn) { 99 | var reqFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || 100 | window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; 101 | 102 | reqFrame.call(window, fn); 103 | }, 104 | 105 | _renderBackground: function (ctx, width, height, zoom, styles) { 106 | var style = MapCSS.restyle(styles, {}, {}, zoom, 'canvas', 'canvas'); 107 | 108 | var fillRect = function () { 109 | ctx.fillRect(-1, -1, width + 1, height + 1); 110 | }; 111 | 112 | for (var i in style) { 113 | Kothic.polygon.fill(ctx, style[i], fillRect); 114 | } 115 | }, 116 | 117 | _renderGeometryFeatures: function (layerIds, layers, ctx, ws, hs, granularity) { 118 | var layersToRender = {}, 119 | i, j, len, features, style, queue, bgQueue; 120 | 121 | for (i = 0; i < layerIds.length; i++) { 122 | features = layers[layerIds[i]]; 123 | 124 | bgQueue = layersToRender._bg = layersToRender._bg || {}; 125 | queue = layersToRender[layerIds[i]] = layersToRender[layerIds[i]] || {}; 126 | 127 | for (j = 0, len = features.length; j < len; j++) { 128 | style = features[j].style; 129 | 130 | if ('fill-color' in style || 'fill-image' in style) { 131 | if (style['fill-position'] === 'background') { 132 | bgQueue.polygons = bgQueue.polygons || []; 133 | bgQueue.polygons.push(features[j]); 134 | } else { 135 | queue.polygons = queue.polygons || []; 136 | queue.polygons.push(features[j]); 137 | } 138 | } 139 | 140 | if ('casing-width' in style) { 141 | queue.casings = queue.casings || []; 142 | queue.casings.push(features[j]); 143 | } 144 | 145 | if ('width' in style) { 146 | queue.lines = queue.lines || []; 147 | queue.lines.push(features[j]); 148 | } 149 | } 150 | } 151 | 152 | layerIds = ['_bg'].concat(layerIds); 153 | 154 | for (i = 0; i < layerIds.length; i++) { 155 | queue = layersToRender[layerIds[i]]; 156 | if (!queue) 157 | continue; 158 | 159 | if (queue.polygons) { 160 | for (j = 0, len = queue.polygons.length; j < len; j++) { 161 | Kothic.polygon.render(ctx, queue.polygons[j], queue.polygons[j + 1], ws, hs, granularity); 162 | } 163 | } 164 | if (queue.casings) { 165 | ctx.lineCap = 'butt'; 166 | for (j = 0, len = queue.casings.length; j < len; j++) { 167 | Kothic.line.renderCasing(ctx, queue.casings[j], queue.casings[j + 1], ws, hs, granularity); 168 | } 169 | } 170 | if (queue.lines) { 171 | ctx.lineCap = 'round'; 172 | for (j = 0, len = queue.lines.length; j < len; j++) { 173 | Kothic.line.render(ctx, queue.lines[j], queue.lines[j + 1], ws, hs, granularity); 174 | } 175 | } 176 | } 177 | }, 178 | 179 | _renderTextAndIcons: function (layerIds, layers, ctx, ws, hs, collisionBuffer) { 180 | //TODO: Move to the features detector 181 | var j, style, i, 182 | passes = []; 183 | 184 | for (i = 0; i < layerIds.length; i++) { 185 | var features = layers[layerIds[i]], 186 | featuresLen = features.length; 187 | 188 | // render icons without text 189 | for (j = featuresLen - 1; j >= 0; j--) { 190 | style = features[j].style; 191 | if (style.hasOwnProperty('icon-image') && !style.text) { 192 | Kothic.texticons.render(ctx, features[j], collisionBuffer, ws, hs, false, true); 193 | } 194 | } 195 | 196 | // render text on features without icons 197 | for (j = featuresLen - 1; j >= 0; j--) { 198 | style = features[j].style; 199 | if (!style.hasOwnProperty('icon-image') && style.text) { 200 | Kothic.texticons.render(ctx, features[j], collisionBuffer, ws, hs, true, false); 201 | } 202 | } 203 | 204 | // for features with both icon and text, render both or neither 205 | for (j = featuresLen - 1; j >= 0; j--) { 206 | style = features[j].style; 207 | if (style.hasOwnProperty('icon-image') && style.text) { 208 | Kothic.texticons.render(ctx, features[j], collisionBuffer, ws, hs, true, true); 209 | } 210 | } 211 | 212 | // render shields with text 213 | for (j = featuresLen - 1; j >= 0; j--) { 214 | style = features[j].style; 215 | if (style['shield-text']) { 216 | Kothic.shields.render(ctx, features[j], collisionBuffer, ws, hs); 217 | } 218 | } 219 | } 220 | 221 | return passes; 222 | } 223 | }; 224 | -------------------------------------------------------------------------------- /src/renderer/line.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.line = { 3 | 4 | renderCasing: function (ctx, feature, nextFeature, ws, hs, granularity) { 5 | var style = feature.style, 6 | nextStyle = nextFeature && nextFeature.style; 7 | 8 | if (!this.pathOpened) { 9 | this.pathOpened = true; 10 | ctx.beginPath(); 11 | } 12 | 13 | Kothic.path(ctx, feature, style["casing-dashes"] || style.dashes, false, ws, hs, granularity); 14 | 15 | if (nextFeature && 16 | nextStyle.width === style.width && 17 | nextStyle['casing-width'] === style['casing-width'] && 18 | nextStyle['casing-color'] === style['casing-color'] && 19 | nextStyle['casing-dashes'] === style['casing-dashes'] && 20 | nextStyle['casing-opacity'] === style['casing-opacity']) { 21 | return; 22 | } 23 | 24 | Kothic.style.setStyles(ctx, { 25 | lineWidth: 2 * style["casing-width"] + (style.hasOwnProperty("width") ? style.width : 0), 26 | strokeStyle: style["casing-color"] || "#000000", 27 | lineCap: style["casing-linecap"] || style.linecap || "butt", 28 | lineJoin: style["casing-linejoin"] || style.linejoin || "round", 29 | globalAlpha: style["casing-opacity"] || 1 30 | }); 31 | 32 | ctx.stroke(); 33 | this.pathOpened = false; 34 | }, 35 | 36 | render: function (ctx, feature, nextFeature, ws, hs, granularity) { 37 | var style = feature.style, 38 | nextStyle = nextFeature && nextFeature.style; 39 | 40 | if (!this.pathOpened) { 41 | this.pathOpened = true; 42 | ctx.beginPath(); 43 | } 44 | 45 | Kothic.path(ctx, feature, style.dashes, false, ws, hs, granularity); 46 | 47 | if (nextFeature && 48 | nextStyle.width === style.width && 49 | nextStyle.color === style.color && 50 | nextStyle.image === style.image && 51 | nextStyle.opacity === style.opacity) { 52 | return; 53 | } 54 | 55 | if ('color' in style || !('image' in style)) { 56 | var t_width = style.width || 1, 57 | t_linejoin = "round", 58 | t_linecap = "round"; 59 | 60 | if (t_width <= 2) { 61 | t_linejoin = "miter"; 62 | t_linecap = "butt"; 63 | } 64 | Kothic.style.setStyles(ctx, { 65 | lineWidth: t_width, 66 | strokeStyle: style.color || '#000000', 67 | lineCap: style.linecap || t_linecap, 68 | lineJoin: style.linejoin || t_linejoin, 69 | globalAlpha: style.opacity || 1, 70 | miterLimit: 4 71 | }); 72 | ctx.stroke(); 73 | } 74 | 75 | 76 | if ('image' in style) { 77 | // second pass fills with texture 78 | var image = MapCSS.getImage(style.image); 79 | 80 | if (image) { 81 | Kothic.style.setStyles(ctx, { 82 | strokeStyle: ctx.createPattern(image, 'repeat') || "#000000", 83 | lineWidth: style.width || 1, 84 | lineCap: style.linecap || "round", 85 | lineJoin: style.linejoin || "round", 86 | globalAlpha: style.opacity || 1 87 | }); 88 | 89 | ctx.stroke(); 90 | } 91 | } 92 | this.pathOpened = false; 93 | }, 94 | 95 | pathOpened: false 96 | }; 97 | -------------------------------------------------------------------------------- /src/renderer/path.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.path = (function () { 3 | // check if the point is on the tile boundary 4 | // returns bitmask of affected tile boundaries 5 | function isTileBoundary(p, size) { 6 | var r = 0; 7 | if (p[0] === 0) 8 | r |= 1; 9 | else if (p[0] === size) 10 | r |= 2; 11 | if (p[1] === 0) 12 | r |= 4; 13 | else if (p[1] === size) 14 | r |= 8; 15 | return r; 16 | } 17 | 18 | /* check if 2 points are both on the same tile boundary 19 | * 20 | * If points of the object are on the same tile boundary it is assumed 21 | * that the object is cut here and would originally continue beyond the 22 | * tile borders. 23 | * 24 | * This does not catch the case where the object is indeed exactly 25 | * on the tile boundaries, but this case can't properly be detected here. 26 | */ 27 | function checkSameBoundary(p, q, size) { 28 | var bp = isTileBoundary(p, size); 29 | if (!bp) 30 | return 0; 31 | 32 | return (bp & isTileBoundary(q, size)); 33 | } 34 | 35 | return function (ctx, feature, dashes, fill, ws, hs, granularity) { 36 | var type = feature.type, 37 | coords = feature.coordinates; 38 | 39 | if (type === "Polygon") { 40 | coords = [coords]; 41 | type = "MultiPolygon"; 42 | } else if (type === "LineString") { 43 | coords = [coords]; 44 | type = "MultiLineString"; 45 | } 46 | 47 | var i, j, k, 48 | points, 49 | len = coords.length, 50 | len2, pointsLen, 51 | prevPoint, point, screenPoint, 52 | dx, dy, dist; 53 | 54 | if (type === "MultiPolygon") { 55 | for (i = 0; i < len; i++) { 56 | for (k = 0, len2 = coords[i].length; k < len2; k++) { 57 | points = coords[i][k]; 58 | pointsLen = points.length; 59 | prevPoint = points[0]; 60 | 61 | for (j = 0; j <= pointsLen; j++) { 62 | point = points[j] || points[0]; 63 | screenPoint = Kothic.geom.transformPoint(point, ws, hs); 64 | 65 | if (j === 0) { 66 | ctx.moveTo(screenPoint[0], screenPoint[1]); 67 | if (dashes) 68 | ctx.setLineDash(dashes); 69 | else 70 | ctx.setLineDash([]); 71 | } else if (!fill && checkSameBoundary(point, prevPoint, granularity)) { 72 | ctx.moveTo(screenPoint[0], screenPoint[1]); 73 | } else { 74 | ctx.lineTo(screenPoint[0], screenPoint[1]); 75 | } 76 | prevPoint = point; 77 | } 78 | } 79 | } 80 | } else if (type === "MultiLineString") { 81 | var pad = 50, // how many pixels to draw out of the tile to avoid path edges when lines crosses tile borders 82 | skip = 2; // do not draw line segments shorter than this 83 | 84 | for (i = 0; i < len; i++) { 85 | points = coords[i]; 86 | pointsLen = points.length; 87 | 88 | for (j = 0; j < pointsLen; j++) { 89 | point = points[j]; 90 | 91 | // continue path off the tile by some amount to fix path edges between tiles 92 | if ((j === 0 || j === pointsLen - 1) && isTileBoundary(point, granularity)) { 93 | k = j; 94 | do { 95 | k = j ? k - 1 : k + 1; 96 | if (k < 0 || k >= pointsLen) 97 | break; 98 | prevPoint = points[k]; 99 | 100 | dx = point[0] - prevPoint[0]; 101 | dy = point[1] - prevPoint[1]; 102 | dist = Math.sqrt(dx * dx + dy * dy); 103 | } while (dist <= skip); 104 | 105 | // all points are so close to each other that it doesn't make sense to 106 | // draw the line beyond the tile border, simply skip the entire line from 107 | // here 108 | if (k < 0 || k >= pointsLen) 109 | break; 110 | 111 | point[0] = point[0] + pad * dx / dist; 112 | point[1] = point[1] + pad * dy / dist; 113 | } 114 | screenPoint = Kothic.geom.transformPoint(point, ws, hs); 115 | 116 | if (j === 0) { 117 | ctx.moveTo(screenPoint[0], screenPoint[1]); 118 | if (dashes) 119 | ctx.setLineDash(dashes); 120 | else 121 | ctx.setLineDash([]); 122 | } else { 123 | ctx.lineTo(screenPoint[0], screenPoint[1]); 124 | } 125 | } 126 | } 127 | } 128 | }; 129 | }()); 130 | -------------------------------------------------------------------------------- /src/renderer/polygon.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.polygon = { 3 | render: function (ctx, feature, nextFeature, ws, hs, granularity) { 4 | var style = feature.style, 5 | nextStyle = nextFeature && nextFeature.style; 6 | 7 | if (!this.pathOpened) { 8 | this.pathOpened = true; 9 | ctx.beginPath(); 10 | } 11 | 12 | Kothic.path(ctx, feature, false, true, ws, hs, granularity); 13 | 14 | if (nextFeature && 15 | (nextStyle['fill-color'] === style['fill-color']) && 16 | (nextStyle['fill-image'] === style['fill-image']) && 17 | (nextStyle['fill-opacity'] === style['fill-opacity'])) { 18 | return; 19 | } 20 | 21 | this.fill(ctx, style); 22 | 23 | this.pathOpened = false; 24 | }, 25 | 26 | fill: function (ctx, style, fillFn) { 27 | var opacity = style["fill-opacity"] || style.opacity, image; 28 | 29 | if (style.hasOwnProperty('fill-color')) { 30 | // first pass fills with solid color 31 | Kothic.style.setStyles(ctx, { 32 | fillStyle: style["fill-color"] || "#000000", 33 | globalAlpha: opacity || 1 34 | }); 35 | if (fillFn) { 36 | fillFn(); 37 | } else { 38 | ctx.fill(); 39 | } 40 | } 41 | 42 | if (style.hasOwnProperty('fill-image')) { 43 | // second pass fills with texture 44 | image = MapCSS.getImage(style['fill-image']); 45 | if (image) { 46 | Kothic.style.setStyles(ctx, { 47 | fillStyle: ctx.createPattern(image, 'repeat'), 48 | globalAlpha: opacity || 1 49 | }); 50 | if (fillFn) { 51 | fillFn(); 52 | } else { 53 | ctx.fill(); 54 | } 55 | } 56 | } 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/renderer/shields.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.shields = { 3 | render: function (ctx, feature, collides, ws, hs) { 4 | var style = feature.style, reprPoint = Kothic.geom.getReprPoint(feature), 5 | point, img, len = 0, found = false, i, sgn; 6 | 7 | if (!reprPoint) { 8 | return; 9 | } 10 | 11 | point = Kothic.geom.transformPoint(reprPoint, ws, hs); 12 | 13 | if (style["shield-image"]) { 14 | img = MapCSS.getImage(style["icon-image"]); 15 | 16 | if (!img) { 17 | return; 18 | } 19 | } 20 | 21 | Kothic.style.setStyles(ctx, { 22 | font: Kothic.style.getFontString(style["shield-font-family"] || style["font-family"], style["shield-font-size"] || style["font-size"], style), 23 | fillStyle: style["shield-text-color"] || "#000000", 24 | globalAlpha: style["shield-text-opacity"] || style.opacity || 1, 25 | textAlign: 'center', 26 | textBaseline: 'middle' 27 | }); 28 | 29 | var text = String(style['shield-text']), 30 | textWidth = ctx.measureText(text).width, 31 | letterWidth = textWidth / text.length, 32 | collisionWidth = textWidth + 2, 33 | collisionHeight = letterWidth * 1.8; 34 | 35 | if (feature.type === 'LineString') { 36 | len = Kothic.geom.getPolyLength(feature.coordinates); 37 | 38 | if (Math.max(collisionHeight / hs, collisionWidth / ws) > len) { 39 | return; 40 | } 41 | 42 | for (i = 0, sgn = 1; i < len / 2; i += Math.max(len / 30, collisionHeight / ws), sgn *= -1) { 43 | reprPoint = Kothic.geom.getAngleAndCoordsAtLength(feature.coordinates, len / 2 + sgn * i, 0); 44 | if (!reprPoint) { 45 | break; 46 | } 47 | 48 | reprPoint = [reprPoint[1], reprPoint[2]]; 49 | 50 | point = Kothic.geom.transformPoint(reprPoint, ws, hs); 51 | if (img && (style["allow-overlap"] !== "true") && 52 | collides.checkPointWH(point, img.width, img.height, feature.kothicId)) { 53 | continue; 54 | } 55 | if ((style["allow-overlap"] !== "true") && 56 | collides.checkPointWH(point, collisionWidth, collisionHeight, feature.kothicId)) { 57 | continue; 58 | } 59 | found = true; 60 | break; 61 | } 62 | } 63 | 64 | if (!found) { 65 | return; 66 | } 67 | 68 | if (style["shield-casing-width"]) { 69 | Kothic.style.setStyles(ctx, { 70 | fillStyle: style["shield-casing-color"] || "#000000", 71 | globalAlpha: style["shield-casing-opacity"] || style.opacity || 1 72 | }); 73 | var p = style["shield-casing-width"] + (style["shield-frame-width"] || 0); 74 | ctx.fillRect(point[0] - collisionWidth / 2 - p, 75 | point[1] - collisionHeight / 2 - p, 76 | collisionWidth + 2 * p, 77 | collisionHeight + 2 * p); 78 | } 79 | 80 | if (style["shield-frame-width"]) { 81 | Kothic.style.setStyles(ctx, { 82 | fillStyle: style["shield-frame-color"] || "#000000", 83 | globalAlpha: style["shield-frame-opacity"] || style.opacity || 1 84 | }); 85 | ctx.fillRect(point[0] - collisionWidth / 2 - style["shield-frame-width"], 86 | point[1] - collisionHeight / 2 - style["shield-frame-width"], 87 | collisionWidth + 2 * style["shield-frame-width"], 88 | collisionHeight + 2 * style["shield-frame-width"]); 89 | } 90 | 91 | if (style["shield-color"]) { 92 | Kothic.style.setStyles(ctx, { 93 | fillStyle: style["shield-color"] || "#000000", 94 | globalAlpha: style["shield-opacity"] || style.opacity || 1 95 | }); 96 | ctx.fillRect(point[0] - collisionWidth / 2, 97 | point[1] - collisionHeight / 2, 98 | collisionWidth, 99 | collisionHeight); 100 | } 101 | 102 | if (img) { 103 | ctx.drawImage(img, 104 | Math.floor(point[0] - img.width / 2), 105 | Math.floor(point[1] - img.height / 2)); 106 | } 107 | Kothic.style.setStyles(ctx, { 108 | fillStyle: style["shield-text-color"] || "#000000", 109 | globalAlpha: style["shield-text-opacity"] || style.opacity || 1 110 | }); 111 | 112 | ctx.fillText(text, point[0], Math.ceil(point[1])); 113 | if (img) { 114 | collides.addPointWH(point, img.width, img.height, 0, feature.kothicId); 115 | } 116 | 117 | collides.addPointWH(point, collisionHeight, collisionWidth, 118 | (parseFloat(style["shield-casing-width"]) || 0) + (parseFloat(style["shield-frame-width"]) || 0) + (parseFloat(style["-x-mapnik-min-distance"]) || 30), feature.kothicId); 119 | 120 | } 121 | }; 122 | -------------------------------------------------------------------------------- /src/renderer/text.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.textOnPath = (function () { 3 | 4 | function getWidth(ctx, text) { 5 | return ctx.measureText(text).width; 6 | } 7 | 8 | function getTextCenter(axy, textWidth) { 9 | return [axy[1] + 0.5 * Math.cos(axy[0]) * textWidth, 10 | axy[2] + 0.5 * Math.sin(axy[0]) * textWidth]; 11 | } 12 | 13 | function getCollisionParams(textWidth, axy, pxoffset) { 14 | var textHeight = textWidth * 1.5, 15 | cos = Math.abs(Math.cos(axy[0])), 16 | sin = Math.abs(Math.sin(axy[0])), 17 | w = cos * textWidth + sin * textHeight, 18 | h = sin * textWidth + cos * textHeight; 19 | 20 | return [getTextCenter(axy, textWidth + 2 * (pxoffset || 0)), w, h, 0]; 21 | } 22 | 23 | function checkCollision(collisions, ctx, text, axy, letterWidth) { 24 | var textWidth = getWidth(ctx, text); 25 | 26 | for (var i = 0; i < textWidth; i += letterWidth) { 27 | if (collisions.checkPointWH.apply(collisions, getCollisionParams(letterWidth, axy, i))) { 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | 34 | function addCollision(collisions, ctx, text, axy, letterWidth) { 35 | var textWidth = getWidth(ctx, text), 36 | params = []; 37 | 38 | for (var i = 0; i < textWidth; i += letterWidth) { 39 | params.push(getCollisionParams(letterWidth, axy, i)); 40 | } 41 | collisions.addPoints(params); 42 | } 43 | 44 | function renderText(ctx, axy, halo) { 45 | var text = axy[4], 46 | textCenter = getTextCenter(axy, getWidth(ctx, text)); 47 | 48 | ctx.translate(textCenter[0], textCenter[1]); 49 | ctx.rotate(axy[0]); 50 | ctx[halo ? 'strokeText' : 'fillText'](text, 0, 0); 51 | ctx.rotate(-axy[0]); 52 | ctx.translate(-textCenter[0], -textCenter[1]); 53 | } 54 | 55 | return function (ctx, points, text, halo, collisions) { 56 | //widthCache = {}; 57 | 58 | // simplify points? 59 | 60 | var textWidth = ctx.measureText(text).width, 61 | textLen = text.length, 62 | pathLen = Kothic.geom.getPolyLength(points); 63 | 64 | if (pathLen < textWidth) { 65 | return; // if label won't fit - don't try to 66 | } 67 | 68 | var avgLetterWidth = getWidth(ctx, 'a'); 69 | 70 | var letter, 71 | widthUsed, 72 | prevAngle, 73 | positions, 74 | solution = 0, 75 | flipCount, 76 | flipped = false, 77 | axy, 78 | letterWidth, 79 | i, 80 | maxAngle = Math.PI / 6; 81 | 82 | // iterating solutions - start from center or from one of the ends 83 | while (solution < 2) { //TODO change to for? 84 | widthUsed = solution ? getWidth(ctx, text.charAt(0)) : (pathLen - textWidth) / 2; // ??? 85 | flipCount = 0; 86 | prevAngle = null; 87 | positions = []; 88 | 89 | // iterating label letter by letter (should be fixed to support ligatures/CJK, ok for Cyrillic/latin) 90 | for (i = 0; i < textLen; i++) { 91 | letter = text.charAt(i); 92 | letterWidth = getWidth(ctx, letter); 93 | axy = Kothic.geom.getAngleAndCoordsAtLength(points, widthUsed, letterWidth); 94 | 95 | // if cannot fit letter - restart with next solution 96 | if (widthUsed >= pathLen || !axy) { 97 | solution++; 98 | positions = []; 99 | if (flipped) { // if label was flipped, flip it back 100 | points.reverse(); 101 | flipped = false; 102 | } 103 | break; 104 | } 105 | 106 | if (!prevAngle) { 107 | prevAngle = axy[0]; 108 | } 109 | 110 | // if label collisions with another, restart it from here 111 | if (checkCollision(collisions, ctx, letter, axy, avgLetterWidth) || Math.abs(prevAngle - axy[0]) > maxAngle) { 112 | widthUsed += letterWidth; 113 | i = -1; 114 | positions = []; 115 | flipCount = 0; 116 | continue; 117 | } 118 | 119 | while (letterWidth < axy[3] && i < textLen) { // try adding following letters to current, until line changes its direction 120 | i++; 121 | letter += text.charAt(i); 122 | letterWidth = getWidth(ctx, letter); 123 | if (checkCollision(collisions, ctx, letter, axy, avgLetterWidth)) { 124 | i = 0; 125 | widthUsed += letterWidth; 126 | positions = []; 127 | flipCount = 0; 128 | letter = text.charAt(i); 129 | letterWidth = getWidth(ctx, letter); 130 | axy = Kothic.geom.getAngleAndCoordsAtLength(points, widthUsed, letterWidth); 131 | break; 132 | } 133 | if (letterWidth >= axy[3]) { 134 | i--; 135 | letter = letter.slice(0, -1); 136 | letterWidth = getWidth(ctx, letter); 137 | break; 138 | } 139 | } 140 | 141 | if (!axy) { 142 | continue; 143 | } 144 | 145 | if ((axy[0] > (Math.PI / 2)) || (axy[0] < (-Math.PI / 2))) { // if current letters cluster was upside-down, count it 146 | flipCount += letter.length; 147 | } 148 | 149 | prevAngle = axy[0]; 150 | axy.push(letter); 151 | positions.push(axy); 152 | widthUsed += letterWidth; 153 | } //for 154 | 155 | if (flipCount > textLen / 2) { // if more than half of the text is upside down, flip it and restart 156 | points.reverse(); 157 | positions = []; 158 | 159 | if (flipped) { // if it was flipped twice - restart with other start point solution 160 | solution++; 161 | points.reverse(); 162 | flipped = false; 163 | } else { 164 | flipped = true; 165 | } 166 | } 167 | 168 | if (solution >= 2) { 169 | return; 170 | } 171 | 172 | if (positions.length > 0) { 173 | break; 174 | } 175 | } //while 176 | 177 | var posLen = positions.length; 178 | 179 | for (i = 0; halo && (i < posLen); i++) { 180 | renderText(ctx, positions[i], true); 181 | } 182 | 183 | for (i = 0; i < posLen; i++) { 184 | axy = positions[i]; 185 | renderText(ctx, axy); 186 | addCollision(collisions, ctx, axy[4], axy, avgLetterWidth); 187 | } 188 | }; 189 | }()); 190 | -------------------------------------------------------------------------------- /src/renderer/texticons.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.texticons = { 3 | 4 | render: function (ctx, feature, collides, ws, hs, renderText, renderIcon) { 5 | var style = feature.style, img, point, w, h; 6 | 7 | if (renderIcon || (renderText && feature.type !== 'LineString')) { 8 | var reprPoint = Kothic.geom.getReprPoint(feature); 9 | if (!reprPoint) { 10 | return; 11 | } 12 | point = Kothic.geom.transformPoint(reprPoint, ws, hs); 13 | } 14 | 15 | if (renderIcon) { 16 | img = MapCSS.getImage(style['icon-image']); 17 | if (!img) { return; } 18 | 19 | w = img.width; 20 | h = img.height; 21 | 22 | if (style['icon-width'] || style['icon-height']){ 23 | if (style['icon-width']) { 24 | w = style['icon-width']; 25 | h = img.height * w / img.width; 26 | } 27 | if (style['icon-height']) { 28 | h = style['icon-height']; 29 | if (!style['icon-width']) { 30 | w = img.width * h / img.height; 31 | } 32 | } 33 | } 34 | if ((style['allow-overlap'] !== 'true') && 35 | collides.checkPointWH(point, w, h, feature.kothicId)) { 36 | return; 37 | } 38 | } 39 | 40 | var text = String(style.text).trim(); 41 | 42 | if (renderText && text) { 43 | Kothic.style.setStyles(ctx, { 44 | lineWidth: style['text-halo-radius'] * 2, 45 | font: Kothic.style.getFontString(style['font-family'], style['font-size'], style) 46 | }); 47 | 48 | var halo = (style.hasOwnProperty('text-halo-radius')); 49 | 50 | Kothic.style.setStyles(ctx, { 51 | fillStyle: style['text-color'] || '#000000', 52 | strokeStyle: style['text-halo-color'] || '#ffffff', 53 | globalAlpha: style['text-opacity'] || style.opacity || 1, 54 | textAlign: 'center', 55 | textBaseline: 'middle' 56 | }); 57 | 58 | if (style['text-transform'] === 'uppercase') 59 | text = text.toUpperCase(); 60 | else if (style['text-transform'] === 'lowercase') 61 | text = text.toLowerCase(); 62 | else if (style['text-transform'] === 'capitalize') 63 | text = text.replace(/(^|\s)\S/g, function(ch) { return ch.toUpperCase(); }); 64 | 65 | if (feature.type === 'Polygon' || feature.type === 'Point') { 66 | var textWidth = ctx.measureText(text).width, 67 | letterWidth = textWidth / text.length, 68 | collisionWidth = textWidth, 69 | collisionHeight = letterWidth * 2.5, 70 | offset = style['text-offset'] || 0; 71 | 72 | if ((style['text-allow-overlap'] !== 'true') && 73 | collides.checkPointWH([point[0], point[1] + offset], collisionWidth, collisionHeight, feature.kothicId)) { 74 | return; 75 | } 76 | 77 | if (halo) { 78 | ctx.strokeText(text, point[0], point[1] + offset); 79 | } 80 | ctx.fillText(text, point[0], point[1] + offset); 81 | 82 | var padding = style['-x-kot-min-distance'] || 20; 83 | collides.addPointWH([point[0], point[1] + offset], collisionWidth, collisionHeight, padding, feature.kothicId); 84 | 85 | } else if (feature.type === 'LineString') { 86 | 87 | var points = Kothic.geom.transformPoints(feature.coordinates, ws, hs); 88 | Kothic.textOnPath(ctx, points, text, halo, collides); 89 | } 90 | } 91 | 92 | if (renderIcon) { 93 | ctx.drawImage(img, 94 | Math.floor(point[0] - w / 2), 95 | Math.floor(point[1] - h / 2), w, h); 96 | 97 | var padding2 = parseFloat(style['-x-kot-min-distance']) || 0; 98 | collides.addPointWH(point, w, h, padding2, feature.kothicId); 99 | } 100 | } 101 | }; 102 | -------------------------------------------------------------------------------- /src/style/mapcss.js: -------------------------------------------------------------------------------- 1 | 2 | var MapCSS = { 3 | styles: {}, 4 | availableStyles: [], 5 | images: {}, 6 | locales: [], 7 | presence_tags: [], 8 | value_tags: [], 9 | cache: {}, 10 | debug: {hit: 0, miss: 0}, 11 | renderQueue: [], 12 | imagesLoaded: false, 13 | 14 | onError: function () { 15 | }, 16 | 17 | onImagesLoad: function () { 18 | MapCSS.imagesLoaded = true; 19 | MapCSS.renderQueue.forEach(function(renderCall) { 20 | renderCall(); 21 | }); 22 | MapCSS.renderQueue = []; 23 | }, 24 | 25 | /** 26 | * Incalidate styles cache 27 | */ 28 | invalidateCache: function () { 29 | this.cache = {}; 30 | }, 31 | 32 | e_min: function (/*...*/) { 33 | return Math.min.apply(null, arguments); 34 | }, 35 | 36 | e_max: function (/*...*/) { 37 | return Math.max.apply(null, arguments); 38 | }, 39 | 40 | e_any: function (/*...*/) { 41 | var i; 42 | 43 | for (i = 0; i < arguments.length; i++) { 44 | if (typeof(arguments[i]) !== 'undefined' && arguments[i] !== '') { 45 | return arguments[i]; 46 | } 47 | } 48 | 49 | return ''; 50 | }, 51 | 52 | e_num: function (arg) { 53 | if (!isNaN(parseFloat(arg))) { 54 | return parseFloat(arg); 55 | } else { 56 | return ''; 57 | } 58 | }, 59 | 60 | e_str: function (arg) { 61 | return arg; 62 | }, 63 | 64 | e_int: function (arg) { 65 | return parseInt(arg, 10); 66 | }, 67 | 68 | e_tag: function (obj, tag) { 69 | if (obj.hasOwnProperty(tag) && obj[tag] !== null) { 70 | return tag; 71 | } else { 72 | return ''; 73 | } 74 | }, 75 | 76 | e_prop: function (obj, tag) { 77 | if (obj.hasOwnProperty(tag) && obj[tag] !== null) { 78 | return obj[tag]; 79 | } else { 80 | return ''; 81 | } 82 | }, 83 | 84 | e_sqrt: function (arg) { 85 | return Math.sqrt(arg); 86 | }, 87 | 88 | e_boolean: function (arg, if_exp, else_exp) { 89 | if (typeof(if_exp) === 'undefined') { 90 | if_exp = 'true'; 91 | } 92 | 93 | if (typeof(else_exp) === 'undefined') { 94 | else_exp = 'false'; 95 | } 96 | 97 | if (arg === '0' || arg === 'false' || arg === '') { 98 | return else_exp; 99 | } else { 100 | return if_exp; 101 | } 102 | }, 103 | 104 | e_metric: function (arg) { 105 | if (/\d\s*mm$/.test(arg)) { 106 | return 1000 * parseInt(arg, 10); 107 | } else if (/\d\s*cm$/.test(arg)) { 108 | return 100 * parseInt(arg, 10); 109 | } else if (/\d\s*dm$/.test(arg)) { 110 | return 10 * parseInt(arg, 10); 111 | } else if (/\d\s*km$/.test(arg)) { 112 | return 0.001 * parseInt(arg, 10); 113 | } else if (/\d\s*in$/.test(arg)) { 114 | return 0.0254 * parseInt(arg, 10); 115 | } else if (/\d\s*ft$/.test(arg)) { 116 | return 0.3048 * parseInt(arg, 10); 117 | } else { 118 | return parseInt(arg, 10); 119 | } 120 | }, 121 | 122 | e_zmetric: function (arg) { 123 | return MapCSS.e_metric(arg); 124 | }, 125 | 126 | e_localize: function (tags, text) { 127 | var locales = MapCSS.locales, i, tag; 128 | 129 | for (i = 0; i < locales.length; i++) { 130 | tag = text + ':' + locales[i]; 131 | if (tags[tag]) { 132 | return tags[tag]; 133 | } 134 | } 135 | 136 | return tags[text]; 137 | }, 138 | 139 | e_join: function () { 140 | if (arguments.length === 2 && Object.prototype.toString.call(arguments[1]) === '[object Array]') { 141 | return arguments[1].join(arguments[0]); 142 | } else { 143 | var tagString = ""; 144 | 145 | for (var i = 1; i < arguments.length; i++) 146 | tagString = tagString.concat(arguments[0]).concat(arguments[i]); 147 | 148 | return tagString.substr(arguments[0].length); 149 | } 150 | }, 151 | 152 | e_split: function (sep, text) { 153 | return text.split(sep); 154 | }, 155 | 156 | e_get: function(arr, index) { 157 | if (Object.prototype.toString.call(arr) !== '[object Array]') 158 | return ""; 159 | 160 | if (!/^[0-9]+$/.test(index) || index >= arr.length()) 161 | return ""; 162 | 163 | return arr[index]; 164 | }, 165 | 166 | e_set: function(arr, index, text) { 167 | if (Object.prototype.toString.call(arr) !== '[object Array]') 168 | return []; 169 | 170 | if (!/^[0-9]+$/.test(index)) 171 | return []; 172 | 173 | arr[index] = text; 174 | 175 | return arr; 176 | }, 177 | 178 | e_count: function(arr) { 179 | if (Object.prototype.toString.call(arr) !== '[object Array]') 180 | return 0; 181 | 182 | return arr.length(); 183 | }, 184 | 185 | e_list: function() { 186 | return arguments; 187 | }, 188 | 189 | e_append: function(lst, v) { 190 | if (Object.prototype.toString.call(lst) !== '[object Array]') 191 | return []; 192 | 193 | lst.push(v); 194 | 195 | return lst; 196 | }, 197 | 198 | e_contains: function(lst, v) { 199 | if (Object.prototype.toString.call(lst) !== '[object Array]') 200 | return false; 201 | 202 | return (lst.indexOf(v) >= 0); 203 | }, 204 | 205 | e_sort: function(lst) { 206 | if (Object.prototype.toString.call(lst) !== '[object Array]') 207 | return []; 208 | 209 | lst.sort(); 210 | 211 | return lst; 212 | }, 213 | 214 | e_reverse: function(lst) { 215 | if (Object.prototype.toString.call(lst) !== '[object Array]') 216 | return []; 217 | 218 | lst.reverse(); 219 | 220 | return lst; 221 | }, 222 | 223 | loadStyle: function (style, restyle, sprite_images, external_images, presence_tags, value_tags) { 224 | var i; 225 | sprite_images = sprite_images || {}; 226 | external_images = external_images || []; 227 | 228 | if (presence_tags) { 229 | for (i = 0; i < presence_tags.length; i++) { 230 | if (this.presence_tags.indexOf(presence_tags[i]) < 0) { 231 | this.presence_tags.push(presence_tags[i]); 232 | } 233 | } 234 | } 235 | 236 | if (value_tags) { 237 | for (i = 0; i < value_tags.length; i++) { 238 | if (this.value_tags.indexOf(value_tags[i]) < 0) { 239 | this.value_tags.push(value_tags[i]); 240 | } 241 | } 242 | } 243 | 244 | MapCSS.styles[style] = { 245 | restyle: restyle, 246 | images: sprite_images, 247 | external_images: external_images, 248 | textures: {}, 249 | sprite_loaded: Object.keys(sprite_images).length === 0, 250 | external_images_loaded: !external_images.length 251 | }; 252 | 253 | MapCSS.availableStyles.push(style); 254 | }, 255 | 256 | /** 257 | * Call MapCSS.onImagesLoad callback if all sprite and external 258 | * images was loaded 259 | */ 260 | _onImagesLoad: function (style) { 261 | if (MapCSS.styles[style].external_images_loaded && 262 | MapCSS.styles[style].sprite_loaded) { 263 | MapCSS.onImagesLoad(); 264 | } 265 | }, 266 | 267 | preloadSpriteImage: function (style, url) { 268 | var images = MapCSS.styles[style].images, 269 | img = new Image(); 270 | 271 | delete MapCSS.styles[style].images; 272 | 273 | img.onload = function () { 274 | var image; 275 | for (image in images) { 276 | if (images.hasOwnProperty(image)) { 277 | images[image].sprite = img; 278 | MapCSS.images[image] = images[image]; 279 | } 280 | } 281 | MapCSS.styles[style].sprite_loaded = true; 282 | MapCSS._onImagesLoad(style); 283 | }; 284 | img.onerror = function (e) { 285 | MapCSS.onError(e); 286 | }; 287 | img.src = url; 288 | }, 289 | 290 | preloadExternalImages: function (style, urlPrefix) { 291 | var external_images = MapCSS.styles[style].external_images; 292 | delete MapCSS.styles[style].external_images; 293 | 294 | urlPrefix = urlPrefix || ''; 295 | var len = external_images.length, loaded = 0, i; 296 | 297 | function loadImage(url) { 298 | var img = new Image(); 299 | img.onload = function () { 300 | loaded++; 301 | MapCSS.images[url] = { 302 | sprite: img, 303 | height: img.height, 304 | width: img.width, 305 | offset: 0 306 | }; 307 | if (loaded === len) { 308 | MapCSS.styles[style].external_images_loaded = true; 309 | MapCSS._onImagesLoad(style); 310 | } 311 | }; 312 | img.onerror = function () { 313 | loaded++; 314 | if (loaded === len) { 315 | MapCSS.styles[style].external_images_loaded = true; 316 | MapCSS._onImagesLoad(style); 317 | } 318 | }; 319 | img.src = url; 320 | } 321 | 322 | for (i = 0; i < len; i++) { 323 | loadImage(urlPrefix + external_images[i]); 324 | } 325 | }, 326 | 327 | getImage: function (ref) { 328 | var img = MapCSS.images[ref]; 329 | 330 | if (img && img.sprite) { 331 | var canvas = document.createElement('canvas'); 332 | canvas.width = img.width; 333 | canvas.height = img.height; 334 | 335 | canvas.getContext('2d').drawImage(img.sprite, 336 | 0, img.offset, img.width, img.height, 337 | 0, 0, img.width, img.height); 338 | 339 | img = MapCSS.images[ref] = canvas; 340 | } 341 | 342 | return img; 343 | }, 344 | 345 | getTagKeys: function (tags, zoom, type, selector) { 346 | var keys = [], i; 347 | for (i = 0; i < this.presence_tags.length; i++) { 348 | if (tags.hasOwnProperty(this.presence_tags[i])) { 349 | keys.push(this.presence_tags[i]); 350 | } 351 | } 352 | 353 | for (i = 0; i < this.value_tags.length; i++) { 354 | if (tags.hasOwnProperty(this.value_tags[i])) { 355 | keys.push(this.value_tags[i] + ':' + tags[this.value_tags[i]]); 356 | } 357 | } 358 | 359 | return [zoom, type, selector, keys.join(':')].join(':'); 360 | }, 361 | 362 | restyle: function (styleNames, tags, zoom, type, selector) { 363 | var i, key = this.getTagKeys(tags, zoom, type, selector), actions = this.cache[key] || {}; 364 | 365 | if (!this.cache.hasOwnProperty(key)) { 366 | this.debug.miss += 1; 367 | for (i = 0; i < styleNames.length; i++) { 368 | actions = MapCSS.styles[styleNames[i]].restyle(actions, tags, zoom, type, selector); 369 | } 370 | this.cache[key] = actions; 371 | } else { 372 | this.debug.hit += 1; 373 | } 374 | 375 | return actions; 376 | } 377 | }; 378 | -------------------------------------------------------------------------------- /src/style/style.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.style = { 3 | 4 | defaultCanvasStyles: { 5 | strokeStyle: 'rgba(0,0,0,0.5)', 6 | fillStyle: 'rgba(0,0,0,0.5)', 7 | lineWidth: 1, 8 | lineCap: 'round', 9 | lineJoin: 'round', 10 | textAlign: 'center', 11 | textBaseline: 'middle' 12 | }, 13 | 14 | populateLayers: function (features, zoom, styles) { 15 | var layers = {}, 16 | i, len, feature, layerId, layerStyle; 17 | 18 | var styledFeatures = Kothic.style.styleFeatures(features, zoom, styles); 19 | 20 | for (i = 0, len = styledFeatures.length; i < len; i++) { 21 | feature = styledFeatures[i]; 22 | layerStyle = feature.style['-x-mapnik-layer']; 23 | layerId = !layerStyle ? feature.properties.layer || 0 : 24 | layerStyle === 'top' ? 10000 : -10000; 25 | 26 | layers[layerId] = layers[layerId] || []; 27 | layers[layerId].push(feature); 28 | } 29 | 30 | return layers; 31 | }, 32 | 33 | getStyle: function (feature, zoom, styleNames) { 34 | var shape = feature.type, 35 | type, selector; 36 | if (shape === 'LineString' || shape === 'MultiLineString') { 37 | type = 'way'; 38 | selector = 'line'; 39 | } else if (shape === 'Polygon' || shape === 'MultiPolygon') { 40 | type = 'way'; 41 | selector = 'area'; 42 | } else if (shape === 'Point' || shape === 'MultiPoint') { 43 | type = 'node'; 44 | selector = 'node'; 45 | } 46 | 47 | return MapCSS.restyle(styleNames, feature.properties !== null ? feature.properties : [], zoom, type, selector); 48 | }, 49 | 50 | styleFeatures: function (features, zoom, styleNames) { 51 | var styledFeatures = [], 52 | i, j, len, feature, style, restyledFeature, k; 53 | 54 | for (i = 0, len = features.length; i < len; i++) { 55 | feature = features[i]; 56 | style = this.getStyle(feature, zoom, styleNames); 57 | 58 | for (j in style) { 59 | if (j === 'default') { 60 | restyledFeature = feature; 61 | } else { 62 | restyledFeature = {}; 63 | for (k in feature) { 64 | restyledFeature[k] = feature[k]; 65 | } 66 | } 67 | 68 | restyledFeature.kothicId = i + 1; 69 | restyledFeature.style = style[j]; 70 | restyledFeature.zIndex = style[j]['z-index'] || 0; 71 | restyledFeature.sortKey = (style[j]['fill-color'] || '') + (style[j].color || ''); 72 | styledFeatures.push(restyledFeature); 73 | } 74 | } 75 | 76 | styledFeatures.sort(function (a, b) { 77 | return a.zIndex !== b.zIndex ? a.zIndex - b.zIndex : 78 | a.sortKey < b.sortKey ? -1 : 79 | a.sortKey > b.sortKey ? 1 : 0; 80 | }); 81 | 82 | return styledFeatures; 83 | }, 84 | 85 | getFontString: function (name, size, st) { 86 | name = name || ''; 87 | size = size || 9; 88 | 89 | var family = name ? name + ', ' : ''; 90 | 91 | name = name.toLowerCase(); 92 | 93 | var styles = []; 94 | if (st['font-style'] === 'italic' || st['font-style'] === 'oblique') { 95 | styles.push(st['font-style']); 96 | } 97 | if (st['font-variant'] === 'small-caps') { 98 | styles.push(st['font-variant']); 99 | } 100 | if (st['font-weight'] === 'bold') { 101 | styles.push(st['font-weight']); 102 | } 103 | 104 | styles.push(size + 'px'); 105 | 106 | if (name.indexOf('serif') !== -1 && name.indexOf('sans-serif') === -1) { 107 | family += 'Georgia, serif'; 108 | } else { 109 | family += '"Helvetica Neue", Arial, Helvetica, sans-serif'; 110 | } 111 | styles.push(family); 112 | 113 | return styles.join(' '); 114 | }, 115 | 116 | setStyles: function (ctx, styles) { 117 | var i; 118 | for (i in styles) { 119 | if (styles.hasOwnProperty(i)) { 120 | ctx[i] = styles[i]; 121 | } 122 | } 123 | } 124 | }; 125 | -------------------------------------------------------------------------------- /src/utils/collisions.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.CollisionBuffer = function (height, width) { 3 | this.buffer = rbush(); 4 | this.height = height; 5 | this.width = width; 6 | }; 7 | 8 | Kothic.CollisionBuffer.prototype = { 9 | addPointWH: function (point, w, h, d, id) { 10 | this.buffer.insert(this.getBoxFromPoint(point, w, h, d, id)); 11 | }, 12 | 13 | addPoints: function (params) { 14 | var points = []; 15 | for (var i = 0, len = params.length; i < len; i++) { 16 | points.push(this.getBoxFromPoint.apply(this, params[i])); 17 | } 18 | this.buffer.load(points); 19 | }, 20 | 21 | checkBox: function (b, id) { 22 | var result = this.buffer.search(b), 23 | i, len; 24 | 25 | if (b[0] < 0 || b[1] < 0 || b[2] > this.width || b[3] > this.height) { return true; } 26 | 27 | for (i = 0, len = result.length; i < len; i++) { 28 | // if it's the same object (only different styles), don't detect collision 29 | if (id !== result[i][4]) { 30 | return true; 31 | } 32 | } 33 | 34 | return false; 35 | }, 36 | 37 | checkPointWH: function (point, w, h, id) { 38 | return this.checkBox(this.getBoxFromPoint(point, w, h, 0), id); 39 | }, 40 | 41 | getBoxFromPoint: function (point, w, h, d, id) { 42 | var dx = w / 2 + d, 43 | dy = h / 2 + d; 44 | 45 | return [ 46 | point[0] - dx, 47 | point[1] - dy, 48 | point[0] + dx, 49 | point[1] + dy, 50 | id 51 | ]; 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/utils/geom.js: -------------------------------------------------------------------------------- 1 | 2 | Kothic.geom = { 3 | transformPoint: function (point, ws, hs) { 4 | return [ws * point[0], hs * point[1]]; 5 | }, 6 | 7 | transformPoints: function (points, ws, hs) { 8 | var transformed = [], i, len; 9 | for (i = 0, len = points.length; i < len; i++) { 10 | transformed.push(this.transformPoint(points[i], ws, hs)); 11 | } 12 | return transformed; 13 | }, 14 | 15 | getReprPoint: function (feature) { 16 | var point, len; 17 | switch (feature.type) { 18 | case 'Point': 19 | point = feature.coordinates; 20 | break; 21 | case 'Polygon': 22 | point = feature.reprpoint; 23 | break; 24 | case 'LineString': 25 | len = Kothic.geom.getPolyLength(feature.coordinates); 26 | point = Kothic.geom.getAngleAndCoordsAtLength(feature.coordinates, len / 2, 0); 27 | point = [point[1], point[2]]; 28 | break; 29 | case 'GeometryCollection': 30 | //TODO: Disassemble geometry collection 31 | return; 32 | case 'MultiPoint': 33 | //TODO: Disassemble multi point 34 | return; 35 | case 'MultiPolygon': 36 | point = feature.reprpoint; 37 | break; 38 | case 'MultiLineString': 39 | //TODO: Disassemble geometry collection 40 | return; 41 | } 42 | return point; 43 | }, 44 | 45 | getPolyLength: function (points) { 46 | var pointsLen = points.length, 47 | c, pc, i, 48 | dx, dy, 49 | len = 0; 50 | 51 | for (i = 1; i < pointsLen; i++) { 52 | c = points[i]; 53 | pc = points[i - 1]; 54 | dx = pc[0] - c[0]; 55 | dy = pc[1] - c[1]; 56 | len += Math.sqrt(dx * dx + dy * dy); 57 | } 58 | return len; 59 | }, 60 | 61 | getAngleAndCoordsAtLength: function (points, dist, width) { 62 | var pointsLen = points.length, 63 | dx, dy, x, y, 64 | i, c, pc, 65 | len = 0, 66 | segLen = 0, 67 | angle, partLen, sameseg = true, 68 | gotxy = false; 69 | 70 | width = width || 0; // by default we think that a letter is 0 px wide 71 | 72 | for (i = 1; i < pointsLen; i++) { 73 | if (gotxy) { 74 | sameseg = false; 75 | } 76 | 77 | c = points[i]; 78 | pc = points[i - 1]; 79 | 80 | dx = c[0] - pc[0]; 81 | dy = c[1] - pc[1]; 82 | segLen = Math.sqrt(dx * dx + dy * dy); 83 | 84 | if (!gotxy && len + segLen >= dist) { 85 | partLen = dist - len; 86 | x = pc[0] + dx * partLen / segLen; 87 | y = pc[1] + dy * partLen / segLen; 88 | 89 | gotxy = true; 90 | } 91 | 92 | if (gotxy && len + segLen >= dist + width) { 93 | partLen = dist + width - len; 94 | dx = pc[0] + dx * partLen / segLen; 95 | dy = pc[1] + dy * partLen / segLen; 96 | angle = Math.atan2(dy - y, dx - x); 97 | 98 | if (sameseg) { 99 | return [angle, x, y, segLen - partLen]; 100 | } else { 101 | return [angle, x, y, 0]; 102 | } 103 | } 104 | 105 | len += segLen; 106 | } 107 | } 108 | }; 109 | 110 | -------------------------------------------------------------------------------- /src/utils/rbush.js: -------------------------------------------------------------------------------- 1 | /* 2 | (c) 2013, Vladimir Agafonkin 3 | RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles. 4 | https://github.com/mourner/rbush 5 | */ 6 | 7 | (function () { 'use strict'; 8 | 9 | function rbush(maxEntries, format) { 10 | 11 | // jshint newcap: false, validthis: true 12 | if (!(this instanceof rbush)) { return new rbush(maxEntries, format); } 13 | 14 | this._maxEntries = Math.max(4, maxEntries || 9); 15 | this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); 16 | 17 | this._initFormat(format); 18 | 19 | this.clear(); 20 | } 21 | 22 | rbush.prototype = { 23 | 24 | search: function (bbox) { 25 | 26 | var node = this.data, 27 | result = []; 28 | 29 | if (!this._intersects(bbox, node.bbox)) { return result; } 30 | 31 | var nodesToSearch = [], 32 | i, len, child, childBBox; 33 | 34 | while (node) { 35 | for (i = 0, len = node.children.length; i < len; i++) { 36 | child = node.children[i]; 37 | childBBox = node.leaf ? this._toBBox(child) : child.bbox; 38 | 39 | if (this._intersects(bbox, childBBox)) { 40 | (node.leaf ? result : nodesToSearch).push(child); 41 | } 42 | } 43 | 44 | node = nodesToSearch.pop(); 45 | } 46 | 47 | return result; 48 | }, 49 | 50 | load: function (data) { 51 | if (!(data && data.length)) { return this; } 52 | 53 | if (data.length < this._minEntries) { 54 | for (var i = 0, len = data.length; i < len; i++) { 55 | this.insert(data[i]); 56 | } 57 | return this; 58 | } 59 | 60 | // recursively build the tree with the given data from stratch using OMT algorithm 61 | var node = this._build(data.slice(), 0); 62 | this._calcBBoxes(node, true); 63 | 64 | if (!this.data.children.length) { 65 | // save as is if tree is empty 66 | this.data = node; 67 | 68 | } else if (this.data.height === node.height) { 69 | // split root if trees have the same height 70 | this._splitRoot(this.data, node); 71 | 72 | } else { 73 | if (this.data.height < node.height) { 74 | // swap trees if inserted one is bigger 75 | var tmpNode = this.data; 76 | this.data = node; 77 | node = tmpNode; 78 | } 79 | 80 | // insert the small tree into the large tree at appropriate level 81 | this._insert(node, this.data.height - node.height - 1, true); 82 | } 83 | 84 | return this; 85 | }, 86 | 87 | insert: function (item) { 88 | if (item) { 89 | this._insert(item, this.data.height - 1); 90 | } 91 | return this; 92 | }, 93 | 94 | clear: function () { 95 | this.data = { 96 | children: [], 97 | leaf: true, 98 | bbox: this._infinite(), 99 | height: 1 100 | }; 101 | return this; 102 | }, 103 | 104 | remove: function (item) { 105 | if (!item) { return this; } 106 | 107 | var node = this.data, 108 | bbox = this._toBBox(item), 109 | path = [], 110 | indexes = [], 111 | i, parent, index, goingUp; 112 | 113 | // depth-first iterative tree traversal 114 | while (node || path.length) { 115 | 116 | if (!node) { // go up 117 | node = path.pop(); 118 | parent = path[path.length - 1]; 119 | i = indexes.pop(); 120 | goingUp = true; 121 | } 122 | 123 | if (node.leaf) { // check current node 124 | index = node.children.indexOf(item); 125 | 126 | if (index !== -1) { 127 | // item found, remove the item and condense tree upwards 128 | node.children.splice(index, 1); 129 | path.push(node); 130 | this._condense(path); 131 | return this; 132 | } 133 | } 134 | 135 | if (!goingUp && !node.leaf && this._intersects(bbox, node.bbox)) { // go down 136 | path.push(node); 137 | indexes.push(i); 138 | i = 0; 139 | parent = node; 140 | node = node.children[0]; 141 | 142 | } else if (parent) { // go right 143 | i++; 144 | node = parent.children[i]; 145 | goingUp = false; 146 | 147 | } else { // nothing found 148 | node = null; 149 | } 150 | } 151 | 152 | return this; 153 | }, 154 | 155 | toJSON: function () { return this.data; }, 156 | 157 | fromJSON: function (data) { 158 | this.data = data; 159 | return this; 160 | }, 161 | 162 | _build: function (items, level, height) { 163 | 164 | var N = items.length, 165 | M = this._maxEntries; 166 | 167 | if (N <= M) { 168 | return { 169 | children: items, 170 | leaf: true, 171 | height: 1 172 | }; 173 | } 174 | 175 | if (!level) { 176 | // target height of the bulk-loaded tree 177 | height = Math.ceil(Math.log(N) / Math.log(M)); 178 | 179 | // target number of root entries to maximize storage utilization 180 | M = Math.ceil(N / Math.pow(M, height - 1)); 181 | 182 | items.sort(this._compareMinX); 183 | } 184 | 185 | // TODO eliminate recursion? 186 | 187 | var node = { 188 | children: [], 189 | height: height 190 | }; 191 | 192 | var N1 = Math.ceil(N / M) * Math.ceil(Math.sqrt(M)), 193 | N2 = Math.ceil(N / M), 194 | compare = level % 2 === 1 ? this._compareMinX : this._compareMinY, 195 | i, j, slice, sliceLen, childNode; 196 | 197 | // split the items into M mostly square tiles 198 | for (i = 0; i < N; i += N1) { 199 | slice = items.slice(i, i + N1).sort(compare); 200 | 201 | for (j = 0, sliceLen = slice.length; j < sliceLen; j += N2) { 202 | // pack each entry recursively 203 | childNode = this._build(slice.slice(j, j + N2), level + 1, height - 1); 204 | node.children.push(childNode); 205 | } 206 | } 207 | 208 | return node; 209 | }, 210 | 211 | _chooseSubtree: function (bbox, node, level, path) { 212 | 213 | var i, len, child, targetNode, area, enlargement, minArea, minEnlargement; 214 | 215 | while (true) { 216 | path.push(node); 217 | 218 | if (node.leaf || path.length - 1 === level) { break; } 219 | 220 | minArea = minEnlargement = Infinity; 221 | 222 | for (i = 0, len = node.children.length; i < len; i++) { 223 | child = node.children[i]; 224 | area = this._area(child.bbox); 225 | enlargement = this._enlargedArea(bbox, child.bbox) - area; 226 | 227 | // choose entry with the least area enlargement 228 | if (enlargement < minEnlargement) { 229 | minEnlargement = enlargement; 230 | minArea = area < minArea ? area : minArea; 231 | targetNode = child; 232 | 233 | } else if (enlargement === minEnlargement) { 234 | // otherwise choose one with the smallest area 235 | if (area < minArea) { 236 | minArea = area; 237 | targetNode = child; 238 | } 239 | } 240 | } 241 | 242 | node = targetNode; 243 | } 244 | 245 | return node; 246 | }, 247 | 248 | _insert: function (item, level, isNode, root) { 249 | 250 | var bbox = isNode ? item.bbox : this._toBBox(item), 251 | insertPath = []; 252 | 253 | // find the best node for accommodating the item, saving all nodes along the path too 254 | var node = this._chooseSubtree(bbox, root || this.data, level, insertPath), 255 | splitOccured; 256 | 257 | // put the item into the node 258 | node.children.push(item); 259 | this._extend(node.bbox, bbox); 260 | 261 | // split on node overflow; propagate upwards if necessary 262 | do { 263 | splitOccured = false; 264 | if (insertPath[level].children.length > this._maxEntries) { 265 | this._split(insertPath, level); 266 | splitOccured = true; 267 | level--; 268 | } 269 | } while (level >= 0 && splitOccured); 270 | 271 | // adjust bboxes along the insertion path 272 | this._adjustParentBBoxes(bbox, insertPath, level); 273 | }, 274 | 275 | // split overflowed node into two 276 | _split: function (insertPath, level) { 277 | 278 | var node = insertPath[level], 279 | M = node.children.length, 280 | m = this._minEntries; 281 | 282 | this._chooseSplitAxis(node, m, M); 283 | 284 | var newNode = { 285 | children: node.children.splice(this._chooseSplitIndex(node, m, M)), 286 | height: node.height 287 | }; 288 | 289 | if (node.leaf) { 290 | newNode.leaf = true; 291 | } 292 | 293 | this._calcBBoxes(node); 294 | this._calcBBoxes(newNode); 295 | 296 | if (level) { 297 | insertPath[level - 1].children.push(newNode); 298 | } else { 299 | this._splitRoot(node, newNode); 300 | } 301 | }, 302 | 303 | _splitRoot: function (node, newNode) { 304 | // split root node 305 | this.data = {}; 306 | this.data.children = [node, newNode]; 307 | this.data.height = node.height + 1; 308 | this._calcBBoxes(this.data); 309 | }, 310 | 311 | _chooseSplitIndex: function (node, m, M) { 312 | 313 | var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index; 314 | 315 | minOverlap = minArea = Infinity; 316 | 317 | for (i = m; i <= M - m; i++) { 318 | bbox1 = this._distBBox(node, 0, i); 319 | bbox2 = this._distBBox(node, i, M); 320 | 321 | overlap = this._intersectionArea(bbox1, bbox2); 322 | area = this._area(bbox1) + this._area(bbox2); 323 | 324 | // choose distribution with minimum overlap 325 | if (overlap < minOverlap) { 326 | minOverlap = overlap; 327 | index = i; 328 | 329 | minArea = area < minArea ? area : minArea; 330 | 331 | } else if (overlap === minOverlap) { 332 | // otherwise choose distribution with minimum area 333 | if (area < minArea) { 334 | minArea = area; 335 | index = i; 336 | } 337 | } 338 | } 339 | 340 | return index; 341 | }, 342 | 343 | // sorts node children by the best axis for split 344 | _chooseSplitAxis: function (node, m, M) { 345 | 346 | var compareMinX = node.leaf ? this._compareMinX : this._compareNodeMinX, 347 | compareMinY = node.leaf ? this._compareMinY : this._compareNodeMinY, 348 | xMargin = this._allDistMargin(node, m, M, compareMinX), 349 | yMargin = this._allDistMargin(node, m, M, compareMinY); 350 | 351 | // if total distributions margin value is minimal for x, sort by minX, 352 | // otherwise it's already sorted by minY 353 | 354 | if (xMargin < yMargin) { 355 | node.children.sort(compareMinX); 356 | } 357 | }, 358 | 359 | // total margin of all possible split distributions where each node is at least m full 360 | _allDistMargin: function (node, m, M, compare) { 361 | 362 | node.children.sort(compare); 363 | 364 | var leftBBox = this._distBBox(node, 0, m), 365 | rightBBox = this._distBBox(node, M - m, M), 366 | margin = this._margin(leftBBox) + this._margin(rightBBox), 367 | i, child; 368 | 369 | for (i = m; i < M - m; i++) { 370 | child = node.children[i]; 371 | this._extend(leftBBox, node.leaf ? this._toBBox(child) : child.bbox); 372 | margin += this._margin(leftBBox); 373 | } 374 | 375 | for (i = M - m - 1; i >= 0; i--) { 376 | child = node.children[i]; 377 | this._extend(rightBBox, node.leaf ? this._toBBox(child) : child.bbox); 378 | margin += this._margin(rightBBox); 379 | } 380 | 381 | return margin; 382 | }, 383 | 384 | // min bounding rectangle of node children from k to p-1 385 | _distBBox: function (node, k, p) { 386 | var bbox = this._infinite(); 387 | 388 | for (var i = k, child; i < p; i++) { 389 | child = node.children[i]; 390 | this._extend(bbox, node.leaf ? this._toBBox(child) : child.bbox); 391 | } 392 | 393 | return bbox; 394 | }, 395 | 396 | _calcBBoxes: function (node, recursive) { 397 | // TODO eliminate recursion 398 | node.bbox = this._infinite(); 399 | 400 | for (var i = 0, len = node.children.length, child; i < len; i++) { 401 | child = node.children[i]; 402 | 403 | if (node.leaf) { 404 | this._extend(node.bbox, this._toBBox(child)); 405 | } else { 406 | if (recursive) { 407 | this._calcBBoxes(child, recursive); 408 | } 409 | this._extend(node.bbox, child.bbox); 410 | } 411 | } 412 | }, 413 | 414 | _adjustParentBBoxes: function (bbox, path, level) { 415 | // adjust bboxes along the given tree path 416 | for (var i = level; i >= 0; i--) { 417 | this._extend(path[i].bbox, bbox); 418 | } 419 | }, 420 | 421 | _condense: function (path) { 422 | // go through the path, removing empty nodes and updating bboxes 423 | for (var i = path.length - 1, parent; i >= 0; i--) { 424 | if (i > 0 && path[i].children.length === 0) { 425 | parent = path[i - 1].children; 426 | parent.splice(parent.indexOf(path[i]), 1); 427 | } else { 428 | this._calcBBoxes(path[i]); 429 | } 430 | } 431 | }, 432 | 433 | _intersects: function (a, b) { 434 | return b[0] <= a[2] && 435 | b[1] <= a[3] && 436 | b[2] >= a[0] && 437 | b[3] >= a[1]; 438 | }, 439 | 440 | _extend: function (a, b) { 441 | a[0] = Math.min(a[0], b[0]); 442 | a[1] = Math.min(a[1], b[1]); 443 | a[2] = Math.max(a[2], b[2]); 444 | a[3] = Math.max(a[3], b[3]); 445 | return a; 446 | }, 447 | 448 | _area: function (a) { return (a[2] - a[0]) * (a[3] - a[1]); }, 449 | _margin: function (a) { return (a[2] - a[0]) + (a[3] - a[1]); }, 450 | 451 | _enlargedArea: function (a, b) { 452 | return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) * 453 | (Math.max(b[3], a[3]) - Math.min(b[1], a[1])); 454 | }, 455 | 456 | _intersectionArea: function (a, b) { 457 | var minX = Math.max(a[0], b[0]), 458 | minY = Math.max(a[1], b[1]), 459 | maxX = Math.min(a[2], b[2]), 460 | maxY = Math.min(a[3], b[3]); 461 | 462 | return Math.max(0, maxX - minX) * 463 | Math.max(0, maxY - minY); 464 | }, 465 | 466 | _infinite: function () { return [Infinity, Infinity, -Infinity, -Infinity]; }, 467 | 468 | _compareNodeMinX: function (a, b) { return a.bbox[0] - b.bbox[0]; }, 469 | _compareNodeMinY: function (a, b) { return a.bbox[1] - b.bbox[1]; }, 470 | 471 | _initFormat: function (format) { 472 | // data format (minX, minY, maxX, maxY accessors) 473 | format = format || ['[0]', '[1]', '[2]', '[3]']; 474 | 475 | // uses eval-type function compilation instead of just accepting a toBBox function 476 | // because the algorithms are very sensitive to sorting functions performance, 477 | // so they should be dead simple and without inner calls 478 | 479 | // jshint evil: true 480 | 481 | var compareArr = ['return a', ' - b', ';']; 482 | 483 | this._compareMinX = new Function('a', 'b', compareArr.join(format[0])); 484 | this._compareMinY = new Function('a', 'b', compareArr.join(format[1])); 485 | 486 | this._toBBox = new Function('a', 'return [a' + format.join(', a') + '];'); 487 | } 488 | }; 489 | 490 | if (typeof module !== 'undefined') { 491 | module.exports = rbush; 492 | } else { 493 | window.rbush = rbush; 494 | } 495 | 496 | })(); 497 | --------------------------------------------------------------------------------