├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── README.md ├── debug ├── cluster-sample.html ├── comparisons.css ├── comparisons.html ├── custom-pane.html ├── dojo-amd-loader.html ├── ignore-flag.html ├── requirejs-amd-loader.html ├── reset-style.html ├── sample.html └── script-tag.html ├── karma.conf.js ├── package-lock.json ├── package.json ├── profiles ├── base.js ├── debug.js └── production.js ├── scripts └── release.sh ├── spec ├── Markers │ └── CrossMarkerSpec.js ├── Renderers │ ├── ClassBreaksRendererSpec.js │ ├── SimpleRendererSpec.js │ ├── SmartClassBreaksRendererSpec.js │ └── UniqueValueRendererSpec.js └── Symbols │ ├── LineSymbolSpec.js │ ├── PointSymbolSpec.js │ └── PolygonSymbolSpec.js └── src ├── EsriLeafletRenderers.js ├── FeatureLayerHook.js ├── Renderers ├── ClassBreaksRenderer.js ├── Renderer.js ├── SimpleRenderer.js └── UniqueValueRenderer.js └── Symbols ├── LineSymbol.js ├── PointSymbol.js ├── PolygonSymbol.js └── Symbol.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac 2 | .DS_Store 3 | Icon 4 | ._* 5 | .Spotlight-V100 6 | 7 | # SublimeText 8 | /*.sublime-project 9 | *.sublime-workspace 10 | *.sublime-project 11 | .idea/* 12 | 13 | # SASS 14 | .sass-cache 15 | 16 | # Node 17 | node_modules 18 | npm-debug.log 19 | 20 | # Coverage 21 | coverage 22 | 23 | # Grunt 24 | .grunt/ 25 | 26 | # Debugging Files 27 | working 28 | 29 | # Docs Build 30 | site/build 31 | 32 | # Debugging Files 33 | debug/* 34 | !debug/cluster-sample.html 35 | !debug/comparisons.css 36 | !debug/comparisons.html 37 | !debug/custom-pane.html 38 | !debug/dojo-amd-loader.html 39 | !debug/ignore-flag.html 40 | !debug/requirejs-amd-loader.html 41 | !debug/reset-style.html 42 | !debug/sample.html 43 | !debug/script-tag.html 44 | 45 | # Built Files 46 | dist/**/*.js 47 | dist/**/*.map 48 | dist/**/*.css 49 | dist/**/*.gif 50 | dist/**/*.png 51 | dist/**/*.jpg 52 | dist/**/*.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | # let travis bump the version of node on its own 4 | node_js: 5 | - node 6 | cache: 7 | directories: 8 | - node_modules 9 | script: 10 | - npm run test:ci 11 | addons: 12 | chrome: stable 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # esri-leaflet-renderers change log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). 6 | 7 | ## [Unreleased] 8 | 9 | ## [3.0.1] - 2023-08-28 10 | 11 | ### Updated 12 | 13 | * Updated dependencies ([#191](https://github.com/Esri/esri-leaflet-renderers/pull/191)) 14 | * Updated version export style ([#190](https://github.com/Esri/esri-leaflet-renderers/pull/190)) 15 | 16 | ## [3.0.0] - 2022-06-24 17 | 18 | ### Changed 19 | 20 | * Updated `esri-leaflet` peer dependency version in `package.json` so that there will not be errors when installing via NPM ([#177](https://github.com/Esri/esri-leaflet-renderers/pull/177)) 21 | 22 | ### Updated 23 | 24 | * Fixed renderers when working with esri-leaflet-cluster ([#179](https://github.com/Esri/esri-leaflet-renderers/pull/179)) 25 | 26 | ## [2.1.3] - 2022-03-08 27 | 28 | ### Updated 29 | 30 | * Updated dependencies and changed build-related settings to be consistent with Esri Leaflet ([#171](https://github.com/Esri/esri-leaflet-renderers/pull/171)) 31 | 32 | ## [2.1.2] - 2020-07-06 33 | 34 | ### Changed 35 | 36 | * Update `esri-leaflet-cluster` version in `package.json` (🙏francharbo🙏 [#160](https://github.com/Esri/esri-leaflet-renderers/pull/160)) 37 | 38 | ## [2.1.1] - 2020-06-23 39 | 40 | ### Fixed 41 | 42 | * Follow-up to [#144](https://github.com/Esri/esri-leaflet-renderers/pull/144) to fix the clustering changes (🙏jgravois🙏 [#158](https://github.com/Esri/esri-leaflet-renderers/pull/158)) 43 | 44 | ## [2.1.0] - 2020-06-19 45 | 46 | ### Added 47 | 48 | * Clustering ([#144](https://github.com/Esri/esri-leaflet-renderers/pull/144)) 49 | 50 | ### Changed 51 | 52 | * esri-leaflet, leaflet, and leaflet-shape-markers are now peer dependencies ([#154](https://github.com/Esri/esri-leaflet-renderers/pull/154)) 53 | 54 | ## [2.0.6] - 2017-06-02 55 | 56 | ### Fixed 57 | 58 | * bug that mangled server side color interpretation 59 | 60 | ## [2.0.5] - 2017-06-02 61 | 62 | ### Added 63 | 64 | * support for loading via AMD 65 | * support for Node 7 66 | 67 | ### Fixed 68 | 69 | * ensure that point geometry layers are assigned to Leaflet's `markerPane` by default 70 | 71 | ## [2.0.4] - 2016-08-17 72 | 73 | ### Added 74 | 75 | * support for picture marker symbols stored outside the ArcGIS ecosystem 76 | 77 | ### Fixed 78 | 79 | * ensure the plugin only fires *one* metadata requests 80 | * ensure that [`esriSLSNull`] symbol types display correctly 81 | 82 | ## [2.0.3] - 2016-07-01 83 | 84 | ### Added 85 | 86 | * support for base64 encoded PictureMarkerSymbols. thx @ynunokawa! 87 | * support for overriding service symbology with 'drawingInfo' provided in `L.esri.featureLayer`s constructor. thx @ynunokawa! 88 | 89 | ```js 90 | var fl = L.esri.featureLayer({ 91 | url: 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/0', 92 | drawingInfo: { 93 | "renderer": { 94 | "type": "simple", 95 | "symbol": { 96 | "type": "esriSMS", 97 | "style": "esriSMSCircle", 98 | "color": [115, 178, 255, 255], 99 | "size": 10, 100 | "angle": 0, 101 | "xoffset": 0, 102 | "yoffset": 0, 103 | "outline": { 104 | "color": [0, 0, 0, 255], 105 | "width": 1 106 | } 107 | }, 108 | "label": "", 109 | "description": "" 110 | }, 111 | "transparency": 0, 112 | "labelingInfo": null 113 | } 114 | }).addTo(map); 115 | ``` 116 | 117 | ### Fixed 118 | 119 | * odd cases where a subset of features were drawn with leaflet symbology. 120 | 121 | ### Added 122 | 123 | ## [2.0.2] - 2016-06-15 124 | 125 | ### Added 126 | 127 | * now its possible for individual featureLayers to *not* utilize the renderer defined by the service (when `ignoreRenderer: true` is included as a contructor option) 128 | 129 | ### Fixed 130 | 131 | * Ensured that its possible to specify custom panes for marker symbols (thanks @pbastia!) 132 | * Race condition encountered when metadata is slow to load 133 | 134 | ### Changed 135 | 136 | * Build system refactored to use latest Rollup and Rollup plugins. 137 | * Reworked bundling directives for various modules systems to resolve and simplify various issues 138 | * WebPack users no longer have to use the Babel loader. 139 | * Babelify with Babel 6 now works 140 | 141 | ## [2.0.1] - 2016-01-19 142 | 143 | ### Added 144 | 145 | * Now developers can now override individual style properties of interest for polyline and polygon services directly in the FeatureLayer constructor. (via [pull #100](https://github.com/Esri/esri-leaflet-renderers/pull/100)) 146 | 147 | ```js 148 | L.esri.featureLayer({ 149 | url: 'http://[server]/arcgis/rest/services/[yourservice]/MapServer/0', 150 | style: function (feature) { 151 | return { 152 | // override service symbology to make polygon fill 50% transparent 153 | fillOpacity: 0.5 154 | }; 155 | } 156 | }).addTo(map); 157 | ``` 158 | 159 | ## [2.0.0] - 2015-09-10 160 | 161 | This is the first release that supports [Leaflet 1.0.0-beta.1](http://leafletjs.com/2015/07/15/leaflet-1.0-beta1-released.html). As with version [1.0.0](https://github.com/Esri/esri-leaflet/releases/tag/v1.0.0) of Esri Leaflet, FeatureLayer constructors now expect `url`s to be provided within an options object (ie: `L.esri.featureLayer(url)` should be replaced with `L.esri.featureLayer( {url: url} )`). 162 | 163 | ## [1.0.1] - 2015-11-30 164 | 165 | ### Added 166 | 167 | * support for clusteredFeatureLayers 168 | * support for unique value renderers based on more than one field 169 | * support for transparency applied to the entire renderer (via the service symbology) 170 | 171 | ### Changed 172 | 173 | * Rewritten build and test systems to rely on ES 2015 Modules specification 174 | * More build and release automation 175 | 176 | ## [1.0.0] - 2015-09-08 177 | 178 | This is expected to be the last (and only) stable release of Esri Leaflet Renderers compatible with Leaflet 0.7.3. All future 1.0.X releases will be compatible with Leaflet 0.7.3 and contain only bug fixes. New features will only be added in Esri Leaflet Renderers 2.0.0 (which will require Leaflet 1.0.0). 179 | 180 | ### Breaking Changes 181 | 182 | * In Esri Leaflet itself, in L.esri.FeatureLayer constructors, the `url` is now provided within an options object (ie: `L.esri.featureLayer(url)` should be replaced with `L.esri.featureLayer( {url: url} )`). 183 | 184 | ### Added 185 | 186 | * support for unique value renderers based on more than one field 187 | * support for transparency applied to the entire renderer 188 | 189 | ### Fixed 190 | 191 | * ensured that tokens are passed through in requests for picture marker symbols 192 | 193 | ## [0.0.1-beta.3] - 2015-03-24 194 | 195 | * Render from the new visualVariables objects in the renderer JSON 196 | * Still backwards compatible with classic renderers 197 | 198 | ## [0.0.1-beta.2] - 2015-03-02 199 | 200 | * Fix to work with Browserify 201 | * Update to work with esri-leaflet 1.0.0-rc.5 202 | * Still backwards compatible with esri-leaflet 1.0.0-rc.4 203 | 204 | ## 0.0.1-beta.1 - 2015-01-29 205 | 206 | * First Beta release 207 | * Works with esri-leaflet 1.0.0-rc.4 208 | 209 | [Unreleased]: https://github.com/Esri/esri-leaflet-renderers/compare/v3.0.1...HEAD 210 | [3.0.1]: https://github.com/Esri/esri-leaflet-renderers/compare/v3.0.0...v3.0.1 211 | [3.0.0]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.1.3...v3.0.0 212 | [2.1.3]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.1.2...v2.1.3 213 | [2.1.2]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.1.1...v2.1.2 214 | [2.1.1]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.1.0...v2.1.1 215 | [2.1.0]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.0.6...v2.1.0 216 | [2.0.6]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.0.5...v2.0.6 217 | [2.0.5]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.0.4...v2.0.5 218 | [2.0.4]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.0.3...v2.0.4 219 | [2.0.3]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.0.2...v2.0.3 220 | [2.0.2]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.0.1...v2.0.2 221 | [2.0.1]: https://github.com/Esri/esri-leaflet-renderers/compare/v2.0.0...v2.0.1 222 | [2.0.0]: https://github.com/Esri/esri-leaflet-renderers/compare/v1.0.0...v2.0.0 223 | [1.0.1]: https://github.com/Esri/esri-leaflet-renderers/compare/v1.0.0...v1.0.1 224 | [1.0.0]: https://github.com/Esri/esri-leaflet-renderers/compare/v0.0.1-beta.3...v1.0.0 225 | [0.0.1-beta.3]: https://github.com/Esri/esri-leaflet-renderers/compare/v0.0.1-beta.2...v0.0.1-beta.3 226 | [0.0.1-beta.2]: https://github.com/Esri/esri-leaflet-renderers/compare/v0.0.1-beta.1...v0.0.1-beta.2 227 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing). 2 | 3 | ### Before filing an issue 4 | 5 | Please take a look at [previous issues](https://github.com/Esri/esri-leaflet-renderers/issues) that resolve common problems. 6 | 7 | If you're just looking for help, you'll probably attract the most eyes if you post in [GIS Stackexchange](http://gis.stackexchange.com/questions/ask?tags=esri-leaflet,leaflet) or the [Esri Leaflet place](https://geonet.esri.com/discussion/create.jspa?sr=pmenu&containerID=1841&containerType=700&tags=esri-leaflet,leaflet) on GeoNet. 8 | 9 | If you think you're encountering a new bug, please feel free to log an [issue](https://github.com/Esri/esri-leaflet-renderers/issues/new) and include the steps to reproduce the problem (and preferably a running sample). 10 | 11 | ### I want to contribute, what should I work on? 12 | 13 | There is a lot of room for contributions to Esri Leaflet and Esri Leaflet Renderers. Make sure you checkout the [development instructions](https://github.com/Esri/esri-leaflet-related#development-instructions) in the readme to help you get started. 14 | 15 | ##### More examples 16 | 17 | The Esri Leaflet website is written using http://assemble.io/ and can be found at https://github.com/Esri/esri-leaflet/tree/master/site/source. You can use the existing examples as a reference. 18 | 19 | ### Setting up a dev environment 20 | 21 | 1. [Fork and clone Esri Leaflet Renderers](https://help.github.com/articles/fork-a-repo) 22 | 2. `cd` into the `esri-leaflet-renderers` folder 23 | 5. Install the dependencies with `npm install` 24 | 5. run `grunt` from the command line. This will start the web server locally at [http://localhost:8001](http://localhost:8001) and start watching the source files and running linting and testing commands. 25 | 6. Make your changes and create a [pull request](https://help.github.com/articles/creating-a-pull-request) 26 | 27 | ### Linting 28 | 29 | Please make sure your changes pass JS Hint. This will help make sure code is consistent throughout Esri Leaflet. You can run JS Hint with `grunt jshint`. 30 | 31 | ### Testing 32 | 33 | Please make sure your changes dont break existing tests. Testing is essential for determining backward compatibility and catching breaking changes. You can run tests with `grunt karma:run`, `grunt karma:watch` or `grunt karma:coverage.` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > This repository has been archived. If you're an active user, please feel free to fork it. 2 | 3 | # Esri Leaflet Renderers 4 | 5 | Leaflet plugin for [ArcGIS Feature Services](http://developers.arcgis.com). Esri Leaflet Renderers works in conjunction with the Esri Leaflet Plugin to draw [feature services](https://developers.arcgis.com/esri-leaflet/samples/simple-feature-layer/) using renderers defined by the service. 6 | 7 | [![travis](https://img.shields.io/travis/Esri/Leaflet.shapeMarkers/master.svg?style=flat-square)](https://travis-ci.org/Esri/Leaflet.shapeMarkers) 8 | 9 | The sole purpose of this plugin is to allow [`L.esri.FeatureLayer`](https://developers.arcgis.com/esri-leaflet/api-reference/layers/feature-layer/) to automatically take on renderers defined in [ArcGIS Feature Services](https://developers.arcgis.com/en/features/cloud-storage/). Esri Leaflet Renderers works in conjunction with Esri Leaflet, but it does not add any additional methods or properties to the class that it extends. 10 | 11 | ### Example 12 | Take a look at the [live demo](https://developers.arcgis.com/esri-leaflet/samples/renderers-plugin/). 13 | 14 | You can also find a side by side comparison of the ArcGIS API for JavaScript [here](https://esri.github.io/esri-leaflet-renderers/spec/comparisons.html). 15 | 16 | ```html 17 | 18 | 19 | 20 | 21 | Renderer from Service 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 42 |
43 | 44 | 55 | 56 | 57 | 58 | ``` 59 | 60 | ### Development Instructions 61 | 62 | 1. [Fork and clone Esri Leaflet Renderers](https://help.github.com/articles/fork-a-repo) 63 | 2. `cd` into the `esri-leaflet-renderers` folder 64 | 3. Install the dependencies with `npm install` 65 | 4. Run `npm start` from the command line. This will compile minified source in a brand new `dist` directory, launch a tiny web server and begin watching the raw source for changes. 66 | 5. Run `npm test` to make sure you haven't introduced a new 'feature' accidentally. 67 | 6. Make your changes and create a [pull request](https://help.github.com/articles/creating-a-pull-request) 68 | 69 | ### Limitations 70 | 71 | * As of `2.0.1`, It is possible to override aspects of polyline and polygon symbology defined by the service in the FeatureLayer constructor. For points, it is not. 72 | * [Simple Marker](http://resources.arcgis.com/en/help/arcgis-rest-api/02r3/02r3000000n5000000.htm#GUID-C8D40B32-5F4B-45EB-8048-6D5A8763E13B) symbols do not support rotation (ie: the 'angle' property is ignored). 73 | * Polygons only support [solid fill](http://resources.arcgis.com/en/help/arcgis-rest-api/02r3/02r3000000n5000000.htm#GUID-517D9B3F-DF13-4E79-9B58-A0D24C5E4994). This does not include advanced fill types like PictureFill, Backward Diagonal, DiagonalCross, etc. 74 | * [Text](http://resources.arcgis.com/en/help/arcgis-rest-api/02r3/02r3000000n5000000.htm#ESRI_SECTION1_94E8CE0A9F614ABC8BEDDBCB0E9DC53A) symbols are not supported. 75 | 76 | ### Dependencies 77 | 78 | * Esri Leaflet Renderers [1.x](https://github.com/Esri/esri-leaflet-renderers/releases/tag/v1.0.0) (available on [CDN](https://unpkg.com/esri-leaflet-renderers@1/dist/esri-leaflet-renderers.js)) can be used in apps alongside: 79 | * [Leaflet](http://leafletjs.com) version 0.7.x. 80 | * [Esri Leaflet](https://developers.arcgis.com/esri-leaflet/) version 1.0.x. 81 | 82 | * Esri Leaflet Renderers [2.x](https://github.com/Esri/esri-leaflet-renderers/releases/tag/v2.0.4) (available on [CDN](https://unpkg.com/esri-leaflet-renderers@2/dist/esri-leaflet-renderers.js)) can be used in apps alongside: 83 | * [Leaflet](http://leafletjs.com) version 1.0.0-rc.3. 84 | * [Esri Leaflet](https://developers.arcgis.com/esri-leaflet/) version 2.0.x. 85 | 86 | * Esri Leaflet Renderers [3.x](https://github.com/Esri/esri-leaflet-renderers/releases/tag/v3.0.0) (available on [CDN](https://unpkg.com/esri-leaflet-renderers@3/dist/esri-leaflet-renderers.js)) can be used in apps alongside: 87 | * [Leaflet](http://leafletjs.com) version 1.x. 88 | * [Esri Leaflet](https://developers.arcgis.com/esri-leaflet/) version 3.x. 89 | 90 | ### Versioning 91 | 92 | For transparency into the release cycle and in striving to maintain backward compatibility, Esri Leaflet is maintained under the Semantic Versioning guidelines and will adhere to these rules whenever possible. 93 | 94 | Releases will be numbered with the following format: 95 | 96 | `..` 97 | 98 | And constructed with the following guidelines: 99 | 100 | * Breaking backward compatibility **bumps the major** while resetting minor and patch 101 | * New additions without breaking backward compatibility **bumps the minor** while resetting the patch 102 | * Bug fixes and misc changes **bumps only the patch** 103 | 104 | For more information on SemVer, please visit . 105 | 106 | ### Contributing 107 | 108 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/Esri/esri-leaflet-renderers/blob/master/CONTRIBUTING.md). 109 | 110 | ### Licensing 111 | Copyright © 2015-2018 Esri 112 | 113 | Licensed under the Apache License, Version 2.0 (the "License"); 114 | you may not use this file except in compliance with the License. 115 | You may obtain a copy of the License at 116 | 117 | > http://www.apache.org/licenses/LICENSE-2.0 118 | 119 | Unless required by applicable law or agreed to in writing, software 120 | distributed under the License is distributed on an "AS IS" BASIS, 121 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 122 | See the License for the specific language governing permissions and 123 | limitations under the License. 124 | 125 | A copy of the license is available in the repository's [LICENSE]( https://raw.github.com/Esri/esri-leaflet/master/LICENSE) file. 126 | -------------------------------------------------------------------------------- /debug/cluster-sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple FeatureLayer 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 |
32 | 33 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /debug/comparisons.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin:0; 3 | padding:0; 4 | font-family: "Lucida Grande", "Segoe UI", "Arial", sans-serif; 5 | color: #4d4d4d; 6 | } 7 | 8 | a { 9 | text-decoration: none; 10 | color: #007AC2; 11 | } 12 | 13 | h3 { 14 | padding: 5px; 15 | margin: 5px; 16 | } 17 | 18 | label { 19 | padding: 10px; 20 | font-weight: bold; 21 | } 22 | 23 | .mapContainer { 24 | position: absolute; 25 | background-color: #DDD; 26 | width:700px; 27 | height:600px; 28 | top:100; 29 | bottom:0; 30 | right:0; 31 | } 32 | #map{ 33 | left: 0px; 34 | } 35 | 36 | #esrimap { 37 | left: 705px; 38 | } 39 | 40 | #featureSelection { 41 | position: absolute; 42 | top: 10px; 43 | left: 705px; 44 | } 45 | 46 | #esriLabel { 47 | position: absolute; 48 | left: 705px; 49 | width: 700px; 50 | top: 700px; 51 | text-align: center; 52 | } 53 | #leafletLabel { 54 | position: absolute; 55 | left: 0px; 56 | width: 700px; 57 | top: 700px; 58 | text-align: center; 59 | } 60 | 61 | #serviceUrl { 62 | padding: 0.5em; 63 | border: 1px solid #ededed; 64 | border-radius: 3px; 65 | -webkit-border-radius: 3px; 66 | -moz-border-radius: 3px; 67 | 68 | min-width: 500px; 69 | } 70 | #serviceUrl:focus { 71 | outline: none; 72 | } 73 | #selector { 74 | padding: 0.5em; 75 | height: 32px; 76 | border: 1px solid #ededed; 77 | min-width: 200px; 78 | -webkit-appearance: menulist-button; 79 | border-radius: 3px; 80 | -webkit-border-radius: 3px; 81 | -moz-border-radius: 3px; 82 | } 83 | #selector:focus { 84 | outline: none; 85 | } 86 | 87 | #loadServiceBtn { 88 | background-color: white; 89 | color: #007AC2; 90 | padding: 0.5em; 91 | border: 1px solid #ededed; 92 | border-radius: 3px; 93 | -webkit-border-radius: 3px; 94 | -moz-border-radius: 3px; 95 | cursor: pointer; 96 | } 97 | #loadServiceBtn:hover { 98 | background-color: #007AC2; 99 | color: white; 100 | } 101 | #loadServiceBtn:focus { 102 | outline: none; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /debug/comparisons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Comparing Leaflet to ArcGIS JS API 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |

26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 |
34 |

Leaflet

35 |

Esri JS API

36 | 37 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /debug/custom-pane.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple FeatureLayer 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 |
26 | 27 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /debug/dojo-amd-loader.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esri-leaflet-renderers loaded via script tag. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 25 | 26 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |

47 |
48 |
49 | 50 |
51 |
52 |
53 |
54 | 55 |

Leaflet

56 | 57 | 58 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /debug/ignore-flag.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ignore Renderer Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 |
27 | 28 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /debug/requirejs-amd-loader.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esri-leaflet-renderers loaded via script tag. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |

35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 | 43 |

Leaflet

44 | 45 | 46 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /debug/reset-style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Reset Style Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 |
27 | 28 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /debug/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple FeatureLayer 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 |
26 | 27 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /debug/script-tag.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | esri-leaflet-renderers loaded via script tag. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |

24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 |

Leaflet

33 | 34 | 35 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri May 30 2014 15:44:45 GMT-0400 (EDT) 3 | 4 | module.exports = function (config) { 5 | var configuration = { 6 | // base path that will be used to resolve all patterns (eg. files, exclude) 7 | basePath: '', 8 | 9 | // frameworks to use 10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 11 | frameworks: ['mocha', 'chai-sinon'], 12 | 13 | // list of files / patterns to load in the browser 14 | files: [ 15 | 'node_modules/leaflet/dist/leaflet.css', 16 | 'node_modules/leaflet/dist/leaflet-src.js', 17 | 'node_modules/esri-leaflet/dist/esri-leaflet-debug.js', 18 | 'node_modules/leaflet-shape-markers/dist/leaflet-shape-markers.js', 19 | 'dist/esri-leaflet-renderers-debug.js', 20 | 'spec/**/*.js' 21 | ], 22 | 23 | // list of files to exclude 24 | exclude: [], 25 | 26 | // preprocess matching files before serving them to the browser 27 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 28 | preprocessors: { 29 | 'dist/**/*.js': ['sourcemap', 'coverage'] 30 | }, 31 | 32 | // test results reporter to use 33 | // possible values: 'dots', 'progress' 34 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 35 | reporters: ['progress'], 36 | 37 | // web server port 38 | port: 9876, 39 | 40 | // enable / disable colors in the output (reporters and logs) 41 | colors: true, 42 | 43 | // level of logging 44 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 45 | logLevel: config.LOG_INFO, 46 | 47 | // enable / disable watching file and executing tests whenever any file changes 48 | autoWatch: true, 49 | 50 | // start these browsers 51 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 52 | browsers: [ 53 | 'Chrome' 54 | // 'ChromeCanary', 55 | // 'Firefox', 56 | // 'Safari', 57 | // 'PhantomJS' 58 | ], 59 | 60 | customLaunchers: { 61 | Chrome_travis_ci: { 62 | base: 'ChromeHeadless', 63 | flags: ['--no-sandbox'] 64 | } 65 | }, 66 | 67 | // Continuous Integration mode 68 | // if true, Karma captures browsers, runs the tests and exits 69 | singleRun: true, 70 | 71 | // Configure the coverage reporters 72 | coverageReporter: { 73 | reporters: [ 74 | { type: 'html', dir: 'coverage/' }, 75 | { type: 'text' } 76 | ] 77 | } 78 | }; 79 | 80 | if (process.env.TRAVIS) { 81 | configuration.browsers = ['Chrome_travis_ci']; 82 | } 83 | 84 | config.set(configuration); 85 | }; 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esri-leaflet-renderers", 3 | "description": "esri-leaflet plugin for rendering", 4 | "version": "3.0.1", 5 | "author": "Rachel Nehmer", 6 | "bugs": { 7 | "url": "https://github.com/esri/esri-leaflet-renderers/issues" 8 | }, 9 | "contributors": [ 10 | "Rachel Nehmer", 11 | "John Gravois (https://johngravois.com)", 12 | "Gavin Rehkemper (https://gavinr.com)" 13 | ], 14 | "peerDependencies": { 15 | "esri-leaflet": "3.x", 16 | "leaflet": "1.x", 17 | "leaflet-shape-markers": "1.x" 18 | }, 19 | "optionalDependencies": { 20 | "esri-leaflet-cluster": "^3.0.0" 21 | }, 22 | "devDependencies": { 23 | "@rollup/plugin-json": "^4.1.0", 24 | "@rollup/plugin-node-resolve": "^13.1.3", 25 | "@rollup/plugin-terser": "^0.3.0", 26 | "chai": "4.3.6", 27 | "chokidar-cli": "^3.0.0", 28 | "esri-leaflet": "3.x", 29 | "gh-release": "^7.0.2", 30 | "http-server": "^14.1.1", 31 | "karma": "^6.3.16", 32 | "karma-chai-sinon": "^0.1.5", 33 | "karma-chrome-launcher": "^3.1.0", 34 | "karma-coverage": "^2.2.0", 35 | "karma-mocha": "^2.0.1", 36 | "karma-mocha-reporter": "^2.2.5", 37 | "karma-sourcemap-loader": "^0.3.8", 38 | "leaflet": "1.x", 39 | "leaflet-shape-markers": "^1.0.6", 40 | "mkdirp": "^1.0.4", 41 | "mocha": "^10.2.0", 42 | "npm-run-all": "^4.1.5", 43 | "rollup": "^2.79.1", 44 | "semistandard": "^14.2.3", 45 | "sinon": "^15.0.1", 46 | "sinon-chai": "3.7.0", 47 | "snazzy": "^9.0.0" 48 | }, 49 | "files": [ 50 | "src/**/*.js", 51 | "dist/*.js", 52 | "dist/*.js.map", 53 | "dist/*.json" 54 | ], 55 | "homepage": "http://developers.arcgis.com/esri-leaflet", 56 | "jsnext:main": "src/EsriLeafletRenderers.js", 57 | "jspm": { 58 | "registry": "npm", 59 | "format": "es6", 60 | "main": "src/EsriLeafletRenderers.js" 61 | }, 62 | "keywords": [ 63 | "arcgis", 64 | "esri", 65 | "esri leaflet", 66 | "gis", 67 | "leaflet plugin", 68 | "mapping", 69 | "renderers", 70 | "symbology" 71 | ], 72 | "license": "Apache-2.0", 73 | "main": "dist/esri-leaflet-renderers-debug.js", 74 | "module": "src/EsriLeafletRenderers.js", 75 | "browser": "dist/esri-leaflet-renderers-debug.js", 76 | "readmeFilename": "README.md", 77 | "repository": { 78 | "type": "git", 79 | "url": "git@github.com:Esri/esri-leaflet-renderers.git" 80 | }, 81 | "scripts": { 82 | "prebuild": "mkdirp dist", 83 | "build": "rollup -c profiles/debug.js & rollup -c profiles/production.js", 84 | "lint": "semistandard src/**/*.js | snazzy", 85 | "fix": "semistandard --fix", 86 | "pretest": "npm run build", 87 | "release": "./scripts/release.sh", 88 | "start-watch": "chokidar src -c \"npm run build\"", 89 | "start": "run-p start-watch serve", 90 | "serve": "http-server -p 5000 -c-1 -o", 91 | "test": "npm run lint && karma start", 92 | "test:ci": "npm run lint && karma start --browsers Chrome_travis_ci" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /profiles/base.js: -------------------------------------------------------------------------------- 1 | // https://github.com/Esri/esri-leaflet/pull/1125 2 | // import config from '../node_modules/esri-leaflet/profiles/base.js'; 3 | 4 | import json from '@rollup/plugin-json'; 5 | import nodeResolve from '@rollup/plugin-node-resolve'; 6 | 7 | var pkg = require('../package.json'); 8 | var copyright = '/* ' + pkg.name + ' - v' + pkg.version + ' - ' + new Date().toString() + '\n' + 9 | ' * Copyright (c) ' + new Date().getFullYear() + ' Environmental Systems Research Institute, Inc.\n' + 10 | ' * ' + pkg.license + ' */'; 11 | 12 | var config = { 13 | input: 'src/EsriLeaflet.js', 14 | external: [ 15 | 'leaflet', 16 | 'esri-leaflet', 17 | 'esri-leaflet-cluster' 18 | ], 19 | plugins: [ 20 | nodeResolve({ 21 | jsnext: true, 22 | main: false, 23 | browser: false, 24 | extensions: [ '.js', '.json' ] 25 | }), 26 | json() 27 | ], 28 | 29 | output: { 30 | banner: copyright, 31 | format: 'umd', 32 | name: 'L.esri', 33 | globals: { 34 | 'leaflet': 'L', 35 | 'esri-leaflet': 'L.esri', 36 | 'esri-leaflet-cluster': 'L.esri.Cluster' 37 | }, 38 | sourcemap: true 39 | } 40 | }; 41 | 42 | // end needless duplication 43 | config.input = 'src/EsriLeafletRenderers.js'; 44 | config.output.name = 'L.esri.Renderers'; 45 | config.output.sourcemap = true; 46 | 47 | export default config; 48 | -------------------------------------------------------------------------------- /profiles/debug.js: -------------------------------------------------------------------------------- 1 | import config from './base.js'; 2 | 3 | config.output.file = 'dist/esri-leaflet-renderers-debug.js'; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /profiles/production.js: -------------------------------------------------------------------------------- 1 | import terser from "@rollup/plugin-terser"; 2 | import config from "./base.js"; 3 | 4 | config.output.file = "dist/esri-leaflet-renderers.js"; 5 | 6 | // use a Regex to preserve copyright text 7 | config.plugins.push(terser({ format: { comments: /Institute, Inc/ } })); 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # config 4 | VERSION=$(node --eval "console.log(require('./package.json').version);") 5 | NAME=$(node --eval "console.log(require('./package.json').name);") 6 | 7 | # build and test 8 | npm test || exit 1 9 | 10 | # Integrity string and save to siteData.json 11 | JS_INTEGRITY=$(cat dist/esri-leaflet-renderers.js | openssl dgst -sha512 -binary | openssl base64 -A) 12 | echo "{\"name\": \"esri-leaflet-renderers\",\"version\": \"$VERSION\",\"lib\": {\"path\": \"dist/esri-leaflet-renderers.js\",\"integrity\": \"sha512-$JS_INTEGRITY\"}}" > dist/siteData.json 13 | 14 | # checkout temp branch for release 15 | git checkout -b gh-release 16 | 17 | # force add files 18 | git add dist -f 19 | 20 | # commit changes with a versioned commit message 21 | git commit -m "build $VERSION" 22 | 23 | # push commit so it exists on GitHub when we run gh-release 24 | git push git@github.com:Esri/esri-leaflet-renderers.git gh-release 25 | 26 | # create a ZIP archive of the dist files 27 | zip -r $NAME-v$VERSION.zip dist 28 | 29 | # run gh-release to create the tag and push release to github 30 | gh-release --assets $NAME-v$VERSION.zip 31 | 32 | # publish release on NPM 33 | npm publish 34 | 35 | # checkout master and delete release branch locally and on GitHub 36 | git checkout master 37 | git branch -D gh-release 38 | git push git@github.com:Esri/esri-leaflet-renderers.git :gh-release 39 | -------------------------------------------------------------------------------- /spec/Markers/CrossMarkerSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('CrossMarker', function () { 3 | describe('#_size', function () { 4 | var map; 5 | beforeEach(function () { 6 | map = L.map(document.createElement('div')); 7 | map.setView([0, 0], 1); 8 | }); 9 | describe('when were ready to get started ', function () { 10 | it('shapeMarkers should have been loaded successfully.', function () { 11 | var marker = L.shapeMarkers.crossMarker(); 12 | expect(marker).to.not.eq(undefined); 13 | }); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /spec/Renderers/ClassBreaksRendererSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('ClassBreaksRenderer', function () { 3 | describe('should create renderer from JSON', function () { 4 | var rendererJson; 5 | 6 | beforeEach(function () { 7 | rendererJson = { 8 | 'type': 'classBreaks', 9 | 'field': 'SHAPE_AREA', 10 | 'defaultSymbol': null, 11 | 'minValue': 41569, 12 | 'classBreakInfos': [ 13 | { 14 | 'classMaxValue': 15327379, 15 | 'symbol': { 16 | 'type': 'esriSFS', 17 | 'style': 'esriSFSSolid', 18 | 'color': [237, 248, 233, 128], 19 | 'outline': { 20 | 'type': 'esriSLS', 21 | 'style': 'esriSLSSolid', 22 | 'color': [0, 0, 0, 255], 23 | 'width': 1.5 24 | } 25 | } 26 | }, 27 | { 28 | 'classMaxValue': 64615118, 29 | 'symbol': { 30 | 'type': 'esriSFS', 31 | 'style': 'esriSFSSolid', 32 | 'color': [116, 196, 118, 128], 33 | 'outline': { 34 | 'type': 'esriSLS', 35 | 'style': 'esriSLSSolid', 36 | 'color': [0, 0, 0, 255], 37 | 'width': 1.5 38 | } 39 | } 40 | }, 41 | { 42 | 'classMaxValue': 38231763, 43 | 'symbol': { 44 | 'type': 'esriSFS', 45 | 'style': 'esriSFSSolid', 46 | 'color': [177, 222, 176, 128], 47 | 'outline': { 48 | 'type': 'esriSLS', 49 | 'style': 'esriSLSSolid', 50 | 'color': [0, 0, 0, 255], 51 | 'width': 1.5 52 | } 53 | } 54 | }] 55 | }; 56 | }); 57 | 58 | it('should create three symbols', function () { 59 | var renderer = L.esri.Renderers.classBreaksRenderer(rendererJson); 60 | expect(renderer._symbols.length).to.be.eq(3); 61 | }); 62 | 63 | it('should order the symbols', function () { 64 | var renderer = L.esri.Renderers.classBreaksRenderer(rendererJson); 65 | 66 | expect(renderer._symbols[0].val).to.be.eq(15327379); 67 | expect(renderer._symbols[1].val).to.be.eq(38231763); 68 | expect(renderer._symbols[2].val).to.be.eq(64615118); 69 | }); 70 | it('should get symbol for a value that falls on a classbreak', function () { 71 | var renderer = L.esri.Renderers.classBreaksRenderer(rendererJson); 72 | var feature = { 'properties': { 'SHAPE_AREA': 38231763 } }; 73 | var sym = renderer._getSymbol(feature); 74 | expect(sym.val).to.be.eq(38231763); 75 | }); 76 | it('should get symbol for a value that falls within a classbreak range', function () { 77 | var renderer = L.esri.Renderers.classBreaksRenderer(rendererJson); 78 | var feature = { 'properties': { 'SHAPE_AREA': 50000000 } }; 79 | var sym = renderer._getSymbol(feature); 80 | expect(sym.val).to.be.eq(64615118); 81 | }); 82 | 83 | it('should merge symbol styles', function () { 84 | var options = { 85 | userDefinedStyle: function (feature) { 86 | return { opacity: 0.5 }; 87 | } 88 | }; 89 | var renderer = L.esri.Renderers.classBreaksRenderer(rendererJson, options); 90 | var feature = { 'properties': { 'SHAPE_AREA': 50000000 } }; 91 | var style = renderer.style(feature); 92 | // user style 93 | expect(style.opacity).to.be.eq(0.5); 94 | // renderer style 95 | expect(style.weight).to.be.greaterThan(1); 96 | }); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /spec/Renderers/SimpleRendererSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('SimpleRenderer', function () { 3 | describe('should create renderer from JSON', function () { 4 | var rendererJson; 5 | 6 | beforeEach(function () { 7 | rendererJson = { 8 | 'type': 'simple', 9 | 'symbol': { 10 | 'type': 'esriSFS', 11 | 'style': 'esriSFSSolid', 12 | 'color': [255, 214, 180, 255], 13 | 'outline': { 14 | 'type': 'esriSLS', 15 | 'style': 'esriSLSSolid', 16 | 'color': [251, 164, 93, 255], 17 | 'width': 0.75 18 | } 19 | } 20 | }; 21 | }); 22 | 23 | it('should create one symbol', function () { 24 | var renderer = L.esri.Renderers.simpleRenderer(rendererJson); 25 | expect(renderer._symbols.length).to.be.eq(1); 26 | }); 27 | 28 | it('should merge symbol styles', function () { 29 | var options = { 30 | userDefinedStyle: function (feature) { 31 | return { opacity: 0.5 }; 32 | } 33 | }; 34 | var renderer = L.esri.Renderers.simpleRenderer(rendererJson, options); 35 | var style = renderer.style(); 36 | // user style 37 | expect(style.opacity).to.be.eq(0.5); 38 | // renderer style 39 | expect(style.weight).to.be.lessThan(1); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /spec/Renderers/SmartClassBreaksRendererSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('SmartClassbreaksRenderer', function () { 3 | describe('should create renderer with size variables from JSON', function () { 4 | var renderer, pointFeatureOne, pointFeatureTwo, sizeInfoJson; 5 | 6 | beforeEach(function () { 7 | sizeInfoJson = { 8 | 'visualVariables': [{ 9 | 'type': 'sizeInfo', 10 | 'field': 'FIELD_ONE', 11 | 'minSize': 1, 12 | 'maxSize': 37.5, 13 | 'minDataValue': 0, 14 | 'maxDataValue': 113 15 | }], 16 | 'type': 'classBreaks', 17 | 'field': 'DIAMETER', 18 | 'minValue': -9007199254740991, 19 | 'classBreakInfos': [{ 20 | 'symbol': { 21 | 'color': [227, 139, 79, 204], 22 | 'size': 9, 23 | 'angle': 0, 24 | 'xoffset': 0, 25 | 'yoffset': 0, 26 | 'type': 'esriSMS', 27 | 'style': 'esriSMSCircle', 28 | 'outline': { 29 | 'color': [255, 255, 255, 255], 30 | 'width': 0.75, 31 | 'type': 'esriSLS', 32 | 'style': 'esriSLSSolid' 33 | } 34 | }, 35 | 'classMaxValue': 9007199254740991 36 | }] 37 | }; 38 | pointFeatureOne = { 39 | 'type': 'Point', 40 | 'properties': { 41 | 'FID': 1, 42 | 'FIELD_ONE': 2, 43 | 'FIELD_TWO': 36 44 | } 45 | }; 46 | pointFeatureTwo = { 47 | 'type': 'Point', 48 | 'properties': { 49 | 'FID': 2, 50 | 'FIELD_ONE': 50, 51 | 'FIELD_TWO': 36 52 | } 53 | }; 54 | renderer = L.esri.Renderers.classBreaksRenderer(sizeInfoJson); 55 | }); 56 | 57 | it('size info visual variables should create one base symbol', function () { 58 | expect(renderer._symbols.length).to.be.eq(1); 59 | expect(renderer._symbols[0].val).to.be.eq(9007199254740991); 60 | }); 61 | 62 | it('should get different circle marker sizes for different feature values', function () { 63 | var sizeOne = renderer.pointToLayer(pointFeatureOne, [0, 0]).options.radius; 64 | var sizeTwo = renderer.pointToLayer(pointFeatureTwo, [0, 0]).options.radius; 65 | expect(sizeOne).not.to.be.eq(sizeTwo); 66 | }); 67 | }); 68 | describe('should create renderer with color variables from JSON', function () { 69 | var renderer, polygonFeatureOne, polygonFeatureTwo, colorInfoJson; 70 | 71 | beforeEach(function () { 72 | colorInfoJson = { 73 | 'visualVariables': [{ 74 | 'type': 'colorInfo', 75 | 'field': 'FIELD_ONE', 76 | 'stops': [ 77 | { 78 | 'value': 4.6, 79 | 'color': [255, 252, 212, 204] 80 | }, { 81 | 'value': 5.95, 82 | 'color': [224, 178, 193, 204] 83 | }, { 84 | 'value': 7.3, 85 | 'color': [193, 104, 173, 204] 86 | }, { 87 | 'value': 8.65, 88 | 'color': [123, 53, 120, 204] 89 | }, { 90 | 'value': 10, 91 | 'color': [53, 2, 66, 204] 92 | } 93 | ] 94 | }], 95 | 'type': 'classBreaks', 96 | 'field': 'FIELD_ONE', 97 | 'minValue': -9007199254740991, 98 | 'classBreakInfos': [{ 99 | 'symbol': { 100 | 'color': [170, 170, 170, 204], 101 | 'outline': { 102 | 'color': [153, 153, 153, 255], 103 | 'width': 0.375, 104 | 'type': 'esriSLS', 105 | 'style': 'esriSLSSolid' 106 | }, 107 | 'type': 'esriSFS', 108 | 'style': 'esriSFSSolid' 109 | }, 110 | 'classMaxValue': 9007199254740991 111 | }] 112 | }; 113 | polygonFeatureOne = { 114 | 'type': 'Polygon', 115 | 'properties': { 116 | 'FID': 1, 117 | 'FIELD_ONE': 6, 118 | 'FIELD_TWO': 10 119 | } 120 | }; 121 | polygonFeatureTwo = { 122 | 'type': 'Polygon', 123 | 'properties': { 124 | 'FID': 2, 125 | 'FIELD_ONE': 8, 126 | 'FIELD_TWO': 36 127 | } 128 | }; 129 | renderer = L.esri.Renderers.classBreaksRenderer(colorInfoJson); 130 | }); 131 | 132 | it('color info visual variables should create one base symbol', function () { 133 | expect(renderer._symbols.length).to.be.eq(1); 134 | expect(renderer._symbols[0].val).to.be.eq(9007199254740991); 135 | }); 136 | 137 | it('should get different polygon fills for different feature values', function () { 138 | var fillOne = renderer.style(polygonFeatureOne).fillColor; 139 | var fillTwo = renderer.style(polygonFeatureTwo).fillColor; 140 | expect(fillOne).not.to.be.eq(fillTwo); 141 | }); 142 | }); 143 | }); 144 | -------------------------------------------------------------------------------- /spec/Renderers/UniqueValueRendererSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('UniqueValueRenderer', function () { 3 | describe('should create renderer from JSON', function () { 4 | var rendererJson; 5 | 6 | beforeEach(function () { 7 | rendererJson = { 8 | 'type': 'uniqueValue', 9 | 'field1': 'ZONE', 10 | 'defaultSymbol': { 11 | 'type': 'esriSFS', 12 | 'style': 'esriSFSSolid', 13 | 'color': [0, 0, 0, 64], 14 | 'outline': { 15 | 'type': 'esriSLS', 16 | 'style': 'esriSLSSolid', 17 | 'color': [0, 0, 0, 51], 18 | 'width': 1 19 | } 20 | }, 21 | 'uniqueValueInfos': [ 22 | { 23 | 'value': '-12', 24 | 'symbol': { 25 | 'type': 'esriSFS', 26 | 'style': 'esriSFSSolid', 27 | 'color': [255, 255, 255, 128], 28 | 'outline': { 29 | 'type': 'esriSLS', 30 | 'style': 'esriSLSSolid', 31 | 'color': [128, 128, 128, 255], 32 | 'width': 1.5 33 | } 34 | } 35 | }, 36 | { 37 | 'value': '-11', 38 | 'symbol': { 39 | 'type': 'esriSFS', 40 | 'style': 'esriSFSSolid', 41 | 'color': [192, 192, 192, 128], 42 | 'outline': { 43 | 'type': 'esriSLS', 44 | 'style': 'esriSLSSolid', 45 | 'color': [128, 128, 128, 255], 46 | 'width': 1.5 47 | } 48 | } 49 | }, 50 | { 51 | 'value': '-10', 52 | 'symbol': { 53 | 'type': 'esriSFS', 54 | 'style': 'esriSFSSolid', 55 | 'color': [255, 255, 255, 128], 56 | 'outline': { 57 | 'type': 'esriSLS', 58 | 'style': 'esriSLSSolid', 59 | 'color': [128, 128, 128, 255], 60 | 'width': 1.5 61 | } 62 | } 63 | } 64 | ] 65 | }; 66 | }); 67 | 68 | it('should create three symbols', function () { 69 | var renderer = L.esri.Renderers.uniqueValueRenderer(rendererJson); 70 | expect(renderer._symbols.length).to.be.eq(3); 71 | }); 72 | 73 | it('should merge symbol styles', function () { 74 | var options = { 75 | userDefinedStyle: function (feature) { 76 | return { opacity: 0.5 }; 77 | } 78 | }; 79 | var renderer = L.esri.Renderers.uniqueValueRenderer(rendererJson, options); 80 | var feature = { 'properties': { 'ZONE': -12, 'VALUE': '$25.00', 'MARKET': '2Z' } }; 81 | var style = renderer.style(feature); 82 | // user style 83 | expect(style.opacity).to.be.eq(0.5); 84 | // renderer style 85 | expect(style.weight).to.be.greaterThan(1); 86 | }); 87 | 88 | describe('symbol transparency', function () { 89 | it('should be equal to symbol value when no layer transparency defined', function () { 90 | var renderer = L.esri.Renderers.uniqueValueRenderer(rendererJson); 91 | expect(renderer._symbols[0]._styles.fillOpacity).to.be.eq(128 / 255.0); 92 | expect(renderer._symbols[0]._lineStyles.opacity).to.be.eq(1); 93 | expect(renderer._defaultSymbol._styles.fillOpacity).to.be.eq(64 / 255.0); 94 | expect(renderer._defaultSymbol._lineStyles.opacity).to.be.eq(51 / 255.0); 95 | }); 96 | 97 | it('should be equal to symbol value with transparency applied when defined', function () { 98 | var renderer = L.esri.Renderers.uniqueValueRenderer(rendererJson, { layerTransparency: 75 }); 99 | expect(renderer._symbols[0]._styles.fillOpacity).to.be.eq(128 / 255.0 * 0.25); 100 | expect(renderer._symbols[0]._lineStyles.opacity).to.be.eq(0.25); 101 | expect(renderer._defaultSymbol._styles.fillOpacity).to.be.eq(64 / 255.0 * 0.25); 102 | expect(renderer._defaultSymbol._lineStyles.opacity).to.be.eq(51 / 255.0 * 0.25); 103 | }); 104 | }); 105 | 106 | it('should create a default symbol', function () { 107 | var renderer = L.esri.Renderers.uniqueValueRenderer(rendererJson); 108 | expect(renderer._defaultSymbol).to.not.equal(undefined); 109 | }); 110 | 111 | it('should get default symbol when no matching value', function () { 112 | var renderer = L.esri.Renderers.uniqueValueRenderer(rendererJson); 113 | var feature = { 'properties': { 'ZONE': 5 } }; 114 | var sym = renderer._getSymbol(feature); 115 | expect(sym.val).to.be.equal(null); 116 | }); 117 | 118 | it('should get symbol for that matches the value', function () { 119 | var renderer = L.esri.Renderers.uniqueValueRenderer(rendererJson); 120 | var feature = { 'properties': { 'ZONE': -10 } }; 121 | var sym = renderer._getSymbol(feature); 122 | expect(sym.val).to.be.eq('-10'); 123 | }); 124 | }); 125 | 126 | describe('should renderer based on multiple fields', function () { 127 | var multipleFieldRendererJson; 128 | 129 | beforeEach(function () { 130 | multipleFieldRendererJson = { 131 | 'type': 'uniqueValue', 132 | 'field1': 'ZONE', 133 | 'field2': 'MARKET', 134 | 'field3': 'VALUE', 135 | 'fieldDelimiter': ',', 136 | 'defaultSymbol': { 137 | 'type': 'esriSFS', 138 | 'style': 'esriSFSSolid', 139 | 'color': [0, 0, 0, 64], 140 | 'outline': { 141 | 'type': 'esriSLS', 142 | 'style': 'esriSLSSolid', 143 | 'color': [0, 0, 0, 51], 144 | 'width': 1 145 | } 146 | }, 147 | 'uniqueValueInfos': [ 148 | { 149 | 'value': '-12,2Z,$25.00', 150 | 'symbol': { 151 | 'type': 'esriSFS', 152 | 'style': 'esriSFSSolid', 153 | 'color': [255, 255, 255, 128], 154 | 'outline': { 155 | 'type': 'esriSLS', 156 | 'style': 'esriSLSSolid', 157 | 'color': [128, 128, 128, 255], 158 | 'width': 1.5 159 | } 160 | } 161 | } 162 | ] 163 | }; 164 | }); 165 | it('should get default symbol when not matching all fields', function () { 166 | var renderer = L.esri.Renderers.uniqueValueRenderer(multipleFieldRendererJson); 167 | var feature = { 'properties': { 'ZONE': -12, 'MARKET': '2Z' } }; 168 | var sym = renderer._getSymbol(feature); 169 | expect(sym.val).to.be.eq(null); 170 | }); 171 | 172 | it('should get symbol for that matches the value', function () { 173 | var renderer = L.esri.Renderers.uniqueValueRenderer(multipleFieldRendererJson); 174 | var feature = { 'properties': { 'ZONE': -12, 'VALUE': '$25.00', 'MARKET': '2Z' } }; 175 | var sym = renderer._getSymbol(feature); 176 | expect(sym.val).to.be.eq('-12,2Z,$25.00'); 177 | }); 178 | }); 179 | }); 180 | -------------------------------------------------------------------------------- /spec/Symbols/LineSymbolSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('LineSymbol', function () { 3 | describe('#_symbolJson', function () { 4 | var symbolHelper; 5 | 6 | beforeEach(function () { 7 | symbolHelper = new L.esri.Renderers.Symbol(); 8 | }); 9 | 10 | it('should set defaults', function () { 11 | var symbol = L.esri.Renderers.lineSymbol({}); 12 | var styles = symbol.style(); 13 | expect(styles.lineCap).to.be.eq('butt'); 14 | expect(styles.lineJoin).to.be.eq('miter'); 15 | expect(styles.weight).to.be.equal(0); 16 | expect(styles.color).to.be.equal(undefined); 17 | expect(styles.dashArray).to.be.equal(undefined); 18 | }); 19 | 20 | describe('should set style from solid line symbol esriSLSSolid', function () { 21 | var solidLine; 22 | 23 | beforeEach(function () { 24 | solidLine = { 25 | 'type': 'esriSLS', 26 | 'style': 'esriSLSSolid', 27 | 'color': [255, 85, 0, 255], 28 | 'width': 1.4 29 | }; 30 | }); 31 | 32 | it('should set solid line parameters', function () { 33 | var symbol = L.esri.Renderers.lineSymbol(solidLine); 34 | var styles = symbol.style(); 35 | var c = symbolHelper.colorValue(solidLine.color); 36 | var o = symbolHelper.alphaValue(solidLine.color); 37 | expect(styles.color).to.be.eq(c); 38 | expect(styles.opacity).to.be.eq(o); 39 | expect(styles.dashArray).to.be.equal(undefined); 40 | expect(styles.weight).to.be.eq(symbolHelper.pixelValue(solidLine.width)); 41 | }); 42 | 43 | it('should set weight to 0 if width is 0', function () { 44 | solidLine.width = 0; 45 | var symbol = L.esri.Renderers.lineSymbol(solidLine); 46 | var styles = symbol.style(); 47 | 48 | expect(styles.weight).to.be.equal(0); 49 | }); 50 | }); 51 | 52 | describe('should set style from pattern line symbol', function () { 53 | var dashedLine; 54 | 55 | beforeEach(function () { 56 | dashedLine = { 57 | 'type': 'esriSLS', 58 | 'color': [0, 0, 0, 255], 59 | 'width': 2 60 | }; 61 | }); 62 | it('should set esriSLSDot parameters', function () { 63 | dashedLine.style = 'esriSLSDot'; 64 | var symbol = L.esri.Renderers.lineSymbol(dashedLine); 65 | var styles = symbol.style(); 66 | expect(styles.dashArray).not.to.be.equal(undefined); 67 | }); 68 | it('should set esriSLSDash line parameters', function () { 69 | dashedLine.style = 'esriSLSDash'; 70 | var symbol = L.esri.Renderers.lineSymbol(dashedLine); 71 | var styles = symbol.style(); 72 | expect(styles.dashArray).not.to.be.equal(undefined); 73 | }); 74 | it('should set esriSLSDashDot line parameters', function () { 75 | dashedLine.style = 'esriSLSDashDot'; 76 | var symbol = L.esri.Renderers.lineSymbol(dashedLine); 77 | var styles = symbol.style(); 78 | expect(styles.dashArray).not.to.be.equal(undefined); 79 | }); 80 | it('should set esriSLSDashDotDot line parameters', function () { 81 | dashedLine.style = 'esriSLSDashDotDot'; 82 | var symbol = L.esri.Renderers.lineSymbol(dashedLine); 83 | var styles = symbol.style(); 84 | expect(styles.dashArray).not.to.be.equal(undefined); 85 | }); 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /spec/Symbols/PointSymbolSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('PointSymbol', function () { 3 | describe('#_symbolJson', function () { 4 | it('should set defaults', function () { 5 | var symbol = L.esri.Renderers.pointSymbol({}); 6 | var styles = symbol._styles; 7 | expect(styles.stroke).to.be.eq(false); 8 | }); 9 | 10 | describe('should set style from point symbol json', function () { 11 | var pointSymbolJson; 12 | 13 | beforeEach(function () { 14 | pointSymbolJson = { 15 | 'type': 'esriSMS', 16 | 'style': 'esriSMSquare', 17 | 'color': [0, 0, 128, 128], 18 | 'size': 15, 19 | 'angle': 0, 20 | 'xoffset': 0, 21 | 'yoffset': 0, 22 | 'outline': { 23 | 'color': [0, 0, 128, 255], 24 | 'width': 1 25 | } 26 | }; 27 | }); 28 | it('should set square parameters', function () { 29 | var symbol = L.esri.Renderers.pointSymbol(pointSymbolJson); 30 | var styles = symbol._styles; 31 | 32 | expect(symbol.icon).to.be.equal(undefined); 33 | expect(styles).not.to.be.equal(undefined); 34 | }); 35 | }); 36 | 37 | describe('should set style from icon symbol json', function () { 38 | var iconSymbolJson, fLayer; 39 | 40 | beforeEach(function () { 41 | iconSymbolJson = { 42 | 'type': 'esriPMS', 43 | 'url': '5661274d49834505d5815990f7696493', 44 | 'imageData': 'iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAP5JREFUKJGFkT1KxGAURc88nqmCbUqZ0kGEwc4thJhiygHdhI3YWFhPkUq0EBcQIX5bsLdw7I0L0P4jXGxGDUOCr7yXc9+fM1KLxeIoxrgXQnjs6z4GSLozs2fgf6AoikNJc0m3294g4O6nkkiS5K0XsnT3l18gz/MUSCVNJC038kee56mZZWZ2I+nYN/S9mZ0Bk36nruve3f+GcPfOAZIkuYoxvrr7LjCRdAHsAJeSortnks5', 45 | 'contentType': 'image/png', 46 | 'width': 9, 47 | 'height': 9, 48 | 'angle': 0, 49 | 'xoffset': 0, 50 | 'yoffset': 0 51 | }; 52 | fLayer = 'http://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Airports/FeatureServer/0'; 53 | }); 54 | 55 | it('should set icon parameters', function () { 56 | var symbol = L.esri.Renderers.pointSymbol(iconSymbolJson, { url: fLayer }); 57 | var styles = symbol._styles; 58 | 59 | expect(symbol.icon).not.to.be.equal(undefined); 60 | expect(symbol.serviceUrl).to.be.eq(fLayer); 61 | expect(styles.stroke).to.be.equal(undefined); 62 | }); 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /spec/Symbols/PolygonSymbolSpec.js: -------------------------------------------------------------------------------- 1 | /* global L beforeEach describe expect it */ 2 | describe('PolygonSymbol', function () { 3 | describe('#_symbolJson', function () { 4 | var symbolHelper; 5 | 6 | beforeEach(function () { 7 | symbolHelper = new L.esri.Renderers.Symbol(); 8 | }); 9 | 10 | describe('should set style from solid polygon symbol esriFSSolid', function () { 11 | var solidPolygon; 12 | 13 | beforeEach(function () { 14 | solidPolygon = { 15 | 'type': 'esriSFS', 16 | 'style': 'esriSFSSolid', 17 | 'color': [237, 248, 233, 128], 18 | 'outline': { 19 | 'type': 'esriSLS', 20 | 'style': 'esriSLSSolid', 21 | 'color': [0, 0, 0, 255], 22 | 'width': 1.5 23 | } 24 | }; 25 | }); 26 | 27 | it('should set solid polygon parameters', function () { 28 | var symbol = L.esri.Renderers.polygonSymbol(solidPolygon); 29 | var styles = symbol.style(); 30 | 31 | expect(styles.fill).to.be.eq(true); 32 | expect(styles.fillColor).to.be.eq(symbolHelper.colorValue(solidPolygon.color)); 33 | expect(styles.fillOpacity).to.be.eq(symbolHelper.alphaValue(solidPolygon.color)); 34 | expect(styles.color).to.be.eq(symbolHelper.colorValue(solidPolygon.outline.color)); 35 | expect(styles.opacity).to.be.eq(symbolHelper.alphaValue(solidPolygon.outline.color)); 36 | }); 37 | 38 | it('should set line stroke to false if line width is 0', function () { 39 | solidPolygon.outline.width = 0; 40 | var symbol = L.esri.Renderers.polygonSymbol(solidPolygon); 41 | var styles = symbol.style(); 42 | 43 | expect(styles.stroke).to.be.eq(false); 44 | }); 45 | 46 | it('should set line stroke to false if outline is null', function () { 47 | solidPolygon.outline = null; 48 | var symbol = L.esri.Renderers.polygonSymbol(solidPolygon); 49 | var styles = symbol.style(); 50 | 51 | expect(styles.stroke).to.be.eq(false); 52 | }); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/EsriLeafletRenderers.js: -------------------------------------------------------------------------------- 1 | import './FeatureLayerHook'; 2 | 3 | import packageInfo from '../package.json'; 4 | var version = packageInfo.version; 5 | export { version as VERSION }; 6 | 7 | export { Renderer } from './Renderers/Renderer'; 8 | export { SimpleRenderer, simpleRenderer } from './Renderers/SimpleRenderer'; 9 | export { ClassBreaksRenderer, classBreaksRenderer } from './Renderers/ClassBreaksRenderer'; 10 | export { UniqueValueRenderer, uniqueValueRenderer } from './Renderers/UniqueValueRenderer'; 11 | 12 | export { Symbol } from './Symbols/Symbol'; 13 | export { PointSymbol, pointSymbol } from './Symbols/PointSymbol'; 14 | export { LineSymbol, lineSymbol } from './Symbols/LineSymbol'; 15 | export { PolygonSymbol, polygonSymbol } from './Symbols/PolygonSymbol'; 16 | -------------------------------------------------------------------------------- /src/FeatureLayerHook.js: -------------------------------------------------------------------------------- 1 | import { Util, GeoJSON, geoJson } from 'leaflet'; 2 | import * as EsriLeaflet from 'esri-leaflet'; 3 | import EsriLeafletCluster, { FeatureLayer as EsriLeafletClusterFeatureLayer } from 'esri-leaflet-cluster'; 4 | import classBreaksRenderer from './Renderers/ClassBreaksRenderer'; 5 | import uniqueValueRenderer from './Renderers/UniqueValueRenderer'; 6 | import simpleRenderer from './Renderers/SimpleRenderer'; 7 | 8 | function wireUpRenderers () { 9 | if (this.options.ignoreRenderer) { 10 | return; 11 | } 12 | var oldOnAdd = Util.bind(this.onAdd, this); 13 | var oldUnbindPopup = Util.bind(this.unbindPopup, this); 14 | var oldOnRemove = Util.bind(this.onRemove, this); 15 | 16 | Util.bind(this.createNewLayer, this); 17 | 18 | this.onAdd = function (map) { 19 | this.metadata(function (error, response) { 20 | if (error) { 21 | console.warn('failed to load metadata from the service.'); 22 | return; 23 | } if (response && response.drawingInfo) { 24 | if (this.options.drawingInfo) { 25 | // allow L.esri.webmap (and others) to override service symbology with info provided in layer constructor 26 | response.drawingInfo = this.options.drawingInfo; 27 | } 28 | 29 | // the default pane for lines and polygons is 'overlayPane', for points it is 'markerPane' 30 | if (this.options.pane === 'overlayPane' && response.geometryType === 'esriGeometryPoint') { 31 | this.options.pane = 'markerPane'; 32 | } 33 | 34 | this._setRenderers(response); 35 | oldOnAdd(map); 36 | this._addPointLayer(map); 37 | } 38 | }, this); 39 | }; 40 | 41 | this.onRemove = function (map) { 42 | oldOnRemove(map); 43 | if (this._pointLayer) { 44 | var pointLayers = this._pointLayer.getLayers(); 45 | for (var i in pointLayers) { 46 | map.removeLayer(pointLayers[i]); 47 | } 48 | } 49 | }; 50 | 51 | this.unbindPopup = function () { 52 | oldUnbindPopup(); 53 | if (this._pointLayer) { 54 | var pointLayers = this._pointLayer.getLayers(); 55 | for (var i in pointLayers) { 56 | pointLayers[i].unbindPopup(); 57 | } 58 | } 59 | }; 60 | 61 | this._addPointLayer = function (map) { 62 | if (this._pointLayer) { 63 | this._pointLayer.addTo(map); 64 | this._pointLayer.bringToFront(); 65 | } 66 | }; 67 | 68 | this._createPointLayer = function () { 69 | if (!this._pointLayer) { 70 | this._pointLayer = geoJson(); 71 | // store the feature ids that have already been added to the map 72 | this._pointLayerIds = {}; 73 | 74 | if (this._popup) { 75 | var popupFunction = function (feature, layer) { 76 | layer.bindPopup(this._popup(feature, layer), this._popupOptions); 77 | }; 78 | this._pointLayer.options.onEachFeature = Util.bind(popupFunction, this); 79 | } 80 | } 81 | }; 82 | 83 | this.createNewLayer = function (geojson) { 84 | var fLayer = GeoJSON.geometryToLayer(geojson, this.options); 85 | 86 | // add a point layer when the polygon is represented as proportional marker symbols 87 | if (this._hasProportionalSymbols) { 88 | var centroid = this.getPolygonCentroid(geojson.geometry.coordinates); 89 | if (!(isNaN(centroid[0]) || isNaN(centroid[0]))) { 90 | this._createPointLayer(); 91 | 92 | var featureId = geojson.id.toString(); 93 | // only add the feature if it does not already exist on the map 94 | if (!this._pointLayerIds[featureId]) { 95 | var pointjson = this.getPointJson(geojson, centroid); 96 | 97 | this._pointLayer.addData(pointjson); 98 | this._pointLayerIds[featureId] = true; 99 | } 100 | 101 | this._pointLayer.bringToFront(); 102 | } 103 | } 104 | return fLayer; 105 | }; 106 | 107 | this.getPolygonCentroid = function (coordinates) { 108 | var pts = coordinates[0][0]; 109 | if (pts.length === 2) { 110 | pts = coordinates[0]; 111 | } 112 | 113 | var twicearea = 0; 114 | var x = 0; 115 | var y = 0; 116 | var nPts = pts.length; 117 | var p1; 118 | var p2; 119 | var f; 120 | 121 | for (var i = 0, j = nPts - 1; i < nPts; j = i++) { 122 | p1 = pts[i]; p2 = pts[j]; 123 | twicearea += p1[0] * p2[1]; 124 | twicearea -= p1[1] * p2[0]; 125 | f = (p1[0] * p2[1]) - (p2[0] * p1[1]); 126 | x += (p1[0] + p2[0]) * f; 127 | y += (p1[1] + p2[1]) * f; 128 | } 129 | f = twicearea * 3; 130 | return [x / f, y / f]; 131 | }; 132 | 133 | this.getPointJson = function (geojson, centroid) { 134 | return { 135 | type: 'Feature', 136 | properties: geojson.properties, 137 | id: geojson.id, 138 | geometry: { 139 | type: 'Point', 140 | coordinates: [centroid[0], centroid[1]] 141 | } 142 | }; 143 | }; 144 | 145 | this._checkForProportionalSymbols = function (geometryType, renderer) { 146 | this._hasProportionalSymbols = false; 147 | if (geometryType === 'esriGeometryPolygon') { 148 | if (renderer.backgroundFillSymbol) { 149 | this._hasProportionalSymbols = true; 150 | } 151 | // check to see if the first symbol in the classbreaks is a marker symbol 152 | if (renderer.classBreakInfos && renderer.classBreakInfos.length) { 153 | var sym = renderer.classBreakInfos[0].symbol; 154 | if (sym && (sym.type === 'esriSMS' || sym.type === 'esriPMS')) { 155 | this._hasProportionalSymbols = true; 156 | } 157 | } 158 | } 159 | }; 160 | 161 | this._setRenderers = function (serviceInfo) { 162 | var rend; 163 | var rendererInfo = serviceInfo.drawingInfo.renderer; 164 | 165 | var options = { 166 | url: this.options.url 167 | }; 168 | 169 | if (this.options.token) { 170 | options.token = this.options.token; 171 | } 172 | 173 | if (this.options.pane) { 174 | options.pane = this.options.pane; 175 | } 176 | 177 | if (serviceInfo.drawingInfo.transparency) { 178 | options.layerTransparency = serviceInfo.drawingInfo.transparency; 179 | } 180 | 181 | if (this.options.style) { 182 | options.userDefinedStyle = this.options.style; 183 | } 184 | 185 | switch (rendererInfo.type) { 186 | case 'classBreaks': 187 | this._checkForProportionalSymbols(serviceInfo.geometryType, rendererInfo); 188 | if (this._hasProportionalSymbols) { 189 | this._createPointLayer(); 190 | var pRend = classBreaksRenderer(rendererInfo, options); 191 | pRend.attachStylesToLayer(this._pointLayer); 192 | options.proportionalPolygon = true; 193 | } 194 | rend = classBreaksRenderer(rendererInfo, options); 195 | break; 196 | case 'uniqueValue': 197 | rend = uniqueValueRenderer(rendererInfo, options); 198 | break; 199 | default: 200 | rend = simpleRenderer(rendererInfo, options); 201 | } 202 | rend.attachStylesToLayer(this); 203 | }; 204 | } 205 | 206 | EsriLeaflet.FeatureLayer.addInitHook(wireUpRenderers); 207 | 208 | if (typeof EsriLeafletCluster !== 'undefined' && EsriLeafletClusterFeatureLayer) { 209 | EsriLeafletClusterFeatureLayer.addInitHook(wireUpRenderers); 210 | } 211 | -------------------------------------------------------------------------------- /src/Renderers/ClassBreaksRenderer.js: -------------------------------------------------------------------------------- 1 | import Renderer from './Renderer'; 2 | 3 | export var ClassBreaksRenderer = Renderer.extend({ 4 | initialize: function (rendererJson, options) { 5 | Renderer.prototype.initialize.call(this, rendererJson, options); 6 | this._field = this._rendererJson.field; 7 | if (this._rendererJson.normalizationType && this._rendererJson.normalizationType === 'esriNormalizeByField') { 8 | this._normalizationField = this._rendererJson.normalizationField; 9 | } 10 | this._createSymbols(); 11 | }, 12 | 13 | _createSymbols: function () { 14 | var symbol; 15 | var classbreaks = this._rendererJson.classBreakInfos; 16 | 17 | this._symbols = []; 18 | 19 | // create a symbol for each class break 20 | for (var i = classbreaks.length - 1; i >= 0; i--) { 21 | if (this.options.proportionalPolygon && this._rendererJson.backgroundFillSymbol) { 22 | symbol = this._newSymbol(this._rendererJson.backgroundFillSymbol); 23 | } else { 24 | symbol = this._newSymbol(classbreaks[i].symbol); 25 | } 26 | symbol.val = classbreaks[i].classMaxValue; 27 | this._symbols.push(symbol); 28 | } 29 | // sort the symbols in ascending value 30 | this._symbols.sort(function (a, b) { 31 | return a.val > b.val ? 1 : -1; 32 | }); 33 | this._createDefaultSymbol(); 34 | this._maxValue = this._symbols[this._symbols.length - 1].val; 35 | }, 36 | 37 | _getSymbol: function (feature) { 38 | var val = feature.properties[this._field]; 39 | if (this._normalizationField) { 40 | var normValue = feature.properties[this._normalizationField]; 41 | if (!isNaN(normValue) && normValue !== 0) { 42 | val = val / normValue; 43 | } else { 44 | return this._defaultSymbol; 45 | } 46 | } 47 | 48 | if (val > this._maxValue) { 49 | return this._defaultSymbol; 50 | } 51 | var symbol = this._symbols[0]; 52 | for (var i = this._symbols.length - 1; i >= 0; i--) { 53 | if (val > this._symbols[i].val) { 54 | break; 55 | } 56 | symbol = this._symbols[i]; 57 | } 58 | return symbol; 59 | } 60 | }); 61 | 62 | export function classBreaksRenderer (rendererJson, options) { 63 | return new ClassBreaksRenderer(rendererJson, options); 64 | } 65 | 66 | export default classBreaksRenderer; 67 | -------------------------------------------------------------------------------- /src/Renderers/Renderer.js: -------------------------------------------------------------------------------- 1 | import { Class, Util, circleMarker } from 'leaflet'; 2 | 3 | import pointSymbol from '../Symbols/PointSymbol'; 4 | import lineSymbol from '../Symbols/LineSymbol'; 5 | import polygonSymbol from '../Symbols/PolygonSymbol'; 6 | 7 | export var Renderer = Class.extend({ 8 | options: { 9 | proportionalPolygon: false, 10 | clickable: true 11 | }, 12 | 13 | initialize: function (rendererJson, options) { 14 | this._rendererJson = rendererJson; 15 | this._pointSymbols = false; 16 | this._symbols = []; 17 | this._visualVariables = this._parseVisualVariables(rendererJson.visualVariables); 18 | Util.setOptions(this, options); 19 | }, 20 | 21 | _parseVisualVariables: function (visualVariables) { 22 | var visVars = {}; 23 | if (visualVariables) { 24 | for (var i = 0; i < visualVariables.length; i++) { 25 | visVars[visualVariables[i].type] = visualVariables[i]; 26 | } 27 | } 28 | return visVars; 29 | }, 30 | 31 | _createDefaultSymbol: function () { 32 | if (this._rendererJson.defaultSymbol) { 33 | this._defaultSymbol = this._newSymbol(this._rendererJson.defaultSymbol); 34 | this._defaultSymbol._isDefault = true; 35 | } 36 | }, 37 | 38 | _newSymbol: function (symbolJson) { 39 | if (symbolJson.type === 'esriSMS' || symbolJson.type === 'esriPMS') { 40 | this._pointSymbols = true; 41 | return pointSymbol(symbolJson, this.options); 42 | } 43 | if (symbolJson.type === 'esriSLS') { 44 | return lineSymbol(symbolJson, this.options); 45 | } 46 | if (symbolJson.type === 'esriSFS') { 47 | return polygonSymbol(symbolJson, this.options); 48 | } 49 | }, 50 | 51 | _getSymbol: function () { 52 | // override 53 | }, 54 | 55 | attachStylesToLayer: function (layer) { 56 | if (this._pointSymbols) { 57 | layer.options.pointToLayer = Util.bind(this.pointToLayer, this); 58 | } else { 59 | layer.options.style = Util.bind(this.style, this); 60 | layer._originalStyle = layer.options.style; 61 | } 62 | }, 63 | 64 | pointToLayer: function (geojson, latlng) { 65 | var sym = this._getSymbol(geojson); 66 | if (sym && sym.pointToLayer) { 67 | // right now custom panes are the only option pushed through 68 | return sym.pointToLayer(geojson, latlng, this._visualVariables, this.options); 69 | } 70 | // invisible symbology 71 | return circleMarker(latlng, { radius: 0, opacity: 0 }); 72 | }, 73 | 74 | style: function (feature) { 75 | var userStyles; 76 | if (this.options.userDefinedStyle) { 77 | userStyles = this.options.userDefinedStyle(feature); 78 | } 79 | // find the symbol to represent this feature 80 | var sym = this._getSymbol(feature); 81 | if (sym) { 82 | return this.mergeStyles(sym.style(feature, this._visualVariables), userStyles); 83 | } else { 84 | // invisible symbology 85 | return this.mergeStyles({ opacity: 0, fillOpacity: 0 }, userStyles); 86 | } 87 | }, 88 | 89 | mergeStyles: function (styles, userStyles) { 90 | var mergedStyles = {}; 91 | var attr; 92 | // copy renderer style attributes 93 | for (attr in styles) { 94 | if (Object.prototype.hasOwnProperty.call(styles, attr)) { 95 | mergedStyles[attr] = styles[attr]; 96 | } 97 | } 98 | // override with user defined style attributes 99 | if (userStyles) { 100 | for (attr in userStyles) { 101 | if (Object.prototype.hasOwnProperty.call(userStyles, attr)) { 102 | mergedStyles[attr] = userStyles[attr]; 103 | } 104 | } 105 | } 106 | return mergedStyles; 107 | } 108 | }); 109 | 110 | export default Renderer; 111 | -------------------------------------------------------------------------------- /src/Renderers/SimpleRenderer.js: -------------------------------------------------------------------------------- 1 | import Renderer from './Renderer'; 2 | 3 | export var SimpleRenderer = Renderer.extend({ 4 | initialize: function (rendererJson, options) { 5 | Renderer.prototype.initialize.call(this, rendererJson, options); 6 | this._createSymbol(); 7 | }, 8 | 9 | _createSymbol: function () { 10 | if (this._rendererJson.symbol) { 11 | this._symbols.push(this._newSymbol(this._rendererJson.symbol)); 12 | } 13 | }, 14 | 15 | _getSymbol: function () { 16 | return this._symbols[0]; 17 | } 18 | }); 19 | 20 | export function simpleRenderer (rendererJson, options) { 21 | return new SimpleRenderer(rendererJson, options); 22 | } 23 | 24 | export default simpleRenderer; 25 | -------------------------------------------------------------------------------- /src/Renderers/UniqueValueRenderer.js: -------------------------------------------------------------------------------- 1 | import Renderer from './Renderer'; 2 | 3 | export var UniqueValueRenderer = Renderer.extend({ 4 | initialize: function (rendererJson, options) { 5 | Renderer.prototype.initialize.call(this, rendererJson, options); 6 | this._field = this._rendererJson.field1; 7 | this._createSymbols(); 8 | }, 9 | 10 | _createSymbols: function () { 11 | var symbol; 12 | var uniques = this._rendererJson.uniqueValueInfos; 13 | 14 | // create a symbol for each unique value 15 | for (var i = uniques.length - 1; i >= 0; i--) { 16 | symbol = this._newSymbol(uniques[i].symbol); 17 | symbol.val = uniques[i].value; 18 | this._symbols.push(symbol); 19 | } 20 | this._createDefaultSymbol(); 21 | }, 22 | 23 | _getSymbol: function (feature) { 24 | var val = feature.properties[this._field]; 25 | // accumulate values if there is more than one field defined 26 | if (this._rendererJson.fieldDelimiter && this._rendererJson.field2) { 27 | var val2 = feature.properties[this._rendererJson.field2]; 28 | if (val2) { 29 | val += this._rendererJson.fieldDelimiter + val2; 30 | var val3 = feature.properties[this._rendererJson.field3]; 31 | if (val3) { 32 | val += this._rendererJson.fieldDelimiter + val3; 33 | } 34 | } 35 | } 36 | 37 | var symbol = this._defaultSymbol; 38 | for (var i = this._symbols.length - 1; i >= 0; i--) { 39 | // using the === operator does not work if the field 40 | // of the unique renderer is not a string 41 | /*eslint-disable */ 42 | if (this._symbols[i].val == val) { 43 | symbol = this._symbols[i]; 44 | } 45 | /* eslint-enable */ 46 | } 47 | return symbol; 48 | } 49 | }); 50 | 51 | export function uniqueValueRenderer (rendererJson, options) { 52 | return new UniqueValueRenderer(rendererJson, options); 53 | } 54 | 55 | export default uniqueValueRenderer; 56 | -------------------------------------------------------------------------------- /src/Symbols/LineSymbol.js: -------------------------------------------------------------------------------- 1 | import Symbol from './Symbol'; 2 | 3 | export var LineSymbol = Symbol.extend({ 4 | statics: { 5 | // Not implemented 'esriSLSNull' 6 | LINETYPES: ['esriSLSDash', 'esriSLSDot', 'esriSLSDashDotDot', 'esriSLSDashDot', 'esriSLSSolid'] 7 | }, 8 | initialize: function (symbolJson, options) { 9 | Symbol.prototype.initialize.call(this, symbolJson, options); 10 | this._fillStyles(); 11 | }, 12 | 13 | _fillStyles: function () { 14 | // set the defaults that show up on arcgis online 15 | this._styles.lineCap = 'butt'; 16 | this._styles.lineJoin = 'miter'; 17 | this._styles.fill = false; 18 | this._styles.weight = 0; 19 | 20 | if (!this._symbolJson) { 21 | return this._styles; 22 | } 23 | 24 | if (this._symbolJson.color) { 25 | this._styles.color = this.colorValue(this._symbolJson.color); 26 | this._styles.opacity = this.alphaValue(this._symbolJson.color); 27 | } 28 | 29 | if (!isNaN(this._symbolJson.width)) { 30 | this._styles.weight = this.pixelValue(this._symbolJson.width); 31 | 32 | var dashValues = []; 33 | 34 | switch (this._symbolJson.style) { 35 | case 'esriSLSDash': 36 | dashValues = [4, 3]; 37 | break; 38 | case 'esriSLSDot': 39 | dashValues = [1, 3]; 40 | break; 41 | case 'esriSLSDashDot': 42 | dashValues = [8, 3, 1, 3]; 43 | break; 44 | case 'esriSLSDashDotDot': 45 | dashValues = [8, 3, 1, 3, 1, 3]; 46 | break; 47 | } 48 | 49 | // use the dash values and the line weight to set dash array 50 | if (dashValues.length > 0) { 51 | for (var i = 0; i < dashValues.length; i++) { 52 | dashValues[i] *= this._styles.weight; 53 | } 54 | 55 | this._styles.dashArray = dashValues.join(','); 56 | } 57 | } 58 | }, 59 | 60 | style: function (feature, visualVariables) { 61 | if (!this._isDefault && visualVariables) { 62 | if (visualVariables.sizeInfo) { 63 | var calculatedSize = this.pixelValue(this.getSize(feature, visualVariables.sizeInfo)); 64 | if (calculatedSize) { 65 | this._styles.weight = calculatedSize; 66 | } 67 | } 68 | if (visualVariables.colorInfo) { 69 | var color = this.getColor(feature, visualVariables.colorInfo); 70 | if (color) { 71 | this._styles.color = this.colorValue(color); 72 | this._styles.opacity = this.alphaValue(color); 73 | } 74 | } 75 | } 76 | return this._styles; 77 | } 78 | }); 79 | 80 | export function lineSymbol (symbolJson, options) { 81 | return new LineSymbol(symbolJson, options); 82 | } 83 | 84 | export default lineSymbol; 85 | -------------------------------------------------------------------------------- /src/Symbols/PointSymbol.js: -------------------------------------------------------------------------------- 1 | import { 2 | marker, 3 | icon as leafletIcon, 4 | extend, 5 | circleMarker 6 | } from 'leaflet'; 7 | import Symbol from './Symbol'; 8 | import { squareMarker, xMarker, crossMarker, diamondMarker } from 'leaflet-shape-markers'; 9 | 10 | export var PointSymbol = Symbol.extend({ 11 | 12 | statics: { 13 | MARKERTYPES: ['esriSMSCircle', 'esriSMSCross', 'esriSMSDiamond', 'esriSMSSquare', 'esriSMSX', 'esriPMS'] 14 | }, 15 | 16 | initialize: function (symbolJson, options) { 17 | var url; 18 | Symbol.prototype.initialize.call(this, symbolJson, options); 19 | if (options) { 20 | this.serviceUrl = options.url; 21 | } 22 | if (symbolJson) { 23 | if (symbolJson.type === 'esriPMS') { 24 | var imageUrl = this._symbolJson.url; 25 | if ((imageUrl && imageUrl.substr(0, 7) === 'http://') || (imageUrl.substr(0, 8) === 'https://')) { 26 | // web image 27 | url = this.sanitize(imageUrl); 28 | this._iconUrl = url; 29 | } else { 30 | url = this.serviceUrl + 'images/' + imageUrl; 31 | this._iconUrl = options && options.token ? url + '?token=' + options.token : url; 32 | } 33 | if (symbolJson.imageData) { 34 | this._iconUrl = 'data:' + symbolJson.contentType + ';base64,' + symbolJson.imageData; 35 | } 36 | // leaflet does not allow resizing icons so keep a hash of different 37 | // icon sizes to try and keep down on the number of icons created 38 | this._icons = {}; 39 | // create base icon 40 | this.icon = this._createIcon(this._symbolJson); 41 | } else { 42 | this._fillStyles(); 43 | } 44 | } 45 | }, 46 | 47 | // prevent html injection in strings 48 | sanitize: function (str) { 49 | if (!str) { 50 | return ''; 51 | } 52 | var text; 53 | try { 54 | // removes html but leaves url link text 55 | text = str.replace(/
/gi, '\n'); 56 | text = text.replace(//gi, '\n'); 57 | text = text.replace(/(.*?)<\/a>/gi, ' $2 ($1) '); 58 | text = text.replace(/<(?:.|\s)*?>/g, ''); 59 | } catch (ex) { 60 | text = null; 61 | } 62 | return text; 63 | }, 64 | 65 | _fillStyles: function () { 66 | if (this._symbolJson.outline && this._symbolJson.size > 0 && this._symbolJson.outline.style !== 'esriSLSNull') { 67 | this._styles.stroke = true; 68 | this._styles.weight = this.pixelValue(this._symbolJson.outline.width); 69 | this._styles.color = this.colorValue(this._symbolJson.outline.color); 70 | this._styles.opacity = this.alphaValue(this._symbolJson.outline.color); 71 | } else { 72 | this._styles.stroke = false; 73 | } 74 | if (this._symbolJson.color) { 75 | this._styles.fillColor = this.colorValue(this._symbolJson.color); 76 | this._styles.fillOpacity = this.alphaValue(this._symbolJson.color); 77 | } else { 78 | this._styles.fillOpacity = 0; 79 | } 80 | 81 | if (this._symbolJson.style === 'esriSMSCircle') { 82 | this._styles.radius = this.pixelValue(this._symbolJson.size) / 2.0; 83 | } 84 | }, 85 | 86 | _createIcon: function (options) { 87 | var width = this.pixelValue(options.width); 88 | var height = width; 89 | if (options.height) { 90 | height = this.pixelValue(options.height); 91 | } 92 | var xOffset = width / 2.0; 93 | var yOffset = height / 2.0; 94 | 95 | if (options.xoffset) { 96 | xOffset += this.pixelValue(options.xoffset); 97 | } 98 | if (options.yoffset) { 99 | yOffset += this.pixelValue(options.yoffset); 100 | } 101 | 102 | var icon = leafletIcon({ 103 | iconUrl: this._iconUrl, 104 | iconSize: [width, height], 105 | iconAnchor: [xOffset, yOffset] 106 | }); 107 | this._icons[options.width.toString()] = icon; 108 | return icon; 109 | }, 110 | 111 | _getIcon: function (size) { 112 | // check to see if it is already created by size 113 | var icon = this._icons[size.toString()]; 114 | if (!icon) { 115 | icon = this._createIcon({ width: size }); 116 | } 117 | return icon; 118 | }, 119 | 120 | pointToLayer: function (geojson, latlng, visualVariables, options) { 121 | var size = this._symbolJson.size || this._symbolJson.width; 122 | if (!this._isDefault) { 123 | if (visualVariables.sizeInfo) { 124 | var calculatedSize = this.getSize(geojson, visualVariables.sizeInfo); 125 | if (calculatedSize) { 126 | size = calculatedSize; 127 | } 128 | } 129 | if (visualVariables.colorInfo) { 130 | var color = this.getColor(geojson, visualVariables.colorInfo); 131 | if (color) { 132 | this._styles.fillColor = this.colorValue(color); 133 | this._styles.fillOpacity = this.alphaValue(color); 134 | } 135 | } 136 | } 137 | 138 | if (this._symbolJson.type === 'esriPMS') { 139 | var layerOptions = extend({}, { icon: this._getIcon(size) }, options); 140 | return marker(latlng, layerOptions); 141 | } 142 | size = this.pixelValue(size); 143 | 144 | switch (this._symbolJson.style) { 145 | case 'esriSMSSquare': 146 | return squareMarker(latlng, size, extend({}, this._styles, options)); 147 | case 'esriSMSDiamond': 148 | return diamondMarker(latlng, size, extend({}, this._styles, options)); 149 | case 'esriSMSCross': 150 | return crossMarker(latlng, size, extend({}, this._styles, options)); 151 | case 'esriSMSX': 152 | return xMarker(latlng, size, extend({}, this._styles, options)); 153 | } 154 | this._styles.radius = size / 2.0; 155 | return circleMarker(latlng, extend({}, this._styles, options)); 156 | } 157 | }); 158 | 159 | export function pointSymbol (symbolJson, options) { 160 | return new PointSymbol(symbolJson, options); 161 | } 162 | 163 | export default pointSymbol; 164 | -------------------------------------------------------------------------------- /src/Symbols/PolygonSymbol.js: -------------------------------------------------------------------------------- 1 | import Symbol from './Symbol'; 2 | import lineSymbol from './LineSymbol'; 3 | 4 | export var PolygonSymbol = Symbol.extend({ 5 | statics: { 6 | // not implemented: 'esriSFSBackwardDiagonal','esriSFSCross','esriSFSDiagonalCross','esriSFSForwardDiagonal','esriSFSHorizontal','esriSFSNull','esriSFSVertical' 7 | POLYGONTYPES: ['esriSFSSolid'] 8 | }, 9 | initialize: function (symbolJson, options) { 10 | Symbol.prototype.initialize.call(this, symbolJson, options); 11 | if (symbolJson) { 12 | if (symbolJson.outline && symbolJson.outline.style === 'esriSLSNull') { 13 | this._lineStyles = { weight: 0 }; 14 | } else { 15 | this._lineStyles = lineSymbol(symbolJson.outline, options).style(); 16 | } 17 | this._fillStyles(); 18 | } 19 | }, 20 | 21 | _fillStyles: function () { 22 | if (this._lineStyles) { 23 | if (this._lineStyles.weight === 0) { 24 | // when weight is 0, setting the stroke to false can still look bad 25 | // (gaps between the polygons) 26 | this._styles.stroke = false; 27 | } else { 28 | // copy the line symbol styles into this symbol's styles 29 | for (var styleAttr in this._lineStyles) { 30 | this._styles[styleAttr] = this._lineStyles[styleAttr]; 31 | } 32 | } 33 | } 34 | 35 | // set the fill for the polygon 36 | if (this._symbolJson) { 37 | if (this._symbolJson.color && 38 | // don't fill polygon if type is not supported 39 | PolygonSymbol.POLYGONTYPES.indexOf(this._symbolJson.style >= 0)) { 40 | this._styles.fill = true; 41 | this._styles.fillColor = this.colorValue(this._symbolJson.color); 42 | this._styles.fillOpacity = this.alphaValue(this._symbolJson.color); 43 | } else { 44 | this._styles.fill = false; 45 | this._styles.fillOpacity = 0; 46 | } 47 | } 48 | }, 49 | 50 | style: function (feature, visualVariables) { 51 | if (!this._isDefault && visualVariables && visualVariables.colorInfo) { 52 | var color = this.getColor(feature, visualVariables.colorInfo); 53 | if (color) { 54 | this._styles.fillColor = this.colorValue(color); 55 | this._styles.fillOpacity = this.alphaValue(color); 56 | } 57 | } 58 | return this._styles; 59 | } 60 | }); 61 | 62 | export function polygonSymbol (symbolJson, options) { 63 | return new PolygonSymbol(symbolJson, options); 64 | } 65 | 66 | export default polygonSymbol; 67 | -------------------------------------------------------------------------------- /src/Symbols/Symbol.js: -------------------------------------------------------------------------------- 1 | import { Class } from 'leaflet'; 2 | 3 | export var Symbol = Class.extend({ 4 | initialize: function (symbolJson, options) { 5 | this._symbolJson = symbolJson; 6 | this.val = null; 7 | this._styles = {}; 8 | this._isDefault = false; 9 | this._layerTransparency = 1; 10 | if (options && options.layerTransparency) { 11 | this._layerTransparency = 1 - (options.layerTransparency / 100.0); 12 | } 13 | }, 14 | 15 | // the geojson values returned are in points 16 | pixelValue: function (pointValue) { 17 | return pointValue * 1.333; 18 | }, 19 | 20 | // color is an array [r,g,b,a] 21 | colorValue: function (color) { 22 | return 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')'; 23 | }, 24 | 25 | alphaValue: function (color) { 26 | var alpha = color[3] / 255.0; 27 | return alpha * this._layerTransparency; 28 | }, 29 | 30 | getSize: function (feature, sizeInfo) { 31 | var attr = feature.properties; 32 | var field = sizeInfo.field; 33 | var size = 0; 34 | var featureValue = null; 35 | 36 | if (field) { 37 | featureValue = attr[field]; 38 | var minSize = sizeInfo.minSize; 39 | var maxSize = sizeInfo.maxSize; 40 | var minDataValue = sizeInfo.minDataValue; 41 | var maxDataValue = sizeInfo.maxDataValue; 42 | var featureRatio; 43 | var normField = sizeInfo.normalizationField; 44 | var normValue = attr ? parseFloat(attr[normField]) : undefined; 45 | 46 | if (featureValue === null || (normField && ((isNaN(normValue) || normValue === 0)))) { 47 | return null; 48 | } 49 | 50 | if (!isNaN(normValue)) { 51 | featureValue /= normValue; 52 | } 53 | 54 | if (minSize !== null && maxSize !== null && minDataValue !== null && maxDataValue !== null) { 55 | if (featureValue <= minDataValue) { 56 | size = minSize; 57 | } else if (featureValue >= maxDataValue) { 58 | size = maxSize; 59 | } else { 60 | featureRatio = (featureValue - minDataValue) / (maxDataValue - minDataValue); 61 | size = minSize + (featureRatio * (maxSize - minSize)); 62 | } 63 | } 64 | size = isNaN(size) ? 0 : size; 65 | } 66 | return size; 67 | }, 68 | 69 | getColor: function (feature, colorInfo) { 70 | // required information to get color 71 | if (!(feature.properties && colorInfo && colorInfo.field && colorInfo.stops)) { 72 | return null; 73 | } 74 | 75 | var attr = feature.properties; 76 | var featureValue = attr[colorInfo.field]; 77 | var lowerBoundColor, upperBoundColor, lowerBound, upperBound; 78 | var normField = colorInfo.normalizationField; 79 | var normValue = attr ? parseFloat(attr[normField]) : undefined; 80 | if (featureValue === null || (normField && ((isNaN(normValue) || normValue === 0)))) { 81 | return null; 82 | } 83 | 84 | if (!isNaN(normValue)) { 85 | featureValue /= normValue; 86 | } 87 | 88 | if (featureValue <= colorInfo.stops[0].value) { 89 | return colorInfo.stops[0].color; 90 | } 91 | var lastStop = colorInfo.stops[colorInfo.stops.length - 1]; 92 | if (featureValue >= lastStop.value) { 93 | return lastStop.color; 94 | } 95 | 96 | // go through the stops to find min and max 97 | for (var i = 0; i < colorInfo.stops.length; i++) { 98 | var stopInfo = colorInfo.stops[i]; 99 | 100 | if (stopInfo.value <= featureValue) { 101 | lowerBoundColor = stopInfo.color; 102 | lowerBound = stopInfo.value; 103 | } else if (stopInfo.value > featureValue) { 104 | upperBoundColor = stopInfo.color; 105 | upperBound = stopInfo.value; 106 | break; 107 | } 108 | } 109 | 110 | // feature falls between two stops, interplate the colors 111 | if (!isNaN(lowerBound) && !isNaN(upperBound)) { 112 | var range = upperBound - lowerBound; 113 | if (range > 0) { 114 | // more weight the further it is from the lower bound 115 | var upperBoundColorWeight = (featureValue - lowerBound) / range; 116 | if (upperBoundColorWeight) { 117 | // more weight the further it is from the upper bound 118 | var lowerBoundColorWeight = (upperBound - featureValue) / range; 119 | if (lowerBoundColorWeight) { 120 | // interpolate the lower and upper bound color by applying the 121 | // weights to each of the rgba colors and adding them together 122 | var interpolatedColor = []; 123 | for (var j = 0; j < 4; j++) { 124 | interpolatedColor[j] = Math.round((lowerBoundColor[j] * lowerBoundColorWeight) + (upperBoundColor[j] * upperBoundColorWeight)); 125 | } 126 | return interpolatedColor; 127 | } else { 128 | // no difference between featureValue and upperBound, 100% of upperBoundColor 129 | return upperBoundColor; 130 | } 131 | } else { 132 | // no difference between featureValue and lowerBound, 100% of lowerBoundColor 133 | return lowerBoundColor; 134 | } 135 | } 136 | } 137 | // if we get to here, none of the cases apply so return null 138 | return null; 139 | } 140 | }); 141 | 142 | // export function symbol (symbolJson) { 143 | // return new Symbol(symbolJson); 144 | // } 145 | 146 | export default Symbol; 147 | --------------------------------------------------------------------------------