├── .gitignore ├── ADVANCED_USAGE.md ├── BUILDS.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── analyze.js ├── create-build-list.sh ├── demo ├── get-epsg-code.web.js ├── get-epsg-code.web.js.map └── index.html ├── enums.js ├── hash.js ├── normalize ├── esriwkt.js ├── ogcwkt.js ├── proj4.js ├── rows.js ├── test.js └── wkt.js ├── package.json ├── parse ├── proj4js.js └── test.js ├── prebuild.js ├── setup.sh ├── src ├── get-proj-type.js └── index.js └── test ├── data ├── 3031.json ├── 32617.gml ├── 32617.json ├── 3857.json ├── 4326.json └── setup.sh ├── test.html └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | lib 107 | *.csv 108 | *.dat 109 | *.esriwkt 110 | *.geoserver 111 | *.gml 112 | *.gz 113 | *.mapfile 114 | *.mapnik 115 | *.postgis 116 | *.proj4 117 | *.txt 118 | test/data/*.js 119 | *.wkt 120 | *.xml 121 | 122 | *.min.js 123 | *.zip 124 | pnpm-lock.yaml 125 | .parcel-cache 126 | log 127 | *bak* 128 | -------------------------------------------------------------------------------- /ADVANCED_USAGE.md: -------------------------------------------------------------------------------- 1 | # Advanced Usage 2 | 3 | ## Builds 4 | The identification of the following formats are included in all the available builds. 5 | This is because it doesn't add any significant size to your bundle to identify the EPSG code for these formats: 6 | - GeoServer Config 7 | - Graphic Markup Language (GML) 8 | - Mapfile (MapServer Configuration) 9 | - Mapnik Configuration 10 | - Open Geospatial Consortium Well-Known Text (OGC WKT) 11 | - PostGIS Statement 12 | - Proj4js Definition Statement 13 | - Open Geospatial Consortium XML 14 | 15 | However, identifying ESRI WKT, Mapfiles, Mapnik Configurations, and Proj4 Strings require embedding a compressed data file of valid hashes. 16 | Therefore, trimmed builds are provided as well, noting which formats are excluded. 17 | 18 | 19 | ### Requiring Bundles 20 | You can require a specific build with the following code: 21 | ```js 22 | const getEPSGCode = require('get-epsg-code/dist/get-epsg-code-including-esriwkt-proj4.node.min.js'); 23 | ``` 24 | You can find a list of all of the builds and their file size at [BUILDS.md](https://github.com/DanielJDufour/get-epsg-code/blob/master/BUILDS.md) 25 | 26 | ### Requiring Un-Bundled Files 27 | You can require un-bundled NodeJS-compatabile JavaScript files inside the lib folder. 28 | ```js 29 | // lookup without mapfile support 30 | const getEPSGCode = require('get-epsg-code/lib/lookup-esriwkt-proj4.js'); 31 | ``` -------------------------------------------------------------------------------- /BUILDS.md: -------------------------------------------------------------------------------- 1 | # Builds 2 | | name | file size | 3 | | ---- | --------- | 4 | | get-epsg-code-including-esriwkt-proj.node.min.js | 94K | 5 | | get-epsg-code-including-esriwkt-proj.web.min.js | 94K | 6 | | get-epsg-code-including-esriwkt-proj4.node.min.js | 94K | 7 | | get-epsg-code-including-esriwkt-proj4.web.min.js | 94K | 8 | | get-epsg-code.node.js | 142K | 9 | | get-epsg-code.node.min.js | 118K | 10 | | get-epsg-code.web.js | 145K | 11 | | get-epsg-code.web.min.js | 118K | 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | # Getting Set Up 4 | 1) Fork https://github.com/danieljdufour/get-epsg-code 5 | 2) Clone your fork 6 | 7 | # Install Dependencies 8 | ```bash 9 | npm install 10 | ``` 11 | 12 | # Download Coordinate Reference System Data 13 | ```bash 14 | npm run download-csv 15 | ``` 16 | 17 | # Download Test Data 18 | ```bash 19 | npm run setup 20 | ``` 21 | 22 | # Build the Compressed Data File 23 | ```bash 24 | npm run prebuild 25 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # get-epsg-code: *beta* 2 | Gets the EPSG Code for Nearly All Coordinate Reference System Formats (including OGC WKT, WKT 2, ESRI WKT, GeoServer Config, Mapfile, Mapnik Config, PostGIS Statement, PROJJSON, PROJ String, and Proj4JS String) 3 | s 4 | # usage 5 | ```javascript 6 | const getEPSGCode = require("get-epsg-code"); 7 | 8 | const proj4string = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"; 9 | 10 | const epsgCode = getEPSGCode(proj4string); 11 | // 3857 12 | ``` 13 | 14 | # purpose 15 | Get the EPSG code for input without the need for a remote server 16 | 17 | # algorithm 18 | Take a CSV of Coordinate Reference System formats, hash the columns and stores the result in a binary file. 19 | 20 | # limitations 21 | This package goes only one way. It only gets EPSG codes for a given input. It cannot provide coordinate information for an EPSG code. 22 | 23 | # advanced usage 24 | For advanced usage, see [ADVANCED_USAGE.md](https://github.com/DanielJDufour/get-epsg-code/blob/master/ADVANCED_USAGE.md) 25 | 26 | # contact 27 | Post an issue at https://github.com/danieljdufour/get-epsg-code/issues or email the package author at daniel.j.dufour@gmail.com 28 | -------------------------------------------------------------------------------- /analyze.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require("fs"); 2 | const { parse } = require("papaparse"); 3 | const normalize_rows = require("./normalize/rows.js"); 4 | 5 | // first read all rows from original CSv 6 | const string = readFileSync("./crs.csv", "utf-8"); 7 | const parsed = parse(string, { header: true, skipEmptyLines: true }); 8 | const original_rows = parsed.data; 9 | 10 | // find dupes 11 | normalize_rows(original_rows); 12 | 13 | const hashed_rows = require("./lib/lookup-esriwkt-mapfile-proj4.js")._load_rows(); 14 | 15 | const hash2code = {}; 16 | hashed_rows.reduce((acc, { epsg_code, ...rest }) => { 17 | Object.entries(rest).forEach(([field, hash]) => { 18 | if (!hash2code[field]) hash2code[field] = {}; 19 | if (!hash2code[field][hash]) hash2code[field][hash] = []; 20 | hash2code[field][hash].push(epsg_code); 21 | }); 22 | }, {}); 23 | // console.log("hash2code:", hash2code); 24 | 25 | let collisions = 0; 26 | 27 | // remove uniques 28 | for (let fieldName in hash2code) { 29 | for (let hash in hash2code[fieldName]) { 30 | if (hash2code[fieldName][hash].length == 1) { 31 | delete hash2code[fieldName][hash]; 32 | } else { 33 | const inputs = hash2code[fieldName][hash].map(code => { 34 | return original_rows.find(r => r.code == code)[fieldName]; 35 | }); 36 | if (new Set(inputs).size > 1) { 37 | // only collision if inputs aren't the same 38 | collisions++; 39 | // throw new Error("collision for field " + fieldName + " hash " + hash + " for codes " + hash2code[fieldName][hash]); 40 | } 41 | } 42 | } 43 | } 44 | 45 | console.log("collisions:", collisions); 46 | 47 | if (collisions > 0) { 48 | throw Error("there are collisions"); 49 | } 50 | -------------------------------------------------------------------------------- /create-build-list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "starting create-build-list.sh" 4 | 5 | echo '# Builds' > BUILDS.md 6 | 7 | echo '| name | file size |' >> BUILDS.md 8 | 9 | echo '| ---- | --------- |' >> BUILDS.md 10 | 11 | ls -alsh dist | grep 'get-epsg-code' | awk 'BEGIN { OFS = " | " }{ print "| "$10,$6" |" }' | grep '.js ' >> BUILDS.md 12 | -------------------------------------------------------------------------------- /demo/get-epsg-code.web.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://get-epsg-code/./enums.js","webpack://get-epsg-code/./lib/enums.js","webpack://get-epsg-code/./lib/get-proj-type.js","webpack://get-epsg-code/./lib/hash.js","webpack://get-epsg-code/./lib/lookup-esriwkt-mapfile-proj4.js","webpack://get-epsg-code/./lib/normalize/esriwkt.js","webpack://get-epsg-code/./lib/normalize/proj4.js","webpack://get-epsg-code/./node_modules/.pnpm/b64ab@0.0.1/node_modules/b64ab/b64ab.js","webpack://get-epsg-code/./node_modules/.pnpm/is-wkt@0.1.0/node_modules/is-wkt/is-wkt.js","webpack://get-epsg-code/./node_modules/.pnpm/utm-utils@0.5.0/node_modules/utm-utils/src/getCodeFromEsriWKT.js","webpack://get-epsg-code/./node_modules/.pnpm/utm-utils@0.5.0/node_modules/utm-utils/src/getCodeFromProjString.js","webpack://get-epsg-code/./node_modules/.pnpm/wkt-parser@1.3.3/node_modules/wkt-parser/index.js","webpack://get-epsg-code/./node_modules/.pnpm/wkt-parser@1.3.3/node_modules/wkt-parser/parser.js","webpack://get-epsg-code/./node_modules/.pnpm/wkt-parser@1.3.3/node_modules/wkt-parser/process.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/count-substring.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/find-tag-by-name.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/find-tag-by-path.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/find-tags-by-name.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/find-tags-by-path.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/get-attribute.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/index-of-match-end.js","webpack://get-epsg-code/./node_modules/.pnpm/xml-utils@1.7.0/node_modules/xml-utils/index-of-match.js","webpack://get-epsg-code/webpack/bootstrap","webpack://get-epsg-code/webpack/runtime/define property getters","webpack://get-epsg-code/webpack/runtime/hasOwnProperty shorthand","webpack://get-epsg-code/webpack/runtime/make namespace object","webpack://get-epsg-code/webpack/runtime/node module decorator","webpack://get-epsg-code/webpack/startup"],"names":[],"mappings":";;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;AChBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;;;;;;;;;;AChBA,cAAc,mBAAO,CAAC,+EAAQ;;AAE9B,OAAO,UAAU,GAAG,mBAAO,CAAC,+BAAa;;AAEzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED;AACA;AACA,yBAAyB;AACzB;AACA,GAAG;AACH;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;AACH;AACA;AACA,KAAK;AACL;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG,eAAe,IAAI;AACtB;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA,sBAAsB;;;;;;;;;;;ACrDtB;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa,mBAAmB;AAChC;AACA;AACA,cAAc;AACd;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA,sBAAsB;;;;;;;;;;;AClCtB,cAAc,mBAAO,CAAC,2EAAO;AAC7B,sBAAsB,mBAAO,CAAC,mHAA4B;AAC1D,sBAAsB,mBAAO,CAAC,mHAA4B;AAC1D,qBAAqB,mBAAO,CAAC,6GAAyB;AACtD,iBAAiB,mBAAO,CAAC,0FAAY;AACrC,2BAA2B,mBAAO,CAAC,kIAAqC;AACxE,8BAA8B,mBAAO,CAAC,wIAAwC;;AAE9E,aAAa,mBAAO,CAAC,gCAAW;AAChC,sBAAsB,iGAAmD;AACzE,oBAAoB,2FAA+C;AACnE,oBAAoB,mBAAO,CAAC,kDAAoB;AAChD,OAAO,UAAU,GAAG,mBAAO,CAAC,kCAAY;;AAExC,aAAa,mBAAO,CAAC,gFAAmC;;AAExD;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA,uBAAuB,cAAc;AACrC;AACA,2BAA2B,gBAAgB;AAC3C;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA,iBAAiB,mBAAmB;AACpC;AACA;;AAEA;AACA;AACA,GAAG;AACH,2EAA2E,QAAQ;AACnF;AACA,GAAG;AACH,+EAA+E,QAAQ;AACvF,GAAG;AACH,gCAAgC,QAAQ;AACxC;;AAEA;AACA;AACA,KAAK;AACL;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH,mCAAmC,IAAI;AACvC,GAAG;AACH;AACA,gDAAgD,KAAK;AACrD,KAAK;AACL,iCAAiC,IAAI;AACrC;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,GAAG;AACH;AACA,+CAA+C;AAC/C;AACA,GAAG;AACH;AACA;AACA;;AAEA,IAAI,KAA6B;AACjC;AACA;AACA;AACA;AACA,CAAC;AACD,oCAAoC;AACpC;;;;;;;;;;;;ACpIA;AACA;;AAEA;AACA,oCAAoC,IAAI;;AAExC;AACA;AACA,aAAa,kBAAkB;;AAE/B;AACA,kBAAkB;;AAElB,IAAI,IAA0C;AAC9C,IAAI,mCAAO,YAAY,SAAS,qBAAqB,EAAE;AAAA,kGAAC;AACxD,C;;;;;;;;;;ACfA;AACA;AACA,WAAW,OAAO;AAClB,aAAa,OAAO;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,KAAK;AACN;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;;AAEA,kBAAkB;;;;;;;;;;;ACjClB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,0BAA0B,QAAQ;AAClC;AACA;AACA;AACA;AACA,iBAAiB,UAAU;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,eAAe;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,IAAI,IAA0C;AAC9C,EAAE,mCAAO,YAAY,eAAe;AAAA,kGAAC;AACrC;;AAEA,IAAI,IAA0B;AAC9B;AACA;;;;;;;;;;;ACxEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA,mBAAmB,qBAAqB;AACxC;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA,IAAI,IAA0C;AAC9C,EAAE,mCAAO;AACT;AACA,GAAG;AAAA,kGAAC;AACJ;;AAEA,IAAI,IAA0B;AAC9B;AACA,EAAE,sBAAsB;AACxB;AACA;AACA;;;;;;;;;;;AC1CA;AACA;AACA;AACA;AACA;AACA,wCAAwC,IAAI;AAC5C;AACA;AACA;AACA;AACA,GAAG,4CAA4C,IAAI;AACnD;AACA;AACA;AACA;AACA;;AAEA,IAAI,IAA0C;AAC9C;AACA;AACA;AACA;;AAEA,IAAI,IAA0B;AAC9B;AACA,EAAE,sBAAsB;AACxB;;;;;;;;;;;AC1BA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;;AAEA,IAAI,IAA0C;AAC9C;AACA;AACA;AACA;;AAEA,IAAI,IAA0B;AAC9B;AACA,EAAE,sBAAsB;AACxB;;;;;;;;;;;;;;;;;;AC5BA;AAC8B;AACE;;;;AAIhC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA,GAAG;AACH;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,yCAAyC,QAAQ;AACjD;AACA;AACA;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA,6BAAe,oCAAS;AACxB,aAAa,gDAAM;AACnB;AACA;AACA;AACA;AACA;AACA,EAAE,+CAAK;AACP;AACA;AACA;;;;;;;;;;;;;;;;AC5MA,iEAAe,WAAW,EAAC;;AAE3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;ACtKA;AACA;AACA;AACA;AACA;AACA,sBAAsB;;AAEtB;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;;AAEO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;AChHA;AACA;AACA;AACA;AACA;;AAEA;AACA,sBAAsB;;;;;;;;;;;ACPtB,qBAAqB,mBAAO,CAAC,0GAAqB;AAClD,wBAAwB,mBAAO,CAAC,kHAAyB;AACzD,uBAAuB,mBAAO,CAAC,4GAAsB;;AAErD;AACA;AACA;;AAEA;;AAEA;;AAEA,uCAAuC,QAAQ;AAC/C;AACA;;AAEA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;;AAEA,UAAU;AACV;;AAEA;AACA,sBAAsB;;;;;;;;;;;AC5DtB,uBAAuB,mBAAO,CAAC,gHAAwB;;AAEvD;AACA;AACA,2CAA2C,6BAA6B;AACxE;AACA;AACA;AACA;AACA,sBAAsB;;;;;;;;;;;ACTtB,sBAAsB,mBAAO,CAAC,8GAAuB;;AAErD;AACA;AACA;AACA;AACA;AACA;AACA,6CAA6C,oBAAoB;AACjE;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,sBAAsB;;;;;;;;;;;ACrBtB,uBAAuB,mBAAO,CAAC,gHAAwB;;AAEvD;AACA;AACA;AACA,gDAAgD,uBAAuB;AACvE;AACA,yBAAyB,yBAAyB;AAClD;AACA;AACA;AACA,0BAA0B,wBAAwB;AAClD;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,sBAAsB;;;;;;;;;;;AChCtB;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA,iBAAiB,uBAAuB;AACxC;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,sBAAsB;;;;;;;;;;;ACvBtB;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,sBAAsB;;;;;;;;;;;ACRtB;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,sBAAsB;;;;;;;UCRtB;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCzBA;WACA;WACA;WACA;WACA,wCAAwC,yCAAyC;WACjF;WACA;WACA,E;;;;;WCPA,wF;;;;;WCAA;WACA;WACA;WACA,sDAAsD,kBAAkB;WACxE;WACA,+CAA+C,cAAc;WAC7D,E;;;;;WCNA;WACA;WACA;WACA;WACA,E;;;;;UCJA;UACA;UACA;UACA","file":"./get-epsg-code.web.js","sourcesContent":["const FORMATS = {\n ESRI_WKT: 'esriwkt',\n GEOSERVER: 'geoserver',\n MAPFILE: 'mapfile',\n MAPNIK: 'mapnik',\n OGC_GML: 'gml',\n OGC_XML: 'xml',\n OGC_WKT: 'wkt',\n POSTGIS: 'postgis',\n PROJ_4: 'proj4',\n PROJ_4_JS: 'js',\n PROJJSON: 'PROJJSON'\n};\n\nmodule.exports = {\n FORMATS\n};\n","const FORMATS = {\n ESRI_WKT: 'esriwkt',\n GEOSERVER: 'geoserver',\n MAPFILE: 'mapfile',\n MAPNIK: 'mapnik',\n OGC_GML: 'gml',\n OGC_XML: 'xml',\n OGC_WKT: 'wkt',\n POSTGIS: 'postgis',\n PROJ_4: 'proj4',\n PROJ_4_JS: 'js',\n PROJJSON: 'PROJJSON'\n};\n\nmodule.exports = {\n FORMATS\n};\n","const isWKT = require(\"is-wkt\");\n\nconst { FORMATS } = require('../enums.js');\n\nconst {\n ESRI_WKT,\n GEOSERVER,\n MAPFILE,\n MAPNIK,\n OGC_GML,\n OGC_XML,\n OGC_WKT,\n POSTGIS,\n PROJ_4,\n PROJ_4_JS,\n PROJJSON\n} = FORMATS;\n\nfunction getProjType(input) {\n // check WKT\n if (input.startsWith(\"{\") && input.includes(\"projjson\")) {\n return PROJJSON;\n } else if (isWKT(input)) {\n if (input.includes(\"AUTHORITY\")) {\n return OGC_WKT;\n } else {\n // appears to be ESRI WKT\n return ESRI_WKT;\n }\n } else if (input.includes(\"gml:ProjectedCRS\") || input.includes(\"gml:GeodeticCRS\") || input.includes(\"gml:GeographicCRS\")) {\n if (input.includes(\"gml:srsID\")) {\n return OGC_XML;\n } else {\n return OGC_GML;\n }\n } else if (input.startsWith(\"+proj=\")) {\n return PROJ_4;\n } else if (input.startsWith(`proj4.defs(\"EPSG:`)) {\n return PROJ_4_JS;\n } else if (/^\\d{1,6}\\=(PROJCS|GEOGCS)/.test(input)) {\n return GEOSERVER;\n } else if (input.startsWith(\"PROJECTION\") && input.endsWith(\"END\")) {\n return MAPFILE;\n } else if (input.endsWith('')) {\n return MAPNIK;\n } else if (input.startsWith('INSERT')) {\n return POSTGIS;\n } else {\n return \"SOMETHING ELSE\";\n }\n}\n\nmodule.exports = getProjType;\nmodule.exports.default = getProjType;\n","function hash(string) {\n // sometimes might have extra space at end from epsg.io\n string = string.trim();\n\n // replace new lines with spaces\n string = string.replace(/\\n/g, ' ');\n\n // replace tabs with spaces\n string = string.replace(/\\t/g, ' ');\n\n // remove any extra spaces\n string = string.replace(/ +/g, ' ');\n\n if (string[0] === \"+\") {\n // if proj4, sort keys\n string = string.split(\" \").sort().join(\" \")\n }\n\n let hash = 0, i, chr;\n if (string.length === 0) return hash;\n const string_length = string.length;\n for (i = 0; i < string_length; i++) {\n chr = string.charCodeAt(i);\n hash = ((hash << 5) - hash) + chr;\n hash |= 0; // Convert to 32bit integer\n }\n\n // convert to 16-bit\n hash = Math.round(hash / Math.pow(2, 16));\n\n return hash;\n}\n\nmodule.exports = hash;\nmodule.exports.default = hash;\n","const b64ab = require(\"b64ab\");\nconst findTagByPath = require('xml-utils/find-tag-by-path');\nconst findTagByName = require('xml-utils/find-tag-by-name');\nconst getAttribute = require('xml-utils/get-attribute');\nconst parseWKT = require('wkt-parser');\nconst getCodeFromEsriWKT = require(\"utm-utils/src/getCodeFromEsriWKT.js\");\nconst getCodeFromProjString = require(\"utm-utils/src/getCodeFromProjString.js\");\n\nconst hash = require(\"./hash.js\")\nconst clean_esriwkt = require('./normalize/esriwkt.js').normalize_esriwkt;\nconst clean_proj4 = require('./normalize/proj4.js').normalize_proj4;\nconst getProjType = require('./get-proj-type.js');\nconst { FORMATS } = require(\"./enums.js\");\n\nconst data = require(\"./data/esriwkt-mapfile-proj4.json\");\n\nconst getDefault = fn => fn.default || fn;\n\nconst rows = []\n\nfunction lookup(input, dataType, debug=false) {\n if (rows.length === 0) {\n const decoded = b64ab.toArrayBuffer(data.data);\n const arr = new Int16Array(decoded);\n if (debug) console.log(\"arr:\", arr);\n \n const num_fields = data.properties.columns.length;\n const num_rows = arr.length / num_fields;\n \n for (let r = 0; r < num_rows; r++) {\n const row = {};\n for (let f = 0; f < num_fields; f++) {\n row[data.properties.columns[f]] = arr[r * num_fields + f];\n }\n rows.push(row);\n }\n if (debug) console.log(\"[get-epsg-code] rows:\", rows);\n }\n\n const hashed = hash(input);\n if (debug) console.log(\"[get-epsg-code] hashed:\", hashed);\n\n const found = rows.filter(row => row[dataType] === hashed);\n if (debug) console.log(\"[get-epsg-code] found:\", found);\n\n return found.map(row => row.epsg_code);\n}\n\nconst {\n ESRI_WKT,\n GEOSERVER,\n MAPFILE, \n MAPNIK,\n OGC_GML,\n OGC_XML,\n OGC_WKT,\n POSTGIS,\n PROJ_4,\n PROJ_4_JS\n} = FORMATS;\n\nfunction getEPSGCode(input, options) {\n //console.log(\"starting get-epsg-code with\", input, options);\n var debug = options && options.debug ? options.debug : false;\n\n const dataType = getProjType(input);\n if (debug) console.log(\"dataType:\", dataType);\n\n if (dataType === OGC_WKT) {\n const parsed = getDefault(parseWKT)(input);\n if (debug) console.log(\"parsed:\", parsed);\n if (parsed.AUTHORITY) {\n const authority = parsed.AUTHORITY;\n return Number(authority.epsg || authority.EPSG);\n }\n } else if (dataType == ESRI_WKT) {\n // try utm parsing\n console.log({getCodeFromEsriWKT})\n const code = getDefault(getCodeFromEsriWKT)(input);\n if (code) return code;\n\n input = clean_esriwkt(input);\n return lookup(input, ESRI_WKT, debug);\n } else if (dataType === OGC_GML) {\n const identifier = getDefault(findTagByName)(input, \"gml:identifier\", { debug }).inner;\n return Number(identifier.replace(\"urn:ogc:def:crs:EPSG::\", \"\"));\n } else if (dataType === OGC_XML) {\n return Number(getDefault(findTagByPath)(input, [\"gml:srsID\", \"gml:name\"], { debug }).inner);\n } else if (dataType === PROJ_4) {\n input = clean_proj4(input, { debug });\n console.log(\"cleaned to input:\", input);\n\n if (input.startsWith('+proj=utm')) {\n return getCodeFromProjString(input);\n } else {\n return lookup(input, PROJ_4, debug);\n }\n } else if (dataType === PROJ_4_JS) {\n return Number(input.substring(17, input.indexOf(`\"`, 17)));\n } else if (dataType === GEOSERVER) {\n return Number(input.match(/^\\d{1,6}/)[0]);\n } else if (dataType === MAPFILE) {\n if (input.includes('init=epsg:')) {\n return Number.parseInt(/(\"init\\=epsg:)(\\d{1,10})(\")/.exec(input)[2]);\n } else if (input.includes('\"proj=utm\"')) {\n const zone = /(\"zone\\=)(\\d{1,2})(\")/.exec(input)[2];\n const south = input.includes('\"south\"');\n if (input.includes(\"ellps=GRS80\") && south === false) {\n return Number.parseInt('269' + zone);\n } else {\n const hemisphere = south ? '7' : '6';\n return Number.parseInt('32' + hemisphere + zone);\n }\n } else {\n return lookup(input, MAPFILE, debug);\n }\n } else if (dataType === MAPNIK) {\n const map = findTagByName(input, 'Map');\n const srs = getAttribute(map.outer, 'srs'); // Proj.4 String\n return getEPSGCode(srs);\n } else if (dataType === POSTGIS) {\n return Number(input.substring(input.indexOf(\"values (\") + 8, input.indexOf(\"EPSG\") - 3).trim());\n }\n};\n\nif (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {\n module.exports = getEPSGCode;\n}\nif (typeof window !== 'undefined') {\n window['getEPSGCode'] = getEPSGCode;\n} else if (typeof self !== 'undefined') {\n self['getEPSGCode'] = getEPSGCode; // jshint ignore:line\n}\n","function normalize_esriwkt(esriwkt) {\n esriwkt = esriwkt.replace(`DATUM[\"D_`, `DATUM[\"`);\n \n // reduce precision of decimal numbers to 15 digits\n esriwkt = esriwkt.replace(/\\.\\d{16,}/g, n => n.substr(0, 16));\n \n return esriwkt;\n}\nconsole.log({normalize_esriwkt})\n\nconsole.log(\"module:\", module);\nmodule.exports = { normalize_esriwkt };\n\nif (typeof define === \"function\" && define.amd) {\n define(function() { return { normalize_esriwkt }; });\n}","/**\n * \n * @param {String} str - PROJ.4 string\n * @returns {String} normalized PROJ.4 string\n */\nfunction normalize_proj4(str, {\n debug = false,\n remove_k1 = true,\n remove_no_defs = true,\n remove_title = true,\n remove_init=true,\n remove_type=true\n} = {}) {\n // replace any accidental extra spaces\n str = str.replace(/ +/g, ' ');\n\n // sort params\n let parts = str.split(/ ?\\+/g).filter(it => it !== \"\").sort();\n\n // seems like k=1 is interpreted as default for example https://epsg.io/3031.proj4 dropped it\n if (remove_k1) parts = parts.filter(it => it !== \"k=1\");\n if (remove_no_defs) parts = parts.filter(it => it !== \"no_defs\");\n if (remove_type) parts = parts.filter(it => !it.startsWith(\"type\"));\n if (remove_init) parts = parts.filter(it => !it.startsWith(\"init\"));\n if (remove_title) parts = parts.filter(it => !it.startsWith(\"title\"));\n\n str = parts.map(it => \"+\" + it).join(\" \");\n\n if (debug) console.log('normalized proj4 string\\nfrom \"' + arguments[0] + '\"\\nto \"' + str + '\"');\n \n return str;\n}\n\nmodule.exports = { normalize_proj4 };\n","var b64ab = {};\n\nb64ab.char2bits = {\n 0: \"110100\", 1: \"110101\", 2: \"110110\", 3: \"110111\", 4: \"111000\",\n 5: \"111001\", 6: \"111010\", 7: \"111011\", 8: \"111100\", 9: \"111101\",\n A: \"000000\", Q: \"010000\", g: \"100000\", w: \"110000\", B: \"000001\",\n R: \"010001\", h: \"100001\", x: \"110001\", C: \"000010\", S: \"010010\",\n i: \"100010\", y: \"110010\", D: \"000011\", T: \"010011\", j: \"100011\",\n z: \"110011\", E: \"000100\", U: \"010100\", k: \"100100\", F: \"000101\",\n V: \"010101\", l: \"100101\", G: \"000110\", W: \"010110\", m: \"100110\",\n H: \"000111\", X: \"010111\", n: \"100111\", I: \"001000\", Y: \"011000\",\n o: \"101000\", J: \"001001\", Z: \"011001\", p: \"101001\", K: \"001010\",\n a: \"011010\", q: \"101010\", L: \"001011\", b: \"011011\", r: \"101011\",\n M: \"001100\", c: \"011100\", s: \"101100\", N: \"001101\", d: \"011101\",\n t: \"101101\", O: \"001110\", e: \"011110\", u: \"101110\", \"+\": \"111110\",\n P: \"001111\", f: \"011111\", v: \"101111\", \"/\": \"111111\"\n};\n\nb64ab.toArrayBuffer = function toArrayBuffer (b64) {\n var char2bits = b64ab.char2bits;\n var eqct = 0;\n for (let i = b64.length; i >= 0; i--) if (b64[i] === \"=\") eqct++;\n var slen = (b64.length - eqct);\n var l = Math.floor(.75 * slen);\n var u8 = new Uint8Array(l);\n var s = \"\";\n for (var i = 0; i < slen; i++) {\n var char = b64[i];\n if (char === \"=\") break;\n s += char2bits[char];\n if (s.length >= 8) {\n u8[Math.floor(.75 * i)] = parseInt(s.substring(0, 8), 2);\n s = s.substring(8);\n }\n }\n return u8.buffer;\n};\n\nb64ab.toBase64String = function toBase64String(ab) {\n if (!b64ab.bits2char) {\n b64ab.bits2char = {};\n for (let char in b64ab.char2bits) b64ab.bits2char[b64ab.char2bits[char]] = char;\n }\n var bits2char = b64ab.bits2char;\n var u8 = new Uint8Array(ab);\n var s = \"\";\n var b = \"\";\n for (var i = 0; i < u8.length; i++) {\n var n = u8[i].toString(2);\n while (n.length < 8) n = \"0\" + n;\n b += n;\n while (b.length >= 6) {\n s += bits2char[b.substring(0, 6)];\n b = b.substring(6);\n }\n }\n if (b.length > 0) {\n while (b.length < 6) b += \"0\";\n s += bits2char[b];\n }\n while (s.length % 4 !== 0) {\n s += \"=\"\n }\n return s;\n};\n\nif (typeof define === \"function\" && define.amd) {\n define(function() { return b64ab })\n}\n\nif (typeof module === \"object\") module.exports = b64ab;\nif (typeof window === \"object\") window.b64ab = b64ab;\nif (typeof self === \"object\") self.b64ab = b64ab;\n","// keywords are from proj4js\nconst keywords = [\n \"PROJECTEDCRS\",\n \"PROJCRS\",\n \"GEOGCS\",\n \"GEOCCS\",\n \"PROJCS\",\n \"LOCAL_CS\",\n \"GEODCRS\",\n \"GEODETICCRS\",\n \"GEODETICDATUM\",\n \"ENGCRS\",\n \"ENGINEERINGCRS\"\n];\n\nfunction isWKT(str) {\n // clean any blank spaces in beginning\n str = str.trim();\n\n if (str.startsWith(\"PROJCS[\") || str.startsWith(\"GEOGCS[\")) {\n for (let i = 0; i < keywords.length; i++) {\n const kw = keywords[i];\n if (str.indexOf(kw) !== -1) {\n return true;\n }\n }\n } else {\n return false;\n }\n}\n\nif (typeof define === \"function\" && define.amd) {\n define(function () {\n return isWKT;\n });\n}\n\nif (typeof module === \"object\") {\n module.exports = isWKT;\n module.exports.default = isWKT;\n}\nif (typeof self === \"object\") self.isWKT = isWKT;\nif (typeof window === \"object\") window.isWKT = isWKT;\n","function getCodeFromEsriWKT(esri_wkt) {\n if (!esri_wkt) return;\n const match = /PROJCS\\[\\\"([^\"]+)\\\"/.exec(esri_wkt);\n if (!match) return;\n const name = match[1];\n if (name.match(/^WGS_1984_UTM_Zone_\\d{1,2}(N|S)$/)) {\n const last_part = name.split(\"_\").pop();\n const zone = last_part.substring(0, last_part.length - 1);\n const hemisphere = last_part.substring(last_part.length - 1) == \"N\" ? 6 : 7;\n return Number.parseInt(\"32\" + hemisphere + zone);\n } else if (name.match(/^NAD_1983_UTM_Zone_\\d{1,2}N$/)) {\n const last_part = name.split(\"_\").pop();\n const zone = last_part.substring(0, last_part.length - 1);\n return Number.parseInt(\"269\" + zone);\n }\n}\n\nif (typeof define === \"function\" && define.amd) {\n return function () {\n return getCodeFromEsriWKT;\n };\n}\n\nif (typeof module === \"object\") {\n module.exports = getCodeFromEsriWKT;\n module.exports.default = getCodeFromEsriWKT;\n}\n","function getCodeFromProjString(proj) {\n if (proj.startsWith(\"+proj=utm\") && proj.includes(\"+zone=\")) {\n // replace any accidental double spaces\n const parts = proj.split(\" \");\n const zone = parts.find(part => part.startsWith(\"+zone=\")).split(\"=\")[1];\n const south = proj.includes(\"+south\");\n\n let ellps = parts.find(part => part.startsWith(\"+ellps=\"));\n if (ellps) ellps = ellps.split(\"=\")[1];\n\n if (ellps === \"GRS80\" && south === false) {\n return Number.parseInt(\"269\" + zone);\n } else {\n const hemisphere = south ? \"7\" : \"6\";\n return Number.parseInt(\"32\" + hemisphere + zone);\n }\n }\n}\n\nif (typeof define === \"function\" && define.amd) {\n return function () {\n return getCodeFromProjString;\n };\n}\n\nif (typeof module === \"object\") {\n module.exports = getCodeFromProjString;\n module.exports.default = getCodeFromProjString;\n}\n","var D2R = 0.01745329251994329577;\nimport parser from './parser';\nimport {sExpr} from './process';\n\n\n\nfunction rename(obj, params) {\n var outName = params[0];\n var inName = params[1];\n if (!(outName in obj) && (inName in obj)) {\n obj[outName] = obj[inName];\n if (params.length === 3) {\n obj[outName] = params[2](obj[outName]);\n }\n }\n}\n\nfunction d2r(input) {\n return input * D2R;\n}\n\nfunction cleanWKT(wkt) {\n if (wkt.type === 'GEOGCS') {\n wkt.projName = 'longlat';\n } else if (wkt.type === 'LOCAL_CS') {\n wkt.projName = 'identity';\n wkt.local = true;\n } else {\n if (typeof wkt.PROJECTION === 'object') {\n wkt.projName = Object.keys(wkt.PROJECTION)[0];\n } else {\n wkt.projName = wkt.PROJECTION;\n }\n }\n if (wkt.AXIS) {\n var axisOrder = '';\n for (var i = 0, ii = wkt.AXIS.length; i < ii; ++i) {\n var axis = [wkt.AXIS[i][0].toLowerCase(), wkt.AXIS[i][1].toLowerCase()];\n if (axis[0].indexOf('north') !== -1 || ((axis[0] === 'y' || axis[0] === 'lat') && axis[1] === 'north')) {\n axisOrder += 'n';\n } else if (axis[0].indexOf('south') !== -1 || ((axis[0] === 'y' || axis[0] === 'lat') && axis[1] === 'south')) {\n axisOrder += 's';\n } else if (axis[0].indexOf('east') !== -1 || ((axis[0] === 'x' || axis[0] === 'lon') && axis[1] === 'east')) {\n axisOrder += 'e';\n } else if (axis[0].indexOf('west') !== -1 || ((axis[0] === 'x' || axis[0] === 'lon') && axis[1] === 'west')) {\n axisOrder += 'w';\n }\n }\n if (axisOrder.length === 2) {\n axisOrder += 'u';\n }\n if (axisOrder.length === 3) {\n wkt.axis = axisOrder;\n }\n }\n if (wkt.UNIT) {\n wkt.units = wkt.UNIT.name.toLowerCase();\n if (wkt.units === 'metre') {\n wkt.units = 'meter';\n }\n if (wkt.UNIT.convert) {\n if (wkt.type === 'GEOGCS') {\n if (wkt.DATUM && wkt.DATUM.SPHEROID) {\n wkt.to_meter = wkt.UNIT.convert*wkt.DATUM.SPHEROID.a;\n }\n } else {\n wkt.to_meter = wkt.UNIT.convert;\n }\n }\n }\n var geogcs = wkt.GEOGCS;\n if (wkt.type === 'GEOGCS') {\n geogcs = wkt;\n }\n if (geogcs) {\n //if(wkt.GEOGCS.PRIMEM&&wkt.GEOGCS.PRIMEM.convert){\n // wkt.from_greenwich=wkt.GEOGCS.PRIMEM.convert*D2R;\n //}\n if (geogcs.DATUM) {\n wkt.datumCode = geogcs.DATUM.name.toLowerCase();\n } else {\n wkt.datumCode = geogcs.name.toLowerCase();\n }\n if (wkt.datumCode.slice(0, 2) === 'd_') {\n wkt.datumCode = wkt.datumCode.slice(2);\n }\n if (wkt.datumCode === 'new_zealand_geodetic_datum_1949' || wkt.datumCode === 'new_zealand_1949') {\n wkt.datumCode = 'nzgd49';\n }\n if (wkt.datumCode === 'wgs_1984' || wkt.datumCode === 'world_geodetic_system_1984') {\n if (wkt.PROJECTION === 'Mercator_Auxiliary_Sphere') {\n wkt.sphere = true;\n }\n wkt.datumCode = 'wgs84';\n }\n if (wkt.datumCode.slice(-6) === '_ferro') {\n wkt.datumCode = wkt.datumCode.slice(0, - 6);\n }\n if (wkt.datumCode.slice(-8) === '_jakarta') {\n wkt.datumCode = wkt.datumCode.slice(0, - 8);\n }\n if (~wkt.datumCode.indexOf('belge')) {\n wkt.datumCode = 'rnb72';\n }\n if (geogcs.DATUM && geogcs.DATUM.SPHEROID) {\n wkt.ellps = geogcs.DATUM.SPHEROID.name.replace('_19', '').replace(/[Cc]larke\\_18/, 'clrk');\n if (wkt.ellps.toLowerCase().slice(0, 13) === 'international') {\n wkt.ellps = 'intl';\n }\n\n wkt.a = geogcs.DATUM.SPHEROID.a;\n wkt.rf = parseFloat(geogcs.DATUM.SPHEROID.rf, 10);\n }\n\n if (geogcs.DATUM && geogcs.DATUM.TOWGS84) {\n wkt.datum_params = geogcs.DATUM.TOWGS84;\n }\n if (~wkt.datumCode.indexOf('osgb_1936')) {\n wkt.datumCode = 'osgb36';\n }\n if (~wkt.datumCode.indexOf('osni_1952')) {\n wkt.datumCode = 'osni52';\n }\n if (~wkt.datumCode.indexOf('tm65')\n || ~wkt.datumCode.indexOf('geodetic_datum_of_1965')) {\n wkt.datumCode = 'ire65';\n }\n if (wkt.datumCode === 'ch1903+') {\n wkt.datumCode = 'ch1903';\n }\n if (~wkt.datumCode.indexOf('israel')) {\n wkt.datumCode = 'isr93';\n }\n }\n if (wkt.b && !isFinite(wkt.b)) {\n wkt.b = wkt.a;\n }\n\n function toMeter(input) {\n var ratio = wkt.to_meter || 1;\n return input * ratio;\n }\n var renamer = function(a) {\n return rename(wkt, a);\n };\n var list = [\n ['standard_parallel_1', 'Standard_Parallel_1'],\n ['standard_parallel_1', 'Latitude of 1st standard parallel'],\n ['standard_parallel_2', 'Standard_Parallel_2'],\n ['standard_parallel_2', 'Latitude of 2nd standard parallel'],\n ['false_easting', 'False_Easting'],\n ['false_easting', 'False easting'],\n ['false-easting', 'Easting at false origin'],\n ['false_northing', 'False_Northing'],\n ['false_northing', 'False northing'],\n ['false_northing', 'Northing at false origin'],\n ['central_meridian', 'Central_Meridian'],\n ['central_meridian', 'Longitude of natural origin'],\n ['central_meridian', 'Longitude of false origin'],\n ['latitude_of_origin', 'Latitude_Of_Origin'],\n ['latitude_of_origin', 'Central_Parallel'],\n ['latitude_of_origin', 'Latitude of natural origin'],\n ['latitude_of_origin', 'Latitude of false origin'],\n ['scale_factor', 'Scale_Factor'],\n ['k0', 'scale_factor'],\n ['latitude_of_center', 'Latitude_Of_Center'],\n ['latitude_of_center', 'Latitude_of_center'],\n ['lat0', 'latitude_of_center', d2r],\n ['longitude_of_center', 'Longitude_Of_Center'],\n ['longitude_of_center', 'Longitude_of_center'],\n ['longc', 'longitude_of_center', d2r],\n ['x0', 'false_easting', toMeter],\n ['y0', 'false_northing', toMeter],\n ['long0', 'central_meridian', d2r],\n ['lat0', 'latitude_of_origin', d2r],\n ['lat0', 'standard_parallel_1', d2r],\n ['lat1', 'standard_parallel_1', d2r],\n ['lat2', 'standard_parallel_2', d2r],\n ['azimuth', 'Azimuth'],\n ['alpha', 'azimuth', d2r],\n ['srsCode', 'name']\n ];\n list.forEach(renamer);\n if (!wkt.long0 && wkt.longc && (wkt.projName === 'Albers_Conic_Equal_Area' || wkt.projName === 'Lambert_Azimuthal_Equal_Area')) {\n wkt.long0 = wkt.longc;\n }\n if (!wkt.lat_ts && wkt.lat1 && (wkt.projName === 'Stereographic_South_Pole' || wkt.projName === 'Polar Stereographic (variant B)')) {\n wkt.lat0 = d2r(wkt.lat1 > 0 ? 90 : -90);\n wkt.lat_ts = wkt.lat1;\n } else if (!wkt.lat_ts && wkt.lat0 && wkt.projName === 'Polar_Stereographic') {\n wkt.lat_ts = wkt.lat0;\n wkt.lat0 = d2r(wkt.lat0 > 0 ? 90 : -90);\n }\n}\nexport default function(wkt) {\n var lisp = parser(wkt);\n var type = lisp.shift();\n var name = lisp.shift();\n lisp.unshift(['name', name]);\n lisp.unshift(['type', type]);\n var obj = {};\n sExpr(lisp, obj);\n cleanWKT(obj);\n return obj;\n}\n","export default parseString;\n\nvar NEUTRAL = 1;\nvar KEYWORD = 2;\nvar NUMBER = 3;\nvar QUOTED = 4;\nvar AFTERQUOTE = 5;\nvar ENDED = -1;\nvar whitespace = /\\s/;\nvar latin = /[A-Za-z]/;\nvar keyword = /[A-Za-z84_]/;\nvar endThings = /[,\\]]/;\nvar digets = /[\\d\\.E\\-\\+]/;\n// const ignoredChar = /[\\s_\\-\\/\\(\\)]/g;\nfunction Parser(text) {\n if (typeof text !== 'string') {\n throw new Error('not a string');\n }\n this.text = text.trim();\n this.level = 0;\n this.place = 0;\n this.root = null;\n this.stack = [];\n this.currentObject = null;\n this.state = NEUTRAL;\n}\nParser.prototype.readCharicter = function() {\n var char = this.text[this.place++];\n if (this.state !== QUOTED) {\n while (whitespace.test(char)) {\n if (this.place >= this.text.length) {\n return;\n }\n char = this.text[this.place++];\n }\n }\n switch (this.state) {\n case NEUTRAL:\n return this.neutral(char);\n case KEYWORD:\n return this.keyword(char)\n case QUOTED:\n return this.quoted(char);\n case AFTERQUOTE:\n return this.afterquote(char);\n case NUMBER:\n return this.number(char);\n case ENDED:\n return;\n }\n};\nParser.prototype.afterquote = function(char) {\n if (char === '\"') {\n this.word += '\"';\n this.state = QUOTED;\n return;\n }\n if (endThings.test(char)) {\n this.word = this.word.trim();\n this.afterItem(char);\n return;\n }\n throw new Error('havn\\'t handled \"' +char + '\" in afterquote yet, index ' + this.place);\n};\nParser.prototype.afterItem = function(char) {\n if (char === ',') {\n if (this.word !== null) {\n this.currentObject.push(this.word);\n }\n this.word = null;\n this.state = NEUTRAL;\n return;\n }\n if (char === ']') {\n this.level--;\n if (this.word !== null) {\n this.currentObject.push(this.word);\n this.word = null;\n }\n this.state = NEUTRAL;\n this.currentObject = this.stack.pop();\n if (!this.currentObject) {\n this.state = ENDED;\n }\n\n return;\n }\n};\nParser.prototype.number = function(char) {\n if (digets.test(char)) {\n this.word += char;\n return;\n }\n if (endThings.test(char)) {\n this.word = parseFloat(this.word);\n this.afterItem(char);\n return;\n }\n throw new Error('havn\\'t handled \"' +char + '\" in number yet, index ' + this.place);\n};\nParser.prototype.quoted = function(char) {\n if (char === '\"') {\n this.state = AFTERQUOTE;\n return;\n }\n this.word += char;\n return;\n};\nParser.prototype.keyword = function(char) {\n if (keyword.test(char)) {\n this.word += char;\n return;\n }\n if (char === '[') {\n var newObjects = [];\n newObjects.push(this.word);\n this.level++;\n if (this.root === null) {\n this.root = newObjects;\n } else {\n this.currentObject.push(newObjects);\n }\n this.stack.push(this.currentObject);\n this.currentObject = newObjects;\n this.state = NEUTRAL;\n return;\n }\n if (endThings.test(char)) {\n this.afterItem(char);\n return;\n }\n throw new Error('havn\\'t handled \"' +char + '\" in keyword yet, index ' + this.place);\n};\nParser.prototype.neutral = function(char) {\n if (latin.test(char)) {\n this.word = char;\n this.state = KEYWORD;\n return;\n }\n if (char === '\"') {\n this.word = '';\n this.state = QUOTED;\n return;\n }\n if (digets.test(char)) {\n this.word = char;\n this.state = NUMBER;\n return;\n }\n if (endThings.test(char)) {\n this.afterItem(char);\n return;\n }\n throw new Error('havn\\'t handled \"' +char + '\" in neutral yet, index ' + this.place);\n};\nParser.prototype.output = function() {\n while (this.place < this.text.length) {\n this.readCharicter();\n }\n if (this.state === ENDED) {\n return this.root;\n }\n throw new Error('unable to parse string \"' +this.text + '\". State is ' + this.state);\n};\n\nfunction parseString(txt) {\n var parser = new Parser(txt);\n return parser.output();\n}\n","\n\nfunction mapit(obj, key, value) {\n if (Array.isArray(key)) {\n value.unshift(key);\n key = null;\n }\n var thing = key ? {} : obj;\n\n var out = value.reduce(function(newObj, item) {\n sExpr(item, newObj);\n return newObj\n }, thing);\n if (key) {\n obj[key] = out;\n }\n}\n\nexport function sExpr(v, obj) {\n if (!Array.isArray(v)) {\n obj[v] = true;\n return;\n }\n var key = v.shift();\n if (key === 'PARAMETER') {\n key = v.shift();\n }\n if (v.length === 1) {\n if (Array.isArray(v[0])) {\n obj[key] = {};\n sExpr(v[0], obj[key]);\n return;\n }\n obj[key] = v[0];\n return;\n }\n if (!v.length) {\n obj[key] = true;\n return;\n }\n if (key === 'TOWGS84') {\n obj[key] = v;\n return;\n }\n if (key === 'AXIS') {\n if (!(key in obj)) {\n obj[key] = [];\n }\n obj[key].push(v);\n return;\n }\n if (!Array.isArray(key)) {\n obj[key] = {};\n }\n\n var i;\n switch (key) {\n case 'UNIT':\n case 'PRIMEM':\n case 'VERT_DATUM':\n obj[key] = {\n name: v[0].toLowerCase(),\n convert: v[1]\n };\n if (v.length === 3) {\n sExpr(v[2], obj[key]);\n }\n return;\n case 'SPHEROID':\n case 'ELLIPSOID':\n obj[key] = {\n name: v[0],\n a: v[1],\n rf: v[2]\n };\n if (v.length === 4) {\n sExpr(v[3], obj[key]);\n }\n return;\n case 'PROJECTEDCRS':\n case 'PROJCRS':\n case 'GEOGCS':\n case 'GEOCCS':\n case 'PROJCS':\n case 'LOCAL_CS':\n case 'GEODCRS':\n case 'GEODETICCRS':\n case 'GEODETICDATUM':\n case 'EDATUM':\n case 'ENGINEERINGDATUM':\n case 'VERT_CS':\n case 'VERTCRS':\n case 'VERTICALCRS':\n case 'COMPD_CS':\n case 'COMPOUNDCRS':\n case 'ENGINEERINGCRS':\n case 'ENGCRS':\n case 'FITTED_CS':\n case 'LOCAL_DATUM':\n case 'DATUM':\n v[0] = ['name', v[0]];\n mapit(obj, key, v);\n return;\n default:\n i = -1;\n while (++i < v.length) {\n if (!Array.isArray(v[i])) {\n return sExpr(v, obj[key]);\n }\n }\n return mapit(obj, key, v);\n }\n}\n","function countSubstring(string, substring) {\n const pattern = new RegExp(substring, \"g\");\n const match = string.match(pattern);\n return match ? match.length : 0;\n}\n\nmodule.exports = countSubstring;\nmodule.exports.default = countSubstring;\n","const indexOfMatch = require(\"./index-of-match.js\");\nconst indexOfMatchEnd = require(\"./index-of-match-end.js\");\nconst countSubstring = require(\"./count-substring.js\");\n\nfunction findTagByName(xml, tagName, options) {\n const debug = (options && options.debug) || false;\n const nested = !(options && typeof options.nested === false);\n\n const startIndex = (options && options.startIndex) || 0;\n\n if (debug) console.log(\"[xml-utils] starting findTagByName with\", tagName, \" and \", options);\n\n const start = indexOfMatch(xml, `\\<${tagName}[ \\n\\>\\/]`, startIndex);\n if (debug) console.log(\"[xml-utils] start:\", start);\n if (start === -1) return undefined;\n\n const afterStart = xml.slice(start + tagName.length);\n\n let relativeEnd = indexOfMatchEnd(afterStart, \"^[^<]*[ /]>\", 0);\n\n const selfClosing = relativeEnd !== -1 && afterStart[relativeEnd - 1] === \"/\";\n if (debug) console.log(\"[xml-utils] selfClosing:\", selfClosing);\n\n if (selfClosing === false) {\n // check if tag has subtags with the same name\n if (nested) {\n let startIndex = 0;\n let openings = 1;\n let closings = 0;\n while ((relativeEnd = indexOfMatchEnd(afterStart, \"[ /]\" + tagName + \">\", startIndex)) !== -1) {\n const clip = afterStart.substring(startIndex, relativeEnd + 1);\n openings += countSubstring(clip, \"<\" + tagName + \"[ \\n\\t>]\");\n closings += countSubstring(clip, \"\");\n // we can't have more openings than closings\n if (closings >= openings) break;\n startIndex = relativeEnd;\n }\n } else {\n relativeEnd = indexOfMatchEnd(afterStart, \"[ /]\" + tagName + \">\", 0);\n }\n }\n\n const end = start + tagName.length + relativeEnd + 1;\n if (debug) console.log(\"[xml-utils] end:\", end);\n if (end === -1) return undefined;\n\n const outer = xml.slice(start, end);\n // tag is like urn:ogc:def:crs:EPSG::32617\n\n let inner;\n if (selfClosing) {\n inner = null;\n } else {\n inner = outer.slice(outer.indexOf(\">\") + 1, outer.lastIndexOf(\"<\"));\n }\n\n return { inner, outer, start, end };\n}\n\nmodule.exports = findTagByName;\nmodule.exports.default = findTagByName;\n","const findTagsByPath = require(\"./find-tags-by-path.js\");\n\nfunction findTagByPath(xml, path, options) {\n const debug = (options && options.debug) || false;\n const found = findTagsByPath(xml, path, { debug, returnOnFirst: true });\n if (Array.isArray(found) && found.length === 1) return found[0];\n else return undefined;\n}\nmodule.exports = findTagByPath;\nmodule.exports.default = findTagByPath;\n","const findTagByName = require(\"./find-tag-by-name.js\");\n\nfunction findTagsByName(xml, tagName, options) {\n const tags = [];\n const debug = (options && options.debug) || false;\n const nested = options && typeof options.nested === \"boolean\" ? options.nested : true;\n let startIndex = (options && options.startIndex) || 0;\n let tag;\n while ((tag = findTagByName(xml, tagName, { debug, startIndex }))) {\n if (nested) {\n startIndex = tag.start + 1 + tagName.length;\n } else {\n startIndex = tag.end;\n }\n tags.push(tag);\n }\n if (debug) console.log(\"findTagsByName found\", tags.length, \"tags\");\n return tags;\n}\n\nmodule.exports = findTagsByName;\nmodule.exports.default = findTagsByName;\n","const findTagsByName = require(\"./find-tags-by-name.js\");\n\nfunction findTagsByPath(xml, path, options) {\n const debug = (options && options.debug) || false;\n const returnOnFirst = (options && options.returnOnFirst) || false;\n let tags = findTagsByName(xml, path.shift(), { debug, nested: false });\n if (debug) console.log(\"first tags are:\", tags);\n for (let pathIndex = 0; pathIndex < path.length; pathIndex++) {\n const tagName = path[pathIndex];\n if (debug) console.log(\"tagName:\", tagName);\n let allSubTags = [];\n for (let tagIndex = 0; tagIndex < tags.length; tagIndex++) {\n const tag = tags[tagIndex];\n const subTags = findTagsByName(tag.outer, tagName, {\n debug,\n startIndex: 1\n });\n if (debug) console.log(\"subTags.length:\", subTags.length);\n if (subTags.length > 0) {\n subTags.forEach(subTag => {\n (subTag.start += tag.start), (subTag.end += tag.start);\n });\n if (returnOnFirst && pathIndex === path.length - 1) return [subTags[0]];\n allSubTags = allSubTags.concat(subTags);\n }\n }\n tags = allSubTags;\n }\n return tags;\n}\n\nmodule.exports = findTagsByPath;\nmodule.exports.default = findTagsByPath;\n","function getAttribute(tag, attributeName, options) {\n const debug = (options && options.debug) || false;\n if (debug) console.log(\"[xml-utils] getting \" + attributeName + \" in \" + tag);\n\n const xml = typeof tag === \"object\" ? tag.outer : tag;\n\n // only search for attributes in the opening tag\n const opening = xml.slice(0, xml.indexOf(\">\") + 1);\n\n const quotechars = ['\"', \"'\"];\n for (let i = 0; i < quotechars.length; i++) {\n const char = quotechars[i];\n const pattern = attributeName + \"\\\\=\" + char + \"([^\" + char + \"]*)\" + char;\n if (debug) console.log(\"[xml-utils] pattern:\", pattern);\n\n const re = new RegExp(pattern);\n const match = re.exec(opening);\n if (debug) console.log(\"[xml-utils] match:\", match);\n if (match) return match[1];\n }\n}\n\nmodule.exports = getAttribute;\nmodule.exports.default = getAttribute;\n","function indexOfMatchEnd(xml, pattern, startIndex) {\n const re = new RegExp(pattern);\n const match = re.exec(xml.slice(startIndex));\n if (match) return startIndex + match.index + match[0].length - 1;\n else return -1;\n}\n\nmodule.exports = indexOfMatchEnd;\nmodule.exports.default = indexOfMatchEnd;\n","function indexOfMatch(xml, pattern, startIndex) {\n const re = new RegExp(pattern);\n const match = re.exec(xml.slice(startIndex));\n if (match) return startIndex + match.index;\n else return -1;\n}\n\nmodule.exports = indexOfMatch;\nmodule.exports.default = indexOfMatch;\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\tloaded: false,\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Flag the module as loaded\n\tmodule.loaded = true;\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nmd = (module) => {\n\tmodule.paths = [];\n\tif (!module.children) module.children = [];\n\treturn module;\n};","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(\"./lib/lookup-esriwkt-mapfile-proj4.js\");\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Paste in Spatial Reference System Information in one of the following formats: ESRI WKT, GeoServer, GML, Mapfile, Mapnik, PostGIS, Proj4, Proj4js, or WKT

8 |

Here's an example of SRS Information: https://epsg.io/3857

9 | 10 |

Input Type:

11 |

EPSG Code(s):

12 | 28 |

Found a Bug? Awesome! Please create an issue at https://github.com/danieljdufour/get-epsg-code/issues or email the project author at daniel.j.dufour@gmail.com

29 | 30 | 31 | -------------------------------------------------------------------------------- /enums.js: -------------------------------------------------------------------------------- 1 | const FORMATS = { 2 | ESRI_WKT: "esriwkt", 3 | GEOSERVER: "geoserver", 4 | MAPFILE: "mapfile", 5 | MAPNIK: "mapnik", 6 | OGC_GML: "gml", 7 | OGC_XML: "xml", 8 | OGC_WKT: "wkt", 9 | POSTGIS: "postgis", 10 | PROJ_4: "proj4", 11 | PROJ_4_JS: "js", 12 | PROJJSON: "PROJJSON", 13 | WKT_2: "wkt2" 14 | }; 15 | 16 | module.exports = { 17 | FORMATS 18 | }; 19 | -------------------------------------------------------------------------------- /hash.js: -------------------------------------------------------------------------------- 1 | function hash32(string) { 2 | // sometimes might have extra space at end from epsg.io 3 | string = string.trim(); 4 | 5 | // replace new lines with spaces 6 | string = string.replace(/\n/g, " "); 7 | 8 | // replace tabs with spaces 9 | string = string.replace(/\t/g, " "); 10 | 11 | // remove any extra spaces 12 | string = string.replace(/ +/g, " "); 13 | 14 | let hash = 0; 15 | let i; 16 | let chr; 17 | if (string.length === 0) return hash; 18 | const string_length = string.length; 19 | for (i = 0; i < string_length; i++) { 20 | chr = string.charCodeAt(i); 21 | hash = (hash << 5) - hash + chr; 22 | hash |= 0; // Convert to 32bit integer 23 | } 24 | 25 | // convert to 16-bit 26 | // hash = Math.round(hash / Math.pow(2, 16)); 27 | 28 | return hash; 29 | } 30 | 31 | module.exports = hash32; 32 | module.exports.default = hash32; 33 | -------------------------------------------------------------------------------- /normalize/esriwkt.js: -------------------------------------------------------------------------------- 1 | const normalize_wkt = require("./wkt.js"); 2 | 3 | function normalize_esriwkt(esriwkt, { debug = false } = { debug: false }) { 4 | return normalize_wkt(esriwkt, { debug }); 5 | } 6 | 7 | module.exports = normalize_esriwkt; 8 | module.exports.default = normalize_esriwkt; 9 | -------------------------------------------------------------------------------- /normalize/ogcwkt.js: -------------------------------------------------------------------------------- 1 | const normalize_wkt = require("./wkt.js"); 2 | 3 | function normalize_ogcwkt(ogcwkt, { debug = false } = { debug: false }) { 4 | return normalize_wkt(ogcwkt, { debug }); 5 | } 6 | 7 | module.exports = normalize_ogcwkt; 8 | module.exports.default = normalize_ogcwkt; 9 | -------------------------------------------------------------------------------- /normalize/proj4.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {String} str - PROJ.4 string 4 | * @returns {String} normalized PROJ.4 string 5 | */ 6 | function normalize_proj4(str, { debug = false, remove_k1 = true, remove_no_defs = true, remove_title = true, remove_init = true, remove_type = true } = {}) { 7 | str = str.trim(); 8 | 9 | // replace any accidental extra spaces 10 | str = str.replace(/ +/g, " "); 11 | 12 | // sort params 13 | let parts = str 14 | .split(/ ?\+/g) 15 | .filter(it => it.trim() !== "") 16 | .sort(); 17 | 18 | // seems like k=1 is interpreted as default 19 | // for example https://epsg.io/3031.proj4 dropped it 20 | if (remove_k1) parts = parts.filter(it => it !== "k=1"); 21 | if (remove_no_defs) parts = parts.filter(it => it !== "no_defs"); 22 | if (remove_type) parts = parts.filter(it => !it.startsWith("type")); 23 | if (remove_init) parts = parts.filter(it => !it.startsWith("init")); 24 | if (remove_title) parts = parts.filter(it => !it.startsWith("title")); 25 | 26 | str = parts.map(it => "+" + it).join(" "); 27 | 28 | if (debug) console.log('normalized proj4 string\nfrom "' + arguments[0] + '"\nto "' + str + '"'); 29 | 30 | return str; 31 | } 32 | 33 | module.exports = normalize_proj4; 34 | module.exports.default = normalize_proj4; 35 | -------------------------------------------------------------------------------- /normalize/rows.js: -------------------------------------------------------------------------------- 1 | const normalize_esriwkt = require("./esriwkt.js"); 2 | const normalize_ogcwkt = require("./ogcwkt.js"); 3 | const normalize_proj4 = require("./proj4.js"); 4 | 5 | // mutates rows 6 | function normalize_rows(rows) { 7 | rows.forEach(row => { 8 | if ("esriwkt" in row) row.esriwkt = normalize_esriwkt(row.esriwkt); 9 | if ("proj4" in row) row.proj4 = normalize_proj4(row.proj4); 10 | if ("wkt" in row) row.wkt = normalize_ogcwkt(row.wkt); 11 | }); 12 | } 13 | 14 | module.exports = normalize_rows; 15 | module.exports.default = normalize_rows; 16 | -------------------------------------------------------------------------------- /normalize/test.js: -------------------------------------------------------------------------------- 1 | const test = require("flug"); 2 | const esriwkt = require("./esriwkt"); 3 | const proj4 = require("./proj4"); 4 | 5 | test("proj-normalize: esriwkt", ({ eq }) => { 6 | // from https://epsg.io/32617.esriwkt 7 | const txt = `PROJCS["WGS_1984_UTM_Zone_17N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",-81.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]`; 8 | const expected = `PROJCS["wgs_84_utm_zone_17n",GEOGCS["wgs_84",DATUM["wgs_84",SPHEROID["wgs_84",6378137,298.257223563]],PRIMEM["greenwich",0],UNIT["degree",0.017453292519943]],PROJECTION["transverse_mercator"],PARAMETER["central_meridian",-81],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],PARAMETER["latitude_of_origin",0],PARAMETER["scale_factor",0.9996],UNIT["meter",1]]`; 9 | eq(esriwkt(txt), expected); 10 | }); 11 | 12 | test("proj-normalize: esriwkt 4326", ({ eq }) => { 13 | // from https://epsg.io/32617.esriwkt 14 | const txt = `GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]`; 15 | const expected = `GEOGCS["wgs_84",DATUM["wgs_84",SPHEROID["wgs_84",6378137,298.257223563]],PRIMEM["greenwich",0],UNIT["degree",0.017453292519943]]`; 16 | eq(esriwkt(txt), expected); 17 | }); 18 | 19 | test("proj-normalize: esriwkt 3031", ({ eq }) => { 20 | // from https://epsg.io/3031.esriwkt 21 | const older_version = `PROJCS["WGS_84_Antarctic_Polar_Stereographic",GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943]],PROJECTION["Stereographic_South_Pole"],PARAMETER["standard_parallel_1",-71],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["Meter",1]]`; 22 | const older_normalized = esriwkt(older_version); 23 | const newer_version = `PROJCS["WGS_1984_Antarctic_Polar_Stereographic",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Stereographic_South_Pole"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",0.0],PARAMETER["Standard_Parallel_1",-71.0],UNIT["Meter",1.0]] `; 24 | const newer_normalized = esriwkt(newer_version); 25 | 26 | const expected = `PROJCS["wgs_84_antarctic_polar_stereographic",GEOGCS["wgs_84",DATUM["wgs_84",SPHEROID["wgs_84",6378137,298.257223563]],PRIMEM["greenwich",0],UNIT["degree",0.017453292519943]],PROJECTION["stereographic_south_pole"],PARAMETER["central_meridian",0],PARAMETER["false_easting",0],PARAMETER["false_northing",0],PARAMETER["standard_parallel_1",-71],UNIT["meter",1]]`; 27 | eq(older_normalized === newer_normalized, true); 28 | eq(older_normalized, expected); 29 | eq(newer_normalized, expected); 30 | }); 31 | 32 | test("normalize proj4", ({ eq }) => { 33 | const txt = `+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +type=crs`; 34 | const expected = "+datum=WGS84 +lat_0=-90 +lat_ts=-71 +lon_0=0 +proj=stere +units=m +x_0=0 +y_0=0"; 35 | eq(proj4(txt), expected); 36 | }); 37 | -------------------------------------------------------------------------------- /normalize/wkt.js: -------------------------------------------------------------------------------- 1 | const wktcrs = require("wkt-crs"); 2 | 3 | function sort_wkt(wkt) { 4 | const { data } = wktcrs.parse(wkt, { raw: true }); 5 | wktcrs.sort(data); 6 | return wktcrs.unparse(data, { raw: true }).data; 7 | } 8 | 9 | function normalize_wkt(wkt, { debug = false } = { debug: false }) { 10 | wkt = wkt.trim(); 11 | 12 | // replace new lines with spaces 13 | wkt = wkt.replace(/\n/g, " "); 14 | 15 | // replace tabs with spaces 16 | wkt = wkt.replace(/\t/g, " "); 17 | 18 | // remove any extra spaces 19 | wkt = wkt.replace(/ +/g, " "); 20 | 21 | // replace spaces with underscores 22 | wkt = wkt.replace(/ /g, "_"); 23 | 24 | wkt = wkt.replace(`GEOGCS["GCS_`, `GEOGCS["`); 25 | 26 | wkt = wkt.replace(`DATUM["D_`, `DATUM["`); 27 | 28 | wkt = wkt.replace("Gauss_Kruger", "GK"); 29 | 30 | // reduce precision of decimal numbers to 15 digits 31 | wkt = wkt.replace(/\.\d{16,}/g, n => n.substr(0, 16)); 32 | 33 | // replace \d.0 with 0 34 | wkt = wkt.replace(/\d+\.0[,"'\]]/g, s => s.substring(0, s.indexOf(".")) + s.charAt(s.length - 1)); 35 | 36 | // replace 1984 with 84 37 | // ex: replace WGS_1984_Antarctic_Polar_Stereographic with WGS_84_Antarctic_Polar_Stereographic 38 | // which is what happened with https://epsg.io/3031.esriwkt 39 | wkt = wkt.replace(/[_|"]1984[_|"]/g, s => s.charAt(0) + "84" + s.charAt(s.length - 1)); 40 | 41 | // happened in epsg.io/3031.esriwkt 42 | // wkt = wkt.replace(/false_easting/g, "False_Easting"); 43 | // wkt = wkt.replace(/false_northing/g, "False_Northing"); 44 | 45 | // lower case value strings 46 | wkt = wkt.replace(/"[A-Za-z_\d]+"/g, s => '"' + s.substring(1, s.length - 1).toLowerCase() + '"'); 47 | 48 | // sort parameters 49 | wkt = sort_wkt(wkt); 50 | 51 | if (debug) console.log('[get-epsg-code] wkt after cleaning: "' + wkt + '"'); 52 | return wkt; 53 | } 54 | 55 | module.exports = normalize_wkt; 56 | module.exports.default = normalize_wkt; 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get-epsg-code", 3 | "version": "1.2.0", 4 | "description": "Gets EPSG Code for Various Inputs (incl. OGC WKT, ESRI WKT, GML, Mapfile, PROJ String, Proj4js String, XML)", 5 | "files": [ 6 | "dist", 7 | "lib" 8 | ], 9 | "main": "./dist/get-epsg-code.node.min.js", 10 | "browser": { 11 | "dist/get-epsg-code.web.min.js": "dist/get-epsg-code.web.min.js" 12 | }, 13 | "unpkg": "./dist/get-epsg-code.web.min.js", 14 | "jsdelivr": "./dist/get-epsg-code.web.min.js", 15 | "scripts": { 16 | "analyze": "node analyze.js", 17 | "build": "npm run prebuild && npm run build:dev && npm run build:prod && npm run analyze", 18 | "build:prod": "npm run build:prod:node && npm run build:prod:web && npm run build:prod:node:esriwkt-proj4 && npm run build:prod:web:esriwkt-proj4", 19 | "build:prod:node": "esbuild ./lib/lookup-esriwkt-mapfile-proj4.js --bundle --keep-names --minify --outfile=./dist/get-epsg-code.node.min.js --platform=node --sourcemap", 20 | "build:prod:web": "esbuild ./lib/lookup-esriwkt-mapfile-proj4.js --bundle --keep-names --minify --outfile=./dist/get-epsg-code.web.min.js --sourcemap", 21 | "build:prod:node:esriwkt-proj4": "esbuild ./lib/lookup-esriwkt-proj4.js --bundle --keep-names --minify --outfile=./dist/get-epsg-code-including-esriwkt-proj4.node.min.js --platform=node --sourcemap", 22 | "build:prod:web:esriwkt-proj4": "esbuild ./lib/lookup-esriwkt-proj4.js --bundle --keep-names --minify --outfile=./dist/get-epsg-code-including-esriwkt-proj4.web.min.js --sourcemap", 23 | "build:demo": "node prebuild && npm run build:dev:web && cp ./dist/get-epsg-code.web.js* ./demo/.", 24 | "build:dev": "npm run build:dev:node && npm run build:dev:web", 25 | "build:dev:node": "esbuild ./lib/lookup-esriwkt-mapfile-proj4.js --bundle --outfile=./dist/get-epsg-code.node.js --platform=node", 26 | "build:dev:web": "esbuild ./lib/lookup-esriwkt-mapfile-proj4.js --bundle --outfile=./dist/get-epsg-code.web.js", 27 | "prebuild": "node prebuild.js", 28 | "prepublishOnly": "npm run clean && npm run build && npm run test && npm run build", 29 | "clean": "rimraf dist/* && rimraf lib/*", 30 | "create-build-list": "bash create-build-list.sh", 31 | "demo": "npm run build:demo && cd demo && npx srvd --debug", 32 | "download-csv": "wget https://s3.amazonaws.com/crs.csv/crs.csv.zip && unzip crs.csv.zip", 33 | "format": "npx prettier --arrow-parens=avoid --print-width=200 --trailing-comma=none --write src normalize parse test *.js", 34 | "setup": "cd test/data && bash setup.sh", 35 | "test": "npm run test:normalize && npm run test:parse && npm run prebuild && npm run test:main", 36 | "test:main": "node ./test/test.js", 37 | "test:normalize": "node ./normalize/test", 38 | "test:parse": "node ./parse/test", 39 | "test:html": "npm run build:dev:web && http-server", 40 | "update-docs": "npm run test && npm run build:everything && npm run create-build-list" 41 | }, 42 | "repository": { 43 | "type": "git", 44 | "url": "git+https://github.com/danieljdufour/get-epsg-code.git" 45 | }, 46 | "keywords": [ 47 | "epsg", 48 | "gis", 49 | "map", 50 | "ogc", 51 | "wkt", 52 | "esri", 53 | "proj", 54 | "proj4", 55 | "proj4js", 56 | "crs", 57 | "geodesy", 58 | "projcrs", 59 | "geogcrs", 60 | "geocrs" 61 | ], 62 | "author": "Daniel J. Dufour", 63 | "license": "CC0-1.0", 64 | "bugs": { 65 | "url": "https://github.com/danieljdufour/get-epsg-code/issues" 66 | }, 67 | "homepage": "https://github.com/danieljdufour/get-epsg-code#readme", 68 | "devDependencies": { 69 | "esbuild": "^0.18.11", 70 | "find-and-read": "^1.2.0", 71 | "flug": "^2.6.0", 72 | "papaparse": "^5.4.1", 73 | "rimraf": "^5.0.1" 74 | }, 75 | "dependencies": { 76 | "b64ab": "^0.0.1", 77 | "is-wkt": "^0.2.0", 78 | "utm-utils": "^0.6.1", 79 | "wkt-crs": "^0.2.0", 80 | "wkt-parser": "^1.3.3", 81 | "xml-utils": "^1.7.0" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /parse/proj4js.js: -------------------------------------------------------------------------------- 1 | function parseProj4JS(input, { debug = false } = { debug: false }) { 2 | const start = input.indexOf(":") + 1; 3 | const end = input.indexOf('"', start + 2); 4 | const str = input.substring(start, end); 5 | if (debug) console.log('parseProj4JS str: "' + str + '"'); 6 | if (str) return Number(str); 7 | } 8 | 9 | module.exports = parseProj4JS; 10 | module.exports.default = parseProj4JS; 11 | -------------------------------------------------------------------------------- /parse/test.js: -------------------------------------------------------------------------------- 1 | const test = require("flug"); 2 | const parseProj4JS = require("./proj4js.js"); 3 | 4 | test("parse multiline", ({ eq }) => { 5 | const text = 'proj4.defs(\n"EPSG:26916",\n"+proj=utm +zone=16 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"\n\n);\n'; 6 | eq(parseProj4JS(text, { debug: false }), 26916); 7 | }); 8 | 9 | test("single line", ({ eq }) => { 10 | const text = 'proj4.defs( "EPSG:26916", "+proj=utm +zone=16 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs" );'; 11 | eq(parseProj4JS(text, { debug: false }), 26916); 12 | }); 13 | -------------------------------------------------------------------------------- /prebuild.js: -------------------------------------------------------------------------------- 1 | const { copyFileSync, mkdirSync, readFileSync, writeFileSync } = require("fs"); 2 | 3 | const b64ab = require("b64ab"); 4 | const rimraf = require("rimraf"); 5 | const { parse } = require("papaparse"); 6 | const { isUTM } = require("utm-utils"); 7 | const { FORMATS } = require("./enums.js"); 8 | const hash = require("./hash.js"); 9 | const normalize_rows = require("./normalize/rows.js"); 10 | 11 | const reset = dirpath => { 12 | rimraf.rimrafSync(dirpath); 13 | mkdirSync(dirpath); 14 | }; 15 | 16 | reset("./lib"); 17 | console.log("[get-epsg-code] reset lib directory"); 18 | 19 | reset("./log"); 20 | console.log("[get-epsg-code] reset log directory"); 21 | 22 | mkdirSync("./lib/data"); 23 | mkdirSync("./lib/normalize"); 24 | mkdirSync("./lib/parse"); 25 | 26 | // copy a few files over 27 | copyFileSync("./hash.js", "./lib/hash.js"); 28 | copyFileSync("./enums.js", "./lib/enums.js"); 29 | copyFileSync("./src/get-proj-type.js", "./lib/get-proj-type.js"); 30 | copyFileSync("./normalize/esriwkt.js", "./lib/normalize/esriwkt.js"); 31 | copyFileSync("./normalize/wkt.js", "./lib/normalize/wkt.js"); 32 | copyFileSync("./normalize/proj4.js", "./lib/normalize/proj4.js"); 33 | copyFileSync("./parse/proj4js.js", "./lib/parse/proj4js.js"); 34 | 35 | const string = readFileSync("./crs.csv", "utf-8"); 36 | console.log("[get-epsg-code] read crs.csv"); 37 | const parsed = parse(string, { header: true, skipEmptyLines: true }); 38 | console.log("[get-epsg-code] parsed crs.csv"); 39 | 40 | let rows = parsed.data; 41 | 42 | // convert code to numbers 43 | rows.forEach(row => (row.code = Number(row.code))); 44 | 45 | // filter out UTM which is calculated by pulling out info from input data 46 | console.log("[get-epsg-code] number of rows before filtering out UTM:", rows.length); 47 | rows = rows.filter(({ code }) => !isUTM(code)); 48 | 49 | normalize_rows(rows); 50 | 51 | rows.sort((a, b) => a.code - b.code); 52 | 53 | writeFileSync("./log/esriwkt", rows.map(r => r.code + "\t" + r.esriwkt).join("\n"), "utf-8"); 54 | writeFileSync("./log/proj4", rows.map(r => r.code + "\t" + r.proj4).join("\n"), "utf-8"); 55 | 56 | console.log("[get-epsg-code] parsed.meta:", parsed.meta); 57 | const num_rows = rows.length; 58 | console.log("[get-epsg-code] number of rows after filtering out UTM:", num_rows); 59 | 60 | const { ESRI_WKT, MAPFILE, PROJ_4 } = FORMATS; 61 | 62 | [[ESRI_WKT, MAPFILE, PROJ_4], [ESRI_WKT, PROJ_4], [PROJ_4]].forEach(hashed_fields => { 63 | const num_fields = hashed_fields.length + 1; 64 | console.log("[get-epsg-code] num_fields:", num_fields); 65 | const num_values = num_rows * num_fields; 66 | console.log("[get-epsg-code] num_values:", num_values); 67 | 68 | const hashed_rows = rows.map(row => [row.code, ...hashed_fields.map(fieldname => hash(row[fieldname]))]); 69 | 70 | writeFileSync(`./log/${hashed_fields.join("-")}.jsonl`, hashed_rows.map(row => row.join("\t")).join("\n"), "utf-8"); 71 | 72 | const data = hashed_rows.flat(); 73 | 74 | const typedData = Int32Array.from(data); 75 | 76 | // verify we can use data type 77 | if (JSON.stringify(data) !== JSON.stringify(Array.from(typedData))) { 78 | throw Error("[get-epsg-code] invalid array data type"); 79 | } 80 | 81 | const arrayBuffer = typedData.buffer; 82 | 83 | const dataBase64 = b64ab.toBase64String(arrayBuffer); 84 | 85 | console.log("[get-epsg-code] dataBase64:", dataBase64.substring(0, 100), "..."); 86 | 87 | // check round-trip through b64ab 88 | if (JSON.stringify(data) !== JSON.stringify(Array.from(new Int32Array(b64ab.toArrayBuffer(dataBase64))))) { 89 | throw Error("[get-epsg-code] invalid b64ab round-trip"); 90 | } 91 | 92 | const result = { 93 | properties: { 94 | columns: ["epsg_code"].concat(hashed_fields), 95 | key: "epsg_code", // assuming in first position 96 | dataType: "int32" 97 | }, 98 | data: dataBase64 99 | }; 100 | 101 | const stringified = JSON.stringify(result, undefined, 2); 102 | console.log("[get-epsg-code] stringified:", stringified.replaceAll("\n", "\\n").substring(0, 500), "..."); 103 | 104 | const filename = hashed_fields.sort().join("-"); 105 | console.log("[get-epsg-code] filename:", filename); 106 | 107 | writeFileSync(`./lib/data/${filename}.json`, stringified, "utf-8"); 108 | 109 | const template = readFileSync("./src/index.js", "utf-8"); 110 | 111 | const content = template.replace("REPLACE_ME", `"./data/${filename}.json"`); 112 | 113 | const filepath = `./lib/lookup-${filename}.js`; 114 | writeFileSync(filepath, content, "utf-8"); 115 | 116 | console.log(`[get-epsg-code] created ${filepath}`); 117 | }); 118 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | wget https://s3.amazonaws.com/crs.csv/crs.csv.zip 2 | unzip crs.csv.zip 3 | rm crs.csv.zip -------------------------------------------------------------------------------- /src/get-proj-type.js: -------------------------------------------------------------------------------- 1 | const isWKT = require("is-wkt"); 2 | const wktcrs = require("wkt-crs"); 3 | 4 | const { FORMATS } = require("./enums.js"); 5 | 6 | const { ESRI_WKT, GEOSERVER, MAPFILE, MAPNIK, OGC_GML, OGC_XML, OGC_WKT, POSTGIS, PROJ_4, PROJ_4_JS, PROJJSON, WKT_2 } = FORMATS; 7 | 8 | function getProjType(input, { debug = false } = { debug: false }) { 9 | // check WKT 10 | if (input.startsWith("{") && input.includes("projjson")) { 11 | return PROJJSON; 12 | } else if (isWKT(input)) { 13 | const parsed = wktcrs.parse(input.toUpperCase()).data; 14 | if ("AUTHORITY" in (parsed.PROJCS || parsed.PROJCRS || parsed.GEOGCS || parsed.GEOGCRS)) { 15 | return OGC_WKT; 16 | } else if ("ID" in parsed) { 17 | return WKT_2; 18 | } else { 19 | // appears to be ESRI WKT 20 | return ESRI_WKT; 21 | } 22 | } else if (input.includes("gml:ProjectedCRS") || input.includes("gml:GeodeticCRS") || input.includes("gml:GeographicCRS")) { 23 | if (input.includes("gml:srsID")) { 24 | return OGC_XML; 25 | } else { 26 | return OGC_GML; 27 | } 28 | } else if (input.startsWith("+") && input.includes("+proj=")) { 29 | return PROJ_4; 30 | } else if (input.startsWith(`proj4.defs(`)) { 31 | return PROJ_4_JS; 32 | } else if (/^\d{1,6}\=(PROJCS|GEOGCS)/.test(input)) { 33 | return GEOSERVER; 34 | } else if (input.startsWith("PROJECTION") && input.endsWith("END")) { 35 | return MAPFILE; 36 | } else if (input.endsWith("")) { 37 | return MAPNIK; 38 | } else if (input.startsWith("INSERT")) { 39 | return POSTGIS; 40 | } else { 41 | if (debug) console.log('get-proj-type could not identify "' + input + '"'); 42 | return "SOMETHING ELSE"; 43 | } 44 | } 45 | 46 | module.exports = getProjType; 47 | module.exports.default = getProjType; 48 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const b64ab = require("b64ab"); 2 | const findTagByPath = require("xml-utils/find-tag-by-path"); 3 | const findTagByName = require("xml-utils/find-tag-by-name"); 4 | const getAttribute = require("xml-utils/get-attribute"); 5 | const parseWKT = require("wkt-parser"); 6 | const utmUtils_getCodeFromEsriWKT = require("utm-utils/src/getCodeFromEsriWKT.js"); 7 | const utmUtils_getCodeFromProjString = require("utm-utils/src/getCodeFromProjString.js"); 8 | 9 | const hash = require("./hash.js"); 10 | const normalize_esriwkt = require("./normalize/esriwkt.js"); 11 | const normalize_proj4 = require("./normalize/proj4.js"); 12 | 13 | const getProjType = require("./get-proj-type.js"); 14 | const parseProj4JS = require("./parse/proj4js.js"); 15 | const { FORMATS } = require("./enums.js"); 16 | 17 | const data = require(REPLACE_ME); 18 | 19 | const getDefault = fn => fn.default || fn; 20 | 21 | const rows = []; 22 | 23 | function _load_rows(debug) { 24 | const decoded = b64ab.toArrayBuffer(data.data); 25 | const arr = new Int32Array(decoded); 26 | if (debug) console.log("arr:", arr); 27 | 28 | const num_fields = data.properties.columns.length; 29 | const num_rows = arr.length / num_fields; 30 | 31 | for (let r = 0; r < num_rows; r++) { 32 | const row = {}; 33 | for (let f = 0; f < num_fields; f++) { 34 | row[data.properties.columns[f]] = arr[r * num_fields + f]; 35 | } 36 | rows.push(row); 37 | } 38 | if (debug) console.log("[get-epsg-code] rows:", rows); 39 | 40 | return rows; 41 | } 42 | 43 | function lookup(input, dataType, debug = false) { 44 | if (!data.properties.columns.includes(dataType)) { 45 | throw new Error("[get-epsg-code] " + dataType + " not supported"); 46 | } 47 | 48 | if (rows.length === 0) { 49 | _load_rows(debug); 50 | } 51 | 52 | const hashed = hash(input); 53 | if (debug) console.log("[get-epsg-code] hashed:", hashed); 54 | 55 | const found = rows.filter(row => row[dataType] === hashed); 56 | if (debug) console.log("[get-epsg-code] found:", found); 57 | 58 | return found.map(row => row.epsg_code); 59 | } 60 | 61 | const { ESRI_WKT, GEOSERVER, MAPFILE, MAPNIK, OGC_GML, OGC_XML, OGC_WKT, POSTGIS, PROJJSON, PROJ_4, PROJ_4_JS, WKT_2 } = FORMATS; 62 | 63 | function getEPSGCodes(input, options) { 64 | //console.log("starting get-epsg-code with", input, options); 65 | var debug = options && options.debug ? options.debug : false; 66 | 67 | if (typeof input === "string") input = input.trim(); 68 | 69 | const dataType = getProjType(input, { debug }); 70 | if (debug) console.log("dataType:", dataType); 71 | 72 | if (dataType === OGC_WKT) { 73 | const parsed = getDefault(parseWKT)(input); 74 | if (debug) console.log("parsed:", parsed); 75 | if (parsed.AUTHORITY) { 76 | const authority = parsed.AUTHORITY; 77 | return { 78 | type: dataType, 79 | codes: [Number(authority.epsg || authority.EPSG)] 80 | }; 81 | } 82 | } else if (dataType === WKT_2) { 83 | const parsed = getDefault(parseWKT)(input); 84 | if (debug) console.log("parsed:", parsed); 85 | if (typeof parsed.ID === "object" && typeof parsed.ID.EPSG === "number") { 86 | return { 87 | type: dataType, 88 | codes: [parsed.ID.EPSG] 89 | }; 90 | } 91 | } else if (dataType == ESRI_WKT) { 92 | // try utm parsing 93 | const code = utmUtils_getCodeFromEsriWKT(input); 94 | if (code) return { type: dataType, codes: [code] }; 95 | 96 | input = normalize_esriwkt(input, { debug }); 97 | return { type: dataType, codes: lookup(input, ESRI_WKT, debug) }; 98 | } else if (dataType === OGC_GML) { 99 | const identifier = getDefault(findTagByName)(input, "gml:identifier", { 100 | debug 101 | }).inner; 102 | return { 103 | type: dataType, 104 | codes: [Number(identifier.replace("urn:ogc:def:crs:EPSG::", ""))] 105 | }; 106 | } else if (dataType === OGC_XML) { 107 | return { 108 | type: dataType, 109 | codes: [Number(getDefault(findTagByPath)(input, ["gml:srsID", "gml:name"], { debug }).inner)] 110 | }; 111 | } else if (dataType === PROJ_4) { 112 | input = normalize_proj4(input, { debug }); 113 | 114 | if (input.startsWith("+") && input.includes("+proj=utm")) { 115 | return { type: dataType, codes: [utmUtils_getCodeFromProjString(input)] }; 116 | } else { 117 | return { type: dataType, codes: lookup(input, PROJ_4, debug) }; 118 | } 119 | } else if (dataType === PROJ_4_JS) { 120 | const code = parseProj4JS(input); 121 | return { 122 | type: dataType, 123 | codes: code ? [code] : [] 124 | }; 125 | } else if (dataType === GEOSERVER) { 126 | return { type: dataType, codes: [Number(input.match(/^\d{1,6}/)[0])] }; 127 | } else if (dataType === MAPFILE) { 128 | if (input.includes("init=epsg:")) { 129 | return { 130 | type: dataType, 131 | codes: [Number.parseInt(/("init\=epsg:)(\d{1,10})(")/.exec(input)[2])] 132 | }; 133 | } else if (input.includes('"proj=utm"')) { 134 | const zone = /("zone\=)(\d{1,2})(")/.exec(input)[2]; 135 | const south = input.includes('"south"'); 136 | if (input.includes("ellps=GRS80") && south === false) { 137 | return { type: dataType, codes: [Number.parseInt("269" + zone)] }; 138 | } else { 139 | const hemisphere = south ? "7" : "6"; 140 | return { 141 | type: dataType, 142 | codes: [Number.parseInt("32" + hemisphere + zone)] 143 | }; 144 | } 145 | } else { 146 | return { type: dataType, codes: lookup(input, MAPFILE, debug) }; 147 | } 148 | } else if (dataType === MAPNIK) { 149 | const map = findTagByName(input, "Map"); 150 | const srs = getAttribute(map.outer, "srs"); // Proj.4 String 151 | return { type: dataType, codes: getEPSGCodes(srs).codes }; 152 | } else if (dataType === POSTGIS) { 153 | return { 154 | type: dataType, 155 | codes: [Number(input.substring(input.indexOf("values (") + 8, input.indexOf("EPSG") - 3).trim())] 156 | }; 157 | } else if (dataType === PROJJSON) { 158 | const data = JSON.parse(input); 159 | if (typeof data.id === "object") { 160 | if (data.id.authority === "EPSG" || !("authority" in data.id)) { 161 | if (typeof data.id.code === "number") { 162 | return { 163 | type: dataType, 164 | codes: [data.id.code] 165 | }; 166 | } 167 | } 168 | } 169 | } 170 | } 171 | 172 | function getEPSGCode(input, options) { 173 | const result = getEPSGCodes(input, options); 174 | if (result) return result.codes[0]; 175 | } 176 | 177 | if (typeof module !== "undefined" && typeof module.exports !== "undefined") { 178 | module.exports = getEPSGCode; 179 | module.exports.getEPSGCode = getEPSGCode; 180 | module.exports.getEPSGCodes = getEPSGCodes; 181 | module.exports._load_rows = _load_rows; 182 | } 183 | if (typeof window !== "undefined") { 184 | window["getEPSGCode"] = getEPSGCode; 185 | window["getEPSGCodes"] = getEPSGCodes; 186 | } else if (typeof self !== "undefined") { 187 | self["getEPSGCode"] = getEPSGCode; // jshint ignore:line 188 | self["getEPSGCodes"] = getEPSGCodes; // jshint ignore:line 189 | } 190 | -------------------------------------------------------------------------------- /test/data/3031.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://proj.org/schemas/v0.5/projjson.schema.json", 3 | "type": "ProjectedCRS", 4 | "name": "WGS 84 / Antarctic Polar Stereographic", 5 | "base_crs": { 6 | "name": "WGS 84", 7 | "datum_ensemble": { 8 | "name": "World Geodetic System 1984 ensemble", 9 | "members": [ 10 | { 11 | "name": "World Geodetic System 1984 (Transit)", 12 | "id": { 13 | "authority": "EPSG", 14 | "code": 1166 15 | } 16 | }, 17 | { 18 | "name": "World Geodetic System 1984 (G730)", 19 | "id": { 20 | "authority": "EPSG", 21 | "code": 1152 22 | } 23 | }, 24 | { 25 | "name": "World Geodetic System 1984 (G873)", 26 | "id": { 27 | "authority": "EPSG", 28 | "code": 1153 29 | } 30 | }, 31 | { 32 | "name": "World Geodetic System 1984 (G1150)", 33 | "id": { 34 | "authority": "EPSG", 35 | "code": 1154 36 | } 37 | }, 38 | { 39 | "name": "World Geodetic System 1984 (G1674)", 40 | "id": { 41 | "authority": "EPSG", 42 | "code": 1155 43 | } 44 | }, 45 | { 46 | "name": "World Geodetic System 1984 (G1762)", 47 | "id": { 48 | "authority": "EPSG", 49 | "code": 1156 50 | } 51 | }, 52 | { 53 | "name": "World Geodetic System 1984 (G2139)", 54 | "id": { 55 | "authority": "EPSG", 56 | "code": 1309 57 | } 58 | } 59 | ], 60 | "ellipsoid": { 61 | "name": "WGS 84", 62 | "semi_major_axis": 6378137, 63 | "inverse_flattening": 298.257223563 64 | }, 65 | "accuracy": "2.0", 66 | "id": { 67 | "authority": "EPSG", 68 | "code": 6326 69 | } 70 | }, 71 | "coordinate_system": { 72 | "subtype": "ellipsoidal", 73 | "axis": [ 74 | { 75 | "name": "Geodetic latitude", 76 | "abbreviation": "Lat", 77 | "direction": "north", 78 | "unit": "degree" 79 | }, 80 | { 81 | "name": "Geodetic longitude", 82 | "abbreviation": "Lon", 83 | "direction": "east", 84 | "unit": "degree" 85 | } 86 | ] 87 | }, 88 | "id": { 89 | "authority": "EPSG", 90 | "code": 4326 91 | } 92 | }, 93 | "conversion": { 94 | "name": "Antarctic Polar Stereographic", 95 | "method": { 96 | "name": "Polar Stereographic (variant B)", 97 | "id": { 98 | "authority": "EPSG", 99 | "code": 9829 100 | } 101 | }, 102 | "parameters": [ 103 | { 104 | "name": "Latitude of standard parallel", 105 | "value": -71, 106 | "unit": "degree", 107 | "id": { 108 | "authority": "EPSG", 109 | "code": 8832 110 | } 111 | }, 112 | { 113 | "name": "Longitude of origin", 114 | "value": 0, 115 | "unit": "degree", 116 | "id": { 117 | "authority": "EPSG", 118 | "code": 8833 119 | } 120 | }, 121 | { 122 | "name": "False easting", 123 | "value": 0, 124 | "unit": "metre", 125 | "id": { 126 | "authority": "EPSG", 127 | "code": 8806 128 | } 129 | }, 130 | { 131 | "name": "False northing", 132 | "value": 0, 133 | "unit": "metre", 134 | "id": { 135 | "authority": "EPSG", 136 | "code": 8807 137 | } 138 | } 139 | ] 140 | }, 141 | "coordinate_system": { 142 | "subtype": "Cartesian", 143 | "axis": [ 144 | { 145 | "name": "Easting", 146 | "abbreviation": "E", 147 | "direction": "north", 148 | "meridian": { 149 | "longitude": 90 150 | }, 151 | "unit": "metre" 152 | }, 153 | { 154 | "name": "Northing", 155 | "abbreviation": "N", 156 | "direction": "north", 157 | "meridian": { 158 | "longitude": 0 159 | }, 160 | "unit": "metre" 161 | } 162 | ] 163 | }, 164 | "scope": "Antarctic Digital Database and small scale topographic mapping.", 165 | "area": "Antarctica.", 166 | "bbox": { 167 | "south_latitude": -90, 168 | "west_longitude": -180, 169 | "north_latitude": -60, 170 | "east_longitude": 180 171 | }, 172 | "id": { 173 | "authority": "EPSG", 174 | "code": 3031 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /test/data/32617.gml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | projected 6 | 1995-06-02 7 | true 8 | false 9 | 10 | 11 | urn:ogc:def:crs:EPSG::32617 12 | WGS 84 / UTM zone 17N 13 | 14 | Large and medium scale topographic mapping and engineering survey. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/data/32617.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://proj.org/schemas/v0.5/projjson.schema.json", 3 | "type": "ProjectedCRS", 4 | "name": "WGS 84 / UTM zone 17N", 5 | "base_crs": { 6 | "name": "WGS 84", 7 | "datum_ensemble": { 8 | "name": "World Geodetic System 1984 ensemble", 9 | "members": [ 10 | { 11 | "name": "World Geodetic System 1984 (Transit)", 12 | "id": { 13 | "authority": "EPSG", 14 | "code": 1166 15 | } 16 | }, 17 | { 18 | "name": "World Geodetic System 1984 (G730)", 19 | "id": { 20 | "authority": "EPSG", 21 | "code": 1152 22 | } 23 | }, 24 | { 25 | "name": "World Geodetic System 1984 (G873)", 26 | "id": { 27 | "authority": "EPSG", 28 | "code": 1153 29 | } 30 | }, 31 | { 32 | "name": "World Geodetic System 1984 (G1150)", 33 | "id": { 34 | "authority": "EPSG", 35 | "code": 1154 36 | } 37 | }, 38 | { 39 | "name": "World Geodetic System 1984 (G1674)", 40 | "id": { 41 | "authority": "EPSG", 42 | "code": 1155 43 | } 44 | }, 45 | { 46 | "name": "World Geodetic System 1984 (G1762)", 47 | "id": { 48 | "authority": "EPSG", 49 | "code": 1156 50 | } 51 | }, 52 | { 53 | "name": "World Geodetic System 1984 (G2139)", 54 | "id": { 55 | "authority": "EPSG", 56 | "code": 1309 57 | } 58 | } 59 | ], 60 | "ellipsoid": { 61 | "name": "WGS 84", 62 | "semi_major_axis": 6378137, 63 | "inverse_flattening": 298.257223563 64 | }, 65 | "accuracy": "2.0", 66 | "id": { 67 | "authority": "EPSG", 68 | "code": 6326 69 | } 70 | }, 71 | "coordinate_system": { 72 | "subtype": "ellipsoidal", 73 | "axis": [ 74 | { 75 | "name": "Geodetic latitude", 76 | "abbreviation": "Lat", 77 | "direction": "north", 78 | "unit": "degree" 79 | }, 80 | { 81 | "name": "Geodetic longitude", 82 | "abbreviation": "Lon", 83 | "direction": "east", 84 | "unit": "degree" 85 | } 86 | ] 87 | }, 88 | "id": { 89 | "authority": "EPSG", 90 | "code": 4326 91 | } 92 | }, 93 | "conversion": { 94 | "name": "UTM zone 17N", 95 | "method": { 96 | "name": "Transverse Mercator", 97 | "id": { 98 | "authority": "EPSG", 99 | "code": 9807 100 | } 101 | }, 102 | "parameters": [ 103 | { 104 | "name": "Latitude of natural origin", 105 | "value": 0, 106 | "unit": "degree", 107 | "id": { 108 | "authority": "EPSG", 109 | "code": 8801 110 | } 111 | }, 112 | { 113 | "name": "Longitude of natural origin", 114 | "value": -81, 115 | "unit": "degree", 116 | "id": { 117 | "authority": "EPSG", 118 | "code": 8802 119 | } 120 | }, 121 | { 122 | "name": "Scale factor at natural origin", 123 | "value": 0.9996, 124 | "unit": "unity", 125 | "id": { 126 | "authority": "EPSG", 127 | "code": 8805 128 | } 129 | }, 130 | { 131 | "name": "False easting", 132 | "value": 500000, 133 | "unit": "metre", 134 | "id": { 135 | "authority": "EPSG", 136 | "code": 8806 137 | } 138 | }, 139 | { 140 | "name": "False northing", 141 | "value": 0, 142 | "unit": "metre", 143 | "id": { 144 | "authority": "EPSG", 145 | "code": 8807 146 | } 147 | } 148 | ] 149 | }, 150 | "coordinate_system": { 151 | "subtype": "Cartesian", 152 | "axis": [ 153 | { 154 | "name": "Easting", 155 | "abbreviation": "E", 156 | "direction": "east", 157 | "unit": "metre" 158 | }, 159 | { 160 | "name": "Northing", 161 | "abbreviation": "N", 162 | "direction": "north", 163 | "unit": "metre" 164 | } 165 | ] 166 | }, 167 | "scope": "Engineering survey, topographic mapping.", 168 | "area": "Between 84°W and 78°W, northern hemisphere between equator and 84°N, onshore and offshore. Bahamas. Ecuador - north of equator. Canada - Nunavut; Ontario; Quebec. Cayman Islands. Colombia. Costa Rica. Cuba. Jamaica. Nicaragua. Panama. United States (USA).", 169 | "bbox": { 170 | "south_latitude": 0, 171 | "west_longitude": -84, 172 | "north_latitude": 84, 173 | "east_longitude": -78 174 | }, 175 | "id": { 176 | "authority": "EPSG", 177 | "code": 32617 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /test/data/3857.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://proj.org/schemas/v0.5/projjson.schema.json", 3 | "type": "ProjectedCRS", 4 | "name": "WGS 84 / Pseudo-Mercator", 5 | "base_crs": { 6 | "name": "WGS 84", 7 | "datum_ensemble": { 8 | "name": "World Geodetic System 1984 ensemble", 9 | "members": [ 10 | { 11 | "name": "World Geodetic System 1984 (Transit)", 12 | "id": { 13 | "authority": "EPSG", 14 | "code": 1166 15 | } 16 | }, 17 | { 18 | "name": "World Geodetic System 1984 (G730)", 19 | "id": { 20 | "authority": "EPSG", 21 | "code": 1152 22 | } 23 | }, 24 | { 25 | "name": "World Geodetic System 1984 (G873)", 26 | "id": { 27 | "authority": "EPSG", 28 | "code": 1153 29 | } 30 | }, 31 | { 32 | "name": "World Geodetic System 1984 (G1150)", 33 | "id": { 34 | "authority": "EPSG", 35 | "code": 1154 36 | } 37 | }, 38 | { 39 | "name": "World Geodetic System 1984 (G1674)", 40 | "id": { 41 | "authority": "EPSG", 42 | "code": 1155 43 | } 44 | }, 45 | { 46 | "name": "World Geodetic System 1984 (G1762)", 47 | "id": { 48 | "authority": "EPSG", 49 | "code": 1156 50 | } 51 | }, 52 | { 53 | "name": "World Geodetic System 1984 (G2139)", 54 | "id": { 55 | "authority": "EPSG", 56 | "code": 1309 57 | } 58 | } 59 | ], 60 | "ellipsoid": { 61 | "name": "WGS 84", 62 | "semi_major_axis": 6378137, 63 | "inverse_flattening": 298.257223563 64 | }, 65 | "accuracy": "2.0", 66 | "id": { 67 | "authority": "EPSG", 68 | "code": 6326 69 | } 70 | }, 71 | "coordinate_system": { 72 | "subtype": "ellipsoidal", 73 | "axis": [ 74 | { 75 | "name": "Geodetic latitude", 76 | "abbreviation": "Lat", 77 | "direction": "north", 78 | "unit": "degree" 79 | }, 80 | { 81 | "name": "Geodetic longitude", 82 | "abbreviation": "Lon", 83 | "direction": "east", 84 | "unit": "degree" 85 | } 86 | ] 87 | }, 88 | "id": { 89 | "authority": "EPSG", 90 | "code": 4326 91 | } 92 | }, 93 | "conversion": { 94 | "name": "Popular Visualisation Pseudo-Mercator", 95 | "method": { 96 | "name": "Popular Visualisation Pseudo Mercator", 97 | "id": { 98 | "authority": "EPSG", 99 | "code": 1024 100 | } 101 | }, 102 | "parameters": [ 103 | { 104 | "name": "Latitude of natural origin", 105 | "value": 0, 106 | "unit": "degree", 107 | "id": { 108 | "authority": "EPSG", 109 | "code": 8801 110 | } 111 | }, 112 | { 113 | "name": "Longitude of natural origin", 114 | "value": 0, 115 | "unit": "degree", 116 | "id": { 117 | "authority": "EPSG", 118 | "code": 8802 119 | } 120 | }, 121 | { 122 | "name": "False easting", 123 | "value": 0, 124 | "unit": "metre", 125 | "id": { 126 | "authority": "EPSG", 127 | "code": 8806 128 | } 129 | }, 130 | { 131 | "name": "False northing", 132 | "value": 0, 133 | "unit": "metre", 134 | "id": { 135 | "authority": "EPSG", 136 | "code": 8807 137 | } 138 | } 139 | ] 140 | }, 141 | "coordinate_system": { 142 | "subtype": "Cartesian", 143 | "axis": [ 144 | { 145 | "name": "Easting", 146 | "abbreviation": "X", 147 | "direction": "east", 148 | "unit": "metre" 149 | }, 150 | { 151 | "name": "Northing", 152 | "abbreviation": "Y", 153 | "direction": "north", 154 | "unit": "metre" 155 | } 156 | ] 157 | }, 158 | "scope": "Web mapping and visualisation.", 159 | "area": "World between 85.06°S and 85.06°N.", 160 | "bbox": { 161 | "south_latitude": -85.06, 162 | "west_longitude": -180, 163 | "north_latitude": 85.06, 164 | "east_longitude": 180 165 | }, 166 | "id": { 167 | "authority": "EPSG", 168 | "code": 3857 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /test/data/4326.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://proj.org/schemas/v0.5/projjson.schema.json", 3 | "type": "GeographicCRS", 4 | "name": "WGS 84", 5 | "datum_ensemble": { 6 | "name": "World Geodetic System 1984 ensemble", 7 | "members": [ 8 | { 9 | "name": "World Geodetic System 1984 (Transit)", 10 | "id": { 11 | "authority": "EPSG", 12 | "code": 1166 13 | } 14 | }, 15 | { 16 | "name": "World Geodetic System 1984 (G730)", 17 | "id": { 18 | "authority": "EPSG", 19 | "code": 1152 20 | } 21 | }, 22 | { 23 | "name": "World Geodetic System 1984 (G873)", 24 | "id": { 25 | "authority": "EPSG", 26 | "code": 1153 27 | } 28 | }, 29 | { 30 | "name": "World Geodetic System 1984 (G1150)", 31 | "id": { 32 | "authority": "EPSG", 33 | "code": 1154 34 | } 35 | }, 36 | { 37 | "name": "World Geodetic System 1984 (G1674)", 38 | "id": { 39 | "authority": "EPSG", 40 | "code": 1155 41 | } 42 | }, 43 | { 44 | "name": "World Geodetic System 1984 (G1762)", 45 | "id": { 46 | "authority": "EPSG", 47 | "code": 1156 48 | } 49 | }, 50 | { 51 | "name": "World Geodetic System 1984 (G2139)", 52 | "id": { 53 | "authority": "EPSG", 54 | "code": 1309 55 | } 56 | } 57 | ], 58 | "ellipsoid": { 59 | "name": "WGS 84", 60 | "semi_major_axis": 6378137, 61 | "inverse_flattening": 298.257223563 62 | }, 63 | "accuracy": "2.0", 64 | "id": { 65 | "authority": "EPSG", 66 | "code": 6326 67 | } 68 | }, 69 | "coordinate_system": { 70 | "subtype": "ellipsoidal", 71 | "axis": [ 72 | { 73 | "name": "Geodetic latitude", 74 | "abbreviation": "Lat", 75 | "direction": "north", 76 | "unit": "degree" 77 | }, 78 | { 79 | "name": "Geodetic longitude", 80 | "abbreviation": "Lon", 81 | "direction": "east", 82 | "unit": "degree" 83 | } 84 | ] 85 | }, 86 | "scope": "Horizontal component of 3D system.", 87 | "area": "World.", 88 | "bbox": { 89 | "south_latitude": -90, 90 | "west_longitude": -180, 91 | "north_latitude": 90, 92 | "east_longitude": 180 93 | }, 94 | "id": { 95 | "authority": "EPSG", 96 | "code": 4326 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /test/data/setup.sh: -------------------------------------------------------------------------------- 1 | for epsg_code in 3857 4326 26916 32149 32617 32717 32739 2 | do 3 | echo "EPSG Code: $epsg_code" 4 | for crs_format in esriwkt geoserver gml js mapfile mapnik postgis proj4 wkt xml 5 | do 6 | echo " CRS Format: $crs_format" 7 | crs_filename="${epsg_code}.${crs_format}" 8 | 9 | # clean previous downloads 10 | rm $crs_filename* 11 | 12 | url="https://epsg.io/$crs_filename" 13 | echo " Getting $url" 14 | wget $url 15 | sleep 2 16 | done 17 | done -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const test = require("flug"); 2 | const { readdirSync, readFileSync } = require("fs"); 3 | const findAndRead = require("find-and-read"); 4 | const readTestData = fp => findAndRead(fp, { encoding: "utf-8" }); 5 | 6 | const TEST_CASE = "esriwkt-mapfile-proj4"; 7 | 8 | const { getEPSGCode, getEPSGCodes } = require(`../lib/lookup-esriwkt-mapfile-proj4.js`); 9 | 10 | const DEBUG = process.env.DEBUG || false; 11 | 12 | test("Throw error when unsupported", ({ eq }) => { 13 | const esriwkt = readTestData("3857.esriwkt"); 14 | let msg; 15 | try { 16 | require(`../lib/lookup-proj4.js`)(esriwkt, { debug: true }); 17 | } catch (error) { 18 | msg = error.message; 19 | } 20 | eq(msg, "[get-epsg-code] esriwkt not supported"); 21 | }); 22 | 23 | test("Should identify MapServer mapfile with init", ({ eq }) => { 24 | try { 25 | const input = `PROJECTION\n "init=epsg:26915"\nEND`; 26 | const actualCode = getEPSGCode(input, { debug: DEBUG }); 27 | eq(actualCode, 26915); 28 | } catch (error) { 29 | if (!TEST_CASE.includes("mapfile")) { 30 | console.error(error); 31 | process.exit(1); 32 | } 33 | } 34 | }); 35 | 36 | test("Should identify all files correctly", ({ eq }) => { 37 | const files = readdirSync("test/data").filter(fn => !fn.endsWith(".sh")); 38 | console.log("files: " + files.join(", ")); 39 | files.forEach(filename => { 40 | if (filename.startsWith("32149")) return; 41 | const dataType = filename.split(".").pop(); 42 | const expectedCode = Number(filename.split(".")[0]); 43 | const filepath = `test/data/${filename}`; 44 | const text = readFileSync(filepath, "utf-8"); 45 | if (text.trim() !== "") { 46 | let actualCodes, actualDataType; 47 | try { 48 | ({ type: actualDataType, codes: actualCodes } = getEPSGCodes(text, { 49 | debug: DEBUG 50 | })); 51 | eq(actualCodes.includes(expectedCode), true); 52 | } catch (error) { 53 | getEPSGCodes(text, { debug: true }); 54 | console.log("================START ERROR LOGGING================"); 55 | console.log("TEST_CASE:", [TEST_CASE]); 56 | console.log("dataType:", [dataType]); 57 | console.log("actualDataType:", [actualDataType]); 58 | console.log("text:", [text]); 59 | console.log("expectedCode:", [expectedCode]); 60 | console.error("actualCodes:", actualCodes); 61 | console.log("error.message:", error); 62 | console.log("================END ERROR LOGGING================"); 63 | process.exit(1); 64 | } 65 | } 66 | }); 67 | }); 68 | 69 | test("issue #16", ({ eq }) => { 70 | const old_esriwkt = `GEOGCS["China Geodetic Coordinate System 2000",DATUM["D_China_2000",SPHEROID["CGCS2000",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943]]`; 71 | eq(getEPSGCode(old_esriwkt, { debug: DEBUG }), 4490); 72 | 73 | const new_esriwkt = `GEOGCS["GCS_China_Geodetic_Coordinate_System_2000",DATUM["D_China_2000",SPHEROID["CGCS2000",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]`; 74 | eq(getEPSGCode(new_esriwkt, { debug: DEBUG }), 4490); 75 | }); 76 | 77 | // test("issue #16 EPSG:4507", ({ eq }) => { 78 | // const old_esriwkt4507 = `PROJCS["CGCS2000_Gauss_Kruger_CM_105E",GEOGCS["GCS_China Geodetic Coordinate System 2000",DATUM["D_China_2000",SPHEROID["CGCS2000",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",105],PARAMETER["scale_factor",1],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]]`; 79 | // eq(getEPSGCode(old_esriwkt4507, { debug: DEBUG }), 4507); 80 | 81 | // const new_esriwkt4507 = `PROJCS["CGCS2000_GK_CM_105E",GEOGCS["GCS_China_Geodetic_Coordinate_System_2000",DATUM["D_China_2000",SPHEROID["CGCS2000",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Gauss_Kruger"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",105.0],PARAMETER["Scale_Factor",1.0],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]`; 82 | // eq(getEPSGCode(new_esriwkt4507, { debug: DEBUG }), 4507); 83 | // }); 84 | --------------------------------------------------------------------------------