├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── benchmarks
└── benchmarks.js
├── bower.json
├── examples
├── .gitignore
├── README.md
├── package.json
└── terraformer-arcgis-fileparser.js
├── index.d.ts
├── jasmine.json
├── package-lock.json
├── package.json
├── release.sh
├── spec
└── arcgisSpec.js
├── terraformer-arcgis-parser.js
├── test.ts
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore complexity output
2 | complexity.xml
3 |
4 | # Ignore Coverage output
5 | coverage
6 |
7 | # Ignore Grunt temp files
8 | .grunt
9 |
10 | # Mac
11 | .DS_Store
12 | Icon
13 | ._*
14 | .Spotlight-V100
15 | # SublimeText
16 | /*.sublime-project
17 | *.sublime-workspace
18 |
19 | #Node
20 | node_modules
21 | npm-debug.log
22 |
23 | # built
24 | terraformer-arcgis-parser.min.js
25 |
26 | # typescript
27 | typings/**
28 | test.js
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: required
3 | notifications:
4 | email: false
5 | before_install: npm install -g grunt-cli
6 | node_js:
7 | - "10"
8 | cache:
9 | directories:
10 | - node_modules
11 | addons:
12 | chrome: stable
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 |
5 | ## [unreleased]
6 |
7 | ## [1.1.0] - 2018-07-09
8 |
9 | ### Added
10 |
11 | * ArcGIS Extents/Envelopes are now converted to GeoJSON polygons 🙏CorinChappy🙏
12 | * great new command line demo app 🙏gseyffert🙏
13 | * an appropriate CRS is appended to output GeoJSON when web mercator geometries are encountered.
14 |
15 | ### Changed
16 |
17 | * Ensure ring-order of GeoJSON is compliant with RFC 7946
18 | * web mercator northings/eastings are no longer reprojected during conversion.
19 | * TypeScript support files updated 🙏JeffJacobson🙏
20 |
21 | ## [1.0.5] - 2016-08-16
22 |
23 | ### Fixed
24 | * Check for GeoJSON feature `id`s before assigning to output [#24](https://github.com/Esri/terraformer-arcgis-parser/pull/24)
25 | * correct conversion of polygons with outer rings not containing holes [#28](https://github.com/Esri/terraformer-arcgis-parser/pull/28)
26 |
27 | ### Added
28 | * typings for TypeScript folks (thx [@JeffJacobson](https://github.com/JeffJacobson)) [#34](https://github.com/Esri/terraformer-arcgis-parser/pull/34)
29 |
30 | ## [1.0.4] - 2014-06-17
31 |
32 | ### Fixed
33 | * Account for breaking change in `Terraformer`
34 |
35 | ### Added
36 | * support for `z` and `m` conversion
37 |
38 | ## [1.0.3] - 2015-02-24
39 |
40 | ### Fixed
41 | * valid output on both ends of conversion [#19](https://github.com/Esri/terraformer-arcgis-parser/issues/19)
42 |
43 | ## [1.0.2] - 2014-02-10
44 |
45 | ### Added
46 | * `parseCompressedGeometry()` [#10](https://github.com/Esri/terraformer-arcgis-parser/issues/10)
47 |
48 | ### Fixed
49 | * `parse()` and `convert()` now close polygons during conversion. [#9](https://github.com/Esri/terraformer-arcgis-parser/issues/9)
50 | * `parse()` now handles `compressedGeometry`
51 |
52 | ## [1.0.1] - 2013-12-04
53 |
54 | ### Fixed
55 | * `ReferenceError: sr is not defined` in `ArcGIS.convert()`
56 |
57 | ## [1.0.0] - 2013-11-12
58 |
59 | Initial Release. Also available on NPM and Bower
60 |
61 | ```
62 | npm install terraformer-arcgis-parser
63 | bower install terraformer-arcgis-parser
64 | ```
65 |
66 | [unreleased]: https://github.com/Esri/terraformer-arcgis-parser/compare/v1.1.0...HEAD
67 | [1.1.0]: https://github.com/Esri/terraformer-arcgis-parser/compare/v1.0.5...v1.1.0
68 | [1.0.5]: https://github.com/Esri/terraformer-arcgis-parser/compare/v1.0.4...v1.0.5
69 | [1.0.4]: https://github.com/Esri/terraformer-arcgis-parser/compare/v1.0.3...v1.0.4
70 | [1.0.3]: https://github.com/Esri/terraformer-arcgis-parser/compare/v1.0.2...v1.0.3
71 | [1.0.2]: https://github.com/Esri/terraformer-arcgis-parser/compare/v1.0.1...v1.0.2
72 | [1.0.1]: https://github.com/Esri/terraformer-arcgis-parser/compare/v1.0.0...v1.0.1
73 | [1.0.0]: https://github.com/Esri/terraformer-arcgis-parser/releases/tag/v1.0.0
74 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | module.exports = function (grunt) {
4 | grunt.initConfig({
5 | pkg: grunt.file.readJSON('package.json'),
6 |
7 | meta: {
8 | banner: '/*! Terraformer ArcGIS Parser - <%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
9 | '* https://github.com/esri/terraformer-arcgis-parser\n' +
10 | '* Copyright (c) 2013-<%= grunt.template.today("yyyy") %> Esri, Inc.\n' +
11 | '* Licensed MIT */'
12 | },
13 |
14 | uglify: {
15 | options: {
16 | report: 'gzip',
17 | banner: '<%= meta.banner %>'
18 | },
19 | arcgis: {
20 | src: ["terraformer-arcgis-parser.js"],
21 | dest: 'terraformer-arcgis-parser.min.js'
22 | }
23 | },
24 |
25 | jasmine: {
26 | coverage: {
27 | src: [
28 | "terraformer-arcgis-parser.js"
29 | ],
30 | options: {
31 | specs: 'spec/*Spec.js',
32 | helpers:[
33 | "node_modules/terraformer/terraformer.js"
34 | ],
35 | //keepRunner: true,
36 | outfile: 'SpecRunner.html',
37 | // template: require('grunt-template-jasmine-istanbul'),
38 | // templateOptions: {
39 | // coverage: './coverage/coverage.json',
40 | // report: './coverage',
41 | // thresholds: {
42 | // lines: 80,
43 | // statements: 80,
44 | // branches: 75,
45 | // functions: 80
46 | // }
47 | // }
48 | }
49 | }
50 | },
51 |
52 | complexity: {
53 | generic: {
54 | src: [ 'terraformer-arcgis-parser.js' ],
55 | options: {
56 | jsLintXML: 'complexity.xml', // create XML JSLint-like report
57 | errorsOnly: false, // show only maintainability errors
58 | cyclomatic: 6,
59 | halstead: 15,
60 | maintainability: 65
61 | }
62 | }
63 | }
64 | });
65 |
66 | grunt.loadNpmTasks('grunt-contrib-uglify');
67 | grunt.loadNpmTasks('grunt-contrib-jasmine');
68 | grunt.loadNpmTasks('grunt-complexity');
69 |
70 | grunt.registerTask('test', ['jasmine']);
71 | grunt.registerTask('version', ['test', 'uglify']);
72 | grunt.registerTask('default', ['test']);
73 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013-2018 Esri
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Important!
2 |
3 | This repo is part of the Terraformer project which has been archived. See https://github.com/Esri/terraformer#important for more details.
4 |
5 | # Terraformer ArcGIS JSON Parser
6 |
7 | [](https://travis-ci.org/Esri/terraformer-arcgis-parser)
8 |
9 | > Two way conversion between [GeoJSON](http://geojson.org/geojson-spec.html) and [ArcGIS Geometry](http://help.arcgis.com/en/arcgisserver/10.0/apis/rest/geometry.html).
10 |
11 | ## Installing
12 |
13 | ### Node.js
14 |
15 | $ npm install terraformer-arcgis-parser
16 |
17 | ### Browser
18 |
19 | In the browser, [Terraformer](http://github.com/esri/terraformer) is required.
20 |
21 | ## Documentation
22 |
23 | For full documentation check https://github.com/Esri/terraformer/blob/master/docs/arcgis-parser.md.
24 |
25 | ### Node.js
26 | ```js
27 | var ArcGIS = require('terraformer-arcgis-parser');
28 |
29 | // parse ArcGIS JSON, convert it to a Terraformer.Primitive (GeoJSON)
30 | var primitive = ArcGIS.parse({
31 | 'x':-122.6764,
32 | 'y':45.5165,
33 | 'spatialReference': {
34 | 'wkid': 4326
35 | }
36 | });
37 |
38 | // take a Terraformer.Primitive or GeoJSON and convert it back to ArcGIS JSON
39 | var point = ArcGIS.convert({
40 | 'type': "Point",
41 | 'coordinates': [45.5165, -122.6764]
42 | });
43 | ```
44 | ### Browser
45 | ```html
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
68 | ```
69 | ### TypeScript
70 | ```
71 | import arcgis = require("terraformer-arcgis-parser");
72 | ```
73 |
74 | ## Issues
75 |
76 | Find a bug or want to request a new feature? Please let us know by submitting an issue.
77 |
78 | ## Contributing
79 |
80 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing).
81 |
82 | ## Licensing
83 |
84 | Copyright © 2013-2018 Esri
85 |
86 | A copy of the license is available in the repository's [LICENSE](./LICENSE) file.
87 |
--------------------------------------------------------------------------------
/benchmarks/benchmarks.js:
--------------------------------------------------------------------------------
1 | ArcGIS = require('../terraformer-arcgis-parser');
2 |
3 | var Benchmark = require('benchmark');
4 |
5 | var suite = new Benchmark.Suite();
6 |
7 | // add tests
8 | suite
9 |
10 | .add('ArcGIS Point to GeoJSON Point', function() {
11 | ArcGIS.parse({
12 | "x": -66.796875,
13 | "y": 20.0390625,
14 | "spatialReference": {
15 | "wkid": 4326
16 | }
17 | });
18 | })
19 |
20 | .add('ArcGIS Polyline to GeoJSON LineString', function() {
21 | ArcGIS.parse({
22 | "paths": [
23 | [ [6.6796875,47.8125],[-65.390625,52.3828125],[-52.3828125,42.5390625] ]
24 | ],
25 | "spatialReference": {
26 | "wkid": 4326
27 | }
28 | });
29 | })
30 |
31 | .add('ArcGIS Polygon to GeoJSON Polygon', function() {
32 | ArcGIS.parse({
33 | "rings": [
34 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
35 | ],
36 | "spatialReference": {
37 | "wkid": 4326
38 | }
39 | });
40 | })
41 |
42 | .add('ArcGIS Multipoint to GeoJSON Multipoint', function(){
43 | ArcGIS.parse({
44 | "points":[ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625] ],
45 | "spatialReference":{
46 | "wkid":4326
47 | }
48 | });
49 | })
50 |
51 | .add('ArcGIS Polyline w/ 2 Paths to GeoJSON MultiLineString', function(){
52 | ArcGIS.parse({
53 | "paths":[
54 | [ [41.8359375,71.015625],[56.953125,33.75] ],
55 | [ [21.796875,36.5625],[41.8359375,71.015625] ]
56 | ],
57 | "spatialReference":{
58 | "wkid":4326
59 | }
60 | });
61 | })
62 |
63 | .add('ArcGIS Polygon w/ 2 Rings to GeoJSON MultiPolygon', function(){
64 | ArcGIS.parse({
65 | "rings":[
66 | [[-122.63,45.52],[-122.57,45.53],[-122.52,45.50],[-122.49,45.48],[-122.64,45.49],[-122.63,45.52],[-122.63,45.52]],
67 | [[-83,35],[-74,35],[-74,41],[-83,41],[-83,35]]
68 | ],
69 | "spatialReference": {
70 | "wkid":4326
71 | }
72 | });
73 | })
74 |
75 | .add('ArcGIS Polygon w/ Holes into GeoJSON MultiPolygon', function(){
76 | ArcGIS.parse({
77 | "type":"polygon",
78 | "rings":[
79 | [ [-100.74462180954974,39.95017165502381],[-94.50439384003792,39.91647453608879],[-94.41650267263967,34.89313438177965],[-100.78856739324887,34.85708140996771],[-100.74462180954974,39.95017165502381] ],
80 | [ [-99.68993678392353,39.341088433448896],[-99.68993678392353,38.24507658785885],[-98.67919734199646,37.86444431771113],[-98.06395917020868,38.210554846669694],[-98.06395917020868,39.341088433448896],[-99.68993678392353,39.341088433448896] ],
81 | [ [-96.83349180978595,37.23732027507514],[-97.31689323047635,35.967330282988534],[-96.5698183075912,35.57512048069255],[-95.42724211456674,36.357601429255965],[-96.83349180978595,37.23732027507514] ],
82 | [ [-101.4916967324349,38.24507658785885],[-101.44775114873578,36.073960493943744],[-103.95263145328033,36.03843312329154],[-103.68895795108557,38.03770050767439],[-101.4916967324349,38.24507658785885] ]
83 | ],
84 | "spatialReference":{
85 | "wkid":4326
86 | }
87 | });
88 | })
89 |
90 | .add('ArcGIS Feature to GeoJSON Feature', function(){
91 | ArcGIS.parse({
92 | "geometry": {
93 | "rings": [
94 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
95 | ],
96 | "spatialReference": {
97 | "wkid": 4326
98 | }
99 | },
100 | "attributes": {
101 | "foo": "bar"
102 | }
103 | });
104 | })
105 |
106 | .add('GeoJSON Point to ArcGIS Point', function() {
107 | ArcGIS.convert({
108 | "type": "Point",
109 | "coordinates": [-58.7109375,47.4609375]
110 | });
111 | })
112 |
113 | .add('GeoJSON LineString to ArcGIS Polyline', function() {
114 | ArcGIS.convert({
115 | "type": "LineString",
116 | "coordinates": [ [21.4453125,-14.0625],[33.3984375,-20.7421875],[38.3203125,-24.609375] ]
117 | });
118 | })
119 |
120 | .add('GeoJSON Polygon to ArcGIS Polygon', function() {
121 | ArcGIS.convert({
122 | "type": "Polygon",
123 | "coordinates": [
124 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
125 | ]
126 | });
127 | })
128 |
129 | .add('GeoJSON Polygon w/ hole to ArcGIS Polygon', function(){
130 | ArcGIS.convert({
131 | "type": "Polygon",
132 | "coordinates": [
133 | [ [100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0] ],
134 | [ [100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2] ]
135 | ]
136 | });
137 | })
138 |
139 | .add('GeoJSON Multipoint to ArcGIS Multipoint', function(){
140 | ArcGIS.convert({
141 | "type": "MultiPoint",
142 | "coordinates": [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625] ]
143 | });
144 | })
145 |
146 | .add('GeoJSON MultiLineString to ArcGIS Polyline', function(){
147 | ArcGIS.convert({
148 | "type": "MultiLineString",
149 | "coordinates": [
150 | [ [41.8359375,71.015625],[56.953125,33.75] ],
151 | [ [21.796875,36.5625],[47.8359375,71.015625] ]
152 | ]
153 | });
154 | })
155 |
156 | .add('GeoJSON MultiPolygon to ArcGIS MultiPolygon', function(){
157 | ArcGIS.convert({
158 | "type": "MultiPolygon",
159 | "coordinates": [
160 | [
161 | [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ]
162 | ],
163 | [
164 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
165 | ]
166 | ]
167 | });
168 | })
169 |
170 | .add('GeoJSON MultiPolygon w/ hole to ArcGIS Polygon', function(){
171 | ArcGIS.convert({
172 | "type": "MultiPolygon",
173 | "coordinates": [
174 | [
175 | [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ]
176 | ],
177 | [
178 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ],
179 | [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]
180 | ]
181 | ]
182 | });
183 | })
184 |
185 | .add('GeoJSON Feature to ArcGIS Feature', function(){
186 | ArcGIS.convert({
187 | "type":"Feature",
188 | "geometry": {
189 | "type": "Polygon",
190 | "coordinates": [
191 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
192 | ]
193 | },
194 | "properties": {
195 | "foo":"bar"
196 | }
197 | });
198 | })
199 |
200 | .add('GeoJSON Feature Collection to Array of ArcGIS Features', function(){
201 | ArcGIS.convert({
202 | "type": "FeatureCollection",
203 | "features": [{
204 | "type": "Feature",
205 | "geometry": {
206 | "type": "Point",
207 | "coordinates": [102.0, 0.5]
208 | },
209 | "properties": {
210 | "prop0": "value0"
211 | }
212 | }, {
213 | "type": "Feature",
214 | "geometry": {
215 | "type": "LineString",
216 | "coordinates": [
217 | [102.0, 0.0],[103.0, 1.0],[104.0, 0.0],[105.0, 1.0]
218 | ]
219 | },
220 | "properties": {
221 | "prop0": "value0"
222 | }
223 | }, {
224 | "type": "Feature",
225 | "geometry": {
226 | "type": "Polygon",
227 | "coordinates": [
228 | [ [100.0, 0.0],[101.0, 0.0],[101.0, 1.0],[100.0, 1.0],[100.0, 0.0] ]
229 | ]
230 | },
231 | "properties": {
232 | "prop0": "value0"
233 | }
234 | }]
235 | });
236 | })
237 |
238 | .add('GeoJSON Geometry Collection to Array of ArcGIS Geometries', function(){
239 | ArcGIS.convert({
240 | "type" : "GeometryCollection",
241 | "geometries" : [{
242 | "type" : "Polygon",
243 | "coordinates" : [[[-95, 43], [-95, 50], [-90, 50], [-91, 42], [-95, 43]]]
244 | }, {
245 | "type" : "LineString",
246 | "coordinates" : [[-89, 42], [-89, 50], [-80, 50], [-80, 42]]
247 | }, {
248 | "type" : "Point",
249 | "coordinates" : [-94, 46]
250 | }]
251 | });
252 | })
253 |
254 | .on('cycle', function(event) {
255 | console.log(String(event.target));
256 | })
257 |
258 | // .on('complete', function() {
259 | // console.log('Fastest is ' + this.filter('fastest').pluck('name'));
260 | // })
261 |
262 | .run({ 'async': false });
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "terraformer-arcgis-parser",
3 | "main": "terraformer-arcgis-parser.min.js",
4 | "ignore" : ["versions", "benchmarks"],
5 | "dependencies": {
6 | "terraformer": "~1.0.4"
7 | }
8 | }
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | *.geojson
2 | *.json
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Terraformer ArcGIS File Parser
2 |
3 | > Convert .geojson and Esri .json back and forth.
4 |
5 | ## Install
6 |
7 | ```bash
8 | $ git clone https://github.com/Esri/terraformer-arcgis-parser
9 | $ cd ./terraformer-arcgis-parser/examples
10 | $ npm install
11 | ```
12 |
13 | ## Use
14 |
15 | ```
16 | $ npm run convert bars.geojson
17 | > bars.json
18 |
19 | $ npm run parse colorado.json
20 | > colorado.geojson
21 | ```
22 |
23 | sample [`.geojson`](https://raw.githubusercontent.com/benbalter/dc-wifi-social/master/bars.geojson) / sample geoservices[ `.json`](http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/3/query?where=STATE_NAME+%3D+%27Colorado%27&maxAllowableOffset=0.01&outSR=4326&f=pjson)
24 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "terraformer-arcgis-fileparser",
3 | "version": "1.0.0",
4 | "description": "A utility that uses terraformer-arcgis-parser to convert .geojson files to ArcGIS formatted .json files & vice-versa.",
5 | "main": "terraformer-arcgis-fileparser.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "convert": "node ./terraformer-arcgis-fileparser.js convert",
11 | "parse": "node ./terraformer-arcgis-fileparser.js parse"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git@github.com:Esri/terraformer-arcgis-parser.git"
16 | },
17 | "keywords": [
18 | "ArcGIS",
19 | "Esri",
20 | "GIS",
21 | "Geography"
22 | ],
23 | "author": "Graham Seyffert ",
24 | "license": "Apache-2.0",
25 | "dependencies": {
26 | "terraformer-arcgis-parser": "~1.0.5",
27 | "terraformer": "~1.0.7"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/terraformer-arcgis-fileparser.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /**
3 | * Copyright 2017 by Graham Freeman Seyffert
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | const fs = require('fs');
18 | let ArcGIS;
19 | try { ArcGIS = require('terraformer-arcgis-parser'); }
20 | catch (err) {
21 | const message = 'Could not load Terraformer - please make sure '
22 | + 'to run \'npm install\' before running.'
23 | return console.log(message, err);
24 | }
25 |
26 | function openFile(fileName) {
27 | return new Promise((resolve, reject) => {
28 | fs.open(fileName, 'r+', (err, fd) => {
29 | if (err) return reject(err);
30 | fs.fstat(fd, (err, stats) => {
31 | if (err) return reject(err);
32 | const fileData = {
33 | fd: fd,
34 | fileName: fileName,
35 | size: stats.size
36 | };
37 | resolve(fileData);
38 | });
39 | });
40 | });
41 | }
42 |
43 | function readFile(file) {
44 | const buffLen = file.size;
45 | const chunkSize = buffLen < 1024 ? buffLen
46 | : 1024;
47 | const buff = Buffer.alloc(buffLen);
48 | return tryRead(file.fd, buff, 0, chunkSize)
49 | .then((fileData) => {
50 | fileData.fileName = file.fileName;
51 | return fileData;
52 | });
53 | }
54 |
55 | // Reads file by chunkSize at a time.
56 | // Will resolve with the buffer if it's full, otherwise will
57 | // resolve with nothing, indicating another read should happen.
58 | function tryRead(fd, buff, offset, length) {
59 | return new Promise((resolve, reject) => {
60 | fs.read(fd, buff, offset, length, null, (err, bytesRead, buffer) => {
61 | if (err) return reject(err);
62 | const buffLen = buffer.length;
63 | offset += bytesRead;
64 | if (offset >= buffLen) return resolve(buff);
65 | if ((offset + length) >= buffLen) length = buffLen - offset;
66 | return resolve();
67 | });
68 | })
69 | .then(
70 | (fullBuffer) => {
71 | return fullBuffer ? { data: fullBuffer, fd: fd }
72 | : tryRead(fd, buff, offset, length);
73 | },
74 | (err) => { throw err; }
75 | );
76 | }
77 |
78 | function convertFile(fileData, operation) {
79 | const toConvert = JSON.parse(fileData.data.toString());
80 | const newFileData = {};
81 | let outputFileExtension;
82 | if (operation === 'convert') {
83 | outputFileExtension = '.json';
84 | newFileData.features = ArcGIS.convert(toConvert);
85 | } else if (operation === 'parse') {
86 | outputFileExtension = '.geojson';
87 | const firstFeature = toConvert.features[0];
88 | const isGeoCollection = firstFeature.rings
89 | || firstFeature.paths
90 | || firstFeature.points
91 | || (firstFeature.x && firstFeature.y);
92 | const converted = toConvert.features.map(feature => ArcGIS.parse(feature));
93 | if (isGeoCollection) {
94 | newFileData.type = 'GeometryCollection';
95 | newFileData.geometries = converted;
96 | } else {
97 | newFileData.type = 'FeatureCollection';
98 | newFileData.features = converted;
99 | }
100 | }
101 | // Considers file paths may be relative or not in CWD
102 | let fileNameSplit = fileData.fileName.split('.');
103 | let fileNameWithoutExtension = fileNameSplit[fileNameSplit.length - 2];
104 | fileNameSplit = fileNameWithoutExtension.split('/');
105 | fileNameWithoutExtension = fileNameSplit[fileNameSplit.length - 1];
106 | const outputFileName = fileNameWithoutExtension + outputFileExtension;
107 | const output = {
108 | data: JSON.stringify(newFileData, null, 2),
109 | fd: fileData.fd,
110 | fileName: outputFileName
111 | };
112 | return output;
113 | }
114 |
115 | let operation = process.argv[2];
116 | if (!operation) operation = 'convert';
117 |
118 | const fileName = process.argv[3];
119 | if (!fileName) {
120 | return console.log('Please specify a file to ' + operation + '.');
121 | }
122 |
123 | // Kickoff
124 | openFile(fileName)
125 | .then(file => { return readFile(file); })
126 | .then(fileData => { return convertFile(fileData, operation); })
127 | .then(output => {
128 | fs.writeFile(output.fileName, output.data, (err) => {
129 | if (err) return console.log(err);
130 | fs.close(output.fd, (err) => {
131 | if (err) console.log(err);
132 | else console.log('Result written to ' + output.fileName);
133 | });
134 | });
135 | })
136 | .catch(err => { return console.log(err); });
137 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | export interface SpatialReference {
4 | }
5 |
6 | export interface SpatialReferenceWkid extends SpatialReference {
7 | wkid?: number;
8 | latestWkid?: number;
9 | vcsWkid?: number;
10 | latestVcsWkid?: number;
11 | }
12 |
13 | export interface SpatialReferenceWkt extends SpatialReference {
14 | wkt?: string;
15 | latestWkt?: string;
16 | }
17 |
18 | export interface Geometry {
19 | spatialReference?: SpatialReference;
20 | }
21 |
22 | export interface HasZM {
23 | hasZ?: boolean;
24 | hasM?: boolean;
25 | }
26 |
27 | export interface Point extends Geometry {
28 | x: number;
29 | y: number;
30 | z?: number;
31 | m?: number;
32 | }
33 |
34 | export interface Polyline extends HasZM, Geometry {
35 | paths: number[][][];
36 | }
37 |
38 | export interface Polygon extends HasZM, Geometry {
39 | rings: number[][][];
40 | }
41 |
42 | export interface Multipoint extends HasZM, Geometry {
43 | points: number[][];
44 | }
45 |
46 | export interface Envelope extends Geometry {
47 | xmin: number;
48 | xmax: number;
49 | ymin: number;
50 | ymax: number;
51 |
52 | zmin?: number;
53 | zmax?: number;
54 |
55 | mmin?: number;
56 | mmax?: number;
57 | }
58 |
59 | export interface Feature {
60 | geometry: Geometry;
61 | attributes: any;
62 | }
63 |
64 | export interface Field {
65 | name: string;
66 | type: string;
67 | alias?: string;
68 | length?: number;
69 | }
70 |
71 | export type esriGeometryType = "esriGeometryPoint" | "esriGeometryMultipoint" | "esriGeometryPolyline" |
72 | "esriGeometryPolygon" | "esriGeometryEnvelope";
73 |
74 | export interface FeatureSet extends HasZM {
75 | objectIdFieldName?: string; // optional
76 | globalIdFieldName?: string; // optional
77 | displayFieldName?: string; // optional
78 | geometryType?: esriGeometryType; // for feature layers only
79 | spatialReference?: SpatialReference; // for feature layers only.
80 | fields?: Field[];
81 | features: Feature[];
82 | }
83 |
84 | export interface ParseOptions {
85 | sr?: number;
86 | idAttribute?: string;
87 | }
88 | export interface ConvertOptions {
89 | idAttribute?: string;
90 | }
91 |
92 | export function parse(json: T, options?: ParseOptions): GeoJSON.GeometryObject;
93 | export function parse(json: Feature, options?: ParseOptions): GeoJSON.Feature;
94 |
95 | export function convert(geoJSON: GeoJSON.FeatureCollection, options?: ConvertOptions): FeatureSet;
96 | export function convert(geoJSON: GeoJSON.Feature, options?: ConvertOptions): Feature;
97 | export function convert(geoJSON: T, options?: ConvertOptions): Geometry;
98 |
--------------------------------------------------------------------------------
/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": ["arcgisSpec.js"],
4 | "helpers": [],
5 | "stopSpecOnExpectationFailure": false,
6 | "random": false
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "terraformer-arcgis-parser",
3 | "version": "1.1.0",
4 | "description": "ArcGIS JSON format parser and converter",
5 | "main": "terraformer-arcgis-parser.js",
6 | "files": [
7 | "index.d.ts",
8 | "terraformer-arcgis-parser.min.js",
9 | "terraformer-arcgis-parser.d.ts"
10 | ],
11 | "directories": {
12 | "test": "test"
13 | },
14 | "scripts": {
15 | "test": "npm run test:node && grunt test",
16 | "test:node": "jasmine --config=jasmine.json",
17 | "test:ts": "tsc"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git@github.com:Esri/terraformer-arcgis-parser.git"
22 | },
23 | "keywords": [
24 | "ArcGIS",
25 | "Esri",
26 | "GIS",
27 | "Geography"
28 | ],
29 | "author": "Patrick Arlt (http://patrickarlt.com)",
30 | "license": "MIT",
31 | "dependencies": {
32 | "@types/geojson": "^1.0.0",
33 | "terraformer": "~1.0.4"
34 | },
35 | "devDependencies": {
36 | "benchmark": "~1.0.0",
37 | "gh-release": "^3.2.1",
38 | "grunt": "^1.0.3",
39 | "grunt-complexity": "~0.1.3",
40 | "grunt-contrib-jasmine": "^2.0.1",
41 | "grunt-contrib-uglify": "~2.0.0",
42 | "grunt-template-jasmine-istanbul": "~0.2.4",
43 | "jasmine": "^3.1.0",
44 | "tslint": "^4.5.1",
45 | "typescript": "^2.2.1"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/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 | # checkout temp branch for release
11 | git checkout -b gh-release
12 |
13 | # create built library (and versioned copy)
14 | grunt uglify
15 |
16 | # force add files
17 | git add terraformer-arcgis-parser.min.js -f
18 |
19 | # commit changes with a versioned commit message
20 | git commit -m "build $VERSION"
21 |
22 | # push commit so it exists on GitHub when we run gh-release
23 | git push https://github.com/Esri/terraformer-arcgis-parser gh-release
24 |
25 | # create copy of minified file with version number appended
26 | cp terraformer-arcgis-parser.min.js $NAME-$VERSION.min.js
27 |
28 | # run gh-release to create the tag and push release to github
29 | gh-release --assets $NAME-$VERSION.min.js
30 |
31 | # remove copy after the asset is attached to the github release
32 | rm $NAME-$VERSION.min.js
33 |
34 | # checkout master and delete release branch locally and on GitHub
35 | git checkout master
36 | git branch -D gh-release
37 | git push https://github.com/Esri/terraformer-arcgis-parser :gh-release
38 |
39 | # publish release on NPM
40 | npm publish
--------------------------------------------------------------------------------
/spec/arcgisSpec.js:
--------------------------------------------------------------------------------
1 | if(typeof module === "object"){
2 | var Terraformer = require("terraformer");
3 | Terraformer.ArcGIS = require("../terraformer-arcgis-parser.js");
4 | }
5 |
6 | describe("ArcGIS Tools", function(){
7 |
8 | it("should convert a GeoJSON Point to an ArcGIS Point", function() {
9 | var input = {
10 | "type": "Point",
11 | "coordinates": [-58.7109375,47.4609375]
12 | };
13 |
14 | var output = Terraformer.ArcGIS.convert(input);
15 |
16 | expect(output).toEqual({
17 | "x":-58.7109375,
18 | "y":47.4609375,
19 | "spatialReference":{
20 | "wkid":4326
21 | }
22 | });
23 | });
24 |
25 | it("should convert a GeoJSON Point with Z to an ArcGIS Point with Z", function() {
26 | var input = {
27 | "type": "Point",
28 | "coordinates": [-58.7109375,47.4609375, 100]
29 | };
30 |
31 | var output = Terraformer.ArcGIS.convert(input);
32 |
33 | expect(output).toEqual({
34 | "x":-58.7109375,
35 | "y":47.4609375,
36 | "z": 100,
37 | "spatialReference":{
38 | "wkid":4326
39 | }
40 | });
41 | });
42 |
43 | it("should convert a GeoJSON Point with Z and M to an ArcGIS Point with Z and M", function() {
44 | var input = {
45 | "type": "Point",
46 | "coordinates": [-58.7109375,47.4609375, 100, 50]
47 | };
48 |
49 | var output = Terraformer.ArcGIS.convert(input);
50 |
51 | expect(output).toEqual({
52 | "x":-58.7109375,
53 | "y":47.4609375,
54 | "z": 100,
55 | "m": 50,
56 | "spatialReference":{
57 | "wkid":4326
58 | }
59 | });
60 | });
61 |
62 | it("should convert a GeoJSON Null Island to an ArcGIS Point", function() {
63 | var input = {
64 | "type": "Point",
65 | "coordinates": [0,0]
66 | };
67 |
68 | var output = Terraformer.ArcGIS.convert(input);
69 |
70 | expect(output).toEqual({
71 | "x":0,
72 | "y":0,
73 | "spatialReference":{
74 | "wkid":4326
75 | }
76 | });
77 | });
78 |
79 | it("should convert a GeoJSON LineString to an ArcGIS Polyline", function() {
80 | var input = {
81 | "type": "LineString",
82 | "coordinates": [ [21.4453125,-14.0625],[33.3984375,-20.7421875],[38.3203125,-24.609375] ]
83 | };
84 |
85 | var output = Terraformer.ArcGIS.convert(input);
86 |
87 | expect(output).toEqual({
88 | "paths":[
89 | [ [21.4453125,-14.0625],[33.3984375,-20.7421875],[38.3203125,-24.609375] ]
90 | ],
91 | "spatialReference":{
92 | "wkid":4326
93 | }
94 | });
95 | });
96 |
97 | it("should convert a GeoJSON Polygon to an ArcGIS Polygon", function() {
98 | var input = {
99 | "type": "Polygon",
100 | "coordinates": [
101 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
102 | ]
103 | };
104 |
105 | var output = Terraformer.ArcGIS.convert(input);
106 |
107 | expect(output).toEqual({
108 | "rings":[
109 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
110 | ],
111 | "spatialReference":{
112 | "wkid":4326
113 | }
114 | });
115 | });
116 |
117 | it("should convert a GeoJSON Polygon w/ a hole to an ArcGIS Polygon w/ 2 rings", function() {
118 | var input = {
119 | "type": "Polygon",
120 | "coordinates": [
121 | [ [100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0] ],
122 | [ [100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2] ]
123 | ]
124 | };
125 |
126 | var output = Terraformer.ArcGIS.convert(input);
127 |
128 | expect(output).toEqual({
129 | "rings": [
130 | [ [100, 0], [100, 1], [101, 1], [101, 0], [100, 0] ],
131 | [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]
132 | ],
133 | "spatialReference":{
134 | "wkid":4326
135 | }
136 | });
137 | });
138 |
139 | it("should strip invalid rings when converting a GeoJSON Polygon to and ArcGIS Polygon", function() {
140 | var input = {
141 | "type": "Polygon",
142 | "coordinates": [
143 | [ [100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0] ],
144 | [ [100.2,0.2],[100.8,0.2],[100.2,0.2] ]
145 | ]
146 | };
147 |
148 | var output = Terraformer.ArcGIS.convert(input);
149 |
150 | expect(output).toEqual({
151 | "rings": [
152 | [ [100, 0], [100, 1], [101, 1], [101, 0], [100, 0] ]
153 | ],
154 | "spatialReference":{
155 | "wkid":4326
156 | }
157 | });
158 | });
159 |
160 | it("should close ring when converting a GeoJSON Polygon w/ a hole to an ArcGIS Polygon", function() {
161 | var input = {
162 | "type": "Polygon",
163 | "coordinates": [
164 | [ [100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0] ],
165 | [ [100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8] ]
166 | ]
167 | };
168 |
169 | var output = Terraformer.ArcGIS.convert(input);
170 |
171 | expect(output).toEqual({
172 | "rings": [
173 | [ [100, 0], [100, 1], [101, 1], [101, 0], [100, 0] ],
174 | [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]
175 | ],
176 | "spatialReference":{
177 | "wkid":4326
178 | }
179 | });
180 | });
181 |
182 | it("should convert a GeoJSON MultiPoint to an ArcGIS Multipoint", function() {
183 | var input = {
184 | "type": "MultiPoint",
185 | "coordinates": [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625] ]
186 | };
187 |
188 | var output = Terraformer.ArcGIS.convert(input);
189 |
190 | expect(output).toEqual({
191 | "points":[ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625] ],
192 | "spatialReference":{
193 | "wkid":4326
194 | }
195 | });
196 | });
197 |
198 | it("should convert a GeoJSON MultiLineString to an ArcGIS Polyline", function() {
199 | var input = {
200 | "type": "MultiLineString",
201 | "coordinates": [
202 | [ [41.8359375,71.015625],[56.953125,33.75] ],
203 | [ [21.796875,36.5625],[47.8359375,71.015625] ]
204 | ]
205 | };
206 |
207 | var output = Terraformer.ArcGIS.convert(input);
208 |
209 | expect(output).toEqual({
210 | "paths":[
211 | [ [41.8359375,71.015625],[56.953125,33.75] ],
212 | [ [21.796875,36.5625],[47.8359375,71.015625] ]
213 | ],
214 | "spatialReference":{
215 | "wkid":4326
216 | }
217 | });
218 | });
219 |
220 | it("should convert a GeoJSON MultiPolygon to an ArcGIS Polygon", function() {
221 | var input = {
222 | "type": "MultiPolygon",
223 | "coordinates": [
224 | [
225 | [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ]
226 | ],
227 | [
228 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
229 | ]
230 | ]
231 | };
232 |
233 | var output = Terraformer.ArcGIS.convert(input);
234 | expect(output).toEqual({
235 | "rings":[
236 | [ [102, 2], [102, 3], [103, 3], [103, 2], [102, 2] ],
237 | [ [100, 0], [100, 1], [101, 1], [101, 0], [100, 0] ]
238 | ],
239 | "spatialReference": {
240 | "wkid":4326
241 | }
242 | });
243 | });
244 |
245 | it("should convert a GeoJSON MultiPolygon w/ holes to an ArcGIS Polygon", function() {
246 | var input = {
247 | "type": "MultiPolygon",
248 | "coordinates": [
249 | [
250 | [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ]
251 | ],
252 | [
253 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ],
254 | [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2] ]
255 | ]
256 | ]
257 | };
258 |
259 | var output = Terraformer.ArcGIS.convert(input);
260 | expect(output).toEqual({
261 | "spatialReference": {
262 | "wkid": 4326
263 | },
264 | "rings": [
265 | [ [102,2],[102,3],[103,3],[103,2],[102,2] ],
266 | [ [100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2] ],
267 | [ [100,0],[100,1],[101,1],[101,0],[100,0] ]
268 | ]
269 | });
270 | });
271 |
272 | it("should close rings when converting a GeoJSON MultiPolygon w/ holes to an ArcGIS Polygon", function() {
273 | var input = {
274 | "type": "MultiPolygon",
275 | "coordinates": [
276 | [
277 | [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0] ]
278 | ],
279 | [
280 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0] ],
281 | [ [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8] ]
282 | ]
283 | ]
284 | };
285 |
286 | var output = Terraformer.ArcGIS.convert(input);
287 | expect(output).toEqual({
288 | "spatialReference": {
289 | "wkid": 4326
290 | },
291 | "rings": [
292 | [ [102,2],[102,3],[103,3],[103,2],[102,2] ],
293 | [ [100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2] ],
294 | [ [100,0],[100,1],[101,1],[101,0],[100,0] ]
295 | ]
296 | });
297 | });
298 |
299 | it('should still parse holes outside the outer rings', function(){
300 | var input = {
301 | "rings": [
302 | [ [-122.45,45.63], [-122.45,45.68], [-122.39,45.68], [-122.39,45.63], [-122.45,45.63] ],
303 | [ [-122.46,45.64], [-122.4,45.64], [-122.4,45.66], [-122.46,45.66], [-122.46,45.64] ]
304 | ]
305 | }
306 |
307 | var output = Terraformer.ArcGIS.parse(input);
308 |
309 | var expected = [
310 | [ [-122.45,45.63], [-122.39,45.63], [-122.39,45.68], [-122.45,45.68], [-122.45,45.63] ],
311 | [ [-122.46,45.64], [-122.46,45.66], [-122.4,45.66], [-122.4,45.64], [-122.46,45.64] ]
312 | ];
313 |
314 | expect(output.coordinates).toEqual(expected);
315 | });
316 |
317 | it("should convert a GeoJSON Feature into an ArcGIS Feature", function(){
318 | var input = {
319 | "type":"Feature",
320 | "id": "foo",
321 | "geometry": {
322 | "type": "Polygon",
323 | "coordinates": [
324 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
325 | ]
326 | },
327 | "properties": {
328 | "foo":"bar"
329 | }
330 | };
331 |
332 | var output = Terraformer.ArcGIS.convert(input);
333 |
334 | expect(output).toEqual({
335 | "geometry":{
336 | "rings":[
337 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
338 | ],
339 | "spatialReference":{
340 | "wkid":4326
341 | }
342 | },
343 | "attributes": {
344 | "foo":"bar",
345 | "OBJECTID": "foo"
346 | }
347 | });
348 | });
349 |
350 | it("should convert a GeoJSON Feature into an ArcGIS Feature w/ a custom id", function(){
351 | var input = {
352 | "type":"Feature",
353 | "id": "foo",
354 | "geometry": {
355 | "type": "Polygon",
356 | "coordinates": [
357 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
358 | ]
359 | },
360 | "properties": {
361 | "foo":"bar"
362 | }
363 | };
364 |
365 | var output = Terraformer.ArcGIS.convert(input, {
366 | idAttribute: "myId"
367 | });
368 |
369 | expect(output).toEqual({
370 | "geometry":{
371 | "rings":[
372 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
373 | ],
374 | "spatialReference":{
375 | "wkid":4326
376 | }
377 | },
378 | "attributes": {
379 | "foo":"bar",
380 | "myId": "foo"
381 | }
382 | });
383 | });
384 |
385 | it("should allow converting a GeoJSON Feature to an ArcGIS Feature with no properties or geometry", function(){
386 | var input = {
387 | "type":"Feature",
388 | "id": "foo",
389 | "geometry": null,
390 | "properties": null
391 | };
392 |
393 | var output = Terraformer.ArcGIS.convert(input);
394 |
395 | expect(output).toEqual({
396 | "attributes": {
397 | "OBJECTID": "foo"
398 | }
399 | });
400 | });
401 |
402 | it("should convert a GeoJSON FeatureCollection into an array of ArcGIS Feature JSON", function(){
403 | var input = {
404 | "type": "FeatureCollection",
405 | "features": [{
406 | "type": "Feature",
407 | "geometry": {
408 | "type": "Point",
409 | "coordinates": [102.0, 0.5]
410 | },
411 | "properties": {
412 | "prop0": "value0"
413 | }
414 | }, {
415 | "type": "Feature",
416 | "geometry": {
417 | "type": "LineString",
418 | "coordinates": [
419 | [102.0, 0.0],[103.0, 1.0],[104.0, 0.0],[105.0, 1.0]
420 | ]
421 | },
422 | "properties": {
423 | "prop0": "value0"
424 | }
425 | }, {
426 | "type": "Feature",
427 | "geometry": {
428 | "type": "Polygon",
429 | "coordinates": [
430 | [ [100.0, 0.0],[101.0, 0.0],[101.0, 1.0],[100.0, 1.0],[100.0, 0.0] ]
431 | ]
432 | },
433 | "properties": {
434 | "prop0": "value0"
435 | }
436 | }]
437 | };
438 |
439 | var output = Terraformer.ArcGIS.convert(input);
440 |
441 | expect(output).toEqual([{
442 | "geometry": {
443 | "x": 102,
444 | "y": 0.5,
445 | "spatialReference": {
446 | "wkid": 4326
447 | }
448 | },
449 | "attributes": {
450 | "prop0": "value0"
451 | }
452 | }, {
453 | "geometry": {
454 | "paths": [
455 | [[102, 0],[103, 1],[104, 0],[105, 1]]
456 | ],
457 | "spatialReference": {
458 | "wkid": 4326
459 | }
460 | },
461 | "attributes": {
462 | "prop0": "value0"
463 | }
464 | }, {
465 | "geometry": {
466 | "rings": [
467 | [ [100,0],[100,1],[101,1],[101,0],[100,0] ]
468 | ],
469 | "spatialReference": {
470 | "wkid": 4326
471 | }
472 | },
473 | "attributes": {
474 | "prop0": "value0"
475 | }
476 | }]);
477 | });
478 |
479 | it("should convert a GeoJSON GeometryCollection into an array of ArcGIS Geometries", function(){
480 | var input = {
481 | "type" : "GeometryCollection",
482 | "geometries" : [{
483 | "type" : "Polygon",
484 | "coordinates" : [[[-95, 43], [-95, 50], [-90, 50], [-91, 42], [-95, 43]]]
485 | }, {
486 | "type" : "LineString",
487 | "coordinates" : [[-89, 42], [-89, 50], [-80, 50], [-80, 42]]
488 | }, {
489 | "type" : "Point",
490 | "coordinates" : [-94, 46]
491 | }]
492 | };
493 |
494 | var output = Terraformer.ArcGIS.convert(input);
495 |
496 | expect(output).toEqual([{
497 | "rings": [
498 | [[-95, 43],[-95, 50],[-90, 50],[-91, 42],[-95, 43]]
499 | ],
500 | "spatialReference": {
501 | "wkid": 4326
502 | }
503 | }, {
504 | "paths": [
505 | [[-89, 42],[-89, 50],[-80, 50],[-80, 42]]
506 | ],
507 | "spatialReference": {
508 | "wkid": 4326
509 | }
510 | }, {
511 | "x": -94,
512 | "y": 46,
513 | "spatialReference": {
514 | "wkid": 4326
515 | }
516 | }]);
517 | });
518 |
519 | it("should not modify the original GeoJSON object", function(){
520 | var primitive = new Terraformer.FeatureCollection({
521 | "type": "FeatureCollection",
522 | "features": [{
523 | "type": "Feature",
524 | "geometry": {
525 | "type": "Point",
526 | "coordinates": [102.0, 0.5]
527 | },
528 | "properties": {
529 | "prop0": "value0"
530 | }
531 | }, {
532 | "type": "Feature",
533 | "geometry": {
534 | "type": "LineString",
535 | "coordinates": [
536 | [102.0, 0.0],[103.0, 1.0],[104.0, 0.0],[105.0, 1.0]
537 | ]
538 | },
539 | "properties": {
540 | "prop0": "value0"
541 | }
542 | }, {
543 | "type": "Feature",
544 | "geometry": {
545 | "type": "Polygon",
546 | "coordinates": [
547 | [ [100.0, 0.0],[101.0, 0.0],[101.0, 1.0],[100.0, 1.0],[100.0, 0.0] ]
548 | ]
549 | },
550 | "properties": {
551 | "prop0": "value0"
552 | }
553 | }]
554 | });
555 |
556 | var original = JSON.stringify(primitive);
557 |
558 | Terraformer.ArcGIS.convert(primitive);
559 |
560 | expect(original).toEqual(JSON.stringify(primitive));
561 | });
562 |
563 | it("if the GeoJSON includes a custom crs, output spatial reference should not be set", function() {
564 | var input = {
565 | "type": "Point",
566 | "coordinates": [123,456],
567 | "crs": {
568 | "type": "name",
569 | "properties": {
570 | "name": "urn:ogc:def:crs:EPSG::2913"
571 | }
572 | }
573 | };
574 |
575 | var output = Terraformer.ArcGIS.convert(input);
576 |
577 | expect(output).toEqual({
578 | "x": 123,
579 | "y": 456,
580 | "spatialReference": null
581 | });
582 | });
583 |
584 | it("if the GeoJSON includes a custom linked crs, output spatial reference should not be set", function() {
585 | var input = {
586 | "type": "Point",
587 | "coordinates": [123,456],
588 | "crs": {
589 | "type": "link",
590 | "properties": {
591 | "href": "http://spatialreference.org/ref/sr-org/7/ogcwkt/",
592 | "type": "ogcwkt"
593 | }
594 | },
595 | };
596 |
597 | var output = Terraformer.ArcGIS.convert(input);
598 | expect(output).toEqual({
599 | "x": 123,
600 | "y": 456,
601 | "spatialReference": null
602 | });
603 | });
604 |
605 | it("should parse an ArcGIS Point in a Terraformer GeoJSON Point", function() {
606 | var input = {
607 | "x": -66.796875,
608 | "y": 20.0390625,
609 | "spatialReference": {
610 | "wkid": 4326
611 | }
612 | };
613 |
614 | var output = Terraformer.ArcGIS.parse(input);
615 |
616 | expect(output.coordinates).toEqual([-66.796875,20.0390625]);
617 | expect(output instanceof Terraformer.Point).toBeTruthy();
618 | });
619 |
620 | it("should parse an ArcGIS Point with Z in a Terraformer GeoJSON Point with Z", function() {
621 | var input = {
622 | "x": -66.796875,
623 | "y": 20.0390625,
624 | "z": 100,
625 | "spatialReference": {
626 | "wkid": 4326
627 | }
628 | };
629 |
630 | var output = Terraformer.ArcGIS.parse(input);
631 |
632 | expect(output.coordinates).toEqual([-66.796875,20.0390625, 100]);
633 | expect(output instanceof Terraformer.Point).toBeTruthy();
634 | });
635 |
636 | it("should parse an ArcGIS Point with Z and M in a Terraformer GeoJSON Point with Z and M", function() {
637 | var input = {
638 | "x": -66.796875,
639 | "y": 20.0390625,
640 | "z": 100,
641 | "m": 50,
642 | "spatialReference": {
643 | "wkid": 4326
644 | }
645 | };
646 |
647 | var output = Terraformer.ArcGIS.parse(input);
648 |
649 | expect(output.coordinates).toEqual([-66.796875,20.0390625, 100, 50]);
650 | expect(output instanceof Terraformer.Point).toBeTruthy();
651 | });
652 |
653 | it("should parse an ArcGIS Point with M in a Terraformer GeoJSON Point with M", function() {
654 | var input = {
655 | "x": -66.796875,
656 | "y": 20.0390625,
657 | "m": 50,
658 | "spatialReference": {
659 | "wkid": 4326
660 | }
661 | };
662 |
663 | var output = Terraformer.ArcGIS.parse(input);
664 |
665 | expect(output.coordinates).toEqual([-66.796875,20.0390625, undefined, 50]);
666 | expect(output instanceof Terraformer.Point).toBeTruthy();
667 | });
668 |
669 | it("should parse an ArcGIS Null Island in a Terraformer GeoJSON Point", function() {
670 | var input = {
671 | "x": 0,
672 | "y": 0,
673 | "spatialReference": {
674 | "wkid": 4326
675 | }
676 | };
677 |
678 | var output = Terraformer.ArcGIS.parse(input);
679 |
680 | expect(output.coordinates).toEqual([0,0]);
681 | expect(output instanceof Terraformer.Point).toBeTruthy();
682 | });
683 |
684 | it("should parse an ArcGIS Polyline in a Terraformer GeoJSON LineString", function() {
685 | var input = {
686 | "paths": [
687 | [ [6.6796875,47.8125],[-65.390625,52.3828125],[-52.3828125,42.5390625] ]
688 | ],
689 | "spatialReference": {
690 | "wkid": 4326
691 | }
692 | };
693 |
694 | var output = Terraformer.ArcGIS.parse(input);
695 |
696 | expect(output.coordinates).toEqual([ [6.6796875,47.8125],[-65.390625,52.3828125],[-52.3828125,42.5390625] ]);
697 | expect(output instanceof Terraformer.LineString).toBeTruthy();
698 | });
699 |
700 | it("should parse an ArcGIS Polygon in a Terraformer GeoJSON Polygon", function() {
701 | var input = {
702 | "rings": [
703 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
704 | ],
705 | "spatialReference": {
706 | "wkid": 4326
707 | }
708 | };
709 |
710 | var output = Terraformer.ArcGIS.parse(input);
711 |
712 | expect(output.coordinates).toEqual([ [ [41.8359375,71.015625],[21.796875,36.5625],[56.953125,33.75],[41.8359375,71.015625] ] ]);
713 | expect(output.type).toEqual("Polygon");
714 | });
715 |
716 | it("should close rings when parsing an ArcGIS Polygon in a Terraformer GeoJSON Polygon", function() {
717 | var input = {
718 | "rings": [
719 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625]]
720 | ],
721 | "spatialReference": {
722 | "wkid": 4326
723 | }
724 | };
725 |
726 | var output = Terraformer.ArcGIS.parse(input);
727 |
728 | expect(output.coordinates).toEqual([ [ [41.8359375,71.015625],[21.796875,36.5625],[56.953125,33.75],[41.8359375,71.015625] ] ]);
729 | expect(output.type).toEqual("Polygon");
730 | });
731 |
732 | it("should parse an ArcGIS Multipoint in a Terraformer GeoJSON MultiPoint", function() {
733 | var input = {
734 | "points":[ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625] ],
735 | "spatialReference":{
736 | "wkid":4326
737 | }
738 | };
739 |
740 | var output = Terraformer.ArcGIS.parse(input);
741 |
742 | expect(output.coordinates).toEqual([ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625] ]);
743 | expect(output instanceof Terraformer.MultiPoint).toBeTruthy();
744 | });
745 |
746 | it("should parse an ArcGIS Polyline in a Terraformer GeoJSON MultiLineString", function() {
747 | var input = {
748 | "paths":[
749 | [ [41.8359375,71.015625],[56.953125,33.75] ],
750 | [ [21.796875,36.5625],[41.8359375,71.015625] ]
751 | ],
752 | "spatialReference":{
753 | "wkid":4326
754 | }
755 | };
756 |
757 | var output = Terraformer.ArcGIS.parse(input);
758 |
759 | expect(output.coordinates).toEqual([[ [41.8359375,71.015625],[56.953125,33.75] ], [ [21.796875,36.5625],[41.8359375,71.015625] ]]);
760 | expect(output instanceof Terraformer.MultiLineString).toBeTruthy();
761 | });
762 |
763 | it("should parse an ArcGIS Polygon in a Terraformer GeoJSON MultiPolygon", function() {
764 | var input = {
765 | "rings":[
766 | [[-122.63,45.52],[-122.57,45.53],[-122.52,45.50],[-122.49,45.48],[-122.64,45.49],[-122.63,45.52],[-122.63,45.52]],
767 | [[-83,35],[-74,35],[-74,41],[-83,41],[-83,35]]
768 | ],
769 | "spatialReference": {
770 | "wkid":4326
771 | }
772 | };
773 |
774 | var output = Terraformer.ArcGIS.parse(input);
775 |
776 | expect(output.coordinates).toEqual([
777 | [
778 | [ [-122.63,45.52],[-122.63,45.52],[-122.64,45.49],[-122.49,45.48],[-122.52,45.5],[-122.57,45.53],[-122.63,45.52] ]
779 | ],
780 | [
781 | [ [-83,35],[-74,35],[-74,41],[-83,41],[-83,35] ]
782 | ]
783 | ]);
784 | expect(output.type).toEqual("MultiPolygon");
785 | });
786 |
787 | it("should strip invalid rings when converting ArcGIS Polygons to GeoJSON", function() {
788 | var input = {
789 | "rings":[
790 | [[-122.63,45.52],[-122.57,45.53],[-122.52,45.50],[-122.49,45.48],[-122.64,45.49],[-122.63,45.52],[-122.63,45.52]],
791 | [[-83,35],[-74,35],[-83,35]] // closed but too small
792 | ],
793 | "spatialReference": {
794 | "wkid":4326
795 | }
796 | };
797 |
798 | var output = Terraformer.ArcGIS.parse(input);
799 |
800 | expect(output.coordinates).toEqual([
801 | [ [-122.63, 45.52],[-122.63, 45.52],[-122.64, 45.49],[-122.49, 45.48],[-122.52, 45.5],[-122.57, 45.53],[-122.63, 45.52] ]
802 | ]);
803 | expect(output.type).toEqual("Polygon");
804 | });
805 |
806 | it("should properly close rings when converting an ArcGIS Polygon in a Terraformer GeoJSON MultiPolygon", function() {
807 | var input = {
808 | "rings":[
809 | [[-122.63,45.52],[-122.57,45.53],[-122.52,45.50],[-122.49,45.48],[-122.64,45.49]],
810 | [[-83,35],[-74,35],[-74,41],[-83,41]]
811 | ],
812 | "spatialReference": {
813 | "wkid":4326
814 | }
815 | };
816 |
817 | var output = Terraformer.ArcGIS.parse(input);
818 |
819 | expect(output.coordinates).toEqual([
820 | [
821 | [ [-122.63, 45.52],[-122.64, 45.49],[-122.49, 45.48],[-122.52, 45.5],[-122.57, 45.53],[-122.63, 45.52] ]
822 | ],
823 | [
824 | [ [-83,35],[-74,35],[-74,41],[-83,41],[-83,35] ]
825 | ]
826 | ]);
827 | expect(output.type).toEqual("MultiPolygon");
828 | });
829 |
830 | it("should parse an ArcGIS MultiPolygon with holes in web mercator to a GeoJSON MultiPolygon", function(){
831 | var input = {
832 | "type":"polygon",
833 | "rings":[
834 | [ [-100.74462180954974,39.95017165502381],[-94.50439384003792,39.91647453608879],[-94.41650267263967,34.89313438177965],[-100.78856739324887,34.85708140996771],[-100.74462180954974,39.95017165502381] ],
835 | [ [-99.68993678392353,39.341088433448896],[-99.68993678392353,38.24507658785885],[-98.67919734199646,37.86444431771113],[-98.06395917020868,38.210554846669694],[-98.06395917020868,39.341088433448896],[-99.68993678392353,39.341088433448896] ],
836 | [ [-96.83349180978595,37.23732027507514],[-97.31689323047635,35.967330282988534],[-96.5698183075912,35.57512048069255],[-95.42724211456674,36.357601429255965],[-96.83349180978595,37.23732027507514] ],
837 | [ [-101.4916967324349,38.24507658785885],[-101.44775114873578,36.073960493943744],[-103.95263145328033,36.03843312329154],[-103.68895795108557,38.03770050767439],[-101.4916967324349,38.24507658785885] ]
838 | ],
839 | "spatialReference":{
840 | "wkid":4326
841 | }
842 | };
843 | var output = Terraformer.ArcGIS.parse(input);
844 |
845 | expect(output.coordinates).toEqual([
846 | [
847 | [ [-100.74462180954974, 39.95017165502381],[-100.78856739324887, 34.85708140996771],[-94.41650267263967, 34.89313438177965],[-94.50439384003792, 39.91647453608879],[-100.74462180954974, 39.95017165502381] ],
848 | [ [-96.83349180978595, 37.23732027507514],[-95.42724211456674, 36.357601429255965],[-96.5698183075912, 35.57512048069255],[-97.31689323047635, 35.967330282988534],[-96.83349180978595, 37.23732027507514] ],
849 | [ [-99.68993678392353, 39.341088433448896],[-98.06395917020868, 39.341088433448896],[-98.06395917020868, 38.210554846669694],[-98.67919734199646, 37.86444431771113],[-99.68993678392353, 38.24507658785885],[-99.68993678392353, 39.341088433448896] ]
850 | ],
851 | [
852 | [ [-101.4916967324349, 38.24507658785885], [-103.68895795108557, 38.03770050767439], [-103.95263145328033, 36.03843312329154], [-101.44775114873578, 36.073960493943744], [-101.4916967324349, 38.24507658785885] ]
853 | ]
854 | ]);
855 | expect(output.type).toEqual("MultiPolygon");
856 | });
857 |
858 | it("should parse an ArcGIS Feature into a Terraformer Feature", function(){
859 | var input = {
860 | "geometry": {
861 | "rings": [
862 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
863 | ],
864 | "spatialReference": {
865 | "wkid": 4326
866 | }
867 | },
868 | "attributes": {
869 | "foo": "bar"
870 | }
871 | };
872 |
873 | var output = Terraformer.ArcGIS.parse(input);
874 |
875 | expect(output.geometry.coordinates).toEqual([
876 | [ [41.8359375,71.015625],[21.796875,36.5625],[56.953125, 33.75],[41.8359375,71.015625] ]
877 | ]);
878 | expect(output.geometry.type).toEqual("Polygon");
879 | expect(output instanceof Terraformer.Feature).toBeTruthy();
880 | });
881 |
882 | it("should parse an ArcGIS Feature w/ OBJECTID into a Terraformer Feature", function(){
883 | var input = {
884 | "geometry": {
885 | "rings": [
886 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
887 | ],
888 | "spatialReference": {
889 | "wkid": 4326
890 | }
891 | },
892 | "attributes": {
893 | "OBJECTID": 123
894 | }
895 | };
896 |
897 | var output = Terraformer.ArcGIS.parse(input);
898 |
899 | expect(output.id).toEqual(123);
900 | });
901 |
902 | it("should parse an ArcGIS Feature w/ FID into a Terraformer Feature", function(){
903 | var input = {
904 | "geometry": {
905 | "rings": [
906 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
907 | ],
908 | "spatialReference": {
909 | "wkid": 4326
910 | }
911 | },
912 | "attributes": {
913 | "FID": 123
914 | }
915 | };
916 |
917 | var output = Terraformer.ArcGIS.parse(input);
918 |
919 | expect(output.id).toEqual(123);
920 | });
921 |
922 | it("should parse an ArcGIS Feature w/ a custom id into a Terraformer Feature", function(){
923 | var input = {
924 | "geometry": {
925 | "rings": [
926 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
927 | ],
928 | "spatialReference": {
929 | "wkid": 4326
930 | }
931 | },
932 | "attributes": {
933 | "FooId": 123
934 | }
935 | };
936 |
937 | var output = Terraformer.ArcGIS.parse(input, {
938 | idAttribute: "FooId"
939 | });
940 |
941 | expect(output.id).toEqual(123);
942 | });
943 |
944 | it("should parse an ArcGIS Feature w/ empty attributes into a Terraformer Feature", function(){
945 | var input = {
946 | "geometry": {
947 | "rings": [
948 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
949 | ],
950 | "spatialReference": {
951 | "wkid": 4326
952 | }
953 | },
954 | "attributes": {}
955 | };
956 |
957 | var output = Terraformer.ArcGIS.parse(input);
958 |
959 | expect(output.geometry.coordinates).toEqual([
960 | [ [41.8359375,71.015625],[21.796875,36.5625],[56.953125,33.75],[41.8359375,71.015625] ]
961 | ]);
962 | expect(output.geometry.type).toEqual("Polygon");
963 | expect(output instanceof Terraformer.Feature).toBeTruthy();
964 | });
965 |
966 | it("should parse an ArcGIS Feature w/ no attributes into a Terraformer Feature", function(){
967 | var input = {
968 | "geometry": {
969 | "rings": [
970 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
971 | ],
972 | "spatialReference": {
973 | "wkid": 4326
974 | }
975 | }
976 | };
977 |
978 | var output = Terraformer.ArcGIS.parse(input);
979 |
980 | expect(output.geometry.coordinates).toEqual([
981 | [ [41.8359375,71.015625],[21.796875,36.5625],[56.953125,33.75],[41.8359375,71.015625] ]
982 | ]);
983 | expect(output.geometry.type).toEqual("Polygon");
984 | expect(output instanceof Terraformer.Feature).toBeTruthy();
985 | expect(output.properties).toEqual(null);
986 | });
987 |
988 | it("should parse an ArcGIS Feature w/ no geometry into a Terraformer Feature", function(){
989 | var input = {
990 | "attributes": {
991 | "foo": "bar"
992 | }
993 | };
994 |
995 | var output = Terraformer.ArcGIS.parse(input);
996 | expect(output.geometry).toEqual(null);
997 | expect(output instanceof Terraformer.Feature).toBeTruthy();
998 | expect(output.properties.foo).toEqual("bar");
999 | });
1000 |
1001 | it("should not reproject to WGS84/4326 while parsing", function(){
1002 | var input = {
1003 | "x": -13580977.876779145,
1004 | "y": 5621521.486191948,
1005 | "spatialReference": {
1006 | "wkid": 3857
1007 | }
1008 | };
1009 |
1010 | var output = Terraformer.ArcGIS.parse(input);
1011 | expect(output.coordinates).toEqual([-13580977.876779145, 5621521.486191948]);
1012 | });
1013 |
1014 | it("should not modify the original ArcGIS Geometry", function(){
1015 | var input = {
1016 | "geometry": {
1017 | "rings": [
1018 | [ [41.8359375,71.015625],[56.953125,33.75],[21.796875,36.5625],[41.8359375,71.015625] ]
1019 | ],
1020 | "spatialReference": {
1021 | "wkid": 4326
1022 | }
1023 | },
1024 | "attributes": {
1025 | "foo": "bar"
1026 | }
1027 | };
1028 |
1029 | var original = JSON.stringify(input);
1030 |
1031 | Terraformer.ArcGIS.parse(input);
1032 |
1033 | expect(original).toEqual(JSON.stringify(input));
1034 | });
1035 |
1036 | it("should decompress ArcGIS compressed geometry features into GeoJSON Features", function(){
1037 | var input = {
1038 | "compressedGeometry": "+1m91-66os4+1poms+1+91+3+3j"
1039 | };
1040 |
1041 | var output = Terraformer.ArcGIS.parse(input);
1042 |
1043 | expect(output.type).toEqual("Feature");
1044 | expect(output.geometry.type).toEqual("LineString");
1045 | expect(output.geometry.coordinates).toEqual([ [ -117.1816137447153, 34.057461545380946 ],[ -117.18159575425025, 34.06266078978142 ], [ -117.18154178285509, 34.06472969326257 ] ]);
1046 | });
1047 |
1048 | it("should decompress ArcGIS compressed geometries into Polylines", function(){
1049 | var output = Terraformer.ArcGIS.parseCompressedGeometry("+1m91-66os4+1poms+1+91+3+3j");
1050 |
1051 | expect(output.type).toEqual("LineString");
1052 | expect(output.coordinates).toEqual([ [ -117.1816137447153, 34.057461545380946 ],[ -117.18159575425025, 34.06266078978142 ], [ -117.18154178285509, 34.06472969326257 ] ]);
1053 | });
1054 |
1055 | it("should parse an ArcGIS Extent into a Terraformer GeoJSON Polygon", function () {
1056 | var input = {
1057 | "xmax": -35.5078125,
1058 | "ymax": 41.244772343082076,
1059 | "xmin": -13.7109375,
1060 | "ymin": 54.36775852406841,
1061 | "spatialReference": {
1062 | "wkid": 4326
1063 | }
1064 | };
1065 |
1066 | var output = Terraformer.ArcGIS.parse(input);
1067 |
1068 | expect(output.coordinates).toEqual([[[-35.5078125, 41.244772343082076], [-13.7109375, 41.244772343082076], [-13.7109375, 54.36775852406841], [-35.5078125, 54.36775852406841], [-35.5078125, 41.244772343082076]]]);
1069 | expect(output.type).toEqual("Polygon");
1070 | });
1071 |
1072 | });
1073 |
--------------------------------------------------------------------------------
/terraformer-arcgis-parser.js:
--------------------------------------------------------------------------------
1 | /* globals Terraformer */
2 | (function (root, factory) {
3 |
4 | // Node.
5 | if(typeof module === 'object' && typeof module.exports === 'object') {
6 | exports = module.exports = factory(require('terraformer'));
7 | }
8 |
9 | // Browser Global.
10 | if(typeof root.navigator === "object") {
11 | if (!root.Terraformer){
12 | throw new Error("Terraformer.ArcGIS requires the core Terraformer library. https://github.com/esri/Terraformer");
13 | }
14 | root.Terraformer.ArcGIS = factory(root.Terraformer);
15 | }
16 |
17 | }(this, function(Terraformer) {
18 | var exports = {};
19 |
20 | // https://github.com/Esri/terraformer-arcgis-parser/issues/10
21 | function decompressGeometry(str) {
22 | var xDiffPrev = 0;
23 | var yDiffPrev = 0;
24 | var points = [];
25 | var x, y;
26 | var strings;
27 | var coefficient;
28 |
29 | // Split the string into an array on the + and - characters
30 | strings = str.match(/((\+|\-)[^\+\-]+)/g);
31 |
32 | // The first value is the coefficient in base 32
33 | coefficient = parseInt(strings[0], 32);
34 |
35 | for (var j = 1; j < strings.length; j += 2) {
36 | // j is the offset for the x value
37 | // Convert the value from base 32 and add the previous x value
38 | x = (parseInt(strings[j], 32) + xDiffPrev);
39 | xDiffPrev = x;
40 |
41 | // j+1 is the offset for the y value
42 | // Convert the value from base 32 and add the previous y value
43 | y = (parseInt(strings[j + 1], 32) + yDiffPrev);
44 | yDiffPrev = y;
45 |
46 | points.push([x / coefficient, y / coefficient]);
47 | }
48 |
49 | return points;
50 | }
51 |
52 | // checks if the first and last points of a ring are equal and closes the ring
53 | function closeRing(coordinates) {
54 | if (!pointsEqual(coordinates[0], coordinates[coordinates.length - 1])) {
55 | coordinates.push(coordinates[0]);
56 | }
57 | return coordinates;
58 | }
59 |
60 | // checks if 2 x,y points are equal
61 | function pointsEqual(a, b) {
62 | for (var i = 0; i < a.length; i++) {
63 | if (a[i] !== b[i]) {
64 | return false;
65 | }
66 | }
67 | return true;
68 | }
69 |
70 | // shallow object clone for feature properties and attributes
71 | // from http://jsperf.com/cloning-an-object/2
72 | function clone(obj) {
73 | var target = {};
74 | for (var i in obj) {
75 | if (obj.hasOwnProperty(i)) {
76 | target[i] = obj[i];
77 | }
78 | }
79 | return target;
80 | }
81 |
82 | // determine if polygon ring coordinates are clockwise. clockwise signifies outer ring, counter-clockwise an inner ring
83 | // or hole. this logic was found at http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-
84 | // points-are-in-clockwise-order
85 | function ringIsClockwise(ringToTest) {
86 | var total = 0,i = 0;
87 | var rLength = ringToTest.length;
88 | var pt1 = ringToTest[i];
89 | var pt2;
90 | for (i; i < rLength - 1; i++) {
91 | pt2 = ringToTest[i + 1];
92 | total += (pt2[0] - pt1[0]) * (pt2[1] + pt1[1]);
93 | pt1 = pt2;
94 | }
95 | return (total >= 0);
96 | }
97 |
98 | // This function ensures that rings are oriented in the right directions
99 | // outer rings are clockwise, holes are counterclockwise
100 | function orientRings(poly){
101 | var output = [];
102 | var polygon = poly.slice(0);
103 | var outerRing = closeRing(polygon.shift().slice(0));
104 | if(outerRing.length >= 4){
105 | if(!ringIsClockwise(outerRing)){
106 | outerRing.reverse();
107 | }
108 |
109 | output.push(outerRing);
110 |
111 | for (var i = 0; i < polygon.length; i++) {
112 | var hole = closeRing(polygon[i].slice(0));
113 | if(hole.length >= 4){
114 | if(ringIsClockwise(hole)){
115 | hole.reverse();
116 | }
117 | output.push(hole);
118 | }
119 | }
120 | }
121 |
122 | return output;
123 | }
124 |
125 | // This function flattens holes in multipolygons to one array of polygons
126 | // [
127 | // [
128 | // [ array of outer coordinates ]
129 | // [ hole coordinates ]
130 | // [ hole coordinates ]
131 | // ],
132 | // [
133 | // [ array of outer coordinates ]
134 | // [ hole coordinates ]
135 | // [ hole coordinates ]
136 | // ],
137 | // ]
138 | // becomes
139 | // [
140 | // [ array of outer coordinates ]
141 | // [ hole coordinates ]
142 | // [ hole coordinates ]
143 | // [ array of outer coordinates ]
144 | // [ hole coordinates ]
145 | // [ hole coordinates ]
146 | // ]
147 | function flattenMultiPolygonRings(rings){
148 | var output = [];
149 | for (var i = 0; i < rings.length; i++) {
150 | var polygon = orientRings(rings[i]);
151 | for (var x = polygon.length - 1; x >= 0; x--) {
152 | var ring = polygon[x].slice(0);
153 | output.push(ring);
154 | }
155 | }
156 | return output;
157 | }
158 |
159 | function coordinatesContainCoordinates(outer, inner){
160 | var intersects = Terraformer.Tools.arraysIntersectArrays(outer, inner);
161 | var contains = Terraformer.Tools.coordinatesContainPoint(outer, inner[0]);
162 | if(!intersects && contains){
163 | return true;
164 | }
165 | return false;
166 | }
167 |
168 | // do any polygons in this array contain any other polygons in this array?
169 | // used for checking for holes in arcgis rings
170 | function convertRingsToGeoJSON(rings){
171 | var outerRings = [];
172 | var holes = [];
173 | var x; // iterator
174 | var outerRing; // current outer ring being evaluated
175 | var hole; // current hole being evaluated
176 |
177 | // for each ring
178 | for (var r = 0; r < rings.length; r++) {
179 | var ring = closeRing(rings[r].slice(0));
180 | if(ring.length < 4){
181 | continue;
182 | }
183 | // is this ring an outer ring? is it clockwise?
184 | if(ringIsClockwise(ring)){
185 | var polygon = [ ring.slice().reverse() ]; // wind outer rings counterclockwise for RFC 7946 compliance
186 | outerRings.push(polygon); // push to outer rings
187 | } else {
188 | holes.push(ring.slice().reverse()); // wind inner rings clockwise for RFC 7946 compliance
189 | }
190 | }
191 |
192 | var uncontainedHoles = [];
193 |
194 | // while there are holes left...
195 | while(holes.length){
196 | // pop a hole off out stack
197 | hole = holes.pop();
198 |
199 | // loop over all outer rings and see if they contain our hole.
200 | var contained = false;
201 | for (x = outerRings.length - 1; x >= 0; x--) {
202 | outerRing = outerRings[x][0];
203 | if(coordinatesContainCoordinates(outerRing, hole)){
204 | // the hole is contained push it into our polygon
205 | outerRings[x].push(hole);
206 | contained = true;
207 | break;
208 | }
209 | }
210 |
211 | // ring is not contained in any outer ring
212 | // sometimes this happens https://github.com/Esri/esri-leaflet/issues/320
213 | if(!contained){
214 | uncontainedHoles.push(hole);
215 | }
216 | }
217 |
218 | // if we couldn't match any holes using contains we can now try intersects...
219 | while(uncontainedHoles.length){
220 | // pop a hole off out stack
221 | hole = uncontainedHoles.pop();
222 |
223 | // loop over all outer rings and see if any intersect our hole.
224 | var intersects = false;
225 | for (x = outerRings.length - 1; x >= 0; x--) {
226 | outerRing = outerRings[x][0];
227 | if(Terraformer.Tools.arraysIntersectArrays(outerRing, hole)){
228 | // the hole intersects the outer ring push it into our polygon
229 | outerRings[x].push(hole);
230 | intersects = true;
231 | break;
232 | }
233 | }
234 |
235 | // hole does not intersect ANY outer ring at this point
236 | // make it an outer ring.
237 | if(!intersects) {
238 | outerRings.push([hole.reverse()]);
239 | }
240 | }
241 |
242 | if(outerRings.length === 1){
243 | return {
244 | type: 'Polygon',
245 | coordinates: outerRings[0]
246 | };
247 | } else {
248 | return {
249 | type: 'MultiPolygon',
250 | coordinates: outerRings
251 | };
252 | }
253 | }
254 |
255 | // ArcGIS -> GeoJSON
256 | function parse(arcgis, options){
257 | var geojson = {};
258 |
259 | options = options || {};
260 | options.idAttribute = options.idAttribute || undefined;
261 |
262 | if (arcgis.spatialReference && (arcgis.spatialReference.wkid === 3857 || arcgis.spatialReference.wkid === 102100)) {
263 | geojson.crs = Terraformer.MercatorCRS;
264 | }
265 |
266 | if(typeof arcgis.x === 'number' && typeof arcgis.y === 'number'){
267 | geojson.type = "Point";
268 | geojson.coordinates = [arcgis.x, arcgis.y];
269 | if (arcgis.z || arcgis.m){
270 | geojson.coordinates.push(arcgis.z);
271 | }
272 | if (arcgis.m){
273 | geojson.coordinates.push(arcgis.m);
274 | }
275 | }
276 |
277 | if(arcgis.points){
278 | geojson.type = "MultiPoint";
279 | geojson.coordinates = arcgis.points.slice(0);
280 | }
281 |
282 | if(arcgis.paths) {
283 | if(arcgis.paths.length === 1){
284 | geojson.type = "LineString";
285 | geojson.coordinates = arcgis.paths[0].slice(0);
286 | } else {
287 | geojson.type = "MultiLineString";
288 | geojson.coordinates = arcgis.paths.slice(0);
289 | }
290 | }
291 |
292 | if(arcgis.rings) {
293 | geojson = convertRingsToGeoJSON(arcgis.rings.slice(0));
294 | }
295 |
296 | if(
297 | typeof arcgis.xmin === "number" &&
298 | typeof arcgis.ymin === "number" &&
299 | typeof arcgis.xmax === "number" &&
300 | typeof arcgis.ymax === "number"
301 | ) {
302 | geojson.type = "Polygon";
303 | geojson.coordinates = [[
304 | [arcgis.xmax, arcgis.ymax],
305 | [arcgis.xmin, arcgis.ymax],
306 | [arcgis.xmin, arcgis.ymin],
307 | [arcgis.xmax, arcgis.ymin],
308 | [arcgis.xmax, arcgis.ymax]
309 | ]];
310 | }
311 |
312 | if(arcgis.compressedGeometry || arcgis.geometry || arcgis.attributes) {
313 | geojson.type = "Feature";
314 |
315 | if(arcgis.compressedGeometry){
316 | arcgis.geometry = {
317 | paths: [
318 | decompressGeometry(arcgis.compressedGeometry)
319 | ]
320 | };
321 | }
322 |
323 | geojson.geometry = (arcgis.geometry) ? parse(arcgis.geometry) : null;
324 | geojson.properties = (arcgis.attributes) ? clone(arcgis.attributes) : null;
325 | if(arcgis.attributes) {
326 | geojson.id = arcgis.attributes[options.idAttribute] || arcgis.attributes.OBJECTID || arcgis.attributes.FID;
327 | }
328 | }
329 |
330 | return new Terraformer.Primitive(geojson);
331 | }
332 |
333 | // GeoJSON -> ArcGIS
334 | function convert(geojson, options){
335 | var spatialReference;
336 |
337 | options = options || {};
338 | var idAttribute = options.idAttribute || "OBJECTID";
339 |
340 | if(options.sr){
341 | spatialReference = { wkid: options.sr };
342 | } else if (geojson && geojson.crs && geojson.crs.properties.name != "urn:ogc:def:crs:OGC:1.3:CRS84") {
343 | spatialReference = null;
344 | } else {
345 | spatialReference = { wkid: 4326 };
346 | }
347 |
348 | var result = {};
349 | var i;
350 |
351 | switch(geojson.type){
352 | case "Point":
353 | result.x = geojson.coordinates[0];
354 | result.y = geojson.coordinates[1];
355 | if(geojson.coordinates[2]) {
356 | result.z = geojson.coordinates[2];
357 | }
358 | if(geojson.coordinates[3]) {
359 | result.m = geojson.coordinates[3];
360 | }
361 | result.spatialReference = spatialReference;
362 | break;
363 | case "MultiPoint":
364 | result.points = geojson.coordinates.slice(0);
365 | result.spatialReference = spatialReference;
366 | break;
367 | case "LineString":
368 | result.paths = [geojson.coordinates.slice(0)];
369 | result.spatialReference = spatialReference;
370 | break;
371 | case "MultiLineString":
372 | result.paths = geojson.coordinates.slice(0);
373 | result.spatialReference = spatialReference;
374 | break;
375 | case "Polygon":
376 | result.rings = orientRings(geojson.coordinates.slice(0));
377 | result.spatialReference = spatialReference;
378 | break;
379 | case "MultiPolygon":
380 | result.rings = flattenMultiPolygonRings(geojson.coordinates.slice(0));
381 | result.spatialReference = spatialReference;
382 | break;
383 | case "Feature":
384 | if(geojson.geometry) {
385 | result.geometry = convert(geojson.geometry, options);
386 | }
387 | result.attributes = (geojson.properties) ? clone(geojson.properties) : {};
388 | if(geojson.id) {
389 | result.attributes[idAttribute] = geojson.id;
390 | }
391 | break;
392 | case "FeatureCollection":
393 | result = [];
394 | for (i = 0; i < geojson.features.length; i++){
395 | result.push(convert(geojson.features[i], options));
396 | }
397 | break;
398 | case "GeometryCollection":
399 | result = [];
400 | for (i = 0; i < geojson.geometries.length; i++){
401 | result.push(convert(geojson.geometries[i], options));
402 | }
403 | break;
404 | }
405 |
406 | return result;
407 | }
408 |
409 | function parseCompressedGeometry(string){
410 | return new Terraformer.LineString(decompressGeometry(string));
411 | }
412 |
413 | exports.parse = parse;
414 | exports.convert = convert;
415 | exports.toGeoJSON = parse;
416 | exports.fromGeoJSON = convert;
417 | exports.parseCompressedGeometry = parseCompressedGeometry;
418 |
419 | return exports;
420 | }));
421 |
--------------------------------------------------------------------------------
/test.ts:
--------------------------------------------------------------------------------
1 | import * as TerraformerArcGIS from "./index";
2 |
3 | console.assert(typeof TerraformerArcGIS !== undefined);
4 |
5 | // parse an ArcGIS Geometry to GeoJSON
6 | let geojsonPoint = TerraformerArcGIS.parse({
7 | x: -122.6764,
8 | y: 45.5165,
9 | spatialReference: {
10 | wkid: 4326
11 | }
12 | });
13 |
14 | // convert a GeoJSON object into an ArcGIS geometry
15 | let arcgisPoint = TerraformerArcGIS.convert(geojsonPoint);
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es5",
5 | "noImplicitAny": false,
6 | "sourceMap": false,
7 | "noEmit": true
8 | },
9 | "exclude": [
10 | "node_modules"
11 | ]
12 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rules": {
4 | "object-literal-sort-keys": false,
5 | "trailing-comma": [
6 | true, {
7 | "singleline": "never",
8 | "multiline": "never"
9 | }
10 | ],
11 | "interface-name": [
12 | true, "never-prefix"
13 | ]
14 | }
15 | }
--------------------------------------------------------------------------------