├── .babelrc ├── .circleci └── config.yml ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── demo ├── colors.js ├── grid.html ├── gridvalue.js ├── metrics.js ├── micapse.html ├── micapse_12h_1km.txt ├── midcapse2.html └── random.html ├── dist ├── maptalks.gridlayer.es.js ├── maptalks.gridlayer.es.js.map ├── maptalks.gridlayer.js ├── maptalks.gridlayer.js.map └── maptalks.gridlayer.min.js ├── karma.config.js ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── rollup.config.js ├── src ├── common.js ├── gridlayer.js ├── index.js └── renderer │ ├── canvas-renderer.js │ └── gl-renderer.js ├── test ├── .eslintrc └── test.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "loose" : true, "modules" : false }] 4 | ], 5 | "plugins": [ 6 | "external-helpers", 7 | "transform-proto-to-assign" 8 | ], 9 | "ignore": [ 10 | "dist/*.js" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:8-browsers 11 | environment: 12 | CHROME_BIN: "/usr/bin/google-chrome" 13 | 14 | # Specify service dependencies here if necessary 15 | # CircleCI maintains a library of pre-built images 16 | # documented at https://circleci.com/docs/2.0/circleci-images/ 17 | # - image: circleci/mongo:3.4.4 18 | 19 | working_directory: ~/repo 20 | 21 | steps: 22 | - checkout 23 | 24 | # Download and cache dependencies 25 | - restore_cache: 26 | keys: 27 | - v1-dependencies-{{ checksum "package.json" }} 28 | # fallback to using the latest cache if no exact match is found 29 | - v1-dependencies- 30 | 31 | - run: npm install 32 | 33 | - save_cache: 34 | paths: 35 | - node_modules 36 | key: v1-dependencies-{{ checksum "package.json" }} 37 | 38 | # run tests! 39 | - run: npm test 40 | - run: bash <(curl -s https://codecov.io/bash) 41 | 42 | 43 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [{package,bower}.json] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [.travis.yml] 19 | indent_style = space 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint:recommended" 5 | ], 6 | "parserOptions": { 7 | "ecmaVersion": 2015, 8 | "sourceType": "module" 9 | }, 10 | "env": { 11 | "browser": true, 12 | "es6": true, 13 | "node": true 14 | }, 15 | "rules": { 16 | "no-prototype-builtins": "off" 17 | }, 18 | "globals": { 19 | "maptalks": true, 20 | "importScripts" : true, 21 | "WorkerGlobalScope": true, 22 | "globalThis": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | *.gz 3 | .idea/ 4 | *.iml 5 | .tern-port 6 | doc/ 7 | 8 | ### *tags 9 | GPATH 10 | GRTAGS 11 | GTAGS 12 | TAGS 13 | 14 | ### OSX template 15 | .DS_Store 16 | 17 | # Created by .ignore support plugin (hsz.mobi) 18 | ### Node template 19 | # Logs 20 | logs 21 | *.log 22 | 23 | # Runtime data 24 | pids 25 | *.pid 26 | *.seed 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | lib-cov 30 | 31 | # Coverage directory used by tools like istanbul 32 | coverage 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (http://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directory 44 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 45 | node_modules 46 | 47 | #example deploy config 48 | examples/replace.json 49 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /**/* 2 | dist/*.gz 3 | !dist/*.js 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 MapTalks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # maptalks.gridlayer 2 | 3 | [![CircleCI](https://circleci.com/gh/maptalks/maptalks.gridlayer.svg?style=shield)](https://circleci.com/gh/maptalks/maptalks.gridlayer) 4 | [![NPM Version](https://img.shields.io/npm/v/maptalks.gridlayer.svg)](https://github.com/maptalks/maptalks.gridlayer) 5 | 6 | GridLayer plugin for maptalks.js. A layer draws grids. 7 | 8 | ![screenshot](https://user-images.githubusercontent.com/13678919/33376245-ec78e626-d547-11e7-8280-ef4693d416fd.png) 9 | 10 | ## Examples 11 | 12 | * [Random square grids](https://maptalks.github.io/maptalks.gridlayer/demo/random.html). 13 | * [Customer analysis grid](https://maptalks.github.io/maptalks.gridlayer/demo/grid.html). 14 | * [1KM grid of rainfall forecast of 2017-06-07](https://maptalks.github.io/maptalks.gridlayer/demo/micapse.html). 15 | 16 | ## Install 17 | 18 | * Install with npm: ```npm install maptalks.gridlayer```. 19 | * Download from [dist directory](https://github.com/maptalks/maptalks.gridlayer/tree/gh-pages/dist). 20 | * Use unpkg CDN: ```https://cdn.jsdelivr.net/npm/maptalks.gridlayer/dist/maptalks.gridlayer.min.js``` 21 | 22 | ## Usage 23 | 24 | As a plugin, `maptalks.gridlayer` must be loaded after `maptalks.js` in browsers. 25 | ```html 26 | 27 | 28 | 29 | 59 | ``` 60 | ## Supported Browsers 61 | 62 | Chrome, Firefox, other modern and mobile browsers. 63 | 64 | ## API Reference 65 | 66 | ```GridLayer``` is a subclass of [maptalks.Layer](https://maptalks.github.io/docs/api/Layer.html) and inherits all the methods of its parent. 67 | 68 | ### `Constructor` 69 | 70 | ```javascript 71 | new maptalks.GridLayer(id, data, options) 72 | ``` 73 | 74 | * id **String** layer id 75 | * data **Object[] | Object** see data format below 76 | * options **Object** options 77 | * renderer **String** renderer, canvas or gl, gl by default 78 | * symbol **Object** symbol of the grid, supported properties: `lineWidth` (fixed to 1 in gl renderer on windows [due to ANGLE](https://bugs.chromium.org/p/angleproject/issues/detail?id=334)), `lineColor`, `polygonFill`, `polygonOpacity` 79 | * Other options defined in [maptalks.Layer](https://maptalks.github.io/docs/api/Layer.html) 80 | 81 | ```javascript 82 | [ 83 | { 84 | center : [0, 0], // center of the grid 85 | width : 100, // width of the grid cell 86 | height : 100, // height of the grid cell 87 | // unit of cell width/height, possible values: 88 | // * projection : projected coordinate 89 | // * meter : meters of geographic distance 90 | // * degree : longtitude/latitude degrees 91 | altitude : 0, // altitude of grid in meter 92 | unit : 'projection', 93 | cols : [1, Infinity], 94 | rows : [2, 5], 95 | // data format 96 | data : [ 97 | //[col, row, { properties : properties, symbol : symbol}] 98 | //supported symbol properties : polygonFill, polygonOpacity 99 | //col: col_index or [beginIndex, endIndex] 100 | //row: col_index or [beginIndex, endIndex] 101 | // col is 1, row is 2 102 | [1, 2, { properties : { foo : 1, foo2 : 'foo' }, symbol : { polygonFill : '#f00' } }], 103 | //col is from 2 to 4 (3 columns), row is 5 104 | [[2, 4] , 5, { symbol : { polygonFill : '#f00' } }], 105 | //col is from 2 to 4 (3 columns), row is from 7 to 8 (2 rows) 106 | [[2, 4] , [7, 8], { symbol : { polygonFill : '#f00' } }] 107 | ] 108 | } 109 | ] 110 | ``` 111 | 112 | ### `getGrid(gridIndex = 0)` 113 | 114 | get layer's grid value 115 | * gridIndex **Number** grid's index, default is 0 116 | 117 | **Returns** `Object` 118 | 119 | ### `setGrid(grid, gridIndex = 0)` 120 | 121 | set a new grid value to the layer 122 | 123 | * grid **Object** new grid value 124 | * gridIndex **Number** grid's index, default is 0 125 | 126 | ### `setGridData(data, gridIndex = 0)` 127 | 128 | update layer's grid data 129 | 130 | * data **Array** set new data 131 | 132 | **Returns** `this` 133 | 134 | ### `redraw()` 135 | 136 | redraw the layer 137 | 138 | **Returns** `this` 139 | 140 | ### `isEmpty()` 141 | 142 | If the layer is empty 143 | 144 | **Returns** `Boolean` 145 | 146 | ### `clear()` 147 | 148 | clear the layer 149 | 150 | **Returns** `this` 151 | 152 | ### `getGridExtent(gridIndex = 0)` 153 | 154 | Get grid's geographic extent 155 | 156 | * gridIndex **Number** grid's index, default is 0 157 | 158 | **Returns** `maptalks.Extent` 159 | 160 | ### `GetCellAt(coordinate, gridIndex = 0)` 161 | 162 | Get cell index at coordinate 163 | 164 | * coordinate **maptalks.Coordinate** coordinate 165 | * gridIndex **Number** grid's index, default is 0 166 | 167 | **Returns** `Number[]` [col, row] 168 | 169 | ### `GetCellGeometry(col, row, gridIndex = 0)` 170 | 171 | Get cell's geometry 172 | 173 | * col **Number** cell's col 174 | * row **Number** cell's row 175 | * gridIndex **Number** grid's index, default is 0 176 | 177 | **Returns** `maptalks.Geometry` cell geometry 178 | 179 | ### `VisitAround(coordinate, cb, gridIndex = 0)` 180 | 181 | Visit data cells around given coordinate 182 | 183 | * coordinate **maptalks.Coordinate** coordinate 184 | * cb **Function** callback function, parameter is [col, row, { properties, symbol }], return false to break the visiting 185 | * gridIndex **Number** grid's index, default is 0 186 | 187 | ### `identify(coordinate, gridIndex = 0)` 188 | 189 | Return cell index and cell geometry at coordinate 190 | 191 | * coordinate **maptalks.Coordinate** coordinate 192 | * gridIndex **Number** grid's index, default is 0 193 | 194 | **Returns** `Object` { col : col, row : row, geometry : cellGeometry } 195 | 196 | ### `toJSON()` 197 | 198 | export the GridLayer's JSON. 199 | 200 | ```javascript 201 | var json = gridlayer.toJSON(); 202 | ``` 203 | 204 | **Returns** `Object` 205 | 206 | ## Contributing 207 | 208 | We welcome any kind of contributions including issue reportings, pull requests, documentation corrections, feature requests and any other helps. 209 | 210 | ## Develop 211 | 212 | The only source file is ```index.js```. 213 | 214 | It is written in ES6. 215 | 216 | ### Scripts 217 | 218 | * Install dependencies 219 | ```shell 220 | $ pnpm i 221 | ``` 222 | 223 | * Watch source changes and generate runnable bundle repeatedly 224 | ```shell 225 | $ npm run dev 226 | ``` 227 | 228 | * Tests 229 | ```shell 230 | $ npm test 231 | ``` 232 | 233 | * Watch source changes and run tests repeatedly 234 | ```shell 235 | $ npm run dev 236 | ``` 237 | 238 | * Package and generate minified bundles to dist directory 239 | ```shell 240 | $ npm run build 241 | ``` 242 | 243 | * Lint 244 | ```shell 245 | $ npm run lint 246 | ``` 247 | -------------------------------------------------------------------------------- /demo/colors.js: -------------------------------------------------------------------------------- 1 | var colors = []; 2 | 3 | for (var r = 15; r <= 255; r += 30) 4 | for (var g = 15; g <= 255; g += 30) 5 | for (var b = 15; b <= 255; b += 30) colors.push([r, g, b]); 6 | 7 | console.log(colors.length); 8 | 9 | function sortedColors(color) { 10 | var unsorted = colors.slice(); 11 | var last = color || [200, 200, 200]; 12 | var sorted = []; 13 | 14 | while (sorted.length < colors.length) { 15 | var minDist = Infinity, 16 | minIndex; 17 | for (var i = 0; i < unsorted.length; i++) { 18 | var c = unsorted[i]; 19 | var dist = distRieRGB(last[0], last[1], last[2], c[0], c[1], c[2]); 20 | if (dist < minDist) { 21 | minIndex = i; 22 | minDist = dist; 23 | } 24 | } 25 | sorted.push(unsorted[minIndex]); 26 | last = unsorted[minIndex]; 27 | unsorted.splice(minIndex, 1); 28 | } 29 | return sorted; 30 | } 31 | -------------------------------------------------------------------------------- /demo/grid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | maptalks.GridLayer demo 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 |
18 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /demo/gridvalue.js: -------------------------------------------------------------------------------- 1 | var gridvalue = [{ 2 | "value": 0.2941, 3 | "x": 5, 4 | "y": 3 5 | }, { 6 | "value": 0.8824, 7 | "x": 8, 8 | "y": 3 9 | }, { 10 | "value": 0.1471, 11 | "x": 1, 12 | "y": 4 13 | }, { 14 | "value": 0.1471, 15 | "x": 6, 16 | "y": 4 17 | }, { 18 | "value": 0.8824, 19 | "x": 8, 20 | "y": 4 21 | }, { 22 | "value": 0.1471, 23 | "x": 9, 24 | "y": 5 25 | }, { 26 | "value": 2.9412, 27 | "x": 10, 28 | "y": 5 29 | }, { 30 | "value": 0.2941, 31 | "x": 19, 32 | "y": 5 33 | }, { 34 | "value": 0.8824, 35 | "x": 7, 36 | "y": 6 37 | }, { 38 | "value": 0.2941, 39 | "x": 8, 40 | "y": 6 41 | }, { 42 | "value": 1.6176, 43 | "x": 6, 44 | "y": 7 45 | }, { 46 | "value": 1.1765, 47 | "x": 7, 48 | "y": 7 49 | }, { 50 | "value": 0.8824, 51 | "x": 8, 52 | "y": 7 53 | }, { 54 | "value": 0.7353, 55 | "x": 10, 56 | "y": 7 57 | }, { 58 | "value": 0.1471, 59 | "x": 3, 60 | "y": 8 61 | }, { 62 | "value": 0.2941, 63 | "x": 7, 64 | "y": 8 65 | }, { 66 | "value": 1.1765, 67 | "x": 9, 68 | "y": 8 69 | }, { 70 | "value": 0.7353, 71 | "x": 5, 72 | "y": 9 73 | }, { 74 | "value": 1.1764, 75 | "x": 7, 76 | "y": 9 77 | }, { 78 | "value": 1.1764, 79 | "x": 9, 80 | "y": 9 81 | }, { 82 | "value": 0.2941, 83 | "x": 10, 84 | "y": 9 85 | }, { 86 | "value": 0.1471, 87 | "x": 12, 88 | "y": 9 89 | }, { 90 | "value": 0.5882, 91 | "x": 13, 92 | "y": 9 93 | }, { 94 | "value": 0.2941, 95 | "x": 3, 96 | "y": 10 97 | }, { 98 | "value": 1.0294, 99 | "x": 8, 100 | "y": 10 101 | }, { 102 | "value": 1.6177, 103 | "x": 9, 104 | "y": 10 105 | }, { 106 | "value": 3.3824, 107 | "x": 10, 108 | "y": 10 109 | }, { 110 | "value": 2.9412, 111 | "x": 11, 112 | "y": 10 113 | }, { 114 | "value": 0.2941, 115 | "x": 12, 116 | "y": 10 117 | }, { 118 | "value": 0.2941, 119 | "x": 13, 120 | "y": 10 121 | }, { 122 | "value": 0.1471, 123 | "x": 15, 124 | "y": 10 125 | }, { 126 | "value": 0.2941, 127 | "x": 17, 128 | "y": 10 129 | }, { 130 | "value": 0.1471, 131 | "x": 3, 132 | "y": 11 133 | }, { 134 | "value": 0.7353, 135 | "x": 8, 136 | "y": 11 137 | }, { 138 | "value": 0.5882, 139 | "x": 9, 140 | "y": 11 141 | }, { 142 | "value": 4.5588, 143 | "x": 10, 144 | "y": 11 145 | }, { 146 | "value": 1.6176, 147 | "x": 11, 148 | "y": 11 149 | }, { 150 | "value": 10.5882, 151 | "x": 12, 152 | "y": 11 153 | }, { 154 | "value": 0.5882, 155 | "x": 8, 156 | "y": 12 157 | }, { 158 | "value": 0.4412, 159 | "x": 10, 160 | "y": 12 161 | }, { 162 | "value": 2.647, 163 | "x": 11, 164 | "y": 12 165 | }, { 166 | "value": 2.6471, 167 | "x": 12, 168 | "y": 12 169 | }, { 170 | "value": 0.2941, 171 | "x": 13, 172 | "y": 12 173 | }, { 174 | "value": 0.2941, 175 | "x": 16, 176 | "y": 12 177 | }, { 178 | "value": 0.1471, 179 | "x": 7, 180 | "y": 13 181 | }, { 182 | "value": 2.353, 183 | "x": 9, 184 | "y": 13 185 | }, { 186 | "value": 0.8824, 187 | "x": 15, 188 | "y": 13 189 | }, { 190 | "value": 0.2941, 191 | "x": 16, 192 | "y": 13 193 | }, { 194 | "value": 0.1471, 195 | "x": 6, 196 | "y": 14 197 | }, { 198 | "value": 0.2941, 199 | "x": 7, 200 | "y": 14 201 | }, { 202 | "value": 0.8824, 203 | "x": 8, 204 | "y": 14 205 | }, { 206 | "value": 1.9117, 207 | "x": 9, 208 | "y": 14 209 | }, { 210 | "value": 0.4412, 211 | "x": 10, 212 | "y": 14 213 | }, { 214 | "value": 0.1471, 215 | "x": 12, 216 | "y": 14 217 | }, { 218 | "value": 0.4412, 219 | "x": 15, 220 | "y": 14 221 | }, { 222 | "value": 0.1471, 223 | "x": 16, 224 | "y": 14 225 | }, { 226 | "value": 0.1471, 227 | "x": 7, 228 | "y": 15 229 | }, { 230 | "value": 0.7353, 231 | "x": 10, 232 | "y": 15 233 | }, { 234 | "value": 2.353, 235 | "x": 12, 236 | "y": 15 237 | }, { 238 | "value": 18.0883, 239 | "x": 13, 240 | "y": 15 241 | }, { 242 | "value": 0.5882, 243 | "x": 14, 244 | "y": 15 245 | }, { 246 | "value": 0.1471, 247 | "x": 17, 248 | "y": 15 249 | }, { 250 | "value": 0.5882, 251 | "x": 6, 252 | "y": 16 253 | }, { 254 | "value": 1.0294, 255 | "x": 9, 256 | "y": 16 257 | }, { 258 | "value": 4.7059, 259 | "x": 12, 260 | "y": 16 261 | }, { 262 | "value": 0.5882, 263 | "x": 16, 264 | "y": 16 265 | }, { 266 | "value": 0.2941, 267 | "x": 8, 268 | "y": 17 269 | }, { 270 | "value": 0.8824, 271 | "x": 9, 272 | "y": 17 273 | }, { 274 | "value": 1.9118, 275 | "x": 13, 276 | "y": 17 277 | }, { 278 | "value": 1.0294, 279 | "x": 14, 280 | "y": 17 281 | }, { 282 | "value": 0.2941, 283 | "x": 15, 284 | "y": 17 285 | }, { 286 | "value": 0.5882, 287 | "x": 6, 288 | "y": 18 289 | }, { 290 | "value": 0.1471, 291 | "x": 7, 292 | "y": 18 293 | }, { 294 | "value": 0.1471, 295 | "x": 17, 296 | "y": 18 297 | }, { 298 | "value": 0.1471, 299 | "x": 7, 300 | "y": 19 301 | }, { 302 | "value": 0.5882, 303 | "x": 15, 304 | "y": 19 305 | }, { 306 | "value": 0.4412, 307 | "x": 10, 308 | "y": 20 309 | }]; 310 | -------------------------------------------------------------------------------- /demo/metrics.js: -------------------------------------------------------------------------------- 1 | 2 | function distRGB(r1, g1, b1, r2, g2, b2) { 3 | var dr = r1 - r2, 4 | dg = g1 - g2, 5 | db = b1 - b2; 6 | return dr * dr + dg * dg + db * db; 7 | } 8 | 9 | function distCIEDE2000(r1, g1, b1, r2, g2, b2) { 10 | var c1 = colordiff.rgb_to_lab({R: r1, G: g1, B: b1}); 11 | var c2 = colordiff.rgb_to_lab({R: r2, G: g2, B: b2}); 12 | return colordiff.diff(c1, c2); 13 | } 14 | 15 | function distRieRGB(r1, g1, b1, r2, g2, b2) { 16 | var mr = (r1 + r2) / 2, 17 | dr = r1 - r2, 18 | dg = g1 - g2, 19 | db = b1 - b2; 20 | return (2 + mr / 256) * dr * dr + 4 * dg * dg + (2 + (255 - mr) / 256) * db * db; 21 | } 22 | 23 | function distYIQ(r1, g1, b1, r2, g2, b2) { 24 | var y = rgb2y(r1, g1, b1) - rgb2y(r2, g2, b2), 25 | i = rgb2i(r1, g1, b1) - rgb2i(r2, g2, b2), 26 | q = rgb2q(r1, g1, b1) - rgb2q(r2, g2, b2); 27 | 28 | return y * y * 0.5053 + i * i * 0.299 + q * q * 0.1957; 29 | } 30 | 31 | function rgb2y(r, g, b) { 32 | return r * 0.29889531 + g * 0.58662247 + b * 0.11448223; 33 | } 34 | function rgb2i(r, g, b) { 35 | return r * 0.59597799 - g * 0.27417610 - b * 0.32180189; 36 | } 37 | function rgb2q(r, g, b) { 38 | return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; 39 | } 40 | -------------------------------------------------------------------------------- /demo/micapse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | maptalks.GridLayer demo 5 | 6 | 7 | 8 | 11 | 12 | 13 |
14 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /demo/micapse_12h_1km.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maptalks/maptalks.gridlayer/2985c4a467071c1a7c8cbd8da95c5d230702cbd6/demo/micapse_12h_1km.txt -------------------------------------------------------------------------------- /demo/midcapse2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | maptalks.GridLayer demo 5 | 6 | 7 | 8 | 11 | 12 | 13 |
14 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /demo/random.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | maptalks.GridLayer demo 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 |
16 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /dist/maptalks.gridlayer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * maptalks.gridlayer v0.7.0 3 | * LICENSE : MIT 4 | * (c) 2016-2025 maptalks.org 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("maptalks")):"function"==typeof define&&define.amd?define(["exports","maptalks"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).maptalks=t.maptalks||{},t.maptalks)}(this,(function(t,e){"use strict";function r(t){var e=Object.create(null);return t&&Object.keys(t).forEach((function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:!0,get:function(){return t[r]}})}})),e.default=t,Object.freeze(e)}var i=r(e);function n(t,r){const i=t.getGridProjection(),n=t.getMap(),a=new e.Coordinate(r.center),s=n.coordinateToPoint(a),o=i.project(a)._add(r.width,r.height),l=n._prjToPoint(o);return[Math.abs(l.x-s.x),Math.abs(l.y-s.y)]}const a={lineColor:"#bbb",lineWidth:1,lineOpacity:1,lineDasharray:[],lineCap:"butt",lineJoin:"round",polygonOpacity:0},s={symbol:i.Util.extend({},a),debug:!1};class o extends i.Layer{static getPainterClass(){return i.VectorLayer.getPainterClass()}constructor(t,e,r){if(super(t,r),!e)return this._gridCenters=[],void(this._grids=[]);Array.isArray(e)||(e=[e]),e.forEach((t=>{t.unit||(t.unit="projection"),t.center.toArray&&(t.center=t.center.toArray())})),this._gridCenters=e.map((t=>t.center.slice(0))),this._grids=e}getGridCount(){return this._grids?this._grids.length:0}getGrid(t=0){const e=this._grids[t];if(!e)return null;let r=e.offset;return r&&(i.Util.isFunction(r)&&(r=r()),e.center[0]=this._gridCenters[t][0]+r[0],e.center[1]=this._gridCenters[t][1]+r[1]),e}setGrid(t,e=0){return t.unit||(t.unit="projection"),t.center.toArray&&(t.center=t.center.toArray()),this._gridCenters[e]=t.center.slice(0),this._grids[e]=t,this.redraw()}setGridData(t,e=0){return this._grids[e].data=t,this.redraw()}redraw(){const t=this._getRenderer();return t?(t.redraw(),this):this}isEmpty(t=0){return!this._grids||!this._grids[t]}clear(){return delete this._grids,this.fire("clear"),this.redraw()}getGridProjection(){return this.options.projectionName?i.projection[this.options.projectionName.toUpperCase()]:this.getMap().getProjection()}getGridExtent(t=0){const e=this.getGrid(t),r=new i.Coordinate(e.center),n=this.getMap(),a=e.width,s=e.height,o=e.cols||[-1/0,1/0],l=e.rows||[-1/0,1/0];if(i.Util.isNil(o[0])&&(o[0]=-1/0),i.Util.isNil(o[1])&&(o[1]=1/0),i.Util.isNil(l[0])&&(l[0]=-1/0),i.Util.isNil(l[1])&&(l[1]=1/0),"projection"===e.unit){const t=this.getGridProjection(),e=t.project(r);let l=e.x+o[0]*a,h=e.x+o[1]*a,u=e.y+o[0]*s,c=e.y+o[1]*s;const d=n.getFullExtent();return l===-1/0&&(l=d.xmin+1),h===1/0&&(h=d.xmax),u===-1/0&&(u=d.ymin),c===1/0&&(c=d.ymax),new i.Extent(l,u,h,c).convertTo((e=>t.unproject(e)))}if("meter"===e.unit){const t=n.locate(r,a*o[0],-s*l[0]),e=n.locate(r,a*o[1],-s*l[1]);return new i.Extent(t,e)}if("degree"===e.unit){const t=r.add(a*o[0],s*l[0]),e=r.add(a*o[1],s*l[1]);return new i.Extent(t,e)}return null}getCellAt(t,e=0){const r=this.getGrid(e),a=this.getMap();if(!this.getGridExtent(e).contains(t))return null;const s=new i.Coordinate(r.center);if("projection"===r.unit){const e=a.coordinateToPoint(s),i=n(this,r),o=a.coordinateToPoint(t);return[Math.floor((o.x-e.x)/i[0]),Math.floor((o.y-e.y)/i[0])]}if("meter"===r.unit){const e=a.computeLength(new i.Coordinate(t.x,s.y),s),n=Math.floor(e/r.width),o=a.computeLength(new i.Coordinate(s.x,t.y),s),l=Math.floor(o/r.height);return[n*(t.x>s.x?1:-1),l*(t.y>s.y?-1:1)]}if("degree"===r.unit){const e=t.x-s.x,i=Math.floor(e/r.width),n=t.y-s.y;return[i,Math.floor(n/r.height)]}return null}getCellGeometry(t,e,r=0){const a=this.getMap(),s=this.getGrid(r),o=new i.Coordinate(s.center);if("projection"===s.unit){const r=a.coordinateToPoint(o),l=n(this,s),h=l[0],u=l[1],c=r.add(h*t,u*e),d=a.pointToCoordinate(c),g=a.pointToCoordinate(c.add(h,0)),f=a.pointToCoordinate(c.add(0,u)),p=a.computeLength(d,g),y=a.computeLength(d,f);return s.altitude&&(d.z=s.altitude),new i.Rectangle(d,p,y)}if("meter"===s.unit){const{width:r,height:n}=s,l=a.locate(o,t*r,-e*n);return s.altitude&&(l.z=s.altitude),new i.Rectangle(l,r,n)}if("degree"===s.unit){const{width:r,height:n}=s,l=o.add(t*r,-e*n),h=l.add(r,0),u=l.add(0,-n),c=a.computeLength(l,h),d=a.computeLength(l,u);return s.altitude&&(l.z=s.altitude),new i.Rectangle(l,c,d)}return null}visitAround(t,e,r=0){const i=this.getGrid(r),n=i.data;if(!t||!i.data||!e)return;const a=[];for(let t=0;tMath.pow(t[0]-s[0],2)+Math.pow(t[1]-s[1],2)-Math.pow(e[0]-s[0],2)-Math.pow(e[1]-s[1],2)));for(let t=0,r=o.length;t[this.getMap()?this.getMap().getZoom():null,null]))}if(!this._compiledSymbols){const t=this.getMap(),e=this.layer.getGridCount();this._compiledSymbols=[];for(let r=0;r{if(!e[2].symbol)return;const a=(s=e[2].properties,function(){return[t.getZoom(),s]});var s;const o=i.Util.convertResourceUrl(e[2].symbol);this._compiledSymbols[r][n]=i.MapboxUtil.loadFunctionTypes(o,a)}))}}}_preDrawGrid(){const t=this.getMap(),e=this.layer.getGridCount(),r=[];for(let n=0;n0||this._compiledGridStyle.polygonColor||this._compiledGridStyle.polygonPatternFile){const t=this._getCellNW(s[1]+1,o[0],a),h=this._getCellNW(s[1]+1,o[1]+1,a),u=this._getCellNW(s[0],o[1]+1,a);if(this.context.beginPath(),this.context.moveTo(l.x,l.y),this.context.lineTo(t.x,t.y),this.context.lineTo(h.x,h.y),this.context.lineTo(u.x,u.y),this.context.closePath(),this.context.fillStyle=this._compiledGridStyle.lineColor,this._compiledGridStyle.polygonOpacity>0?i.Canvas.fillCanvas(this.context,this._compiledGridStyle.polygonOpacity,l.x,l.y):i.Canvas.fillCanvas(this.context,1,l.x,l.y),e<.5||n<.5){r.push(null);continue}}}r.push({cols:s,rows:o,altitude:e.altitude||0,gridInfo:a,p0:l})}return r}_drawGrid(){const t=this._preDrawGrid();for(let e=0;e{t[2].symbol&&r&&(this._drawDataGrid(t,this._compiledSymbols[e][i],r),this._drawLabel(e,t,i,r))}))}}_drawDataGrid(t,e,r){const n=this.context,a=this.getMap().getContainerExtent();let s=!1;const o=Array.isArray(t[0])?t[0]:[t[0],t[0]],l=Array.isArray(t[1])?t[1]:[t[1],t[1]],h=this._getCellNW(o[0],l[0],r);let u,c,d,g,f;for(let t=o[0];t<=o[1];t++)for(let o=l[0];o<=l[1];o++)u=this._getCellNW(t,o,r),c=this._getCellNW(t+1,o,r),d=this._getCellNW(t+1,o+1,r),g=this._getCellNW(t,o+1,r),f=new i.PointExtent(u.x,u.y,d.x,d.y),f.getWidth()>1e4||f.getHeight()>1e4||!a.intersects(f)||(s||(s=!0,this._setCanvasStyle(e),n.beginPath()),n.moveTo(u.x,u.y),n.lineTo(c.x,c.y),n.lineTo(d.x,d.y),n.lineTo(g.x,g.y),n.closePath());s&&(i.Canvas.fillCanvas(n,e.polygonOpacity,h.x,h.y),i.Canvas._stroke(n,e.lineOpacity))}_testSymbol(t){for(let e=l.length-1;e>=0;e--)if(l[e].test(t))return!0;return!1}_drawLabel(t,e,r,n){if(this._gridSymbolTests||(this._gridSymbolTests={}),this._gridSymbolTests[t]||(this._gridSymbolTests[t]=[]),i.Util.isNil(this._gridSymbolTests[t][r])&&(this._gridSymbolTests[t][r]=this._testSymbol(e[2].symbol)),!this._gridSymbolTests[t][r])return;const a=e[2].symbol,s=this.getMap().getExtent();this._dataMarkers||(this._dataMarkers={});let o=this._dataMarkers[t];o||(this._dataMarkers[t]=o=[]);const l=[];if(Array.isArray(e[0])||Array.isArray(e[1])){const t=Array.isArray(e[0])?e[0]:[e[0],e[0]],r=Array.isArray(e[1])?e[1]:[e[1],e[1]];for(let e=t[0];e<=t[1];e++)for(let t=r[0];t<=r[1];t++){const r=this._getCellCenter(e,t,n);s.contains(r)&&l.push(r)}}else{const t=this._getCellCenter(e[0],e[1],n);s.contains(t)&&l.push(t)}if(0===l.length)return;let h=o[r];const u=n.altitude+(this.layer.options.altitude||0);if(u)for(let t=0;t1&&(n-=1)),[360*n,100*a,100*h]},m.rgb.hwb=function(t){var e=t[0],r=t[1],i=t[2];return[m.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(r,i))),100*(i=1-1/255*Math.max(e,Math.max(r,i)))]},m.rgb.cmyk=function(t){var e,r=t[0]/255,i=t[1]/255,n=t[2]/255;return[100*((1-r-(e=Math.min(1-r,1-i,1-n)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-n-e)/(1-e)||0),100*e]},m.rgb.keyword=function(t){var e=p[t];if(e)return e;var r,i,n,a=1/0;for(var s in f)if(f.hasOwnProperty(s)){var o=f[s],l=(i=t,n=o,Math.pow(i[0]-n[0],2)+Math.pow(i[1]-n[1],2)+Math.pow(i[2]-n[2],2));l.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(r=r>.04045?Math.pow((r+.055)/1.055,2.4):r/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*r+.0722*i),100*(.0193*e+.1192*r+.9505*i)]},m.rgb.lab=function(t){var e=m.rgb.xyz(t),r=e[0],i=e[1],n=e[2];return i/=100,n/=108.883,r=(r/=95.047)>.008856?Math.pow(r,1/3):7.787*r+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(r-i),200*(i-(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116))]},m.hsl.rgb=function(t){var e,r,i,n,a,s=t[0]/360,o=t[1]/100,l=t[2]/100;if(0===o)return[a=255*l,a,a];e=2*l-(r=l<.5?l*(1+o):l+o-l*o),n=[0,0,0];for(var h=0;h<3;h++)(i=s+1/3*-(h-1))<0&&i++,i>1&&i--,a=6*i<1?e+6*(r-e)*i:2*i<1?r:3*i<2?e+(r-e)*(2/3-i)*6:e,n[h]=255*a;return n},m.hsl.hsv=function(t){var e=t[0],r=t[1]/100,i=t[2]/100,n=r,a=Math.max(i,.01);return r*=(i*=2)<=1?i:2-i,n*=a<=1?a:2-a,[e,100*(0===i?2*n/(a+n):2*r/(i+r)),100*((i+r)/2)]},m.hsv.rgb=function(t){var e=t[0]/60,r=t[1]/100,i=t[2]/100,n=Math.floor(e)%6,a=e-Math.floor(e),s=255*i*(1-r),o=255*i*(1-r*a),l=255*i*(1-r*(1-a));switch(i*=255,n){case 0:return[i,l,s];case 1:return[o,i,s];case 2:return[s,i,l];case 3:return[s,o,i];case 4:return[l,s,i];case 5:return[i,s,o]}},m.hsv.hsl=function(t){var e,r,i,n=t[0],a=t[1]/100,s=t[2]/100,o=Math.max(s,.01);return i=(2-a)*s,r=a*o,[n,100*(r=(r/=(e=(2-a)*o)<=1?e:2-e)||0),100*(i/=2)]},m.hwb.rgb=function(t){var e,r,i,n,a,s,o,l=t[0]/360,h=t[1]/100,u=t[2]/100,c=h+u;switch(c>1&&(h/=c,u/=c),i=6*l-(e=Math.floor(6*l)),1&e&&(i=1-i),n=h+i*((r=1-u)-h),e){default:case 6:case 0:a=r,s=n,o=h;break;case 1:a=n,s=r,o=h;break;case 2:a=h,s=r,o=n;break;case 3:a=h,s=n,o=r;break;case 4:a=n,s=h,o=r;break;case 5:a=r,s=h,o=n}return[255*a,255*s,255*o]},m.cmyk.rgb=function(t){var e=t[0]/100,r=t[1]/100,i=t[2]/100,n=t[3]/100;return[255*(1-Math.min(1,e*(1-n)+n)),255*(1-Math.min(1,r*(1-n)+n)),255*(1-Math.min(1,i*(1-n)+n))]},m.xyz.rgb=function(t){var e,r,i,n=t[0]/100,a=t[1]/100,s=t[2]/100;return r=-.9689*n+1.8758*a+.0415*s,i=.0557*n+-.204*a+1.057*s,e=(e=3.2406*n+-1.5372*a+-.4986*s)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,r=r>.0031308?1.055*Math.pow(r,1/2.4)-.055:12.92*r,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(r=Math.min(Math.max(0,r),1)),255*(i=Math.min(Math.max(0,i),1))]},m.xyz.lab=function(t){var e=t[0],r=t[1],i=t[2];return r/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116)-16,500*(e-r),200*(r-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},m.lab.xyz=function(t){var e,r,i,n=t[0];e=t[1]/500+(r=(n+16)/116),i=r-t[2]/200;var a=Math.pow(r,3),s=Math.pow(e,3),o=Math.pow(i,3);return r=a>.008856?a:(r-16/116)/7.787,e=s>.008856?s:(e-16/116)/7.787,i=o>.008856?o:(i-16/116)/7.787,[e*=95.047,r*=100,i*=108.883]},m.lab.lch=function(t){var e,r=t[0],i=t[1],n=t[2];return(e=360*Math.atan2(n,i)/2/Math.PI)<0&&(e+=360),[r,Math.sqrt(i*i+n*n),e]},m.lch.lab=function(t){var e,r=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[r,i*Math.cos(e),i*Math.sin(e)]},m.rgb.ansi16=function(t){var e=t[0],r=t[1],i=t[2],n=1 in arguments?arguments[1]:m.rgb.hsv(t)[2];if(0===(n=Math.round(n/50)))return 30;var a=30+(Math.round(i/255)<<2|Math.round(r/255)<<1|Math.round(e/255));return 2===n&&(a+=60),a},m.hsv.ansi16=function(t){return m.rgb.ansi16(m.hsv.rgb(t),t[2])},m.rgb.ansi256=function(t){var e=t[0],r=t[1],i=t[2];return e===r&&r===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(r/255*5)+Math.round(i/255*5)},m.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var r=.5*(1+~~(t>50));return[(1&e)*r*255,(e>>1&1)*r*255,(e>>2&1)*r*255]},m.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var r;return t-=16,[Math.floor(t/36)/5*255,Math.floor((r=t%36)/6)/5*255,r%6/5*255]},m.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},m.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var r=e[0];3===e[0].length&&(r=r.split("").map((function(t){return t+t})).join(""));var i=parseInt(r,16);return[i>>16&255,i>>8&255,255&i]},m.rgb.hcg=function(t){var e,r=t[0]/255,i=t[1]/255,n=t[2]/255,a=Math.max(Math.max(r,i),n),s=Math.min(Math.min(r,i),n),o=a-s;return e=o<=0?0:a===r?(i-n)/o%6:a===i?2+(n-r)/o:4+(r-i)/o+4,e/=6,[360*(e%=1),100*o,100*(o<1?s/(1-o):0)]},m.hsl.hcg=function(t){var e=t[1]/100,r=t[2]/100,i=1,n=0;return(i=r<.5?2*e*r:2*e*(1-r))<1&&(n=(r-.5*i)/(1-i)),[t[0],100*i,100*n]},m.hsv.hcg=function(t){var e=t[1]/100,r=t[2]/100,i=e*r,n=0;return i<1&&(n=(r-i)/(1-i)),[t[0],100*i,100*n]},m.hcg.rgb=function(t){var e=t[0]/360,r=t[1]/100,i=t[2]/100;if(0===r)return[255*i,255*i,255*i];var n,a=[0,0,0],s=e%1*6,o=s%1,l=1-o;switch(Math.floor(s)){case 0:a[0]=1,a[1]=o,a[2]=0;break;case 1:a[0]=l,a[1]=1,a[2]=0;break;case 2:a[0]=0,a[1]=1,a[2]=o;break;case 3:a[0]=0,a[1]=l,a[2]=1;break;case 4:a[0]=o,a[1]=0,a[2]=1;break;default:a[0]=1,a[1]=0,a[2]=l}return n=(1-r)*i,[255*(r*a[0]+n),255*(r*a[1]+n),255*(r*a[2]+n)]},m.hcg.hsv=function(t){var e=t[1]/100,r=e+t[2]/100*(1-e),i=0;return r>0&&(i=e/r),[t[0],100*i,100*r]},m.hcg.hsl=function(t){var e=t[1]/100,r=t[2]/100*(1-e)+.5*e,i=0;return r>0&&r<.5?i=e/(2*r):r>=.5&&r<1&&(i=e/(2*(1-r))),[t[0],100*i,100*r]},m.hcg.hwb=function(t){var e=t[1]/100,r=e+t[2]/100*(1-e);return[t[0],100*(r-e),100*(1-r)]},m.hwb.hcg=function(t){var e=t[1]/100,r=1-t[2]/100,i=r-e,n=0;return i<1&&(n=(r-i)/(1-i)),[t[0],100*i,100*n]},m.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},m.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},m.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},m.gray.hsl=m.gray.hsv=function(t){return[0,0,t[0]]},m.gray.hwb=function(t){return[0,100,t[0]]},m.gray.cmyk=function(t){return[0,0,0,t[0]]},m.gray.lab=function(t){return[t[0],0,0]},m.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),r=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(r.length)+r},m.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]};var _=g.exports,x=_;function M(t){var e=function(){for(var t={},e=Object.keys(x),r=e.length,i=0;i1&&(e=Array.prototype.slice.call(arguments));var r=t(e);if("object"==typeof r)for(var i=r.length,n=0;n1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var P=G,T={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},E={getRgba:R,getHsla:N,getRgb:function(t){var e=R(t);return e&&e.slice(0,3)},getHsl:function(t){var e=N(t);return e&&e.slice(0,3)},getHwb:j,getAlpha:function(t){var e=R(t);if(e)return e[3];if(e=N(t))return e[3];if(e=j(t))return e[3]},hexString:function(t){return"#"+L(t[0])+L(t[1])+L(t[2])},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return O(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:O,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return D(t,e);var r=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),n=Math.round(t[2]/255*100);return"rgb("+r+"%, "+i+"%, "+n+"%)"},percentaString:D,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return U(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:U,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return F[t.slice(0,3)]}};function R(t){if(t){var e=[0,0,0],r=1,i=t.match(/^#([a-fA-F0-9]{3})$/);if(i){i=i[1];for(var n=0;nr?(e+.05)/(r+.05):(r+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){return this.values.hsl[2]+=this.values.hsl[2]*t,this.setValues("hsl",this.values.hsl),this},darken:function(t){return this.values.hsl[2]-=this.values.hsl[2]*t,this.setValues("hsl",this.values.hsl),this},saturate:function(t){return this.values.hsl[1]+=this.values.hsl[1]*t,this.setValues("hsl",this.values.hsl),this},desaturate:function(t){return this.values.hsl[1]-=this.values.hsl[1]*t,this.setValues("hsl",this.values.hsl),this},whiten:function(t){return this.values.hwb[1]+=this.values.hwb[1]*t,this.setValues("hwb",this.values.hwb),this},blacken:function(t){return this.values.hwb[2]+=this.values.hwb[2]*t,this.setValues("hwb",this.values.hwb),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){return this.setValues("alpha",this.values.alpha-this.values.alpha*t),this},opaquer:function(t){return this.setValues("alpha",this.values.alpha+this.values.alpha*t),this},rotate:function(t){var e=this.values.hsl[0];return e=(e=(e+t)%360)<0?360+e:e,this.values.hsl[0]=e,this.setValues("hsl",this.values.hsl),this},mix:function(t,e){var r=this,i=t,n=void 0===e?.5:e,a=2*n-1,s=r.alpha()-i.alpha(),o=((a*s==-1?a:(a+s)/(1+a*s))+1)/2,l=1-o;return this.rgb(o*r.red()+l*i.red(),o*r.green()+l*i.green(),o*r.blue()+l*i.blue()).alpha(r.alpha()*n+i.alpha()*(1-n))},toJSON:function(){return this.rgb()},clone:function(){var t=new q;return t.values=V(this.values),t}},q.prototype.getValues=function(t){for(var e={},r=0;r{this.gl.deleteBuffer(t)})),delete this.gridBuffer,delete this.dataGridBuffer,delete this.dataGridIndexBuffer,delete this.dataColorsBuffer,this._buffers=[]}_meterToPoint(t,e){const r=this.getMap();if(r.getGLZoom){const n=r.getGLZoom(),a=r.locate(t,e,0),s=r.coordToPoint(t,n),o=r.coordToPoint(a,n);return Math.abs(o.x-s.x)*i.Util.sign(e)}if(r.altitudeToPoint)return this._meterToGL||(this._meterToGL=r.altitudeToPoint(1,r.getGLRes())),this._meterToGL*e;{const n=r.getGLRes(),a=r.locate(t,e,0),s=r.coordToPointAtRes(t,n),o=r.coordToPointAtRes(a,n);return Math.abs(o.x-s.x)*i.Util.sign(e)}}_updateUniforms(){this.gl.uniformMatrix4fv(this.program.u_matrix,!1,this.getMap().projViewMatrix)}_useGridProgram(){this.gridProgram||(this.gridProgram=this.createProgram(Z.vertexShader,Z.fragmentShader,["u_matrix","u_color","u_opacity"])),this.useProgram(this.gridProgram),this.program=this.gridProgram}_drawGrid(){this.gridBuffer||(this.gridBuffer=[]),this._useGridProgram();const t=this._preDrawGrid(),e=this.getMap(),r=this.layer.options.altitude||0;for(let i=0;i{g[f++]=t};for(let t=a[0];t<=a[1];t++)l=this._getCellNWPoint(t,s[0],o),h=this._getCellNWPoint(t,s[1],o),[l.x,l.y,u,h.x,h.y,u].forEach(p);for(let t=s[0];t<=s[1];t++)l=this._getCellNWPoint(a[0],t,o),h=this._getCellNWPoint(a[1],t,o),[l.x,l.y,u,h.x,h.y,u].forEach(p);this.gridBuffer[i]||(this.gridBuffer[i]=this.createBuffer());const y=this.gl;y.lineWidth(this._compiledGridStyle.lineWidth||1),this._updateUniforms(),y.uniform1f(this.program.u_opacity,this._compiledGridStyle.lineOpacity||1);const m=Y(this._compiledGridStyle.lineColor||"#000").rgbaArrayNormalized();y.uniform4fv(this.program.u_color,m||[0,0,0,1]),y.bindBuffer(y.ARRAY_BUFFER,this.gridBuffer[i]),y.bufferData(y.ARRAY_BUFFER,g,y.DYNAMIC_DRAW),this.enableVertexAttrib(["a_position",3]),y.drawArrays(y.LINES,0,g.length/3),y.lineWidth(1)}}_useDataGridProgram(){this.dataGridProgram||(this.dataGridProgram=this.createProgram(H.vertexShader,H.fragmentShader,["u_matrix"])),this.useProgram(this.dataGridProgram),this.program=this.dataGridProgram}_glDrawDataGrid(){this.paintedGridNum||(this.paintedGridNum=[],this._dataVertices=[],this._dataColors=[],this._dataIndices=[],this.dataGridBuffer=[],this.dataGridIndexBuffer=[],this.dataColorsBuffer=[]);const t=this.gl;this._useDataGridProgram();const e=this.layer.getGridCount();for(let r=0;r{e[2].symbol&&(t=this._drawDataGrid({vertices:o,colors:l,indices:h},t,e,this._compiledSymbols[r][i],n))}))}this.dataGridBuffer[r]||(o=this._dataVertices[r]=new Float32Array(o),l=this._dataColors[r]=new Uint8Array(l),h=this._dataIndices[r]=new Uint32Array(h),this.dataGridBuffer[r]=this.createBuffer(),this.dataGridIndexBuffer[r]=this.createBuffer(),this.dataColorsBuffer[r]=this.createBuffer()),this._updateUniforms(),t.bindBuffer(t.ARRAY_BUFFER,this.dataGridBuffer[r]),this.enableVertexAttrib([["a_position",3]]),o.length>0&&t.bufferData(t.ARRAY_BUFFER,o,s?t.DYNAMIC_DRAW:t.STATIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.dataColorsBuffer[r]),this.enableVertexAttrib([["a_color",3,"UNSIGNED_BYTE"],["a_opacity",1,"UNSIGNED_BYTE"]]),l.length>0&&t.bufferData(t.ARRAY_BUFFER,l,s?t.DYNAMIC_DRAW:t.STATIC_DRAW),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.dataGridIndexBuffer[r]),h.length>0&&(t.bufferData(t.ELEMENT_ARRAY_BUFFER,h,s?t.DYNAMIC_DRAW:t.STATIC_DRAW),this.paintedGridNum[r]=h.length),t.drawElements(t.TRIANGLES,this.paintedGridNum[r],t.UNSIGNED_INT,0)}t.disableVertexAttribArray(t.getAttribLocation(t.program,"a_position")),t.disableVertexAttribArray(t.getAttribLocation(t.program,"a_color")),t.disableVertexAttribArray(t.getAttribLocation(t.program,"a_opacity"))}_drawDataGrid({vertices:t,indices:e,colors:r},i,n,a,s){const o=this.getMap(),l=Array.isArray(n[0])?n[0]:[n[0],n[0]],h=Array.isArray(n[1])?n[1]:[n[1],n[1]],u=(this.layer.options.altitude||0)+(s.altitude||0);let c=0;u&&(c=this._meterToPoint(o.getCenter(),u));let d=i/3*4,g=i/2;const f=e=>{t[i++]=e},p=t=>{e[g++]=t},y=t=>{r[d++]=t};let m=a.polygonFill,b=void 0===a.polygonOpacity?1:a.polygonOpacity;m||(m="#fff",b=0);const v=Y(m).rgbaArray();let w,_,x,M;v[3]*=255*b;for(let t=l[0];t<=l[1];t++)for(let e=h[0];e<=h[1];e++){w=this._getCellNWPoint(t,e,s),x=this._getCellNWPoint(t+1,e+1,s),_=w.add(x.x-w.x,0),M=w.add(0,x.y-w.y);const r=i/3;p(r),p(r+1),p(r+2),p(r),p(r+2),p(r+3),f(w.x),f(w.y),f(c),v.forEach(y),f(_.x),f(_.y),f(c),v.forEach(y),f(x.x),f(x.y),f(c),v.forEach(y),f(M.x),f(M.y),f(c),v.forEach(y)}return i}_getCellNWPoint(t,e,r){const n=this.getMap(),a=n.getGLRes?n.getGLRes():null,s=n.getGLZoom?n.getGLZoom():null;if("projection"===r.unit){const o=new i.Point(r.center.x+t*r.width,r.center.y+e*r.height);return null!==a?n._pointToPointAtRes(o,a):n._pointToPointAtZoom(o,s)}if("meter"===r.unit){const i=r.center,o=n.locate(i,r.width*t,-r.height*e);return null!==a?n.coordToPointAtRes(o,a):n.coordToPoint(o,s)}if("degree"===r.unit){const i=r.center.add(t*r.width,-e*r.height);return null!==a?n.coordToPointAtRes(i,a):n.coordToPoint(i,s)}return null}_drawAllLabels(){const t=this.layer.getGridCount();for(let e=0;e{this._drawLabel(e,t,i,r)}))}}onRemove(){super.onRemove();const t=this.gl;if(!t)return;this._buffers&&(this._buffers.forEach((function(e){t.deleteBuffer(e)})),delete this._buffers),this._textures&&(this._textures.forEach((e=>t.deleteTexture(e))),delete this._textures);const e=t.program;t.deleteProgram(e),t.deleteShader(e.fragmentShader),t.deleteShader(e.vertexShader),delete this.paintedGridNum,delete this._dataVertices,delete this._dataColors,delete this._dataIndices}onCanvasCreate(){this.canvas2=i.Canvas.createCanvas(this.canvas.width,this.canvas.height);const t=this.gl=this._createGLContext(this.canvas2,this.layer.options.glOptions);t.getExtension("OES_element_index_uint"),t.enable(t.BLEND),t.blendFunc(t.ONE,t.ONE_MINUS_SRC_ALPHA),t.clearColor(0,0,0,0),t.viewport(0,0,this.canvas.width,this.canvas.height)}resizeCanvas(t){this.canvas&&(super.resizeCanvas(t),this.canvas2.width===this.canvas.width&&this.canvas2.height===this.canvas.height||(this.canvas2.width=this.canvas.width,this.canvas2.height=this.canvas.height,this.gl.viewport(0,0,this.canvas.width,this.canvas.height)))}clearCanvas(){this.canvas&&(super.clearCanvas(),this.gl.clear(this.gl.COLOR_BUFFER_BIT))}_drawGlCanvas(){const t=this.context,e=this.getMap(),r=e.getDevicePixelRatio?e.getDevicePixelRatio():2;i.Browser.retina&&(t.save(),t.scale(1/r,1/r)),t.drawImage(this.canvas2,0,0,this.canvas2.width,this.canvas2.height),i.Browser.retina&&t.restore()}createBuffer(){const t=this.gl.createBuffer();if(!t)throw new Error("Failed to create the buffer object");return this._buffers||(this._buffers=[]),this._buffers.push(t),t}enableVertexAttrib(t){const e=this.gl;if(Array.isArray(t[0])){let r=0;for(let e=0;e=0&&(i=i.substring(0,a),n=n.substring(0,a)),t[i]=this._getUniform(t,n)}}_getUniform(t,e){const r=this.gl.getUniformLocation(t,e);if(!r)throw new Error("Failed to get the storage location of "+e);return r}}J.include({copy16:function(){const t=i.Browser.ie9?null:new Float32Array(16);return function(e){for(let r=0;r<16;r++)t[r]=e[r];return t}}()}),o.registerRenderer("canvas",h),o.registerRenderer("gl",J),o.mergeOptions({renderer:"gl"}),t.GridLayer=o,"undefined"!=typeof console&&console.log("maptalks.gridlayer v0.7.0")})); 7 | //# sourceMappingURL=maptalks.gridlayer.js.map 8 | -------------------------------------------------------------------------------- /dist/maptalks.gridlayer.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * maptalks.gridlayer v0.6.9 3 | * LICENSE : MIT 4 | * (c) 2016-2024 maptalks.org 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("maptalks")):"function"==typeof define&&define.amd?define(["exports","maptalks"],e):e(t.maptalks=t.maptalks||{},t.maptalks)}(this,function(t,e){"use strict";function r(t,r){var n=t.getGridProjection(),i=t.getMap(),a=new e.Coordinate(r.center),o=i.coordinateToPoint(a),s=n.project(a)._add(r.width,r.height),l=i._prjToPoint(s);return[Math.abs(l.x-o.x),Math.abs(l.y-o.y)]}function n(t,e){return e={exports:{}},t(e,e.exports),e.exports}function i(t){var e=function(){for(var t={},e=G.length,r=0;r0&&void 0!==arguments[0]?arguments[0]:0,r=this._grids[t];if(!r)return null;var n=r.offset;return n&&(e.Util.isFunction(n)&&(n=n()),r.center[0]=this._gridCenters[t][0]+n[0],r.center[1]=this._gridCenters[t][1]+n[1]),r},n.prototype.setGrid=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return t.unit||(t.unit="projection"),t.center.toArray&&(t.center=t.center.toArray()),this._gridCenters[e]=t.center.slice(0),this._grids[e]=t,this.redraw()},n.prototype.setGridData=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return this._grids[e].data=t,this.redraw()},n.prototype.redraw=function(){var t=this._getRenderer();return t?(t.redraw(),this):this},n.prototype.isEmpty=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;return!this._grids||!this._grids[t]},n.prototype.clear=function(){return delete this._grids,this.fire("clear"),this.redraw()},n.prototype.getGridProjection=function(){return this.options.projectionName?e.projection[this.options.projectionName.toUpperCase()]:this.getMap().getProjection()},n.prototype.getGridExtent=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,r=this.getGrid(t),n=new e.Coordinate(r.center),i=this.getMap(),a=r.width,o=r.height,s=r.cols||[-1/0,1/0],l=r.rows||[-1/0,1/0];if(e.Util.isNil(s[0])&&(s[0]=-1/0),e.Util.isNil(s[1])&&(s[1]=1/0),e.Util.isNil(l[0])&&(l[0]=-1/0),e.Util.isNil(l[1])&&(l[1]=1/0),"projection"===r.unit){var h=this.getGridProjection(),u=h.project(n),c=u.x+s[0]*a,d=u.x+s[1]*a,f=u.y+s[0]*o,g=u.y+s[1]*o,p=i.getFullExtent();return c===-1/0&&(c=p.xmin+1),d===1/0&&(d=p.xmax),f===-1/0&&(f=p.ymin),g===1/0&&(g=p.ymax),new e.Extent(c,f,d,g).convertTo(function(t){return h.unproject(t)})}if("meter"===r.unit){var v=i.locate(n,a*s[0],-o*l[0]),y=i.locate(n,a*s[1],-o*l[1]);return new e.Extent(v,y)}if("degree"===r.unit){var b=n.add(a*s[0],o*l[0]),m=n.add(a*s[1],o*l[1]);return new e.Extent(b,m)}return null},n.prototype.getCellAt=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=this.getGrid(n),a=this.getMap();if(!this.getGridExtent(n).contains(t))return null;var o=new e.Coordinate(i.center);if("projection"===i.unit){var s=a.coordinateToPoint(o),l=r(this,i),h=a.coordinateToPoint(t);return[Math.floor((h.x-s.x)/l[0]),Math.floor((h.y-s.y)/l[0])]}if("meter"===i.unit){var u=a.computeLength(new e.Coordinate(t.x,o.y),o),c=Math.floor(u/i.width),d=a.computeLength(new e.Coordinate(o.x,t.y),o),f=Math.floor(d/i.height);return[c*(t.x>o.x?1:-1),f*(t.y>o.y?-1:1)]}if("degree"===i.unit){var g=t.x-o.x,p=Math.floor(g/i.width),v=t.y-o.y;return[p,Math.floor(v/i.height)]}return null},n.prototype.getCellGeometry=function(t,n){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=this.getMap(),o=this.getGrid(i),s=new e.Coordinate(o.center);if("projection"===o.unit){var l=a.coordinateToPoint(s),h=r(this,o),u=h[0],c=h[1],d=l.add(u*t,c*n),f=a.pointToCoordinate(d),g=a.pointToCoordinate(d.add(u,0)),p=a.pointToCoordinate(d.add(0,c)),v=a.computeLength(f,g),y=a.computeLength(f,p);return new e.Rectangle(f,v,y)}if("meter"===o.unit){var b=o.width,m=o.height,_=a.locate(s,t*b,-n*m);return new e.Rectangle(_,b,m)}if("degree"===o.unit){var w=o.width,x=o.height,M=s.add(t*w,-n*x),C=M.add(w,0),A=M.add(0,-x),S=a.computeLength(M,C),G=a.computeLength(M,A);return new e.Rectangle(M,S,G)}return null},n.prototype.visitAround=function(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,n=this.getGrid(r),i=n.data;if(t&&n.data&&e){for(var a=[],o=0;o1&&void 0!==arguments[1]?arguments[1]:0;if(!t)return null;if(!this.getGridExtent(e).contains(t))return null;var r=this.getCellAt(t,e),n=this.getCellGeometry(r[0],r[1],e);return{col:r[0],row:r[1],geometry:n}},n.prototype.toJSON=function(){var t=this._grids;return{type:"GridLayer",id:this.getId(),options:this.config(),grid:t}},n.fromJSON=function(t){return t&&"GridLayer"===t.type?new n(t.id,t.grid,t.options):null},n}(e.Layer);w.mergeOptions(_),w.registerJSONType("GridLayer");var x=[e.symbolizer.ImageMarkerSymbolizer,e.symbolizer.TextMarkerSymbolizer,e.symbolizer.VectorMarkerSymbolizer,e.symbolizer.VectorPathMarkerSymbolizer],M=function(t){function n(){return y(this,n),m(this,t.apply(this,arguments))}return b(n,t),n.prototype.needToRedraw=function(){var e=this.getMap();return!(!e.getPitch()&&e.isZooming())&&t.prototype.needToRedraw.call(this)},n.prototype.draw=function(){this.layer.getGrid(0)?(this.prepareCanvas(),this._setCanvasStyle(this._compiledGridStyle),this._drawGrid(),this._drawData(),this.completeRender()):this.completeRender()},n.prototype.drawOnInteracting=function(){this.draw()},n.prototype.checkResources=function(){if(this._compileStyles(),this._resources)return null;var t=[];if(this._compiledGridStyle&&(t=e.Util.getExternalResources(this._compiledGridStyle)),this._compiledSymbols)for(var r=0;r0||this._compiledGridStyle.polygonColor||this._compiledGridStyle.polygonPatternFile){var f=this._getCellNW(s[1]+1,l[0],o),g=this._getCellNW(s[1]+1,l[1]+1,o),p=this._getCellNW(s[0],l[1]+1,o);if(this.context.beginPath(),this.context.moveTo(h.x,h.y),this.context.lineTo(f.x,f.y),this.context.lineTo(g.x,g.y),this.context.lineTo(p.x,p.y),this.context.closePath(),this.context.fillStyle=this._compiledGridStyle.lineColor,this._compiledGridStyle.polygonOpacity>0?e.Canvas.fillCanvas(this.context,this._compiledGridStyle.polygonOpacity,h.x,h.y):e.Canvas.fillCanvas(this.context,1,h.x,h.y),c<.5||d<.5){n.push(null);continue}}}n.push({cols:s,rows:l,gridInfo:o,p0:h})}}return n},n.prototype._drawGrid=function(){for(var t=this._preDrawGrid(),r=0;r1e4||g.getHeight()>1e4||!a.intersects(g)||(o||(o=!0,this._setCanvasStyle(r),i.beginPath()),i.moveTo(u.x,u.y),i.lineTo(c.x,c.y),i.lineTo(d.x,d.y),i.lineTo(f.x,f.y),i.closePath());o&&(e.Canvas.fillCanvas(i,r.polygonOpacity,h.x,h.y),e.Canvas._stroke(i,r.lineOpacity))},n.prototype._testSymbol=function(t){for(var e=x.length-1;e>=0;e--)if(x[e].test(t))return!0;return!1},n.prototype._drawLabel=function(t,r,n,i){if(this._gridSymbolTests||(this._gridSymbolTests={}),this._gridSymbolTests[t]||(this._gridSymbolTests[t]=[]),e.Util.isNil(this._gridSymbolTests[t][n])&&(this._gridSymbolTests[t][n]=this._testSymbol(r[2].symbol)),this._gridSymbolTests[t][n]){var a=r[2].symbol,o=this.getMap().getExtent();this._dataMarkers||(this._dataMarkers={});var s=this._dataMarkers[t];s||(this._dataMarkers[t]=s=[]);var l=[];if(Array.isArray(r[0])||Array.isArray(r[1]))for(var h=Array.isArray(r[0])?r[0]:[r[0],r[0]],u=Array.isArray(r[1])?r[1]:[r[1],r[1]],c=h[0];c<=h[1];c++)for(var d=u[0];d<=u[1];d++){var f=this._getCellCenter(c,d,i);o.contains(f)&&l.push(f)}else{var g=this._getCellCenter(r[0],r[1],i);o.contains(g)&&l.push(g)}if(0!==l.length){var p=s[n];if(p){var v=this._toRedraw;p.setCoordinates(l),this._toRedraw=v}else{var y=e.Util.extend({},a);y.markerPlacement="point",y.textPlacement="point",y.lineOpacity=0,y.polygonOpacity=0,(p=new e.LineString(l,{symbol:y,properties:r[2].properties,debug:this.layer.options.debug}))._bindLayer(this.layer),s[n]=p}p._getPainter().paint()}}},n.prototype._setCanvasStyle=function(t){var r=e.Util.extend({},p,t);e.Canvas.prepareCanvas(this.context,r,this._resources)},n.prototype.onRemove=function(){delete this._compiledGridStyle,delete this._compiledSymbols,delete this._gridSymbolTests,delete this._dataMarkers},n}(e.renderer.CanvasRenderer),C=n(function(t){var e=function(){function t(e,n,i,a){function o(e,i){if(null===e)return null;if(0==i)return e;var u,c;if("object"!=(void 0===e?"undefined":v(e)))return e;if(t.__isArray(e))u=[];else if(t.__isRegExp(e))u=new RegExp(e.source,r(e)),e.lastIndex&&(u.lastIndex=e.lastIndex);else if(t.__isDate(e))u=new Date(e.getTime());else{if(h&&Buffer.isBuffer(e))return u=new Buffer(e.length),e.copy(u),u;void 0===a?(c=Object.getPrototypeOf(e),u=Object.create(c)):(u=Object.create(a),c=a)}if(n){var d=s.indexOf(e);if(-1!=d)return l[d];s.push(e),l.push(u)}for(var f in e){var g;c&&(g=Object.getOwnPropertyDescriptor(c,f)),g&&null==g.set||(u[f]=o(e[f],i-1))}return u}"object"===(void 0===n?"undefined":v(n))&&(i=n.depth,a=n.prototype,n.filter,n=n.circular);var s=[],l=[],h="undefined"!=typeof Buffer;return void 0===n&&(n=!0),void 0===i&&(i=1/0),o(e,i)}function e(t){return Object.prototype.toString.call(t)}function r(t){var e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),e}return t.clonePrototype=function(t){if(null===t)return null;var e=function(){};return e.prototype=t,new e},t.__objToStr=e,t.__isDate=function(t){return"object"===(void 0===t?"undefined":v(t))&&"[object Date]"===e(t)},t.__isArray=function(t){return"object"===(void 0===t?"undefined":v(t))&&"[object Array]"===e(t)},t.__isRegExp=function(t){return"object"===(void 0===t?"undefined":v(t))&&"[object RegExp]"===e(t)},t.__getRegExpFlags=r,t}();t.exports&&(t.exports=e)}),A={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},S=n(function(t){function e(t,e){return Math.pow(t[0]-e[0],2)+Math.pow(t[1]-e[1],2)+Math.pow(t[2]-e[2],2)}var r={};for(var n in A)A.hasOwnProperty(n)&&(r[A[n]]=n);var i=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var a in i)if(i.hasOwnProperty(a)){if(!("channels"in i[a]))throw new Error("missing channels property: "+a);if(!("labels"in i[a]))throw new Error("missing channel labels property: "+a);if(i[a].labels.length!==i[a].channels)throw new Error("channel and label counts mismatch: "+a);var o=i[a].channels,s=i[a].labels;delete i[a].channels,delete i[a].labels,Object.defineProperty(i[a],"channels",{value:o}),Object.defineProperty(i[a],"labels",{value:s})}i.rgb.hsl=function(t){var e,r,n,i=t[0]/255,a=t[1]/255,o=t[2]/255,s=Math.min(i,a,o),l=Math.max(i,a,o),h=l-s;return l===s?e=0:i===l?e=(a-o)/h:a===l?e=2+(o-i)/h:o===l&&(e=4+(i-a)/h),(e=Math.min(60*e,360))<0&&(e+=360),n=(s+l)/2,r=l===s?0:n<=.5?h/(l+s):h/(2-l-s),[e,100*r,100*n]},i.rgb.hsv=function(t){var e,r,n,i=t[0],a=t[1],o=t[2],s=Math.min(i,a,o),l=Math.max(i,a,o),h=l-s;return r=0===l?0:h/l*1e3/10,l===s?e=0:i===l?e=(a-o)/h:a===l?e=2+(o-i)/h:o===l&&(e=4+(i-a)/h),(e=Math.min(60*e,360))<0&&(e+=360),n=l/255*1e3/10,[e,r,n]},i.rgb.hwb=function(t){var e=t[0],r=t[1],n=t[2],a=i.rgb.hsl(t)[0],o=1/255*Math.min(e,Math.min(r,n));return n=1-1/255*Math.max(e,Math.max(r,n)),[a,100*o,100*n]},i.rgb.cmyk=function(t){var e,r,n,i,a=t[0]/255,o=t[1]/255,s=t[2]/255;return i=Math.min(1-a,1-o,1-s),e=(1-a-i)/(1-i)||0,r=(1-o-i)/(1-i)||0,n=(1-s-i)/(1-i)||0,[100*e,100*r,100*n,100*i]},i.rgb.keyword=function(t){var n=r[t];if(n)return n;var i,a=1/0;for(var o in A)if(A.hasOwnProperty(o)){var s=e(t,A[o]);s.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(r=r>.04045?Math.pow((r+.055)/1.055,2.4):r/12.92)+.1805*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)),100*(.2126*e+.7152*r+.0722*n),100*(.0193*e+.1192*r+.9505*n)]},i.rgb.lab=function(t){var e,r,n,a=i.rgb.xyz(t),o=a[0],s=a[1],l=a[2];return o/=95.047,s/=100,l/=108.883,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,s=s>.008856?Math.pow(s,1/3):7.787*s+16/116,l=l>.008856?Math.pow(l,1/3):7.787*l+16/116,e=116*s-16,r=500*(o-s),n=200*(s-l),[e,r,n]},i.hsl.rgb=function(t){var e,r,n,i,a,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return a=255*l,[a,a,a];e=2*l-(r=l<.5?l*(1+s):l+s-l*s),i=[0,0,0];for(var h=0;h<3;h++)(n=o+1/3*-(h-1))<0&&n++,n>1&&n--,a=6*n<1?e+6*(r-e)*n:2*n<1?r:3*n<2?e+(r-e)*(2/3-n)*6:e,i[h]=255*a;return i},i.hsl.hsv=function(t){var e,r,n=t[0],i=t[1]/100,a=t[2]/100,o=i,s=Math.max(a,.01);return a*=2,i*=a<=1?a:2-a,o*=s<=1?s:2-s,r=(a+i)/2,e=0===a?2*o/(s+o):2*i/(a+i),[n,100*e,100*r]},i.hsv.rgb=function(t){var e=t[0]/60,r=t[1]/100,n=t[2]/100,i=Math.floor(e)%6,a=e-Math.floor(e),o=255*n*(1-r),s=255*n*(1-r*a),l=255*n*(1-r*(1-a));switch(n*=255,i){case 0:return[n,l,o];case 1:return[s,n,o];case 2:return[o,n,l];case 3:return[o,s,n];case 4:return[l,o,n];case 5:return[n,o,s]}},i.hsv.hsl=function(t){var e,r,n,i=t[0],a=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return n=(2-a)*o,e=(2-a)*s,r=a*s,r/=e<=1?e:2-e,r=r||0,n/=2,[i,100*r,100*n]},i.hwb.rgb=function(t){var e,r,n,i,a=t[0]/360,o=t[1]/100,s=t[2]/100,l=o+s;l>1&&(o/=l,s/=l),r=1-s,n=6*a-(e=Math.floor(6*a)),0!=(1&e)&&(n=1-n),i=o+n*(r-o);var h,u,c;switch(e){default:case 6:case 0:h=r,u=i,c=o;break;case 1:h=i,u=r,c=o;break;case 2:h=o,u=r,c=i;break;case 3:h=o,u=i,c=r;break;case 4:h=i,u=o,c=r;break;case 5:h=r,u=o,c=i}return[255*h,255*u,255*c]},i.cmyk.rgb=function(t){var e,r,n,i=t[0]/100,a=t[1]/100,o=t[2]/100,s=t[3]/100;return e=1-Math.min(1,i*(1-s)+s),r=1-Math.min(1,a*(1-s)+s),n=1-Math.min(1,o*(1-s)+s),[255*e,255*r,255*n]},i.xyz.rgb=function(t){var e,r,n,i=t[0]/100,a=t[1]/100,o=t[2]/100;return e=3.2406*i+-1.5372*a+-.4986*o,r=-.9689*i+1.8758*a+.0415*o,n=.0557*i+-.204*a+1.057*o,e=e>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,r=r>.0031308?1.055*Math.pow(r,1/2.4)-.055:12.92*r,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,e=Math.min(Math.max(0,e),1),r=Math.min(Math.max(0,r),1),n=Math.min(Math.max(0,n),1),[255*e,255*r,255*n]},i.xyz.lab=function(t){var e,r,n,i=t[0],a=t[1],o=t[2];return i/=95.047,a/=100,o/=108.883,i=i>.008856?Math.pow(i,1/3):7.787*i+16/116,a=a>.008856?Math.pow(a,1/3):7.787*a+16/116,o=o>.008856?Math.pow(o,1/3):7.787*o+16/116,e=116*a-16,r=500*(i-a),n=200*(a-o),[e,r,n]},i.lab.xyz=function(t){var e,r,n,i=t[0];e=t[1]/500+(r=(i+16)/116),n=r-t[2]/200;var a=Math.pow(r,3),o=Math.pow(e,3),s=Math.pow(n,3);return r=a>.008856?a:(r-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,n=s>.008856?s:(n-16/116)/7.787,e*=95.047,r*=100,n*=108.883,[e,r,n]},i.lab.lch=function(t){var e,r,n,i=t[0],a=t[1],o=t[2];return e=Math.atan2(o,a),(r=360*e/2/Math.PI)<0&&(r+=360),n=Math.sqrt(a*a+o*o),[i,n,r]},i.lch.lab=function(t){var e,r,n,i=t[0],a=t[1];return n=t[2]/360*2*Math.PI,e=a*Math.cos(n),r=a*Math.sin(n),[i,e,r]},i.rgb.ansi16=function(t){var e=t[0],r=t[1],n=t[2],a=1 in arguments?arguments[1]:i.rgb.hsv(t)[2];if(0===(a=Math.round(a/50)))return 30;var o=30+(Math.round(n/255)<<2|Math.round(r/255)<<1|Math.round(e/255));return 2===a&&(o+=60),o},i.hsv.ansi16=function(t){return i.rgb.ansi16(i.hsv.rgb(t),t[2])},i.rgb.ansi256=function(t){var e=t[0],r=t[1],n=t[2];if(e===r&&r===n)return e<8?16:e>248?231:Math.round((e-8)/247*24)+232;return 16+36*Math.round(e/255*5)+6*Math.round(r/255*5)+Math.round(n/255*5)},i.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),e=e/10.5*255,[e,e,e];var r=.5*(1+~~(t>50));return[(1&e)*r*255,(e>>1&1)*r*255,(e>>2&1)*r*255]},i.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}t-=16;var r;return[Math.floor(t/36)/5*255,Math.floor((r=t%36)/6)/5*255,r%6/5*255]},i.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},i.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var r=e[0];3===e[0].length&&(r=r.split("").map(function(t){return t+t}).join(""));var n=parseInt(r,16);return[n>>16&255,n>>8&255,255&n]},i.rgb.hcg=function(t){var e,r,n=t[0]/255,i=t[1]/255,a=t[2]/255,o=Math.max(Math.max(n,i),a),s=Math.min(Math.min(n,i),a),l=o-s;return e=l<1?s/(1-l):0,r=l<=0?0:o===n?(i-a)/l%6:o===i?2+(a-n)/l:4+(n-i)/l+4,r/=6,r%=1,[360*r,100*l,100*e]},i.hsl.hcg=function(t){var e=t[1]/100,r=t[2]/100,n=1,i=0;return(n=r<.5?2*e*r:2*e*(1-r))<1&&(i=(r-.5*n)/(1-n)),[t[0],100*n,100*i]},i.hsv.hcg=function(t){var e=t[1]/100,r=t[2]/100,n=e*r,i=0;return n<1&&(i=(r-n)/(1-n)),[t[0],100*n,100*i]},i.hcg.rgb=function(t){var e=t[0]/360,r=t[1]/100,n=t[2]/100;if(0===r)return[255*n,255*n,255*n];var i=[0,0,0],a=e%1*6,o=a%1,s=1-o,l=0;switch(Math.floor(a)){case 0:i[0]=1,i[1]=o,i[2]=0;break;case 1:i[0]=s,i[1]=1,i[2]=0;break;case 2:i[0]=0,i[1]=1,i[2]=o;break;case 3:i[0]=0,i[1]=s,i[2]=1;break;case 4:i[0]=o,i[1]=0,i[2]=1;break;default:i[0]=1,i[1]=0,i[2]=s}return l=(1-r)*n,[255*(r*i[0]+l),255*(r*i[1]+l),255*(r*i[2]+l)]},i.hcg.hsv=function(t){var e=t[1]/100,r=e+t[2]/100*(1-e),n=0;return r>0&&(n=e/r),[t[0],100*n,100*r]},i.hcg.hsl=function(t){var e=t[1]/100,r=t[2]/100*(1-e)+.5*e,n=0;return r>0&&r<.5?n=e/(2*r):r>=.5&&r<1&&(n=e/(2*(1-r))),[t[0],100*n,100*r]},i.hcg.hwb=function(t){var e=t[1]/100,r=e+t[2]/100*(1-e);return[t[0],100*(r-e),100*(1-r)]},i.hwb.hcg=function(t){var e=t[1]/100,r=1-t[2]/100,n=r-e,i=0;return n<1&&(i=(r-n)/(1-n)),[t[0],100*n,100*i]},i.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},i.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},i.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},i.gray.hsl=i.gray.hsv=function(t){return[0,0,t[0]]},i.gray.hwb=function(t){return[0,100,t[0]]},i.gray.cmyk=function(t){return[0,0,0,t[0]]},i.gray.lab=function(t){return[t[0],0,0]},i.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),r=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(r.length)+r},i.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}),G=Object.keys(S),P={};Object.keys(S).forEach(function(t){P[t]={},Object.defineProperty(P[t],"channels",{value:S[t].channels}),Object.defineProperty(P[t],"labels",{value:S[t].labels});var e=function(t){for(var e=i(t),r={},n=Object.keys(e),a=n.length,s=0;s1&&(e=Array.prototype.slice.call(arguments));var r=t(e);if("object"===(void 0===r?"undefined":v(r)))for(var n=r.length,i=0;i1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(n)})});var k=P,T={getRgba:s,getHsla:l,getRgb:function(t){var e=s(t);return e&&e.slice(0,3)},getHsl:function(t){var e=l(t);return e&&e.slice(0,3)},getHwb:h,getAlpha:function(t){var e=s(t);return e?e[3]:(e=l(t))?e[3]:(e=h(t))?e[3]:void 0},hexString:function(t){return"#"+g(t[0])+g(t[1])+g(t[2])},rgbString:function(t,e){return e<1||t[3]&&t[3]<1?u(t,e):"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:u,percentString:function(t,e){return e<1||t[3]&&t[3]<1?c(t,e):"rgb("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%)"},percentaString:c,hslString:function(t,e){return e<1||t[3]&&t[3]<1?d(t,e):"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:d,hwbString:function(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return E[t.slice(0,3)]}},E={};for(var R in A)E[A[R]]=R;var N=function t(e){if(e instanceof t)return e;if(!(this instanceof t))return new t(e);this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1};var r;if("string"==typeof e)if(r=T.getRgba(e))this.setValues("rgb",r);else if(r=T.getHsla(e))this.setValues("hsl",r);else{if(!(r=T.getHwb(e)))throw new Error('Unable to parse color from string "'+e+'"');this.setValues("hwb",r)}else if("object"===(void 0===e?"undefined":v(e)))if(void 0!==(r=e).r||void 0!==r.red)this.setValues("rgb",r);else if(void 0!==r.l||void 0!==r.lightness)this.setValues("hsl",r);else if(void 0!==r.v||void 0!==r.value)this.setValues("hsv",r);else if(void 0!==r.w||void 0!==r.whiteness)this.setValues("hwb",r);else{if(void 0===r.c&&void 0===r.cyan)throw new Error("Unable to parse color from object "+JSON.stringify(e));this.setValues("cmyk",r)}};(N.prototype={rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){return 1!==this.values.alpha?this.values.hwb.concat([this.values.alpha]):this.values.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){return this.values.rgb.concat([this.values.alpha])},rgbaArrayNormalized:function(){for(var t=this.values.rgb,e=[],r=0;r<3;r++)e[r]=t[r]/255;return e.push(this.values.alpha),e},hslaArray:function(){return this.values.hsl.concat([this.values.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return T.hexString(this.values.rgb)},rgbString:function(){return T.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return T.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return T.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return T.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return T.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return T.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return T.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){return this.values.rgb[0]<<16|this.values.rgb[1]<<8|this.values.rgb[2]},luminosity:function(){for(var t=this.values.rgb,e=[],r=0;rr?(e+.05)/(r+.05):(r+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){return this.values.hsl[2]+=this.values.hsl[2]*t,this.setValues("hsl",this.values.hsl),this},darken:function(t){return this.values.hsl[2]-=this.values.hsl[2]*t,this.setValues("hsl",this.values.hsl),this},saturate:function(t){return this.values.hsl[1]+=this.values.hsl[1]*t,this.setValues("hsl",this.values.hsl),this},desaturate:function(t){return this.values.hsl[1]-=this.values.hsl[1]*t,this.setValues("hsl",this.values.hsl),this},whiten:function(t){return this.values.hwb[1]+=this.values.hwb[1]*t,this.setValues("hwb",this.values.hwb),this},blacken:function(t){return this.values.hwb[2]+=this.values.hwb[2]*t,this.setValues("hwb",this.values.hwb),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){return this.setValues("alpha",this.values.alpha-this.values.alpha*t),this},opaquer:function(t){return this.setValues("alpha",this.values.alpha+this.values.alpha*t),this},rotate:function(t){var e=this.values.hsl[0];return e=(e+t)%360,e=e<0?360+e:e,this.values.hsl[0]=e,this.setValues("hsl",this.values.hsl),this},mix:function(t,e){var r=t,n=void 0===e?.5:e,i=2*n-1,a=this.alpha()-r.alpha(),o=((i*a==-1?i:(i+a)/(1+i*a))+1)/2,s=1-o;return this.rgb(o*this.red()+s*r.red(),o*this.green()+s*r.green(),o*this.blue()+s*r.blue()).alpha(this.alpha()*n+r.alpha()*(1-n))},toJSON:function(){return this.rgb()},clone:function(){var t=new N;return t.values=C(this.values),t}}).getValues=function(t){for(var e={},r=0;r0&&r.bufferData(r.ARRAY_BUFFER,l,s?r.DYNAMIC_DRAW:r.STATIC_DRAW),r.bindBuffer(r.ARRAY_BUFFER,t.dataColorsBuffer[n]),t.enableVertexAttrib([["a_color",3,"UNSIGNED_BYTE"],["a_opacity",1,"UNSIGNED_BYTE"]]),h.length>0&&r.bufferData(r.ARRAY_BUFFER,h,s?r.DYNAMIC_DRAW:r.STATIC_DRAW),r.bindBuffer(r.ELEMENT_ARRAY_BUFFER,t.dataGridIndexBuffer[n]),u.length>0&&(r.bufferData(r.ELEMENT_ARRAY_BUFFER,u,s?r.DYNAMIC_DRAW:r.STATIC_DRAW),t.paintedGridNum[n]=u.length),r.drawElements(r.TRIANGLES,t.paintedGridNum[n],r.UNSIGNED_INT,0)},a=0;a=0&&(n=n.substring(0,a),i=i.substring(0,a)),t[n]=this._getUniform(t,i)}},r.prototype._getUniform=function(t,e){var r=this.gl.getUniformLocation(t,e);if(!r)throw new Error("Failed to get the storage location of "+e);return r},r}(M);F.include({copy16:function(){var t=e.Browser.ie9?null:new Float32Array(16);return function(e){for(var r=0;r<16;r++)t[r]=e[r];return t}}()}),w.registerRenderer("canvas",M),w.registerRenderer("gl",F),w.mergeOptions({renderer:"gl"}),t.GridLayer=w,Object.defineProperty(t,"__esModule",{value:!0}),"undefined"!=typeof console&&console.log("maptalks.gridlayer v0.6.9")}); -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | const pkg = require('./package.json'); 2 | 3 | module.exports = { 4 | basePath : '.', 5 | frameworks: ['mocha'], 6 | plugins: ['karma-mocha', 'karma-mocha-reporter', 'karma-chrome-launcher'], 7 | files: [ 8 | 'node_modules/maptalks/dist/maptalks.js', 9 | 'dist/' + pkg.name + '.js', 10 | 'test/**/*.js' 11 | ], 12 | preprocessors: { 13 | }, 14 | browsers: ['Chrome'], 15 | reporters: ['mocha'], 16 | singleRun : true 17 | }; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maptalks.gridlayer", 3 | "version": "0.7.0", 4 | "description": "GridLayer plugin for maptalks.js. A layer draws grids.", 5 | "license": "MIT", 6 | "type": "module", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/maptalks/maptalks.gridlayer.git" 10 | }, 11 | "main": "dist/maptalks.gridlayer.js", 12 | "module": "dist/maptalks.gridlayer.es.js", 13 | "jsnext:main": "dist/maptalks.gridlayer.es.js", 14 | "scripts": { 15 | "version": "npm run build && git add -A dist", 16 | "lint": "eslint src/**/*.js test/**/*.js", 17 | "test": "karma start --single-run", 18 | "pretest": "npm run lint", 19 | "prepublish": "npm run lint && npm run build", 20 | "build": "rollup --environment BUILD:production -c rollup.config.js", 21 | "dev": "rollup -w -c rollup.config.js" 22 | }, 23 | "devDependencies": { 24 | "@rollup/plugin-commonjs": "^25.0.7", 25 | "@rollup/plugin-json": "^6.1.0", 26 | "@rollup/plugin-node-resolve": "^15.2.3", 27 | "@rollup/plugin-terser": "0.4.4", 28 | "@rollup/plugin-typescript": "^11.1.6", 29 | "eslint": "^8.57.0", 30 | "eslint-plugin-mocha": "^10.5.0", 31 | "expect.js": "^0.3.1", 32 | "karma": "^6.4.4", 33 | "karma-chrome-launcher": "^3.1.1", 34 | "karma-mocha": "^2.0.1", 35 | "karma-mocha-reporter": "^2.2.5", 36 | "maptalks": "^1.0.5", 37 | "mocha": "^10.3.0", 38 | "rollup": "^4.17.2", 39 | "rollup-plugin-dts": "^6.1.0" 40 | }, 41 | "dependencies": { 42 | "color": "^0.11.4" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | // Rollup plugins 2 | import { nodeResolve as resolve } from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import terser from '@rollup/plugin-terser'; 5 | import json from '@rollup/plugin-json'; 6 | import pkg from './package.json' with { type: "json" }; 7 | 8 | const production = process.env.BUILD === 'production'; 9 | 10 | 11 | const banner = `/*!\n * ${pkg.name} v${pkg.version}\n * LICENSE : ${pkg.license}\n * (c) 2016-${new Date().getFullYear()} maptalks.org\n */`; 12 | 13 | let outro = pkg.name + ' v' + pkg.version; 14 | 15 | 16 | outro = `typeof console !== 'undefined' && console.log('${outro}');`; 17 | 18 | const external = ['maptalks']; 19 | const FILEMANE = pkg.name; 20 | 21 | const plugins = [ 22 | json(), 23 | resolve(), 24 | commonjs() 25 | ]; 26 | 27 | 28 | function getEntry() { 29 | return './src/index.js'; 30 | } 31 | 32 | const bundles = [ 33 | { 34 | input: getEntry(), 35 | plugins: production ? plugins.concat([terser()]) : plugins, 36 | external, 37 | output: { 38 | 'format': 'umd', 39 | 'name': 'maptalks', 40 | 'file': `dist/${FILEMANE}.js`, 41 | 'sourcemap': true, 42 | 'extend': true, 43 | 'banner': banner, 44 | 'outro': outro, 45 | 'globals': { 46 | 'maptalks': 'maptalks' 47 | } 48 | } 49 | }, 50 | { 51 | input: getEntry(), 52 | plugins: plugins, 53 | external, 54 | output: { 55 | 'sourcemap': true, 56 | 'format': 'es', 57 | 'file': `dist/${FILEMANE}.es.js`, 58 | 'extend': true, 59 | 'banner': banner, 60 | 'globals': { 61 | 'maptalks': 'maptalks' 62 | } 63 | } 64 | } 65 | 66 | ]; 67 | 68 | export default production ? bundles : bundles.slice(0, 1); 69 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | import { Coordinate } from 'maptalks'; 2 | 3 | export function getCellPointSize(layer, grid) { 4 | const projection = layer.getGridProjection(), 5 | map = layer.getMap(), 6 | gridCenter = new Coordinate(grid.center), 7 | center = map.coordinateToPoint(gridCenter), 8 | target = projection.project(gridCenter)._add(grid.width, grid.height), 9 | ptarget = map._prjToPoint(target), 10 | width = Math.abs(ptarget.x - center.x), 11 | height = Math.abs(ptarget.y - center.y); 12 | return [width, height]; 13 | } 14 | 15 | 16 | export const defaultSymbol = { 17 | 'lineColor' : '#bbb', 18 | 'lineWidth' : 1, 19 | 'lineOpacity' : 1, 20 | 'lineDasharray': [], 21 | 'lineCap' : 'butt', 22 | 'lineJoin' : 'round', 23 | 'polygonOpacity' : 0 24 | }; 25 | -------------------------------------------------------------------------------- /src/gridlayer.js: -------------------------------------------------------------------------------- 1 | import { getCellPointSize, defaultSymbol } from './common'; 2 | 3 | import * as maptalks from 'maptalks'; 4 | 5 | const options = { 6 | 'symbol' : maptalks.Util.extend({}, defaultSymbol), 7 | 'debug' : false 8 | }; 9 | 10 | /** 11 | * @classdesc 12 | * A layer draws grid. 13 | * A grid config contains: 14 | * { 15 | center : [0, 0], // center of the grid 16 | width : 100, // width of the grid cell 17 | height : 100, // height of the grid cell 18 | unit : 'projection' , // 网格的单位: projection指投影坐标, meter指真实距离, degree指经纬度 19 | altitude: 0, // altitude of the grid 20 | cols : [1, Infinity], 21 | rows : [2, 5], 22 | data : [ 23 | //长度为3的数组, arr[0]是x序号, arr[0]为y的序号, arr[2]为对象, properties属性为数据, symbol属性为样式 24 | [1, 2, { properties : { foo : 1, foo2 : 'foo' }, symbol : { ... } }], 25 | //arr[0]为数组, 表示区间内的格子, 此处表示第5行,2到4列的格子 26 | [[2, 4] , 5, { symbo : {...} }] //[] 27 | ] 28 | } 29 | * @class 30 | * @category layer 31 | * @extends {maptalks.Layer} 32 | * @param {String|Number} id - layer's id 33 | * @param {Object} grid - grid 34 | * @param {Object} [options=null] - construct options 35 | * @param {*} options.* - options defined in [maptalks.Layer]{@link maptalks.Layer#options} 36 | */ 37 | export class GridLayer extends maptalks.Layer { 38 | 39 | static getPainterClass() { 40 | return maptalks.VectorLayer.getPainterClass(); 41 | } 42 | 43 | constructor(id, grids, options) { 44 | super(id, options); 45 | if (!grids) { 46 | this._gridCenters = []; 47 | this._grids = []; 48 | return; 49 | } 50 | if (!Array.isArray(grids)) { 51 | grids = [grids]; 52 | } 53 | 54 | grids.forEach(grid => { 55 | if (!grid['unit']) { 56 | grid['unit'] = 'projection'; 57 | } 58 | if (grid.center.toArray) { 59 | grid.center = grid.center.toArray(); 60 | } 61 | }); 62 | this._gridCenters = grids.map(grid => grid.center.slice(0)); 63 | this._grids = grids; 64 | } 65 | 66 | getGridCount() { 67 | if (!this._grids) { 68 | return 0; 69 | } 70 | return this._grids.length; 71 | } 72 | 73 | /** 74 | * Get grid at given index 75 | * @return {Object} grid object 76 | */ 77 | getGrid(gridIndex = 0) { 78 | const grid = this._grids[gridIndex]; 79 | if (!grid) { 80 | return null; 81 | } 82 | let offset = grid['offset']; 83 | if (offset) { 84 | if (maptalks.Util.isFunction(offset)) { 85 | offset = offset(); 86 | } 87 | grid.center[0] = this._gridCenters[gridIndex][0] + offset[0]; 88 | grid.center[1] = this._gridCenters[gridIndex][1] + offset[1]; 89 | } 90 | return grid; 91 | } 92 | 93 | setGrid(grid, gridIndex = 0) { 94 | if (!grid['unit']) { 95 | grid['unit'] = 'projection'; 96 | } 97 | if (grid.center.toArray) { 98 | grid.center = grid.center.toArray(); 99 | } 100 | this._gridCenters[gridIndex] = grid.center.slice(0); 101 | this._grids[gridIndex] = grid; 102 | return this.redraw(); 103 | } 104 | 105 | setGridData(data, gridIndex = 0) { 106 | this._grids[gridIndex].data = data; 107 | return this.redraw(); 108 | } 109 | 110 | redraw() { 111 | const renderer = this._getRenderer(); 112 | if (!renderer) { 113 | return this; 114 | } 115 | renderer.redraw(); 116 | return this; 117 | } 118 | 119 | isEmpty(gridIndex = 0) { 120 | if (!this._grids || !this._grids[gridIndex]) { 121 | return true; 122 | } 123 | return false; 124 | } 125 | 126 | clear() { 127 | delete this._grids; 128 | this.fire('clear'); 129 | return this.redraw(); 130 | } 131 | 132 | getGridProjection() { 133 | if (this.options['projectionName']) { 134 | return maptalks.projection[this.options['projectionName'].toUpperCase()]; 135 | } else { 136 | return this.getMap().getProjection(); 137 | } 138 | } 139 | 140 | /** 141 | * Get grid's geographic exteng 142 | * @param {Number} [gridIndex=0] - grid's gridIndex 143 | * @return {Extent} 144 | */ 145 | getGridExtent(gridIndex = 0) { 146 | const grid = this.getGrid(gridIndex), 147 | center = new maptalks.Coordinate(grid.center), 148 | map = this.getMap(), 149 | w = grid.width, 150 | h = grid.height, 151 | cols = grid.cols || [-Infinity, Infinity], 152 | rows = grid.rows || [-Infinity, Infinity]; 153 | if (maptalks.Util.isNil(cols[0])) cols[0] = -Infinity; 154 | if (maptalks.Util.isNil(cols[1])) cols[1] = Infinity; 155 | if (maptalks.Util.isNil(rows[0])) rows[0] = -Infinity; 156 | if (maptalks.Util.isNil(rows[1])) rows[1] = Infinity; 157 | if (grid['unit'] === 'projection') { 158 | // meters in projected coordinate system 159 | const projection = this.getGridProjection(), 160 | pcenter = projection.project(center); 161 | let xmin = pcenter.x + cols[0] * w, 162 | xmax = pcenter.x + cols[1] * w, 163 | ymin = pcenter.y + cols[0] * h, 164 | ymax = pcenter.y + cols[1] * h; 165 | const fullExtent = map.getFullExtent(); 166 | if (xmin === -Infinity) { 167 | xmin = fullExtent['xmin'] + 1; 168 | } 169 | if (xmax === Infinity) { 170 | xmax = fullExtent['xmax']; 171 | } 172 | if (ymin === -Infinity) { 173 | ymin = fullExtent['ymin']; 174 | } 175 | if (ymax === Infinity) { 176 | ymax = fullExtent['ymax']; 177 | } 178 | return new maptalks.Extent(xmin, ymin, xmax, ymax).convertTo(c => projection.unproject(c)); 179 | } else if (grid['unit'] === 'meter') { 180 | // distance in geographic meters 181 | const sw = map.locate(center, w * cols[0], -h * rows[0]), 182 | ne = map.locate(center, w * cols[1], -h * rows[1]); 183 | return new maptalks.Extent(sw, ne); 184 | } else if (grid['unit'] === 'degree') { 185 | const sw = center.add(w * cols[0], h * rows[0]), 186 | ne = center.add(w * cols[1], h * rows[1]); 187 | return new maptalks.Extent(sw, ne); 188 | } 189 | return null; 190 | } 191 | 192 | /** 193 | * Get cell index at coordinate 194 | * @param {Coordinate} coordinate 195 | * @param {Number} gridIndex 196 | * @return {Object} 197 | * @private 198 | */ 199 | getCellAt(coordinate, gridIndex = 0) { 200 | const grid = this.getGrid(gridIndex), 201 | map = this.getMap(), 202 | extent = this.getGridExtent(gridIndex); 203 | if (!extent.contains(coordinate)) { 204 | return null; 205 | } 206 | const gridCenter = new maptalks.Coordinate(grid.center); 207 | if (grid['unit'] === 'projection') { 208 | const center = map.coordinateToPoint(gridCenter), 209 | size = getCellPointSize(this, grid); 210 | const p = map.coordinateToPoint(coordinate), 211 | col = Math.floor((p.x - center.x) / size[0]), 212 | row = Math.floor((p.y - center.y) / size[0]); 213 | return [col, row]; 214 | } else if (grid['unit'] === 'meter') { 215 | const distX = map.computeLength(new maptalks.Coordinate(coordinate.x, gridCenter.y), gridCenter), 216 | col = Math.floor(distX / grid.width); 217 | const distY = map.computeLength(new maptalks.Coordinate(gridCenter.x, coordinate.y), gridCenter), 218 | row = Math.floor(distY / grid.height); 219 | const dx = coordinate.x > gridCenter.x ? 1 : -1; 220 | const dy = coordinate.y > gridCenter.y ? -1 : 1; 221 | return [col * dx, row * dy]; 222 | } else if (grid['unit'] === 'degree') { 223 | const distX = coordinate.x - gridCenter.x, 224 | col = Math.floor(distX / grid.width); 225 | const distY = coordinate.y - gridCenter.y, 226 | row = Math.floor(distY / grid.height); 227 | return [col, row]; 228 | } 229 | return null; 230 | } 231 | 232 | /** 233 | * Get cell's geometry 234 | * @param {Number} col cell col 235 | * @param {Number} row cell row 236 | * @param {Number} gridIndex 237 | * @returns {maptalks.Geometry} 238 | */ 239 | getCellGeometry(col, row, gridIndex = 0) { 240 | const map = this.getMap(), 241 | grid = this.getGrid(gridIndex); 242 | const gridCenter = new maptalks.Coordinate(grid.center); 243 | if (grid['unit'] === 'projection') { 244 | const center = map.coordinateToPoint(gridCenter), 245 | size = getCellPointSize(this, grid), 246 | width = size[0], 247 | height = size[1]; 248 | const cnw = center.add(width * col, height * row); 249 | const nw = map.pointToCoordinate(cnw), 250 | ne = map.pointToCoordinate(cnw.add(width, 0)), 251 | sw = map.pointToCoordinate(cnw.add(0, height)); 252 | const w = map.computeLength(nw, ne), 253 | h = map.computeLength(nw, sw); 254 | if (grid.altitude) { 255 | nw.z = grid.altitude; 256 | } 257 | return new maptalks.Rectangle(nw, w, h); 258 | } else if (grid['unit'] === 'meter') { 259 | const { width, height } = grid; 260 | const nw = map.locate(gridCenter, col * width, -row * height); 261 | if (grid.altitude) { 262 | nw.z = grid.altitude; 263 | } 264 | return new maptalks.Rectangle(nw, width, height); 265 | } else if (grid['unit'] === 'degree') { 266 | const { width, height } = grid; 267 | const nw = gridCenter.add(col * width, -row * height); 268 | const ne = nw.add(width, 0); 269 | const sw = nw.add(0, -height); 270 | const w = map.computeLength(nw, ne), 271 | h = map.computeLength(nw, sw); 272 | if (grid.altitude) { 273 | nw.z = grid.altitude; 274 | } 275 | return new maptalks.Rectangle(nw, w, h); 276 | } 277 | return null; 278 | } 279 | 280 | /** 281 | * Visit data cells around given coordinate 282 | * @param {maptalks.Coordinate} coordinate 283 | * @param {Function} cb callback function, parameter is [col, row, { properties, symbol }], return false to break the visiting 284 | * @param {Number} gridIndex 285 | */ 286 | visitAround(coordinate, cb, gridIndex = 0) { 287 | const grid = this.getGrid(gridIndex), 288 | gridData = grid.data; 289 | if (!coordinate || !grid.data || !cb) { 290 | return; 291 | } 292 | const data = []; 293 | for (let i = 0; i < gridData.length; i++) { 294 | let cols = gridData[i][0], 295 | rows = gridData[i][1]; 296 | const value = gridData[i][2]; 297 | if (!Array.isArray(cols)) { 298 | cols = [cols, cols]; 299 | } 300 | if (!Array.isArray(rows)) { 301 | rows = [rows, rows]; 302 | } 303 | for (let ii = cols[0]; ii <= cols[1]; ii++) { 304 | for (let iii = rows[0]; iii <= rows[1]; iii++) { 305 | data.push([ii, iii, value]); 306 | } 307 | } 308 | } 309 | if (!data.length) { 310 | return; 311 | } 312 | 313 | const startCell = this.getCellAt(coordinate, gridIndex); 314 | if (!startCell) { 315 | return; 316 | } 317 | //根据与开始网格的距离对所有网格排序 318 | const sorted = data.sort((a, b) => { 319 | return Math.pow((a[0] - startCell[0]), 2) + Math.pow((a[1] - startCell[1]), 2) - Math.pow((b[0] - startCell[0]), 2) - Math.pow((b[1] - startCell[1]), 2); 320 | }); 321 | 322 | for (let i = 0, l = sorted.length; i < l; i++) { 323 | if (cb(sorted[i]) === false) { 324 | break; 325 | } 326 | } 327 | } 328 | 329 | /** 330 | * Return cell index and cell geometry at coordinate 331 | * @param {maptalks.Coordinate} coordinate coordinate 332 | * @param {Number} gridIndex 333 | * @returns {Object} { col : col, row : row, geometry : cellGeometry } 334 | */ 335 | identify(coordinate, gridIndex = 0) { 336 | if (!coordinate) { 337 | return null; 338 | } 339 | const extent = this.getGridExtent(gridIndex); 340 | if (!extent.contains(coordinate)) { 341 | return null; 342 | } 343 | const idx = this.getCellAt(coordinate, gridIndex); 344 | const rectangle = this.getCellGeometry(idx[0], idx[1], gridIndex); 345 | return { 346 | col : idx[0], 347 | row : idx[1], 348 | geometry : rectangle 349 | }; 350 | } 351 | 352 | /** 353 | * Export the GridLayer's JSON. 354 | * @return {Object} layer's JSON 355 | */ 356 | toJSON() { 357 | const grid = this._grids; 358 | return { 359 | 'type' : 'GridLayer', 360 | 'id' : this.getId(), 361 | 'options' : this.config(), 362 | 'grid' : grid 363 | }; 364 | } 365 | 366 | /** 367 | * Reproduce a GridLayer from layer's JSON. 368 | * @param {Object} json - layer's JSON 369 | * @return {maptalks.GridLayer} 370 | * @static 371 | * @private 372 | * @function 373 | */ 374 | static fromJSON(json) { 375 | if (!json || json['type'] !== 'GridLayer') { return null; } 376 | return new GridLayer(json['id'], json['grid'], json['options']); 377 | } 378 | } 379 | 380 | GridLayer.mergeOptions(options); 381 | 382 | GridLayer.registerJSONType('GridLayer'); 383 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { GridLayer } from './gridlayer'; 2 | import GridCanvasRenderer from './renderer/canvas-renderer'; 3 | import GridGLRenderer from './renderer/gl-renderer'; 4 | 5 | GridLayer.registerRenderer('canvas', GridCanvasRenderer); 6 | GridLayer.registerRenderer('gl', GridGLRenderer); 7 | 8 | GridLayer.mergeOptions({ 9 | 'renderer' : 'gl' 10 | }); 11 | 12 | export { GridLayer }; 13 | -------------------------------------------------------------------------------- /src/renderer/canvas-renderer.js: -------------------------------------------------------------------------------- 1 | import * as maptalks from 'maptalks'; 2 | import { getCellPointSize, defaultSymbol } from '../common'; 3 | 4 | const symbolizers = [ 5 | maptalks.symbolizer.ImageMarkerSymbolizer, 6 | maptalks.symbolizer.TextMarkerSymbolizer, 7 | maptalks.symbolizer.VectorMarkerSymbolizer, 8 | maptalks.symbolizer.VectorPathMarkerSymbolizer 9 | ]; 10 | 11 | export default class GridCanvasRenderer extends maptalks.renderer.CanvasRenderer { 12 | 13 | needToRedraw() { 14 | const map = this.getMap(); 15 | if (!map.getPitch() && map.isZooming()) { 16 | return false; 17 | } 18 | return super.needToRedraw(); 19 | } 20 | 21 | draw() { 22 | const grid = this.layer.getGrid(0); 23 | if (!grid) { 24 | this.completeRender(); 25 | return; 26 | } 27 | this.prepareCanvas(); 28 | this._setCanvasStyle(this._compiledGridStyle); 29 | this._drawGrid(); 30 | this._drawData(); 31 | this.completeRender(); 32 | } 33 | 34 | drawOnInteracting() { 35 | this.draw(); 36 | } 37 | 38 | checkResources() { 39 | this._compileStyles(); 40 | if (this._resources) { 41 | return null; 42 | } 43 | let resources = []; 44 | if (this._compiledGridStyle) { 45 | resources = maptalks.Util.getExternalResources(this._compiledGridStyle); 46 | } 47 | if (this._compiledSymbols) { 48 | for (let i = 0; i < this._compiledSymbols.length; i++) { 49 | const gridSymbols = this._compiledSymbols[i]; 50 | for (let ii = 0; ii < gridSymbols.length; ii++) { 51 | const c = maptalks.Util.getExternalResources(gridSymbols[ii]); 52 | if (c) { 53 | resources = resources.concat(c); 54 | } 55 | } 56 | } 57 | } 58 | return resources; 59 | } 60 | 61 | redraw() { 62 | this.reset(); 63 | this.setToRedraw(); 64 | } 65 | 66 | reset() { 67 | delete this._compiledSymbols; 68 | } 69 | 70 | _compileStyles() { 71 | if (!this._compiledGridStyle) { 72 | const symbol = maptalks.Util.convertResourceUrl(this.layer.options['symbol']); 73 | this._compiledGridStyle = maptalks.MapboxUtil.loadFunctionTypes(symbol, () => { 74 | return [this.getMap() ? this.getMap().getZoom() : null, null]; 75 | }); 76 | } 77 | if (!this._compiledSymbols) { 78 | const map = this.getMap(), 79 | gridCount = this.layer.getGridCount(); 80 | this._compiledSymbols = []; 81 | for (let i = 0; i < gridCount; i++) { 82 | const grid = this.layer.getGrid(i), 83 | data = grid['data']; 84 | this._compiledSymbols[i] = []; 85 | if (data) { 86 | data.forEach((gridData, index) => { 87 | if (!gridData[2]['symbol']) { 88 | return; 89 | } 90 | const argFn = (function (props) { 91 | return function () { 92 | return [map.getZoom(), props]; 93 | }; 94 | })(gridData[2]['properties']); 95 | const s = maptalks.Util.convertResourceUrl(gridData[2]['symbol']); 96 | this._compiledSymbols[i][index] = maptalks.MapboxUtil.loadFunctionTypes(s, argFn); 97 | }); 98 | } 99 | } 100 | 101 | } 102 | } 103 | 104 | _preDrawGrid() { 105 | const map = this.getMap(); 106 | const count = this.layer.getGridCount(); 107 | const gridInfos = []; 108 | for (let i = 0; i < count; i++) { 109 | const grid = this.layer.getGrid(i), 110 | gridInfo = grid['unit'] === 'projection' ? this._getProjGridToDraw(grid, i) : this._getGridToDraw(grid, i); 111 | if (!gridInfo || this._compiledGridStyle.lineOpacity <= 0 || this._compiledGridStyle.lineWidth <= 0) { 112 | gridInfos.push(null); 113 | continue; 114 | } 115 | const cols = gridInfo.cols, 116 | rows = gridInfo.rows; 117 | const p0 = this._getCellNW(cols[0], rows[0], gridInfo); 118 | if (map.getPitch() === 0) { 119 | const p1 = this._getCellNW(cols[0] + 1, rows[0] + 1, gridInfo); 120 | const width = Math.abs(p0.x - p1.x); 121 | const height = Math.abs(p0.y - p1.y); 122 | if (width < 0.5 || height < 0.5 || (this._compiledGridStyle['polygonOpacity'] > 0 || this._compiledGridStyle['polygonColor'] || this._compiledGridStyle['polygonPatternFile'])) { 123 | const p2 = this._getCellNW(cols[1] + 1, rows[0], gridInfo); 124 | const p3 = this._getCellNW(cols[1] + 1, rows[1] + 1, gridInfo); 125 | const p4 = this._getCellNW(cols[0], rows[1] + 1, gridInfo); 126 | this.context.beginPath(); 127 | // this.context.rect(p0.x, p0.y, p2.x - p0.x, p2.y - p0.y); 128 | this.context.moveTo(p0.x, p0.y); 129 | this.context.lineTo(p2.x, p2.y); 130 | this.context.lineTo(p3.x, p3.y); 131 | this.context.lineTo(p4.x, p4.y); 132 | this.context.closePath(); 133 | 134 | this.context.fillStyle = this._compiledGridStyle.lineColor; 135 | if (this._compiledGridStyle['polygonOpacity'] > 0) { 136 | maptalks.Canvas.fillCanvas(this.context, this._compiledGridStyle['polygonOpacity'], p0.x, p0.y); 137 | } else { 138 | maptalks.Canvas.fillCanvas(this.context, 1, p0.x, p0.y); 139 | } 140 | if (width < 0.5 || height < 0.5) { 141 | gridInfos.push(null); 142 | continue; 143 | } 144 | } 145 | } 146 | gridInfos.push({ 147 | cols : cols, 148 | rows : rows, 149 | altitude : grid.altitude || 0, 150 | gridInfo : gridInfo, 151 | p0 : p0 152 | }); 153 | } 154 | return gridInfos; 155 | } 156 | 157 | _drawGrid() { 158 | const colRows = this._preDrawGrid(); 159 | for (let i = 0; i < colRows.length; i++) { 160 | const colRow = colRows[i]; 161 | if (!colRow) { 162 | continue; 163 | } 164 | this.context.beginPath(); 165 | const cols = colRow['cols'], 166 | rows = colRow['rows'], 167 | gridInfo = colRow['gridInfo'], 168 | p0 = colRow['p0']; 169 | let p1, p2; 170 | for (let i = cols[0]; i <= cols[1]; i++) { 171 | p1 = this._getCellNW(i, rows[0], gridInfo); 172 | p2 = this._getCellNW(i, rows[1], gridInfo); 173 | this.context.moveTo(p1.x, p1.y); 174 | this.context.lineTo(p2.x, p2.y); 175 | } 176 | for (let i = rows[0]; i <= rows[1]; i++) { 177 | p1 = this._getCellNW(cols[0], i, gridInfo); 178 | p2 = this._getCellNW(cols[1], i, gridInfo); 179 | this.context.moveTo(p1.x, p1.y); 180 | this.context.lineTo(p2.x, p2.y); 181 | } 182 | maptalks.Canvas._stroke(this.context, this._compiledGridStyle['lineOpacity'], p0.x, p0.y); 183 | } 184 | 185 | } 186 | 187 | _getProjGridToDraw(grid) { 188 | const map = this.getMap(), 189 | // projection = map.getProjection(), 190 | extent = map._get2DExtent(), 191 | gridX = grid.cols || [-Infinity, Infinity], 192 | gridY = grid.rows || [-Infinity, Infinity], 193 | gridCenter = new maptalks.Coordinate(grid.center), 194 | center = map.coordinateToPoint(gridCenter), 195 | size = getCellPointSize(this.layer, grid), 196 | width = size[0], 197 | height = size[1]; 198 | if (maptalks.Util.isNil(gridX[0])) gridX[0] = -Infinity; 199 | if (maptalks.Util.isNil(gridX[1])) gridX[1] = Infinity; 200 | if (maptalks.Util.isNil(gridY[0])) gridY[0] = -Infinity; 201 | if (maptalks.Util.isNil(gridY[1])) gridY[1] = Infinity; 202 | const gridExtent = new maptalks.PointExtent( 203 | center.x + gridX[0] * width, 204 | center.y + gridY[0] * height, 205 | center.x + gridX[1] * width, 206 | center.y + gridY[1] * height 207 | ); 208 | const intersection = extent.intersection(gridExtent); 209 | if (!intersection) { 210 | return null; 211 | } 212 | const delta = 1E-6; 213 | const cols = [ 214 | -Math.ceil((center.x - intersection.xmin - delta) / width), 215 | Math.ceil((intersection.xmax - center.x - delta) / width) 216 | ]; 217 | const rows = [ 218 | -Math.ceil((center.y - intersection.ymin - delta) / height), 219 | Math.ceil((intersection.ymax - center.y - delta) / height) 220 | ]; 221 | return { 222 | cols : cols, 223 | rows : rows, 224 | altitude: grid.altitude || 0, 225 | width : width, 226 | height : height, 227 | center : center, 228 | unit : grid.unit 229 | }; 230 | } 231 | 232 | _getGridToDraw(grid, index) { 233 | const map = this.getMap(), 234 | // projection = map.getProjection(), 235 | extent = map.getExtent(), 236 | gridCenter = new maptalks.Coordinate(grid.center), 237 | gridExtent = this.layer.getGridExtent(index), 238 | w = grid.width, 239 | h = grid.height; 240 | 241 | const intersection = extent.intersection(gridExtent); 242 | if (!intersection) { 243 | return null; 244 | } 245 | const delta = 1E-6; 246 | let cols, rows; 247 | if (grid['unit'] === 'meter') { 248 | const dx1 = map.computeLength(new maptalks.Coordinate(intersection.xmin, gridCenter.y), gridCenter), 249 | dx2 = map.computeLength(new maptalks.Coordinate(intersection.xmax, gridCenter.y), gridCenter); 250 | //经纬度里,ymax在上方,ymin在下方,和projection时是反的 251 | const dy1 = map.computeLength(new maptalks.Coordinate(gridCenter.x, intersection.ymax), gridCenter), 252 | dy2 = map.computeLength(new maptalks.Coordinate(gridCenter.x, intersection.ymin), gridCenter); 253 | cols = [ 254 | -Math.round(dx1 / grid.width), 255 | Math.round(dx2 / grid.width) 256 | ]; 257 | rows = [ 258 | -Math.round(dy1 / grid.height), 259 | Math.round(dy2 / grid.height) 260 | ]; 261 | } else if (grid['unit'] === 'degree') { 262 | cols = [ 263 | -Math.ceil((gridCenter.x - intersection.xmin - delta) / w), 264 | Math.ceil((intersection.xmax - gridCenter.x - delta) / w) 265 | ]; 266 | rows = [ 267 | -Math.ceil((intersection.ymax - gridCenter.y - delta) / h), 268 | Math.ceil((gridCenter.y - intersection.ymin - delta) / h) 269 | ]; 270 | } 271 | 272 | return { 273 | cols : cols, 274 | rows : rows, 275 | center : gridCenter, 276 | altitude: grid.altitude || 0, 277 | unit : grid.unit, 278 | width : grid.width, 279 | height : grid.height 280 | }; 281 | } 282 | 283 | _getCellNWPoint(col, row, gridInfo, targetZ) { 284 | const map = this.getMap(); 285 | if (gridInfo['unit'] === 'projection') { 286 | const p = new maptalks.Point( 287 | gridInfo.center.x + col * gridInfo.width, 288 | gridInfo.center.y + row * gridInfo.height 289 | ); 290 | if (map._pointToPointAtZoom) { 291 | return map._pointToPointAtZoom(p, targetZ); 292 | } else { 293 | return map._pointToPointAtRes(p, map.getResolution(targetZ)); 294 | } 295 | } else if (gridInfo['unit'] === 'meter') { 296 | const center = gridInfo.center; 297 | const target = map.locate(center, gridInfo.width * col, -gridInfo.height * row); 298 | return map.coordToPoint(target, targetZ); 299 | } else if (gridInfo['unit'] === 'degree') { 300 | const center = gridInfo.center; 301 | const target = center.add(col * gridInfo.width, -row * gridInfo.height); 302 | return map.coordToPoint(target, targetZ); 303 | } 304 | return null; 305 | } 306 | 307 | _getCellNW(col, row, gridInfo) { 308 | const point = this._getCellNWPoint(col, row, gridInfo); 309 | return this.getMap()._pointToContainerPoint(point); 310 | } 311 | 312 | _getCellCenter(col, row, gridInfo) { 313 | const map = this.getMap(); 314 | if (gridInfo['unit'] === 'projection') { 315 | const p = new maptalks.Point( 316 | gridInfo.center.x + (col + 1 / 2) * gridInfo.width, 317 | gridInfo.center.y + (row + 1 / 2) * gridInfo.height 318 | ); 319 | return map.pointToCoordinate(p); 320 | } else if (gridInfo['unit'] === 'meter') { 321 | const center = gridInfo.center; 322 | return map.locate(center, gridInfo.width * (col + 1 / 2), -gridInfo.height * (row + 1 / 2)); 323 | } else if (gridInfo['unit'] === 'degree') { 324 | const center = gridInfo.center; 325 | return center.add(gridInfo.width * (col + 1 / 2), -gridInfo.height * (row + 1 / 2)); 326 | } 327 | return null; 328 | } 329 | 330 | _drawData() { 331 | const count = this.layer.getGridCount(); 332 | for (let i = 0; i < count; i++) { 333 | const grid = this.layer.getGrid(i), 334 | gridInfo = grid['unit'] === 'projection' ? this._getProjGridToDraw(grid, i) : this._getGridToDraw(grid, i), 335 | data = grid['data']; 336 | if (!Array.isArray(data) || !data.length) { 337 | return; 338 | } 339 | data.forEach((gridData, index) => { 340 | if (!gridData[2]['symbol'] || !gridInfo) { 341 | return; 342 | } 343 | this._drawDataGrid(gridData, this._compiledSymbols[i][index], gridInfo); 344 | this._drawLabel(i, gridData, index, gridInfo); 345 | }); 346 | } 347 | 348 | } 349 | 350 | _drawDataGrid(gridData, symbol, gridInfo) { 351 | const ctx = this.context, 352 | map = this.getMap(), 353 | mapExtent = map.getContainerExtent(); 354 | let painted = false; 355 | const cols = Array.isArray(gridData[0]) ? gridData[0] : [gridData[0], gridData[0]], 356 | rows = Array.isArray(gridData[1]) ? gridData[1] : [gridData[1], gridData[1]], 357 | p0 = this._getCellNW(cols[0], rows[0], gridInfo); 358 | let p1, p2, p3, p4, gridExtent; 359 | for (let i = cols[0]; i <= cols[1]; i++) { 360 | for (let ii = rows[0]; ii <= rows[1]; ii++) { 361 | p1 = this._getCellNW(i, ii, gridInfo); 362 | p2 = this._getCellNW(i + 1, ii, gridInfo); 363 | p3 = this._getCellNW(i + 1, ii + 1, gridInfo); 364 | p4 = this._getCellNW(i, ii + 1, gridInfo); 365 | gridExtent = new maptalks.PointExtent(p1.x, p1.y, p3.x, p3.y); 366 | // marker as an invalid grid if width or height is abnormally large, due to containerPoint conversion 367 | if (gridExtent.getWidth() > 1E4 || gridExtent.getHeight() > 1E4 || !mapExtent.intersects(gridExtent)) { 368 | continue; 369 | } 370 | if (!painted) { 371 | painted = true; 372 | this._setCanvasStyle(symbol); 373 | ctx.beginPath(); 374 | } 375 | ctx.moveTo(p1.x, p1.y); 376 | ctx.lineTo(p2.x, p2.y); 377 | ctx.lineTo(p3.x, p3.y); 378 | ctx.lineTo(p4.x, p4.y); 379 | ctx.closePath(); 380 | } 381 | } 382 | if (painted) { 383 | maptalks.Canvas.fillCanvas(ctx, symbol['polygonOpacity'], p0.x, p0.y); 384 | maptalks.Canvas._stroke(ctx, symbol['lineOpacity']); 385 | } 386 | } 387 | 388 | _testSymbol(symbol) { 389 | for (let i = symbolizers.length - 1; i >= 0; i--) { 390 | if (symbolizers[i].test(symbol)) { 391 | return true; 392 | } 393 | } 394 | return false; 395 | } 396 | 397 | _drawLabel(gridIndex, gridData, index, gridInfo) { 398 | if (!this._gridSymbolTests) { 399 | this._gridSymbolTests = {}; 400 | } 401 | if (!this._gridSymbolTests[gridIndex]) { 402 | this._gridSymbolTests[gridIndex] = []; 403 | } 404 | if (maptalks.Util.isNil(this._gridSymbolTests[gridIndex][index])) { 405 | this._gridSymbolTests[gridIndex][index] = this._testSymbol(gridData[2]['symbol']); 406 | } 407 | if (!this._gridSymbolTests[gridIndex][index]) { 408 | return; 409 | } 410 | const symbol = gridData[2]['symbol']; 411 | const map = this.getMap(), 412 | extent = map.getExtent(); 413 | if (!this._dataMarkers) { 414 | this._dataMarkers = {}; 415 | } 416 | let dataMarkers = this._dataMarkers[gridIndex]; 417 | if (!dataMarkers) { 418 | this._dataMarkers[gridIndex] = dataMarkers = []; 419 | } 420 | const coordinates = []; 421 | if (!Array.isArray(gridData[0]) && !Array.isArray(gridData[1])) { 422 | const p = this._getCellCenter(gridData[0], gridData[1], gridInfo); 423 | if (extent.contains(p)) { 424 | coordinates.push(p); 425 | } 426 | } else { 427 | const cols = Array.isArray(gridData[0]) ? gridData[0] : [gridData[0], gridData[0]], 428 | rows = Array.isArray(gridData[1]) ? gridData[1] : [gridData[1], gridData[1]]; 429 | for (let i = cols[0]; i <= cols[1]; i++) { 430 | for (let ii = rows[0]; ii <= rows[1]; ii++) { 431 | const p = this._getCellCenter(i, ii, gridInfo); 432 | if (extent.contains(p)) { 433 | coordinates.push(p); 434 | } 435 | } 436 | } 437 | } 438 | 439 | if (coordinates.length === 0) { 440 | return; 441 | } 442 | let line = dataMarkers[index]; 443 | const altitude = gridInfo.altitude + (this.layer.options['altitude'] || 0); 444 | if (altitude) { 445 | for (let i = 0; i < coordinates.length; i++) { 446 | coordinates[i].z = altitude; 447 | } 448 | } 449 | if (!line) { 450 | const lineSymbol = maptalks.Util.extend({}, symbol); 451 | lineSymbol['markerPlacement'] = 'point'; 452 | lineSymbol['textPlacement'] = 'point'; 453 | lineSymbol['lineOpacity'] = 0; 454 | lineSymbol['polygonOpacity'] = 0; 455 | line = new maptalks.LineString(coordinates, { 456 | 'symbol' : lineSymbol, 457 | 'properties' : gridData[2]['properties'], 458 | 'debug' : this.layer.options['debug'] 459 | }); 460 | line._bindLayer(this.layer); 461 | dataMarkers[index] = line; 462 | } else { 463 | const redraw = this._toRedraw; 464 | line.setCoordinates(coordinates); 465 | this._toRedraw = redraw; 466 | } 467 | line._getPainter().paint(); 468 | } 469 | 470 | _setCanvasStyle(symbol) { 471 | const s = maptalks.Util.extend({}, defaultSymbol, symbol); 472 | maptalks.Canvas.prepareCanvas(this.context, s, this._resources); 473 | } 474 | 475 | onRemove() { 476 | delete this._compiledGridStyle; 477 | delete this._compiledSymbols; 478 | delete this._gridSymbolTests; 479 | delete this._dataMarkers; 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /src/renderer/gl-renderer.js: -------------------------------------------------------------------------------- 1 | import GridCanvasRenderer from './canvas-renderer'; 2 | import * as maptalks from 'maptalks'; 3 | import Color from 'color'; 4 | 5 | const shaders = { 6 | 'vertexShader' : ` 7 | attribute vec3 a_position; 8 | 9 | uniform mat4 u_matrix; 10 | 11 | void main() { 12 | gl_Position = u_matrix * vec4(a_position, 1.0); 13 | } 14 | `, 15 | // fragment shader, can be replaced by layer.options.fragmentShader 16 | 'fragmentShader' : ` 17 | precision mediump float; 18 | 19 | uniform float u_opacity; 20 | 21 | uniform vec4 u_color; 22 | 23 | void main() { 24 | gl_FragColor = u_color * u_opacity; 25 | } 26 | ` 27 | }; 28 | 29 | const dataGridShaders = { 30 | 'vertexShader' : ` 31 | attribute vec3 a_position; 32 | attribute vec3 a_color; 33 | attribute float a_opacity; 34 | 35 | varying vec4 v_color; 36 | 37 | uniform mat4 u_matrix; 38 | 39 | void main() { 40 | v_color = vec4(a_color / 255.0, 1.0) * (a_opacity / 255.0); 41 | gl_Position = u_matrix * vec4(a_position, 1.0); 42 | } 43 | `, 44 | // fragment shader, can be replaced by layer.options.fragmentShader 45 | 'fragmentShader' : ` 46 | precision mediump float; 47 | 48 | varying vec4 v_color; 49 | 50 | void main() { 51 | vec4 color = v_color; 52 | // color = vec4(0.0, 0.0, 0.0, 1.0); 53 | gl_FragColor = color; 54 | } 55 | ` 56 | }; 57 | 58 | export default class GridGLRenderer extends GridCanvasRenderer { 59 | 60 | draw() { 61 | const grid = this.layer.getGrid(); 62 | if (!grid) { 63 | this.completeRender(); 64 | return; 65 | } 66 | this.prepareCanvas(); 67 | this._setCanvasStyle(this._compiledGridStyle); 68 | this._drawGrid(); 69 | this._glDrawDataGrid(); 70 | this._drawGlCanvas(); 71 | this._drawAllLabels(); 72 | this.completeRender(); 73 | } 74 | 75 | drawOnInteracting() { 76 | this.draw(); 77 | } 78 | 79 | reset() { 80 | super.reset(); 81 | delete this.paintedGridNum; 82 | delete this._dataVertices; 83 | delete this._dataColors; 84 | delete this._dataIndices; 85 | if (this.gl) { 86 | if (this._buffers) { 87 | this._buffers.forEach(buffer => { 88 | this.gl.deleteBuffer(buffer); 89 | }); 90 | } 91 | } 92 | delete this.gridBuffer; 93 | delete this.dataGridBuffer; 94 | delete this.dataGridIndexBuffer; 95 | delete this.dataColorsBuffer; 96 | this._buffers = []; 97 | } 98 | 99 | _meterToPoint(center, altitude) { 100 | const map = this.getMap(); 101 | if (map.getGLZoom) { 102 | // compliance for older version of maptalks 103 | const z = map.getGLZoom(); 104 | const target = map.locate(center, altitude, 0); 105 | const p0 = map.coordToPoint(center, z), 106 | p1 = map.coordToPoint(target, z); 107 | return Math.abs(p1.x - p0.x) * maptalks.Util.sign(altitude); 108 | } else if (map.altitudeToPoint) { 109 | if (!this._meterToGL) { 110 | this._meterToGL = map.altitudeToPoint(1, map.getGLRes()); 111 | } 112 | return this._meterToGL * altitude; 113 | } else { 114 | const res = map.getGLRes(); 115 | const target = map.locate(center, altitude, 0); 116 | const p0 = map.coordToPointAtRes(center, res), 117 | p1 = map.coordToPointAtRes(target, res); 118 | return Math.abs(p1.x - p0.x) * maptalks.Util.sign(altitude); 119 | } 120 | } 121 | 122 | _updateUniforms() { 123 | const gl = this.gl; 124 | gl.uniformMatrix4fv(this.program['u_matrix'], false, this.getMap().projViewMatrix); 125 | } 126 | 127 | _useGridProgram() { 128 | if (!this.gridProgram) { 129 | this.gridProgram = this.createProgram(shaders['vertexShader'], shaders['fragmentShader'], ['u_matrix', 'u_color', 'u_opacity']); 130 | } 131 | this.useProgram(this.gridProgram); 132 | this.program = this.gridProgram; 133 | } 134 | 135 | _drawGrid() { 136 | if (!this.gridBuffer) { 137 | this.gridBuffer = []; 138 | } 139 | this._useGridProgram(); 140 | const colRows = this._preDrawGrid(); 141 | const map = this.getMap(); 142 | const layerAltitude = this.layer.options['altitude'] || 0; 143 | for (let i = 0; i < colRows.length; i++) { 144 | const colRow = colRows[i]; 145 | if (!colRow) { 146 | continue; 147 | } 148 | const cols = colRow['cols'], 149 | rows = colRow['rows'], 150 | gridInfo = colRow['gridInfo']; 151 | let p1, p2; 152 | let z = 0; 153 | const altitude = (colRow.altitude || 0) + layerAltitude; 154 | if (altitude) { 155 | z = this._meterToPoint(map.getCenter(), altitude); 156 | } 157 | 158 | const count = (cols[1] - cols[0] + 1) * 6 + (rows[1] - rows[0] + 1) * 6; 159 | const vertices = new Float32Array(count); 160 | let c = 0; 161 | const set = p => { 162 | vertices[c++] = p; 163 | }; 164 | //划线 165 | for (let i = cols[0]; i <= cols[1]; i++) { 166 | p1 = this._getCellNWPoint(i, rows[0], gridInfo); 167 | p2 = this._getCellNWPoint(i, rows[1], gridInfo); 168 | [p1.x, p1.y, z, p2.x, p2.y, z].forEach(set); 169 | } 170 | for (let i = rows[0]; i <= rows[1]; i++) { 171 | p1 = this._getCellNWPoint(cols[0], i, gridInfo); 172 | p2 = this._getCellNWPoint(cols[1], i, gridInfo); 173 | [p1.x, p1.y, z, p2.x, p2.y, z].forEach(set); 174 | } 175 | 176 | if (!this.gridBuffer[i]) { 177 | this.gridBuffer[i] = this.createBuffer(); 178 | } 179 | const gl = this.gl; 180 | gl.lineWidth(this._compiledGridStyle.lineWidth || 1); 181 | 182 | this._updateUniforms(); 183 | 184 | gl.uniform1f(this.program['u_opacity'], this._compiledGridStyle.lineOpacity || 1); 185 | const color = Color(this._compiledGridStyle.lineColor || '#000').rgbaArrayNormalized(); 186 | gl.uniform4fv(this.program['u_color'], color || [0, 0, 0, 1]); 187 | 188 | gl.bindBuffer(gl.ARRAY_BUFFER, this.gridBuffer[i]); 189 | gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW); 190 | this.enableVertexAttrib(['a_position', 3]); 191 | gl.drawArrays(gl.LINES, 0, vertices.length / 3); 192 | gl.lineWidth(1); 193 | } 194 | } 195 | 196 | _useDataGridProgram() { 197 | if (!this.dataGridProgram) { 198 | this.dataGridProgram = this.createProgram(dataGridShaders['vertexShader'], dataGridShaders['fragmentShader'], ['u_matrix']); 199 | } 200 | this.useProgram(this.dataGridProgram); 201 | this.program = this.dataGridProgram; 202 | } 203 | 204 | _glDrawDataGrid() { 205 | if (!this.paintedGridNum) { 206 | this.paintedGridNum = []; 207 | this._dataVertices = []; 208 | this._dataColors = []; 209 | this._dataIndices = []; 210 | this.dataGridBuffer = []; 211 | this.dataGridIndexBuffer = []; 212 | this.dataColorsBuffer = []; 213 | } 214 | const gl = this.gl; 215 | this._useDataGridProgram(); 216 | const count = this.layer.getGridCount(); 217 | for (let i = 0; i < count; i++) { 218 | const grid = this.layer.getGrid(i), 219 | gridInfo = grid['unit'] === 'projection' ? this._getProjGridToDraw(grid, i) : this._getGridToDraw(grid, i), 220 | data = grid['data']; 221 | if (!gridInfo || !Array.isArray(data) || !data.length) { 222 | continue; 223 | } 224 | 225 | const isDynamic = maptalks.Util.isFunction(grid.offset); 226 | let vertices = this._dataVertices[i] || [], colors = this._dataColors[i] || []; 227 | let indices = this._dataIndices[i] || []; 228 | if (!this.paintedGridNum[i] || isDynamic) { 229 | let c = 0; 230 | data.forEach((gridData, index) => { 231 | if (!gridData[2]['symbol']) { 232 | return; 233 | } 234 | c = this._drawDataGrid({ vertices, colors, indices }, c, gridData, this._compiledSymbols[i][index], gridInfo); 235 | }); 236 | } 237 | 238 | if (!this.dataGridBuffer[i]) { 239 | vertices = this._dataVertices[i] = new Float32Array(vertices); 240 | colors = this._dataColors[i] = new Uint8Array(colors); 241 | indices = this._dataIndices[i] = new Uint32Array(indices); 242 | this.dataGridBuffer[i] = this.createBuffer(); 243 | this.dataGridIndexBuffer[i] = this.createBuffer(); 244 | this.dataColorsBuffer[i] = this.createBuffer(); 245 | } 246 | 247 | this._updateUniforms(); 248 | 249 | gl.bindBuffer(gl.ARRAY_BUFFER, this.dataGridBuffer[i]); 250 | this.enableVertexAttrib([['a_position', 3]]); 251 | if (vertices.length > 0) { 252 | gl.bufferData(gl.ARRAY_BUFFER, vertices, isDynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW); 253 | } 254 | 255 | gl.bindBuffer(gl.ARRAY_BUFFER, this.dataColorsBuffer[i]); 256 | this.enableVertexAttrib([['a_color', 3, 'UNSIGNED_BYTE'], ['a_opacity', 1, 'UNSIGNED_BYTE']]); 257 | if (colors.length > 0) { 258 | gl.bufferData(gl.ARRAY_BUFFER, colors, isDynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW); 259 | } 260 | 261 | // Write the indices to the buffer object 262 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.dataGridIndexBuffer[i]); 263 | if (indices.length > 0) { 264 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, isDynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW); 265 | this.paintedGridNum[i] = indices.length; 266 | } 267 | gl.drawElements(gl.TRIANGLES, this.paintedGridNum[i], gl.UNSIGNED_INT, 0); 268 | 269 | } 270 | 271 | gl.disableVertexAttribArray(gl.getAttribLocation(gl.program, 'a_position')); 272 | gl.disableVertexAttribArray(gl.getAttribLocation(gl.program, 'a_color')); 273 | gl.disableVertexAttribArray(gl.getAttribLocation(gl.program, 'a_opacity')); 274 | } 275 | 276 | _drawDataGrid({ vertices, indices, colors }, c, gridData, symbol, gridInfo) { 277 | const map = this.getMap(); 278 | const cols = Array.isArray(gridData[0]) ? gridData[0] : [gridData[0], gridData[0]], 279 | rows = Array.isArray(gridData[1]) ? gridData[1] : [gridData[1], gridData[1]]; 280 | const altitude = (this.layer.options['altitude'] || 0) + (gridInfo.altitude || 0); 281 | let z = 0; 282 | if (altitude) { 283 | z = this._meterToPoint(map.getCenter(), altitude); 284 | } 285 | 286 | let b = c / 3 * 4, 287 | a = c / 2; 288 | 289 | const set = p => { 290 | vertices[c++] = p; 291 | }; 292 | 293 | const setIndices = p => { 294 | indices[a++] = p; 295 | }; 296 | 297 | const setColor = p => { 298 | colors[b++] = p; 299 | }; 300 | 301 | let color = symbol['polygonFill']; 302 | let opacity = symbol['polygonOpacity'] === undefined ? 1 : symbol['polygonOpacity']; 303 | if (!color) { 304 | color = '#fff'; 305 | opacity = 0; 306 | } 307 | 308 | const style = Color(color).rgbaArray(); 309 | style[3] *= opacity * 255; 310 | 311 | let p1, p2, p3, p4; 312 | for (let i = cols[0]; i <= cols[1]; i++) { 313 | for (let ii = rows[0]; ii <= rows[1]; ii++) { 314 | p1 = this._getCellNWPoint(i, ii, gridInfo); 315 | p3 = this._getCellNWPoint(i + 1, ii + 1, gridInfo); 316 | 317 | p2 = p1.add(p3.x - p1.x, 0); 318 | // p3 = p1.add(w, h); 319 | p4 = p1.add(0, p3.y - p1.y); 320 | const idx = c / 3; 321 | setIndices(idx); 322 | setIndices(idx + 1); 323 | setIndices(idx + 2); 324 | setIndices(idx); 325 | setIndices(idx + 2); 326 | setIndices(idx + 3); 327 | set(p1.x); 328 | set(p1.y); 329 | set(z); 330 | style.forEach(setColor); 331 | set(p2.x); 332 | set(p2.y); 333 | set(z); 334 | style.forEach(setColor); 335 | set(p3.x); 336 | set(p3.y); 337 | set(z); 338 | style.forEach(setColor); 339 | set(p4.x); 340 | set(p4.y); 341 | set(z); 342 | style.forEach(setColor); 343 | } 344 | } 345 | 346 | return c; 347 | } 348 | 349 | 350 | 351 | _getCellNWPoint(col, row, gridInfo) { 352 | const map = this.getMap(); 353 | const res = map.getGLRes ? map.getGLRes() : null; 354 | const glZoom = map.getGLZoom ? map.getGLZoom() : null; 355 | if (gridInfo['unit'] === 'projection') { 356 | const p = new maptalks.Point( 357 | gridInfo.center.x + col * gridInfo.width, 358 | gridInfo.center.y + row * gridInfo.height 359 | ); 360 | if (res !== null) { 361 | return map._pointToPointAtRes(p, res); 362 | } else { 363 | return map._pointToPointAtZoom(p, glZoom); 364 | } 365 | } else if (gridInfo['unit'] === 'meter') { 366 | const center = gridInfo.center; 367 | const target = map.locate(center, gridInfo.width * col, -gridInfo.height * row); 368 | if (res !== null) { 369 | return map.coordToPointAtRes(target, res); 370 | } else { 371 | return map.coordToPoint(target, glZoom); 372 | } 373 | } else if (gridInfo['unit'] === 'degree') { 374 | const center = gridInfo.center; 375 | const target = center.add(col * gridInfo.width, -row * gridInfo.height); 376 | if (res !== null) { 377 | return map.coordToPointAtRes(target, res); 378 | } else { 379 | return map.coordToPoint(target, glZoom); 380 | } 381 | } 382 | return null; 383 | } 384 | 385 | 386 | 387 | _drawAllLabels() { 388 | const count = this.layer.getGridCount(); 389 | for (let i = 0; i < count; i++) { 390 | const grid = this.layer.getGrid(i), 391 | gridInfo = grid['unit'] === 'projection' ? this._getProjGridToDraw(grid, i) : this._getGridToDraw(grid, i), 392 | data = grid['data']; 393 | if (!gridInfo || !Array.isArray(data) || !data.length) { 394 | continue; 395 | } 396 | data.forEach((gridData, index) => { 397 | this._drawLabel(i, gridData, index, gridInfo); 398 | }); 399 | } 400 | 401 | } 402 | 403 | onRemove() { 404 | super.onRemove(); 405 | // release resources 406 | const gl = this.gl; 407 | if (!gl) { 408 | return; 409 | } 410 | if (this._buffers) { 411 | this._buffers.forEach(function (b) { 412 | gl.deleteBuffer(b); 413 | }); 414 | delete this._buffers; 415 | } 416 | if (this._textures) { 417 | this._textures.forEach(t => gl.deleteTexture(t)); 418 | delete this._textures; 419 | } 420 | const program = gl.program; 421 | gl.deleteProgram(program); 422 | gl.deleteShader(program.fragmentShader); 423 | gl.deleteShader(program.vertexShader); 424 | delete this.paintedGridNum; 425 | delete this._dataVertices; 426 | delete this._dataColors; 427 | delete this._dataIndices; 428 | } 429 | 430 | onCanvasCreate() { 431 | //create a canvas2 to draw grids with webgl 432 | //texts will be still drawn by (this.canvas + this.context) 433 | this.canvas2 = maptalks.Canvas.createCanvas(this.canvas.width, this.canvas.height); 434 | const gl = this.gl = this._createGLContext(this.canvas2, this.layer.options['glOptions']); 435 | gl.getExtension('OES_element_index_uint'); 436 | gl.enable(gl.BLEND); 437 | gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); 438 | gl.clearColor(0.0, 0.0, 0.0, 0.0); 439 | gl.viewport(0, 0, this.canvas.width, this.canvas.height); 440 | // const map = this.getMap(); 441 | // gl.viewport(0, 0, map.width, map.height); 442 | // gl.disable(gl.DEPTH_TEST); 443 | // gl.disable(gl.STENCIL_TEST); 444 | } 445 | 446 | resizeCanvas(canvasSize) { 447 | if (!this.canvas) { 448 | return; 449 | } 450 | super.resizeCanvas(canvasSize); 451 | if (this.canvas2.width !== this.canvas.width || this.canvas2.height !== this.canvas.height) { 452 | this.canvas2.width = this.canvas.width; 453 | this.canvas2.height = this.canvas.height; 454 | this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); 455 | } 456 | } 457 | 458 | clearCanvas() { 459 | if (!this.canvas) { 460 | return; 461 | } 462 | super.clearCanvas(); 463 | this.gl.clear(this.gl.COLOR_BUFFER_BIT); 464 | } 465 | 466 | _drawGlCanvas() { 467 | const ctx = this.context; 468 | const map = this.getMap(); 469 | const dpr = map.getDevicePixelRatio ? map.getDevicePixelRatio() : 2; 470 | if (maptalks.Browser.retina) { 471 | ctx.save(); 472 | // ctx.translate(map.width / 2 / dpr, map.height / 2 / dpr); 473 | ctx.scale(1 / dpr, 1 / dpr); 474 | } 475 | // draw gl canvas on layer canvas 476 | ctx.drawImage(this.canvas2, 0, 0, this.canvas2.width, this.canvas2.height); 477 | if (maptalks.Browser.retina) { 478 | ctx.restore(); 479 | } 480 | } 481 | 482 | //----------------------- webgl utils unlike to change --------------------------------- 483 | 484 | createBuffer() { 485 | const gl = this.gl; 486 | // Create the buffer object 487 | const buffer = gl.createBuffer(); 488 | if (!buffer) { 489 | throw new Error('Failed to create the buffer object'); 490 | } 491 | if (!this._buffers) { 492 | this._buffers = []; 493 | } 494 | this._buffers.push(buffer); 495 | return buffer; 496 | } 497 | 498 | /** 499 | * 500 | * @param {Array} attributes [[name, stride, type], [name, stride, type]...] 501 | */ 502 | enableVertexAttrib(attributes) { 503 | const gl = this.gl; 504 | if (Array.isArray(attributes[0])) { 505 | let STRIDE = 0; 506 | for (let i = 0; i < attributes.length; i++) { 507 | STRIDE += (attributes[i][1] || 0); 508 | } 509 | let offset = 0; 510 | for (let i = 0; i < attributes.length; i++) { 511 | const attr = gl.getAttribLocation(gl.program, attributes[i][0]); 512 | if (attr < 0) { 513 | throw new Error('Failed to get the storage location of ' + attributes[i][0]); 514 | } 515 | let FSIZE; 516 | if (!attributes[i][2] || attributes[i][2] === 'FLOAT') { 517 | FSIZE = 4; 518 | } else if (attributes[i][2] === 'BYTE' || attributes[i][2] === 'UNSIGNED_BYTE') { 519 | FSIZE = 1; 520 | } else { 521 | FSIZE = 2; 522 | } 523 | gl.enableVertexAttribArray(attr); 524 | gl.vertexAttribPointer(attr, attributes[i][1], gl[attributes[i][2] || 'FLOAT'], false, FSIZE * STRIDE, FSIZE * offset); 525 | offset += (attributes[i][1] || 0); 526 | 527 | } 528 | } else { 529 | const attr = gl.getAttribLocation(gl.program, attributes[0]); 530 | gl.enableVertexAttribArray(attr); 531 | gl.vertexAttribPointer(attr, attributes[1], gl[attributes[2] || 'FLOAT'], false, 0, 0); 532 | } 533 | } 534 | 535 | /** 536 | * Create the linked program object 537 | * @param vshader a vertex shader program (string) 538 | * @param fshader a fragment shader program (string) 539 | * @return created program object, or null if the creation has failed 540 | */ 541 | createProgram(vshader, fshader, uniforms) { 542 | const gl = this.gl; 543 | // Create shader object 544 | const vertexShader = this._compileShader(gl, gl.VERTEX_SHADER, vshader); 545 | const fragmentShader = this._compileShader(gl, gl.FRAGMENT_SHADER, fshader); 546 | if (!vertexShader || !fragmentShader) { 547 | return null; 548 | } 549 | 550 | // Create a program object 551 | const program = gl.createProgram(); 552 | if (!program) { 553 | return null; 554 | } 555 | 556 | // Attach the shader objects 557 | gl.attachShader(program, vertexShader); 558 | gl.attachShader(program, fragmentShader); 559 | 560 | // Link the program object 561 | gl.linkProgram(program); 562 | gl.vertexShader = vertexShader; 563 | gl.fragmentShader = fragmentShader; 564 | // Check the result of linking 565 | const linked = gl.getProgramParameter(program, gl.LINK_STATUS); 566 | if (!linked) { 567 | const error = gl.getProgramInfoLog(program); 568 | gl.deleteProgram(program); 569 | gl.deleteShader(fragmentShader); 570 | gl.deleteShader(vertexShader); 571 | throw new Error('Failed to link program: ' + error); 572 | } 573 | 574 | this._initUniforms(program, uniforms); 575 | 576 | return program; 577 | } 578 | 579 | useProgram(program) { 580 | const gl = this.gl; 581 | gl.useProgram(program); 582 | gl.program = program; 583 | return this; 584 | } 585 | 586 | enableSampler(sampler, texIdx) { 587 | const gl = this.gl; 588 | const uSampler = this._getUniform(gl.program, sampler); 589 | if (!texIdx) { 590 | texIdx = 0; 591 | } 592 | // Set the texture unit to the sampler 593 | gl.uniform1i(uSampler, texIdx); 594 | return uSampler; 595 | } 596 | 597 | _createGLContext(canvas) { 598 | const attributes = { 599 | 'alpha': true, 600 | 'preserveDrawingBuffer': true 601 | }; 602 | const names = ['webgl', 'experimental-webgl']; 603 | let context = null; 604 | /* eslint-disable no-empty */ 605 | for (let i = 0; i < names.length; ++i) { 606 | try { 607 | context = canvas.getContext(names[i], attributes); 608 | } catch (e) {} 609 | if (context) { 610 | break; 611 | } 612 | } 613 | return context; 614 | /* eslint-enable no-empty */ 615 | } 616 | 617 | /** 618 | * Create a shader object 619 | * @param gl GL context 620 | * @param type the type of the shader object to be created 621 | * @param source shader program (string) 622 | * @return created shader object, or null if the creation has failed. 623 | */ 624 | _compileShader(gl, type, source) { 625 | // Create shader object 626 | const shader = gl.createShader(type); 627 | if (shader == null) { 628 | throw new Error('unable to create shader'); 629 | } 630 | 631 | // Set the shader program 632 | gl.shaderSource(shader, source); 633 | // Compile the shader 634 | gl.compileShader(shader); 635 | 636 | // Check the result of compilation 637 | const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); 638 | if (!compiled) { 639 | const error = gl.getShaderInfoLog(shader); 640 | 641 | gl.deleteShader(shader); 642 | throw new Error('Failed to compile shader: ' + error); 643 | } 644 | 645 | return shader; 646 | } 647 | 648 | _initUniforms(program, uniforms) { 649 | for (let i = 0; i < uniforms.length; i++) { 650 | let name = uniforms[i]; 651 | let uniform = uniforms[i]; 652 | const b = name.indexOf('['); 653 | if (b >= 0) { 654 | name = name.substring(0, b); 655 | uniform = uniform.substring(0, b); 656 | } 657 | program[name] = this._getUniform(program, uniform); 658 | } 659 | } 660 | 661 | _getUniform(program, uniformName) { 662 | const gl = this.gl; 663 | const uniform = gl.getUniformLocation(program, uniformName); 664 | if (!uniform) { 665 | throw new Error('Failed to get the storage location of ' + uniformName); 666 | } 667 | return uniform; 668 | } 669 | } 670 | 671 | GridGLRenderer.include({ 672 | 673 | copy16: function () { 674 | const m = maptalks.Browser.ie9 ? null : new Float32Array(16); 675 | return function (arr) { 676 | for (let i = 0; i < 16; i++) { 677 | m[i] = arr[i]; 678 | } 679 | return m; 680 | }; 681 | }() 682 | }); 683 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect" : true, 7 | "sinon": true, 8 | "happen": true 9 | }, 10 | "plugins": ["mocha"], 11 | "rules": { 12 | "no-var" : 0, 13 | "prefer-const": 0 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | describe('GridLayer', function () { 2 | var container, map; 3 | beforeEach(function () { 4 | container = document.createElement('div'); 5 | container.style.width = '400px'; 6 | container.style.height = '300px'; 7 | document.body.appendChild(container); 8 | map = new maptalks.Map(container, { 9 | center : [0, 0], 10 | zoom : 17 11 | }); 12 | }); 13 | 14 | afterEach(function () { 15 | map.remove(); 16 | maptalks.DomUtil.removeDomNode(container); 17 | }); 18 | 19 | it('add to map', function (done) { 20 | var layer = new maptalks.GridLayer('g', { 21 | projection : true, 22 | center : map.getCenter(), 23 | width : 100, 24 | height : 100, 25 | cols : [-5, 5], 26 | rows : [-5, 5] 27 | }, { 28 | renderer : 'canvas' 29 | }); 30 | layer.on('layerload', function () { 31 | expect(layer).to.be.painted(); 32 | done(); 33 | }) 34 | .addTo(map); 35 | }); 36 | 37 | it('add to map with altitude', function (done) { 38 | var layer = new maptalks.GridLayer('g', { 39 | projection : true, 40 | center : map.getCenter(), 41 | width : 100, 42 | height : 100, 43 | altitude: 100, 44 | cols : [-5, 5], 45 | rows : [-5, 5] 46 | }, { 47 | renderer : 'canvas' 48 | }); 49 | layer.on('layerload', function () { 50 | expect(layer).to.be.painted(); 51 | done(); 52 | }) 53 | .addTo(map); 54 | }); 55 | 56 | it('add again', function (done) { 57 | var layer = new maptalks.GridLayer('g', { 58 | projection : true, 59 | center : map.getCenter(), 60 | width : 100, 61 | height : 100, 62 | cols : [-5, 5], 63 | rows : [-5, 5] 64 | }, { 65 | renderer : 'canvas' 66 | }); 67 | layer.once('layerload', function () { 68 | expect(layer).to.be.painted(); 69 | map.removeLayer(layer); 70 | layer.once('layerload', function () { 71 | expect(layer).to.be.painted(); 72 | done(); 73 | }); 74 | map.addLayer(layer); 75 | }); 76 | map.addLayer(layer); 77 | }); 78 | 79 | it('with a symbol', function (done) { 80 | var symbol = { 81 | 'lineColor' : '#000', 82 | 'lineOpacity' : 1, 83 | 'polygonFill' : 'rgb(0, 0, 0)', 84 | 'polygonOpacity' : 0.4 85 | }; 86 | var layer = new maptalks.GridLayer('g', { 87 | projection : true, 88 | center : map.getCenter(), 89 | width : 100, 90 | height : 100, 91 | cols : [-5, 5], 92 | rows : [-5, 5] 93 | }, { 94 | renderer : 'canvas', 95 | 'symbol' : symbol 96 | }); 97 | layer.on('layerload', function () { 98 | expect(layer).to.be.painted(); 99 | done(); 100 | }) 101 | .addTo(map); 102 | }); 103 | 104 | it('multiple grids', function (done) { 105 | var symbol = { 106 | 'lineColor' : '#000', 107 | 'lineOpacity' : 1, 108 | 'polygonFill' : 'rgb(0, 0, 0)', 109 | 'polygonOpacity' : 0.4 110 | }; 111 | var layer = new maptalks.GridLayer('g', [ 112 | { 113 | projection : true, 114 | center : map.getCenter(), 115 | width : 100, 116 | height : 100, 117 | cols : [-5, 5], 118 | rows : [-5, 5], 119 | data : [ 120 | [0, 0, { properties : 1 }] 121 | ] 122 | }, 123 | { 124 | projection : true, 125 | center : map.getCenter(), 126 | width : 50, 127 | height : 50, 128 | cols : [-5, 5], 129 | rows : [-5, 5], 130 | data : [ 131 | [0, 0, { properties : 1 }] 132 | ] 133 | } 134 | ], { 135 | renderer : 'canvas', 136 | 'symbol' : symbol 137 | }); 138 | layer.on('layerload', function () { 139 | expect(layer).to.be.painted(); 140 | done(); 141 | }) 142 | .addTo(map); 143 | }); 144 | 145 | it('can identify', function (done) { 146 | var layer = new maptalks.GridLayer('g', { 147 | projection : true, 148 | center : map.getCenter(), 149 | width : 100, 150 | height : 100, 151 | cols : [-5, 5], 152 | rows : [-5, 5], 153 | data : [ 154 | [0, 0, { properties : 1 }] 155 | ] 156 | }, { 157 | renderer : 'canvas' 158 | }); 159 | layer.on('layerload', function () { 160 | var expected = maptalks.Geometry.fromJSON({ 'type':'Feature', 'geometry':{ 'type':'Polygon', 'coordinates':[[[0, 0], [0.0008983152841103206, 0], [0.0008983152842207313, -0.0008983152841103206], [0, -0.0008983152841103206], [0, 0]]] }, 'properties':null }); 161 | var actual = layer.identify(map.getCenter()).geometry; 162 | expect(actual.getCenter()).to.be.closeTo(expected.getCenter()); 163 | done(); 164 | }) 165 | .addTo(map); 166 | }); 167 | 168 | describe('test layer with data', function () { 169 | function testLayerWithData(done, data) { 170 | var layer = new maptalks.GridLayer('g', { 171 | projection : true, 172 | center : map.getCenter(), 173 | width : 100, 174 | height : 100, 175 | cols : [-5, 5], 176 | rows : [-5, 5], 177 | data : data 178 | }, { 179 | renderer : 'canvas' 180 | }); 181 | layer.on('layerload', function () { 182 | expect(layer).to.be.painted(); 183 | done(); 184 | }) 185 | .addTo(map); 186 | } 187 | 188 | it('with data of text symbol', function (done) { 189 | testLayerWithData(done, [ 190 | [ 191 | [1, 2], 4, { 'property' : { 'foo':1 }, 'symbol' : { 192 | 'textName' : 'text', 193 | 'textSize' : { type:'interval', stops: [[0, 0], [16, 5], [17, 10], [18, 20], [19, 40]] } 194 | }}, 195 | [4, [2, 3], { 'symbol' : { 'textName' : 'text', 'textSize' : 14 }}] 196 | ] 197 | ]); 198 | }); 199 | }); 200 | }); 201 | --------------------------------------------------------------------------------