├── .gitignore ├── EPSG ├── data │ ├── VerticalCS.js │ ├── KnownBaseUnits.js │ ├── GeogPrimeMeridianGeoKey.js │ ├── Overrides.js │ ├── Methods.js │ ├── ProjCoordTransGeoKey.js │ ├── toDeg.js │ ├── MethodParameters.js │ ├── EllipsoidsNamesToProj.js │ ├── PCSKeys.js │ ├── GeogEllipsoidGeoKey.js │ ├── Units.js │ ├── KnownDatums.js │ └── GeogGeodeticDatumGeoKey.js ├── MeridiansWorker.js ├── PostgresClientFactory.js ├── utils │ ├── csNameToObj.js │ └── csUomToMultiplier.js ├── VerticalCSWorker.js ├── UnitsWorker.js ├── README.md ├── EllipsoidsWorker.js ├── forEachEntryInEPSG.js ├── ConversionsWorker.js ├── DatumsWorker.js ├── update.js └── CRSWorker.js ├── .github ├── workflows │ ├── gh-pages.yml │ └── npm-publish.yml └── FUNDING.yml ├── dbConnectionParams.js ├── jsdoc.conf.json ├── postgres_prep └── prepareData.js ├── updateAll.js ├── LICENSE ├── package.json ├── test.js ├── types.d.ts ├── README.md └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | docs 2 | postgres_prep/*.sql 3 | postgres_prep/*.txt 4 | node_modules 5 | 6 | # Editor directories and files 7 | .vscode/* 8 | !.vscode/extensions.json 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | *.sw? 16 | *.iml -------------------------------------------------------------------------------- /EPSG/data/VerticalCS.js: -------------------------------------------------------------------------------- 1 | 2 | // WARNING: This file has been generated automatically 3 | 4 | /** 5 | * Maps vertical CS codes to height/depth multipliers. Resulting value always points up. 6 | * @type {Object} 7 | */ 8 | 9 | module.exports = {"1030":0.3048,"1043":-0.3048006096,"6495":-0.3048,"6496":0.3048007491,"6497":0.3048006096,"6498":-1,"6499":1} -------------------------------------------------------------------------------- /EPSG/data/KnownBaseUnits.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Maps base units to their types description, for example, meters per seconds 3 | * @type {Object} 4 | */ 5 | module.exports = { 6 | "1026": "metre per second", 7 | "1035": "radian per second", 8 | "1036": "scale per second", 9 | "1040": "second", 10 | "9001": "metre", 11 | "9101": "radian", 12 | "9201": "scale", 13 | } -------------------------------------------------------------------------------- /EPSG/data/GeogPrimeMeridianGeoKey.js: -------------------------------------------------------------------------------- 1 | 2 | // WARNING: This file has been generated automatically 3 | 4 | /** 5 | * Maps EPSG prime meridians to their longitudes. Proj4 parameter is "+pm". 6 | * @type {Object} 7 | */ 8 | module.exports = {"1026":12.57775,"8901":0,"8902":-9.131906111111112,"8903":2.337229169999998,"8904":-74.08091666666667,"8905":-3.6873750000000003,"8906":12.452333333333332,"8907":7.439583333333333,"8908":106.80771944444444,"8909":-17.666666666666668,"8910":4.3679749999999995,"8911":18.05827777777778,"8912":23.716337499999998,"8913":10.722916666666666,"8914":2.3372083333333333} -------------------------------------------------------------------------------- /EPSG/MeridiansWorker.js: -------------------------------------------------------------------------------- 1 | /* This script generates meridians from epsg database. Can be used standalone or in a worker. */ 2 | 3 | const forEach = require("./forEachEntryInEPSG.js"); 4 | const toDeg = require("./data/toDeg.js"); 5 | 6 | forEach(` 7 | SELECT pm.prime_meridian_code AS id, pm.greenwich_longitude AS lng, pm.uom_code AS uom 8 | FROM epsg.epsg_primemeridian AS pm 9 | `, (result) => { 10 | let degs = toDeg(result.lng, result.uom); 11 | if (degs === null) 12 | return undefined; 13 | return degs; 14 | }, "GeogPrimeMeridianGeoKey", `/** 15 | * Maps EPSG prime meridians to their longitudes. Proj4 parameter is "+pm". 16 | * @type {Object} 17 | */`); 18 | -------------------------------------------------------------------------------- /EPSG/PostgresClientFactory.js: -------------------------------------------------------------------------------- 1 | const {Client} = require("pg"); 2 | const argConnectionParams = require("../dbConnectionParams.js"); 3 | const {workerData} = require("worker_threads"); 4 | let connectionParams = (argConnectionParams) ? argConnectionParams : workerData; 5 | 6 | /** 7 | * Creates new client for Postgres database 8 | * @return {Promise} 9 | */ 10 | module.exports = async function () { 11 | let client; 12 | try { 13 | client = new Client(connectionParams); 14 | await client.connect(); 15 | } catch (e) { 16 | console.error(e); 17 | console.error("Can't connect to the database! See error above for more information."); 18 | process.exit(1); 19 | } 20 | return client; 21 | } -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/marketplace/actions/deploy-to-github-pages 2 | 3 | name: Deploy to GitHub Pages 4 | 5 | on: 6 | release: 7 | types: [created] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | deploy: 12 | concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: 18 19 | - run: npm install 20 | - run: npm run docs 21 | - name: Deploy 22 | uses: JamesIves/github-pages-deploy-action@v4 23 | with: 24 | folder: ./docs 25 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | publish-npm: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: 14 18 | registry-url: https://registry.npmjs.org/ 19 | - run: npm install 20 | - run: npm publish 21 | env: 22 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 23 | -------------------------------------------------------------------------------- /dbConnectionParams.js: -------------------------------------------------------------------------------- 1 | let connectionParams = { 2 | user: "user", 3 | host: "localhost", 4 | database: "postgres", 5 | password: "12345", 6 | port: 5432, 7 | } 8 | 9 | for (let param in connectionParams) { 10 | if (!connectionParams.hasOwnProperty(param)) 11 | continue; 12 | 13 | let index = process.argv.indexOf("--" + param); 14 | if (index === -1) 15 | continue; 16 | 17 | let value = process.argv[index + 1]; 18 | if (param === "port") 19 | value = parseFloat(value); 20 | connectionParams[param] = value; 21 | } 22 | 23 | /** 24 | * Database connection parameters parsed from CLI arguments. 25 | * If imported in a worker thread, will be null. 26 | * @type {Object|null} 27 | */ 28 | module.exports = (process.argv.length === 2) ? null : connectionParams; -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: matafokka # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /jsdoc.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "plugins/markdown" 4 | ], 5 | "templates": { 6 | "systemName": "geotiff-geokeys-to-proj4", 7 | "theme": "cerulean", 8 | "linenums": true, 9 | "navType": "inline", 10 | "footer": "
(c) matafokka
", 11 | "monospaceLinks": true, 12 | "default": { 13 | "useLongnameInNav": true 14 | } 15 | }, 16 | "source": { 17 | "exclude": [ 18 | "./test.js", 19 | "./main-dist.js", 20 | "./node_modules/", 21 | "./EPSG/", 22 | "./dbConnectionParams.js" 23 | ] 24 | }, 25 | "opts": { 26 | "recurse": true, 27 | "destination": "./docs/", 28 | "template": "./node_modules/ink-docstrap/template", 29 | "readme": "./README.md" 30 | } 31 | } -------------------------------------------------------------------------------- /postgres_prep/prepareData.js: -------------------------------------------------------------------------------- 1 | /* This script updates Postgres database from scripts from this directory. Drop SQL files here and run "npm run-script update-all". */ 2 | 3 | const fs = require("fs"); 4 | const dbParams = require("../dbConnectionParams.js"); 5 | const clientFactory = require("../EPSG/PostgresClientFactory.js"); 6 | 7 | const names = ["Table", "Data", "FKey"]; 8 | 9 | (async function () { 10 | let client = await clientFactory(); 11 | // Drop existing schema and create a new one 12 | await client.query(`DROP SCHEMA IF EXISTS epsg CASCADE`); 13 | await client.query(`CREATE SCHEMA epsg AUTHORIZATION ${dbParams.user}`); 14 | 15 | // Import stuff 16 | for (let name of names) { 17 | let fileName = __dirname + `/PostgreSQL_${name}_Script.sql`; 18 | // Read file, convert buffer to string and remove freaking BOM which makes postgres throw syntax error 19 | let script = fs.readFileSync(fileName).toString().replace("\uFEFF", ""); 20 | await client.query("\nSET SCHEMA 'epsg';\n\n" + script); 21 | } 22 | client.end(); 23 | })(); -------------------------------------------------------------------------------- /updateAll.js: -------------------------------------------------------------------------------- 1 | /* 2 | This script updates database from scripts from epsg.org, updates local files from that database and rebuilds the project. 3 | Unfortunately, we can't pass CLI args to chained commands (npm limitation), that's why this script exists 4 | */ 5 | 6 | const spawnSync = require("child_process").spawnSync; 7 | 8 | const buildScript = "npm run-script build"; // This one is exception, we shouldn't pass anything to it 9 | const scripts = ["node postgres_prep/prepareData.js", "npm run-script update-existing", buildScript]; 10 | const options = {stdio: "inherit"}; 11 | const args = process.argv.slice(2); 12 | 13 | for (let script of scripts) { 14 | let split = script.split(" "); 15 | let command = split[0]; // Only program name should be in command, other stuff goes to args 16 | if (command === "npm" && process.platform === "win32") // Solves bug in npm for Windows 17 | command = "npm.cmd"; 18 | 19 | let newArgs = split.slice(1); 20 | newArgs.push("--"); 21 | if (script !== buildScript) 22 | newArgs = newArgs.concat(args); 23 | 24 | let res = spawnSync(command, newArgs, options); 25 | if (res.error) { 26 | console.error(res.error); 27 | break; 28 | } 29 | } -------------------------------------------------------------------------------- /EPSG/utils/csNameToObj.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Parses CS name. Returns key-value pairs. 3 | * @param {string} name CS name 4 | * @returns {Record} Parsed CS params 5 | */ 6 | module.exports = function csNameToObj(name) { 7 | // For some reason, CS parameters are merged into one string instead of being split to the columns 8 | // Example: 9 | // Ellipsoidal 3D CS. Axes: latitude, longitude, ellipsoidal height. Orientations: north, east, up. UoM: degree, degree, metre. 10 | // Let's just hope this structure won't change later, ha-ha. 11 | 12 | let sentencesStr = name.toLowerCase(); // Normalize string, though, it already seems normalized 13 | if (sentencesStr.endsWith(".")) 14 | sentencesStr = sentencesStr.substring(0, sentencesStr.length - 1); 15 | const sentences = sentencesStr.split(". "); 16 | 17 | // Split each sentence into parameter name (string before column) and values separated by a comma 18 | const cs = {}; 19 | for (const sentence of sentences) { 20 | let paramName = "", columnIndex = 0; 21 | 22 | for (const symbol of sentence) { 23 | columnIndex++; // Accounting space 24 | if (symbol === ":") 25 | break; 26 | paramName += symbol; 27 | } 28 | 29 | cs[paramName] = sentence.substring(columnIndex + 1).split(", "); 30 | } 31 | 32 | return cs; 33 | } -------------------------------------------------------------------------------- /EPSG/VerticalCSWorker.js: -------------------------------------------------------------------------------- 1 | const forEach = require("./forEachEntryInEPSG.js"); 2 | const csNameToObj = require("./utils/csNameToObj.js"); 3 | const csUomToMultiplier = require("./utils/csUomToMultiplier.js"); 4 | 5 | forEach(` 6 | SELECT cs.coord_sys_code AS id, 7 | cs.coord_sys_name as name 8 | FROM epsg.epsg_coordinatesystem as cs 9 | WHERE cs.coord_sys_type = 'vertical' 10 | `, (result) => { 11 | const name = result.name.toLowerCase(); 12 | 13 | // Local depth is measured from the reference point which we can't get. 14 | // I don't think that files with such CS should be supported. 15 | if (name.includes("local depth")) 16 | return; 17 | 18 | const cs = csNameToObj(name); 19 | const uom = cs.uom && cs.uom[0]; 20 | 21 | if (!uom) 22 | return; 23 | 24 | let m = 1; 25 | 26 | const orientation = cs.orientation && cs.orientation[0] 27 | const axis = cs.axis && cs.axis[0]; 28 | 29 | if (orientation) 30 | m = orientation.includes("u") ? 1 : -1; 31 | else if (axis?.includes("d")) // If no orientation, and axis is depth 32 | m = -1; 33 | 34 | const { m: uomM } = csUomToMultiplier(uom); 35 | return uomM ? m * uomM : undefined; 36 | }, "VerticalCS", `/** 37 | * Maps vertical CS codes to height/depth multipliers. Resulting value always points up. 38 | * @type {Object} 39 | */ 40 | `); -------------------------------------------------------------------------------- /EPSG/data/Overrides.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Some parameters must be overridden or transformed in some way. This function performs all necessary transforms. 3 | * @param tokens {Record} Proj4 string tokens 4 | * @param geoKeys {Record} Geokeys 5 | */ 6 | module.exports = function override(tokens, geoKeys) { 7 | let proj = tokens["+proj"], a = tokens["+a"], b = tokens["+b"]; 8 | 9 | // Some geokeys should be mapped to different Proj4 parameters than specified in PCSKeys.js 10 | 11 | if (proj === "cea") { 12 | tokens["+lat_ts"] = tokens["+lat_1"]; 13 | delete tokens["+lat_1"]; 14 | } 15 | 16 | if (proj === "merc") { 17 | tokens["+lat_ts"] = tokens["+lat_0"]; 18 | delete tokens["+lat_0"]; 19 | } 20 | 21 | const crsCode = geoKeys.ProjectedCRSGeoKey || geoKeys.ProjectedCSTypeGeoKey; 22 | 23 | // Web Mercator requires a sphere. 24 | // Original CRS defines an ellipsoid for some reason, it also should be replaced with a sphere. 25 | // See: https://github.com/matafokka/geotiff-geokeys-to-proj4/issues/7 26 | if (crsCode === 3857 && tokens["+a"]) { 27 | tokens["+b"] = tokens["+a"]; 28 | } 29 | 30 | // These projections doesn't work with spheres, proj4 requires +approx parameter in this case 31 | if (a === b && a !== undefined && (proj === "tmerc" || proj === "utm" || proj === "etmerc")) 32 | tokens["+approx"] = null; 33 | } -------------------------------------------------------------------------------- /EPSG/UnitsWorker.js: -------------------------------------------------------------------------------- 1 | /* This script generates units from EPSG database. Can be used standalone or in a worker. */ 2 | 3 | const BaseUnits = require("./data/KnownBaseUnits.js"); 4 | const forEach = require("./forEachEntryInEPSG.js"); 5 | 6 | forEach(` 7 | SELECT u.uom_code AS id, u.factor_b AS b, u.factor_c AS c, u.target_uom_code AS baseunit 8 | FROM epsg.epsg_unitofmeasure AS u 9 | `, (result) => { 10 | 11 | // If current unit is base unit, no multiplication needed 12 | let baseUnit = BaseUnits[result.id.toString()]; 13 | if (baseUnit) { 14 | return { 15 | m: 1, 16 | t: baseUnit, 17 | }; 18 | } 19 | 20 | // If current unit is base but can't be used in GeoTIFF 21 | if (result.id === result.baseunit || result.b === null || result.c === null) 22 | return; 23 | 24 | baseUnit = BaseUnits[result.baseunit.toString()]; 25 | if (!baseUnit) 26 | return; 27 | 28 | return { 29 | m: result.b / result.c, 30 | t: baseUnit, 31 | }; 32 | 33 | }, "Units", `/** 34 | * Maps EPSG units to their multipliers to convert to meters (or standard base values, see below). Proj4 parameter is "+to_meter". 35 | * 36 | * Some of these units for some reason represents speed, angular speed and time. They're converted to m/s, rad/s and s respectively. Moreover, there's unity and unity/s where length is dimensionless. I guess, they should be treated as meters when projecting. 37 | * 38 | * Each value is an object where \`t\` is unit description and \`m\` is a multiplier. 39 | * 40 | * @type {Object} 41 | */`); -------------------------------------------------------------------------------- /EPSG/README.md: -------------------------------------------------------------------------------- 1 | # Meta 2 | 3 | This directory contains scripts that will fetch data from EPSG database. Scripts can be ran both standalone or in a web worker. 4 | 5 | To run all scripts, run `npm run-script update-existing` script. To see help on arguments, run `npm run-script update-existing -- -h` *(**note:** when running npm scripts, arguments should always be separated from the script name by `--`)*. 6 | 7 | Each entity is being built into proj4 string with exception of EllipsoidalCS which contains coordinates conversion parameters that can't be passed to proj4. User needs to convert coordinates through a special function before using them. 8 | 9 | Strings will be merged in the client. 10 | 11 | `data` directory contains fetched entities. 12 | 13 | # Why scripts are not documented? 14 | 15 | Because I would need to copy half of GeoTIFF and GML docs. Don't even ask, just accept everything as it is. 16 | 17 | # What's with objects' names and values? 18 | 19 | To save up space, keys are reduced to one letter, and from values often stripped parameters' names or "+" signs. Sorry about that, we're trying to reduce library size here. 20 | 21 | # Why strings? 22 | 23 | We could split proj4 strings to object, but, I believe, objects uses more RAM than string, though, files will take less space. Since there's a lot of data to store, we'll reduce RAM usage by using strings and parsing them in client. 24 | 25 | Anyway, there shouldn't be much difference between the two, and I already encoded everything in strings :p -------------------------------------------------------------------------------- /EPSG/EllipsoidsWorker.js: -------------------------------------------------------------------------------- 1 | /* This script generates ellipsoids from epsg database. Can be used standalone or in a worker. */ 2 | 3 | const forEach = require("./forEachEntryInEPSG.js"); 4 | const Units = require("./data/Units.js"); 5 | const names = require("./data/EllipsoidsNamesToProj.js"); 6 | 7 | forEach(` 8 | SELECT e.ellipsoid_code AS id, 9 | e.ellipsoid_name AS name, 10 | e.semi_major_axis AS a, 11 | e.semi_minor_axis AS b, 12 | e.inv_flattening AS f, 13 | e.uom_code AS uom 14 | FROM epsg.epsg_ellipsoid AS e 15 | `, (result) => { 16 | 17 | // Get ellipsoid definition 18 | let ellipsoidString = ""; 19 | let fromCode = names[result.id.toString()]; 20 | if (fromCode) 21 | ellipsoidString = fromCode; 22 | else { 23 | let prevName = ""; 24 | for (let name in names) { 25 | if (result.name.startsWith(name) && name.length > prevName.length) { 26 | prevName = name; 27 | ellipsoidString = names[name]; 28 | } 29 | } 30 | } 31 | 32 | if (ellipsoidString !== "") 33 | ellipsoidString = "+ellps=" + ellipsoidString + " "; 34 | 35 | // Get axes 36 | let uom = Units[result.uom.toString()]; 37 | if (!uom) 38 | return undefined; 39 | uom = uom.m; 40 | 41 | let a = result.a * uom, b = result.b * uom; 42 | if (result.f) 43 | b = a - a / result.f; 44 | 45 | if (!a || !b) 46 | return; 47 | 48 | return `${ellipsoidString}+a=${a} +b=${b}`; 49 | }, "GeogEllipsoidGeoKey", `/** 50 | * Maps EPSG ellipsoids to their data. Proj4 parameter is "+ellps". 51 | * @type {Object} 52 | */`); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, matafokka 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /EPSG/data/Methods.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Maps EPSG method codes to their Proj4 definitions 3 | * @type {Object} 4 | */ 5 | module.exports = { 6 | "9807": "tmerc", 7 | "9804": "merc", 8 | "9805": "merc", 9 | "1044": "merc", 10 | "1026": "merc +ellps=sphere", 11 | "9812": "omerc", 12 | "9818": "poly", 13 | "9815": "omerc", 14 | "9808": "tmerc +k_0=1", 15 | "1111": "tmerc +k_0=1", 16 | "1024": "merc", 17 | "9841": "merc +ellps=sphere", 18 | "9835": "cea", 19 | "9820": "laea", 20 | "9817": "lcc", // This is Lambert Conic Near-Conformal, a variation of the Lambert Conformal Conic (1 Parallel) projection, in which the series expansion was truncated to the third order, such that the projection is not fully conformal (source: https://www.bluemarblegeo.com/knowledgebase/calculator-2020sp1/projections/Lambert_Near_Conformal_Conic.htm). Since Proj4 doesn't support this projection, I think, we can assume that it's somewhat equal to Lambert Conic Conformal. 21 | "9801": "lcc", 22 | "9802": "lcc", 23 | "9834": "cea +ellps=sphere", 24 | "1051": "lcc +k_0=1.0000382", 25 | "1027": "laea +ellps=sphere", 26 | "9821": "laea +ellps=sphere", 27 | "9822": "aea", 28 | "9832": "aeqd", 29 | "1028": "eqc", 30 | "1029": "eqc +ellps=sphere", 31 | "9842": "eqc", 32 | "9823": "eqc +ellps=sphere", 33 | "9809": "sterea", 34 | "9810": "stere", 35 | "9829": "stere", 36 | "9806": "cass", 37 | "9833": "cass +hyperbolic", 38 | "9840": "ortho", 39 | "9811": "nzmg", 40 | "9827": "bonne", 41 | "1052": "col_urban", 42 | "1078": "eqearth", 43 | "9819": "krovak", 44 | "9813": "labrd", 45 | "9604": "molodensky", 46 | "9605": "molodensky +abridged", 47 | "9831": "aeqd +guam", 48 | "1041": "krovak +lat_0=49.5 +alpha=30.2881397527778 +k=0.9999", 49 | "9803": "lcc", 50 | } -------------------------------------------------------------------------------- /EPSG/utils/csUomToMultiplier.js: -------------------------------------------------------------------------------- 1 | const Units = require("../data/Units.js"); 2 | 3 | module.exports = function csUomToMultiplier(uom) { 4 | let m = 0, isAngle = false; 5 | 6 | if (uom === "deg" || uom === "degree" || uom === "degrees") { // Can't just find deg because there're degree with hemisphere and dec degree 7 | m = 1; 8 | isAngle = true; 9 | } else if (uom.includes("grad") || uom.includes("gon")) { 10 | m = Units["9105"].m * 180 / Math.PI; 11 | isAngle = true; 12 | } else if (uom.includes("rad")) { // grad handled by previous case 13 | m = Units["9101"].m * 180 / Math.PI; 14 | isAngle = true; 15 | } else if (uom === "m" || uom.includes("met")) 16 | m = 1; 17 | else if (uom.includes("br36")) 18 | m = 0.3048007491; // British feet (1936) 19 | else if (uom === "ft") 20 | m = 0.3048; 21 | else if (uom === "ftus") 22 | m = 0.3048006096; 23 | else if (uom === "ydind") 24 | m = 0.3047995; 25 | else if (uom === "ftcla") 26 | m = 0.3047972654; 27 | else if (uom === "ydcl") 28 | m = 3.3047972654; 29 | else if (uom === "chbnb") 30 | m = Units["9042"].m; 31 | else if (uom === "chse") 32 | m = 20.1167651215526; 33 | else if (uom === "chse(t)") 34 | m = 20.116756; 35 | else if (uom === "ftgc") 36 | m = 0.304799710181509; 37 | else if (uom === "ftse") 38 | m = 0.304799471538676; 39 | else if (uom === "km") 40 | m = 1000; 41 | else if (uom === "lkcla") 42 | m = 0.201166195164; 43 | else if (uom === "ydse") 44 | m = 0.914398414616029; 45 | else if (uom === "glm") 46 | m = 1.0000135965; 47 | else if (uom === "lk") 48 | m = 0.201168; 49 | 50 | // Uoms don't use other units than specified above for now, but let's kinda future-proof it 51 | if (uom.includes("μ") || (isAngle && uom.includes("m"))) 52 | m *= 0.000001; 53 | 54 | return { m, isAngle }; 55 | } -------------------------------------------------------------------------------- /EPSG/forEachEntryInEPSG.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const clientFactory = require("./PostgresClientFactory.js"); 3 | 4 | /** 5 | * Runs given query on a database, calls a callback for each row and writes received data to the file. 6 | * 7 | * A callback should accept received data and return data to write to the file. To skip an entry, callback should return false. 8 | * 9 | * @param query {string} Query to run. For a column containing EPSG code, create an alias called "id", i.e. `SELECT crs.coord_ref_sys_code AS id FROM epsg.epsg_coordinatereferencesystem AS crs` 10 | * @param callback {function(Object, Object)} A callback. First argument accepts current record and second - current object which will be written to the specified file. 11 | * @param filename {string} Name of the file without ".js' extension to write to 12 | * @param comment {string} Comment to append to the file 13 | * @param afterAll {(obj: Object) => void} Called after all entries has been processed 14 | */ 15 | module.exports = async function (query, callback, filename, comment = "", afterAll = undefined) { 16 | let client = await clientFactory(); 17 | let object = {}; 18 | client.query(query, async (err, res) => { 19 | if (err) { 20 | console.error(err); 21 | process.exit(1); 22 | } 23 | 24 | for (let result of res.rows) { 25 | if (result.id === undefined) 26 | throw new Error("ID is not defined. Create an alias for a column that should be used as an ID."); 27 | 28 | let v = await callback(result, object); 29 | if (v !== undefined) 30 | object[result.id] = v; 31 | } 32 | 33 | if (afterAll) 34 | await afterAll(object); 35 | 36 | client.end(); 37 | 38 | fs.writeFileSync(`EPSG/data/${filename}.js`, ` 39 | // WARNING: This file has been generated automatically 40 | 41 | ${comment} 42 | module.exports = ${JSON.stringify(object)}`); 43 | }); 44 | 45 | } -------------------------------------------------------------------------------- /EPSG/ConversionsWorker.js: -------------------------------------------------------------------------------- 1 | /* This script generates conversions (used by GeoTIFF as ProjectionGeoKey). Can be used standalone or in a worker. */ 2 | 3 | const methods = require("./data/Methods.js") 4 | const parameters = require("./data/MethodParameters.js"); 5 | const Units = require("./data/Units.js"); 6 | const toDeg = require("./data/toDeg.js"); 7 | const forEach = require("./forEachEntryInEPSG.js"); 8 | 9 | forEach(` 10 | SELECT op.coord_op_code AS id, 11 | op.coord_op_method_code AS method, 12 | json_agg((param.parameter_code, param.parameter_value, uom_code)) AS params 13 | FROM epsg.epsg_coordoperation op 14 | INNER JOIN 15 | epsg.epsg_coordoperationmethod method ON op.coord_op_method_code = method.coord_op_method_code 16 | INNER JOIN 17 | epsg.epsg_coordoperationparamvalue param ON op.coord_op_code = param.coord_op_code 18 | GROUP BY id 19 | `, (result) => { 20 | 21 | let method = methods[result.method.toString()]; 22 | if (!method) 23 | return; 24 | 25 | for (let param of result.params) { 26 | let paramDef = parameters[param.f1.toString()], value = param.f2, uomCode = param.f3.toString(); 27 | if (!paramDef) 28 | continue; 29 | 30 | if (uomCode in Units) { 31 | let {m} = Units[uomCode]; 32 | if (paramDef.includes("lat") || paramDef.includes("lon") || paramDef.includes("alpha") || paramDef.includes("gamma")) 33 | m *= 180 / Math.PI; // Radians are angular base units 34 | value *= m; 35 | } else 36 | value = toDeg(value, param.f3); 37 | 38 | if (value === null) 39 | return; 40 | 41 | method += ` +${paramDef}=${value}` 42 | } 43 | 44 | return method; 45 | 46 | }, "ProjectionGeoKey", `/** 47 | * Maps EPSG conversions to their proj4 definitions. 48 | * Corresponding geokeys are GeographicTypeGeoKey and ProjectedCSTypeGeoKey. 49 | * @type {Object} 50 | */`); -------------------------------------------------------------------------------- /EPSG/DatumsWorker.js: -------------------------------------------------------------------------------- 1 | /* This script generates datums. Can be used standalone or in a worker. */ 2 | 3 | const KnownDatums = require("./data/KnownDatums.js"); 4 | const meridians = require("./data/GeogPrimeMeridianGeoKey.js"); 5 | const ellipsoids = require("./data/GeogEllipsoidGeoKey.js"); 6 | const forEach = require("./forEachEntryInEPSG.js"); 7 | 8 | const addedIds = {}; // For ensemble datums 9 | 10 | forEach(` 11 | SELECT 12 | d.datum_code AS id, 13 | d.ellipsoid_code AS ellipsoid, 14 | d.prime_meridian_code AS pm, 15 | member_data.ellipsoid_code AS ref_ellipsoid, 16 | member_data.prime_meridian_code AS ref_pm 17 | FROM epsg.epsg_datum as d 18 | LEFT JOIN epsg.epsg_datumensemblemember member ON d.datum_code = member.datum_ensemble_code 19 | LEFT JOIN epsg.epsg_datum member_data on member_data.datum_code = member.datum_code 20 | WHERE 21 | d.datum_type NOT LIKE 'vertical' AND 22 | d.datum_type NOT LIKE 'engineering' AND 23 | ( 24 | d.ellipsoid_code IS NOT NULL OR 25 | member_data.ellipsoid_code IS NOT NULL 26 | ) AND 27 | ( 28 | d.prime_meridian_code IS NOT NULL OR 29 | member_data.prime_meridian_code IS NOT NULL 30 | ) 31 | -- Get newest ensemble datums realizations first 32 | ORDER BY member.datum_sequence DESC 33 | 34 | `, (result) => { 35 | const id = result.id + ""; 36 | 37 | if (addedIds[id]) 38 | return; 39 | 40 | addedIds[id] = true; 41 | const meridian = (result.pm || result.ref_pm) + ""; 42 | const ellipsoid = (result.ellipsoid || result.ref_ellipsoid) + ""; 43 | let str = ""; 44 | 45 | if (ellipsoid in ellipsoids) 46 | str += ellipsoids[ellipsoid]; 47 | 48 | if (meridian in meridians) 49 | str += " +pm=" + meridians[meridian]; 50 | 51 | if (KnownDatums[id]) 52 | str += " +towgs84=" + KnownDatums[id]; 53 | 54 | return str; 55 | }, "GeogGeodeticDatumGeoKey", `/** 56 | * Maps EPSG datums to their proj4 definition. Append values directly to Proj4 string. 57 | * @type {Object} 58 | */`); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geotiff-geokeys-to-proj4", 3 | "version": "2024.04.13", 4 | "description": "A library that converts GeoTIFFs geokeys to Proj4 string", 5 | "main": "main-dist.js", 6 | "module": "main-dist.mjs", 7 | "types": "types.d.ts", 8 | "exports": { 9 | ".": { 10 | "require": "./main-dist.js", 11 | "import": "./main-dist.mjs", 12 | "module": "./main-dist.mjs", 13 | "types": "./types.d.ts" 14 | } 15 | }, 16 | "scripts": { 17 | "test": "node test.js", 18 | "update-existing": "node EPSG/update.js", 19 | "build:iife": "esbuild ./main.js --bundle --outfile=\"\" --format=iife --global-name=geokeysToProj4 --minify=false --log-level=error | npx google-closure-compiler --js_output_file=main-dist-iife.js --language_out ECMASCRIPT3", 20 | "build:cjs": "esbuild ./main.js --bundle --outfile=\"\" --format=cjs --minify=false --log-level=error | npx google-closure-compiler --js_output_file=main-dist.js --language_out ECMASCRIPT3 --isolation_mode=IIFE", 21 | "build:mjs": "node ./build-mjs.js", 22 | "build": "npm run build:iife & npm run build:cjs & npm run build:mjs", 23 | "update-all": "node updateAll.js", 24 | "docs": "jsdoc ./ -c jsdoc.conf.json" 25 | }, 26 | "keywords": [ 27 | "geotiff", 28 | "geokeys", 29 | "geo-keys", 30 | "proj", 31 | "proj4", 32 | "proj4js", 33 | "geotiff-keys", 34 | "geotiff-geokeys", 35 | "geotiff-geo-keys" 36 | ], 37 | "author": "matafokka", 38 | "license": "BSD-3-Clause", 39 | "homepage": "https://github.com/matafokka/geotiff-geokeys-to-proj4", 40 | "repository": "https://github.com/matafokka/geotiff-geokeys-to-proj4", 41 | "devDependencies": { 42 | "@otris/jsdoc-tsd": "2.0.4", 43 | "core-js": "3.36.1", 44 | "esbuild": "0.20.2", 45 | "google-closure-compiler": "20240317.0.0", 46 | "ink-docstrap": "1.3.2", 47 | "jsdoc": "3.6.6", 48 | "nanoid": "5.0.7", 49 | "pg": "8.6.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /EPSG/data/ProjCoordTransGeoKey.js: -------------------------------------------------------------------------------- 1 | // Commented lines describes projections which are not defined in Proj4 and I can't find correct definition. 2 | // Even libgeotiff doesn't provide those definitions, what can a simple person like me do then? Source: https://github.com/OSGeo/libgeotiff/blob/master/libgeotiff/geotiff_proj4.c 3 | 4 | /** 5 | * Maps Coordinate Transformation Codes to "+proj" definitions 6 | * @type {Object} 7 | */ 8 | module.exports = { 9 | "1": "tmerc", // CT_TransverseMercator 10 | // "2": "", // CT_TransvMercator_Modified_Alaska 11 | "3": "omerc", // CT_ObliqueMercator 12 | "4": "labrd", // CT_ObliqueMercator_Laborde (source: https://www.bluemarblegeo.com/knowledgebase/GeoCalcPBW/Content/ClassDef/Projection/Projections/Laborde.html) 13 | // "5": "", // CT_ObliqueMercator_Rosenmund 14 | "6": "omerc +ellps=sphere", // CT_ObliqueMercator_Spherical. Assumed to be oblique mercator but using spherical ellipsoid. 15 | "7": "merc", // CT_Mercator 16 | "8": "lcc", // CT_LambertConfConic_2SP. For some reason, libgeotiff defines it exactly the same as CT_LambertConfConic_Helmert (source: https://github.com/OSGeo/libgeotiff/blob/7da5bacae7814c65ebb78f0b64e1141fbcb3de1e/libgeotiff/geotiff_proj4.c#L1237) 17 | "9": "lcc", // CT_LambertConfConic_Helmert, this one is 1SP. 18 | "10": "laea", // CT_LambertAzimEqualArea 19 | "11": "aea", // CT_AlbersEqualArea 20 | "12": "aeqd", // CT_AzimuthalEquidistant 21 | "13": "eqdc", // CT_EquidistantConic 22 | "14": "stere", // CT_Stereographic 23 | "15": "stere", // CT_PolarStereographic 24 | "16": "sterea", // CT_ObliqueStereographic 25 | "17": "eqc", // CT_Equirectangular 26 | "18": "cass", // CT_CassiniSoldner 27 | "19": "gnom", // CT_Gnomonic 28 | "20": "mill", // CT_MillerCylindrical 29 | "21": "ortho", // CT_Orthographic 30 | "22": "poly", // CT_Polyconic 31 | "23": "robin", // CT_Robinson 32 | "24": "sinu", // CT_Sinusoidal 33 | "25": "vandg", // CT_VanDerGrinten 34 | "26": "nzmg", // CT_NewZealandMapGrid 35 | "27": "tmerc +k_0=1", // CT_TransvMercator_SouthOriented 36 | "28": "cea", // Can't find this one in GeoTIFF docs, but it's CEA: https://download.osgeo.org/geotiff/samples/gdal_eg/cea.txt 37 | } -------------------------------------------------------------------------------- /EPSG/data/toDeg.js: -------------------------------------------------------------------------------- 1 | const Units = require("./Units.js"); 2 | 3 | /** 4 | * Returns sign from given hemisphere 5 | * @param hemisphere {"n"|"s"|"e"|"w"} Hemisphere letter, case insensitive 6 | * @return Sign from given hemisphere 7 | */ 8 | function signFromHemisphere(hemisphere) { 9 | let h = hemisphere.toLowerCase(); 10 | if (h === "n" || h === "e") 11 | return 1; 12 | return -1; 13 | } 14 | 15 | /** 16 | * Converts value with given units EPSG code to degrees 17 | * @param value {number|string} Value to convert 18 | * @param code {number} Units EPSG code 19 | * @return {number|null} Given value in degrees or null, if units can't be converted 20 | */ 21 | module.exports = function (value, code) { 22 | let codeStr = code.toString(); 23 | if (codeStr in Units) 24 | return value * Units[codeStr].m * 180 / Math.PI; 25 | 26 | // Messed up shit. Remark from the database: 27 | // Pseudo unit. Format: signed degrees - period - minutes (2 digits) - integer seconds (2 digits) - fraction of seconds (any precision). Must include leading zero in minutes and seconds and exclude decimal point for seconds. Convert to deg using algorithm. 28 | if (code === 9110) { 29 | let valStr = value.toString(); 30 | let ptPos = valStr.indexOf("."); 31 | 32 | // Add point and zeroes to normalize 33 | if (ptPos === -1) { 34 | ptPos = valStr.length; 35 | valStr += ".0000"; 36 | } 37 | 38 | // Add missing zeroes to normalize 39 | const lengthAfterPoint = valStr.length - 1 - ptPos; 40 | if (lengthAfterPoint < 4) { 41 | for (let i = 0; i < 4 - lengthAfterPoint; i++) 42 | valStr += "0"; 43 | } 44 | 45 | // Parse parts 46 | const sign = Math.sign(value); 47 | const deg = Math.abs(parseFloat(valStr.substring(0, ptPos))); 48 | const min = parseFloat(valStr.substring(ptPos + 1, ptPos + 3)); 49 | let secStr = valStr.substring(ptPos + 3); 50 | secStr = secStr ? secStr.substring(0, 2) + "." + secStr.substring(2) : ""; 51 | const sec = parseFloat(secStr || 0); 52 | return sign * (deg + min / 60 + sec / 3600); 53 | } 54 | 55 | // Hemisphere degree 56 | if (code === 9117) 57 | return signFromHemisphere(value[0]) * parseFloat(value.substring(1)); 58 | 59 | // Degree hemisphere 60 | if (code === 9116) 61 | return signFromHemisphere(value[value.length - 1]) * parseFloat(value.substring(0, value.length - 1)); 62 | 63 | return null; 64 | } -------------------------------------------------------------------------------- /EPSG/data/MethodParameters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Maps methods' parameters to their Proj4 definitions. 3 | * 4 | * Based on data from epsg.org, enriched by manually querying epsg.io 5 | * 6 | * @type {Object} 7 | */ 8 | module.exports = { 9 | "1036": "alpha", 10 | "1039": "h_0", 11 | "8801": "lat_0", 12 | "8802": "lon_0", 13 | "8805": "k_0", 14 | "8806": "x_0", 15 | "8807": "y_0", 16 | "8811": "lat_0", // For an oblique projection, this is the latitude of the point at which the azimuth of the central line is defined 17 | "8812": "lonc", // For an oblique projection, this is the longitude of the point at which the azimuth of the central line is defined 18 | "8813": "alpha", // Source: http://geotiff.maptools.org/proj_list/hotine_oblique_mercator.html 19 | "8814": "gamma", // The angle at the natural origin of an oblique projection through which the natural coordinate reference system is rotated to make the projection north axis parallel with true north. 20 | "8815": "k_0", 21 | "8816": "x_0", 22 | "8817": "y_0", 23 | "8821": "lat_0", 24 | "8822": "lon_0", 25 | "8823": "lat_1", 26 | "8824": "lat_2", 27 | "8826": "x_0", 28 | "8827": "y_0", 29 | "8828": "lat_0", 30 | "8829": "lon_0", 31 | 32 | // For polar aspect azimuthal projections 33 | 34 | "8832": "lat_ts", 35 | "8833": "lon_0", 36 | 37 | // For topocentric CS 38 | 39 | "8834": "lat_0", 40 | "8835": "lon_0", 41 | "8836": "h_0", 42 | "8837": "X_0", 43 | "8838": "Y_0", 44 | "8839": "Z_0", 45 | 46 | // For vertical perspective projections 47 | 48 | "8840": "h", 49 | 50 | // Proj4 transforms parameters 51 | 52 | "8601": "dlat", 53 | "8602": "dlon", 54 | "8603": "dh", 55 | "8604": "dh", 56 | "8605": "dx", 57 | "8606": "dy", 58 | "8607": "dz", 59 | "8608": "rx", 60 | "8609": "ry", 61 | "8610": "rz", 62 | "8611": "s", 63 | 64 | // Only used in krovak for the moment being 65 | // "8818": "lat_1", // Pseudo standard parallel, not sure if it's not supported by Proj4 or hardcoded into it 66 | "8819": "k_0", // Scale factor on pseudo standard parallel 67 | 68 | // Proj4 doesn't support commented parameters. 69 | // I feel like I should leave them here anyway. 70 | 71 | // "8814": "", 72 | // "8830": "", // The longitude of the western limit of the first zone of a Transverse Mercator zoned grid system. 73 | // "8831": "" // The longitude width of a zone of a Transverse Mercator zoned grid system 74 | 75 | } -------------------------------------------------------------------------------- /EPSG/data/EllipsoidsNamesToProj.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Maps EPSG ellipsoids names to their Proj4 definitions. 3 | * 4 | * If this object contains EPSG code, use the value directly. Else, loop over all keys and check if ellipsoid name begins with it. If there're multiple matches, pick one with the greatest length. 5 | * 6 | * @type {Object} 7 | */ 8 | module.exports = { 9 | "7011": "clrk80ign",// 269 Clarke 1880 (IGN). 10 | "7001": "airy", // Airy 1830 11 | // "": "andrae", // Andrae 1876 (Den., Iclnd.) 12 | // "": "danish", // Andrae 1876 (Denmark, Iceland) 13 | // "": "AL4.9,", // Appl. Physics. 1965 14 | "7003": "aust_SA", // Australian Natl & S. Amer. 1969 15 | "Bessel Namibia": "bess_nam", // Bessel 1841 (Namibia) 16 | "Bessel": "bessel", // Bessel 1841 17 | "Clarke 1866": "clrk66", // Clarke 1866 18 | "Clarke 1880": "clrk80", // Clarke 1880 mod. 19 | // "": "CPM", // Comm. des Poids et Mesures 1799 20 | // "": "delmbr", // Delambre 1810 (Belgium) 21 | // "": "engelis", // Engelis 1985 22 | // "": "evrstSS", // Everest (Sabah & Sarawak) 23 | "7081": "evrst30", // Everest 1830 24 | "Everest 1830": "evrst30", // Everest 1830 25 | // "": "evrst48", // Everest 1948 26 | // "": "evrst56", // Everest 1956 27 | "7056": "evrst69", // Everest 1969 28 | // "": "fschr60", // Fischer (Mercury Datum) 1960 29 | // "": "fschr68", // Fischer 1968 30 | "GRS 1980": "GRS80", // GRS 1980(IUGG, 1980) 31 | "GRS 1967": "GRS67", // GRS 67(IUGG 1967) 32 | "1025": "GSK2011", // GSK-2011 33 | "7020": "helmert", // Helmert 1906 34 | "7053": "hough", // Hough 35 | // "": "IAU76", // IAU 1976 36 | "7022": "intl", // International 1909 (Hayford) 37 | // "": "kaula", // Kaula 1961 38 | "7024": "krass", // Krassovsky, 1942 39 | // "": "lerch", // Lerch 1979 40 | // "": "MERIT", // MERIT 1983 41 | // "": "mprts", // Maupertius 1738 42 | "7002": "mod_airy", // Modified Airy 43 | // "": "fschr60m", // Modified Fischer 1960 44 | "7025": "NWL9D", // Naval Weapons Lab., 1965 45 | // "": "new_intl", // New International 1967 46 | "7035": "sphere", // Normal Sphere (r=6370997) 47 | "7054": "PZ90", // PZ-90 48 | "7027": "plessis", // Plessis 1817 (France) 49 | // "": "SEasia", // Southeast Asia 50 | // "": "SGS85", // Soviet Geodetic System 85 51 | // "": "WGS60", // WGS 60 52 | // "": "WGS66", // WGS 66 53 | "7043": "WGS72", // WGS 72 54 | "7030": "WGS84", // WGS 84 55 | // "": "walbeck", // Walbeck 56 | } -------------------------------------------------------------------------------- /EPSG/update.js: -------------------------------------------------------------------------------- 1 | if (process.argv.indexOf("-h") !== -1 || process.argv.indexOf("--help") !== -1) { 2 | console.log(` 3 | This script generates data from EPSG database. 4 | 5 | How to generate data: 6 | 1. Set up PostgreSQL. This script doesn't support other databases. 7 | 2. Download PostgreSQL database (archive with scripts) from epsg-registry.org. 8 | 3. Run scripts from the archive on "epsg" schema. Run scripts in order described in readme file in the archive. 9 | 4. Run this script and provide connection parameters through its arguments. 10 | 11 | Arguments: 12 | -h or --help - Show this help and exit 13 | --host - PostgreSQL host - Defaults to "localhost" 14 | --port - PostgreSQL port - Defaults to "5432" 15 | --database - Database containing "epsg" schema - Defaults to "postgres" 16 | --user - PostgreSQL database user - Defaults to "user" 17 | --password - Password for the user - Defaults to "12345" 18 | `); 19 | process.exit(); 20 | } 21 | 22 | const {Worker} = require('worker_threads'); 23 | const baseDir = "./EPSG/"; 24 | const options = {workerData: require("../dbConnectionParams.js")}; 25 | 26 | // Fetch units first because all other entities depends on it 27 | fetchLinearUnits(); 28 | 29 | function fetchLinearUnits() { 30 | let unitsWorker = new Worker(baseDir + "UnitsWorker.js", options); 31 | unitsWorker.on("exit", () => { 32 | new Worker(baseDir + "ConversionsWorker.js", options); // Conversions depends only on units, everything else depends on each other 33 | fetchBasicEntities(); 34 | }); 35 | } 36 | 37 | // Fetch ellipsoids and meridians on which all other entities depends 38 | function fetchBasicEntities() { 39 | let files = [ 40 | {name: "EllipsoidsWorker", completed: false}, 41 | {name: "MeridiansWorker", completed: false}, 42 | {name: "VerticalCSWorker", completed: false}, 43 | ]; 44 | for (let file of files) { 45 | let w = new Worker(`${baseDir + file.name}.js`, options); 46 | w.on("exit", () => { 47 | file.completed = true; 48 | for (let f of files) { 49 | if (!f.completed) 50 | return; 51 | } 52 | fetchOtherEntities(); 53 | }); 54 | } 55 | } 56 | 57 | // Fetch all other entities. Each next entity depends on previous one. 58 | async function fetchOtherEntities() { 59 | let files = ["DatumsWorker", "CRSWorker"]; 60 | for (let file of files) { 61 | await new Promise(resolve => { 62 | let w = new Worker(`${baseDir + file}.js`, options); 63 | w.on("exit", () => { 64 | resolve(); 65 | }); 66 | }) 67 | } 68 | } -------------------------------------------------------------------------------- /EPSG/data/PCSKeys.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Fields of this object contains mapping of some "simple" PCS keys to an object where: 4 | * 1. `p` - Proj4 definitions 5 | * 1. `u` - If set to 1, units are angular. if 2 -- linear, and 3 -- ratio. 6 | * 7 | * Yup, there's an object in an object. Fields of this object are o1, o2 and o3. Latter Fields takes precedence over previous ones. Let me explain. 8 | * 9 | * GeoTIFF defines following centers: natural origin, projection center and just center. Natural and projection's centers seems to have no difference at all, even libgeotiff defines them as same. Proj4 doesn't support natural origins, so projection's center should take precedence. 10 | * 11 | * And there's "just center" which appears to be as same as the other centers. Though, libgeotiff doesn't support it at all. So, let's assume them to be as same as the other centers and make other centers override them. 12 | * 13 | * So the centers hierarchy is following: "just center", natural origin, projection center. 14 | * 15 | * @type {Object} 16 | */ 17 | let k = { 18 | 19 | /** 20 | * Keys without overrides and "just center" keys 21 | */ 22 | o1: { 23 | ProjStdParallel1GeoKey: { 24 | u: 1, 25 | p: "lat_1" 26 | }, 27 | ProjStdParallel2GeoKey: { 28 | u: 1, 29 | p: "lat_2" 30 | }, 31 | 32 | ProjCenterLongGeoKey: { 33 | u: 1, 34 | p: "lon_0" 35 | }, 36 | ProjCenterLatGeoKey: { 37 | u: 1, 38 | p: "lat_0" 39 | }, 40 | ProjCenterEastingGeoKey: { 41 | u: 2, 42 | p: "x_0" 43 | }, 44 | ProjCenterNorthingGeoKey: { 45 | u: 2, 46 | p: "y_0" 47 | }, 48 | ProjScaleAtCenterGeoKey: { 49 | u: 3, 50 | p: "k_0" 51 | }, 52 | }, 53 | 54 | /** 55 | * Natural origin keys 56 | */ 57 | o2: { 58 | ProjNatOriginLongGeoKey: { 59 | u: 1, 60 | p: "lon_0" 61 | }, 62 | ProjNatOriginLatGeoKey: { 63 | u: 1, 64 | p: "lat_0" 65 | }, 66 | ProjFalseOriginEastingGeoKey: { 67 | u: 2, 68 | p: "x_0" 69 | }, 70 | ProjFalseOriginNorthingGeoKey: { 71 | u: 2, 72 | p: "y_0" 73 | }, 74 | ProjScaleAtNatOriginGeoKey: { 75 | u: 3, 76 | p: "k_0" 77 | }, 78 | }, 79 | 80 | 81 | /** 82 | * Projection center keys 83 | */ 84 | o3: { 85 | ProjFalseOriginLongGeoKey: { 86 | u: 1, 87 | p: "lon_0" 88 | }, 89 | ProjFalseOriginLatGeoKey: { 90 | u: 1, 91 | p: "lat_0" 92 | }, 93 | ProjFalseEastingGeoKey: { 94 | u: 2, 95 | p: "x_0" 96 | }, 97 | ProjFalseNorthingGeoKey: { 98 | u: 2, 99 | p: "y_0" 100 | }, 101 | } 102 | } 103 | 104 | // Aliases 105 | 106 | k.o2.ProjStdParallelGeoKey = k.o1.ProjStdParallel1GeoKey; 107 | k.o3.ProjOriginLongGeoKey = k.o2.ProjNatOriginLongGeoKey; 108 | k.o3.ProjOriginLatGeoKey = k.o2.ProjNatOriginLatGeoKey; 109 | k.o3.ProjScaleAtOriginGeoKey = k.o2.ProjScaleAtNatOriginGeoKey; 110 | 111 | module.exports = k; -------------------------------------------------------------------------------- /EPSG/data/GeogEllipsoidGeoKey.js: -------------------------------------------------------------------------------- 1 | 2 | // WARNING: This file has been generated automatically 3 | 4 | /** 5 | * Maps EPSG ellipsoids to their data. Proj4 parameter is "+ellps". 6 | * @type {Object} 7 | */ 8 | module.exports = {"1024":"+a=6378137 +b=6356752.314140356","1025":"+ellps=GSK2011 +a=6378136.5 +b=6356751.757955603","1026":"+a=6376045 +b=6355477.112903226","7001":"+ellps=airy +a=6377563.396 +b=6356256.909237285","7002":"+ellps=mod_airy +a=6377340.189 +b=6356034.447938534","7003":"+ellps=aust_SA +a=6378160 +b=6356774.719195306","7004":"+ellps=bessel +a=6377397.155 +b=6356078.962818189","7005":"+ellps=bessel +a=6377492.018 +b=6356173.508712696","7006":"+ellps=bess_nam +a=6377483.865 +b=6356165.382966326","7007":"+a=6378293.645208759 +b=6356617.987679838","7008":"+ellps=clrk66 +a=6378206.4 +b=6356583.8","7009":"+ellps=clrk66 +a=6378450.047548896 +b=6356826.621488444","7010":"+ellps=clrk80 +a=6378300.789 +b=6356566.435","7011":"+ellps=clrk80ign +a=6378249.2 +b=6356515","7012":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755","7013":"+ellps=clrk80 +a=6378249.145 +b=6356514.966398753","7014":"+ellps=clrk80 +a=6378249.2 +b=6356514.996941779","7015":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239","7016":"+ellps=evrst30 +a=6377298.556 +b=6356097.550300896","7018":"+ellps=evrst30 +a=6377304.063 +b=6356103.038993155","7019":"+ellps=GRS80 +a=6378137 +b=6356752.314140356","7020":"+ellps=helmert +a=6378200 +b=6356818.169627891","7021":"+a=6378160 +b=6356774.50408554","7022":"+ellps=intl +a=6378388 +b=6356911.9461279465","7024":"+ellps=krass +a=6378245 +b=6356863.018773047","7025":"+ellps=NWL9D +a=6378145 +b=6356759.769488684","7027":"+ellps=plessis +a=6376523 +b=6355862.933255573","7028":"+a=6378298.3 +b=6356657.142669562","7029":"+a=6378300 +b=6356751.689189189","7030":"+ellps=WGS84 +a=6378137 +b=6356752.314245179","7031":"+a=6378137 +b=6356752.314245179","7032":"+a=6378136.2 +b=6356751.516927429","7033":"+a=6378136.3 +b=6356751.616592146","7034":"+ellps=clrk80 +a=6378249.144808011 +b=6356514.966204134","7035":"+ellps=sphere +a=6371000 +b=6371000","7036":"+ellps=GRS67 +a=6378160 +b=6356774.516090714","7041":"+a=6378135 +b=6356750.304921594","7042":"+a=6377299.36559538 +b=6356098.359005156","7043":"+ellps=WGS72 +a=6378135 +b=6356750.520016094","7044":"+ellps=evrst30 +a=6377301.243 +b=6356100.230165385","7045":"+ellps=evrst30 +a=6377299.151 +b=6356098.145120132","7046":"+ellps=bess_nam +a=6377483.865280419 +b=6356165.383245807","7047":"+ellps=GRS80 +a=6370997 +b=6370997","7048":"+ellps=GRS80 +a=6371007 +b=6371007","7049":"+a=6378140 +b=6356755.288157528","7050":"+ellps=GRS67 +a=6378160 +b=6356774.719195306","7051":"+a=6377019.27 +b=6355762.5391","7052":"+ellps=clrk66 +a=6370997 +b=6370997","7053":"+ellps=hough +a=6378270 +b=6356794.343434343","7054":"+ellps=PZ90 +a=6378136 +b=6356751.361745712","7055":"+ellps=clrk80 +a=6378306.3696 +b=6356571.996","7056":"+ellps=evrst69 +a=6377295.664 +b=6356094.667915204","7057":"+a=6371228 +b=6371228","7058":"+a=6378273 +b=6356889.449","7059":"+a=6378137 +b=6378137"} -------------------------------------------------------------------------------- /EPSG/data/Units.js: -------------------------------------------------------------------------------- 1 | 2 | // WARNING: This file has been generated automatically 3 | 4 | /** 5 | * Maps EPSG units to their multipliers to convert to meters (or standard base values, see below). Proj4 parameter is "+to_meter". 6 | * 7 | * Some of these units for some reason represents speed, angular speed and time. They're converted to m/s, rad/s and s respectively. Moreover, there's unity and unity/s where length is dimensionless. I guess, they should be treated as meters when projecting. 8 | * 9 | * Each value is an object where `t` is unit description and `m` is a multiplier. 10 | * 11 | * @type {Object} 12 | */ 13 | module.exports = {"1024":{"m":1,"t":"scale"},"1025":{"m":0.001,"t":"metre"},"1026":{"m":1,"t":"metre per second"},"1027":{"m":3.168876517273149e-11,"t":"metre per second"},"1028":{"m":1e-9,"t":"scale"},"1029":{"m":31556925.445,"t":"second"},"1030":{"m":3.1688765172731483e-17,"t":"scale per second"},"1031":{"m":4.848136811095355e-9,"t":"radian"},"1032":{"m":1.5363146893207598e-16,"t":"radian per second"},"1033":{"m":0.01,"t":"metre"},"1034":{"m":3.1688765172731484e-10,"t":"metre per second"},"1035":{"m":1,"t":"radian per second"},"1036":{"m":1,"t":"scale per second"},"1040":{"m":1,"t":"second"},"1041":{"m":3.1688765172731486e-14,"t":"scale per second"},"1042":{"m":3.1688765172731486e-8,"t":"metre per second"},"1043":{"m":1.5363146893207598e-13,"t":"radian per second"},"9001":{"m":1,"t":"metre"},"9002":{"m":0.3048,"t":"metre"},"9003":{"m":0.30480060960121924,"t":"metre"},"9005":{"m":0.3047972654,"t":"metre"},"9014":{"m":1.8288,"t":"metre"},"9030":{"m":1852,"t":"metre"},"9031":{"m":1.0000135965,"t":"metre"},"9033":{"m":20.11684023368047,"t":"metre"},"9034":{"m":0.2011684023368047,"t":"metre"},"9035":{"m":1609.3472186944375,"t":"metre"},"9036":{"m":1000,"t":"metre"},"9037":{"m":0.9143917962,"t":"metre"},"9038":{"m":20.1166195164,"t":"metre"},"9039":{"m":0.201166195164,"t":"metre"},"9040":{"m":0.9143984146160287,"t":"metre"},"9041":{"m":0.3047994715386762,"t":"metre"},"9042":{"m":20.116765121552632,"t":"metre"},"9043":{"m":0.2011676512155263,"t":"metre"},"9050":{"m":0.9143992,"t":"metre"},"9051":{"m":0.3047997333333333,"t":"metre"},"9052":{"m":20.1167824,"t":"metre"},"9053":{"m":0.201167824,"t":"metre"},"9060":{"m":0.9143992042898124,"t":"metre"},"9061":{"m":0.30479973476327077,"t":"metre"},"9062":{"m":20.116782494375872,"t":"metre"},"9063":{"m":0.2011678249437587,"t":"metre"},"9070":{"m":0.30480083333333335,"t":"metre"},"9080":{"m":0.30479951024814694,"t":"metre"},"9081":{"m":0.30479841,"t":"metre"},"9082":{"m":0.3047996,"t":"metre"},"9083":{"m":0.3047995,"t":"metre"},"9084":{"m":0.9143985307444408,"t":"metre"},"9085":{"m":0.91439523,"t":"metre"},"9086":{"m":0.9143988,"t":"metre"},"9087":{"m":0.9143985,"t":"metre"},"9093":{"m":1609.344,"t":"metre"},"9094":{"m":0.3047997101815088,"t":"metre"},"9095":{"m":0.3048007491,"t":"metre"},"9096":{"m":0.9144,"t":"metre"},"9097":{"m":20.1168,"t":"metre"},"9098":{"m":0.201168,"t":"metre"},"9099":{"m":0.914398,"t":"metre"},"9101":{"m":1,"t":"radian"},"9102":{"m":0.017453292519943278,"t":"radian"},"9103":{"m":0.0002908882086657213,"t":"radian"},"9104":{"m":0.000004848136811095355,"t":"radian"},"9105":{"m":0.01570796326794895,"t":"radian"},"9106":{"m":0.01570796326794895,"t":"radian"},"9109":{"m":0.000001,"t":"radian"},"9112":{"m":0.0001570796326794895,"t":"radian"},"9113":{"m":0.000001570796326794895,"t":"radian"},"9114":{"m":0.0009817477042468094,"t":"radian"},"9122":{"m":0.017453292519943278,"t":"radian"},"9201":{"m":1,"t":"scale"},"9202":{"m":0.000001,"t":"scale"},"9203":{"m":1,"t":"scale"},"9204":{"m":100.58420116840234,"t":"metre"},"9205":{"m":50.29210058420117,"t":"metre"},"9206":{"m":25.146050292100586,"t":"metre"},"9207":{"m":37.5,"t":"metre"},"9208":{"m":25,"t":"metre"},"9209":{"m":12.5,"t":"metre"},"9210":{"m":6.25,"t":"metre"},"9211":{"m":3.125,"t":"metre"},"9300":{"m":0.30479933333333337,"t":"metre"},"9301":{"m":20.116756,"t":"metre"},"9302":{"m":0.20116756,"t":"metre"}} -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* 2 | About comparing keys. 3 | 4 | For now, it's unclear to me how keys should be overridden, so library's results are different from, let's say, QGIS's. If someone will provide list in which geokeys should take precedence, I'll change main script and might implement key-to-key comparison. 5 | 6 | There's also a couple of problems which will render such tests misleading. 7 | 8 | 1. Some keys (for example, +a and +b for ellipsoids if they match ellipsoid's defaults) might be redundant, but this library will specify them anyway. QGIS will drop those. 9 | 10 | 2. This library provides multipliers that converts units to base ones. It doesn't add +units definition. So, for example, instead of specifying +units=km, user will multiply coordinates either by hand or by using convertCoordinates(). 11 | 12 | 3. There might be floating point errors when dealing with really small units. 13 | */ 14 | 15 | const toProj = require("./main-dist.js"); // Can be replaced with main.js, testing main-dist.js to test if final build works fine 16 | 17 | /** 18 | * Geokeys from images from https://download.osgeo.org/geotiff/samples/ 19 | * Proj4 strings has been taken from QGIS 20 | * @type {Object[]} 21 | */ 22 | let testKeys = [ 23 | { 24 | name: "cea.tif", 25 | GTCitationGeoKey: "unnamed", 26 | GTModelTypeGeoKey: 1, 27 | GTRasterTypeGeoKey: 1, 28 | GeogAngularUnitsGeoKey: 9102, 29 | GeogCitationGeoKey: "NAD27", 30 | GeographicTypeGeoKey: 4267, 31 | ProjCoordTransGeoKey: 28, 32 | ProjFalseEastingGeoKey: 0, 33 | ProjFalseNorthingGeoKey: 0, 34 | ProjLinearUnitsGeoKey: 9001, 35 | ProjNatOriginLongGeoKey: -117.333333333333, 36 | ProjStdParallel1GeoKey: 33.75, 37 | ProjectedCSTypeGeoKey: 32767, 38 | ProjectionGeoKey: 32767, 39 | proj4: "+proj=cea +lon_0=-117.333333333333 +lat_ts=33.75 +x_0=0 +y_0=0 +datum=NAD27 +units=m +no_defs", 40 | }, 41 | 42 | { 43 | name: "bogota.tif", 44 | GTModelTypeGeoKey: 1, 45 | GTRasterTypeGeoKey: 1, 46 | ProjectedCSTypeGeoKey: 21892, 47 | proj4: "+proj=tmerc +lat_0=4.599047222222222 +lon_0=-74.08091666666667 +k=1 +x_0=1000000 +y_0=1000000 +ellps=intl +towgs84=307,304,-318,0,0,0,0 +units=m +no_defs", 48 | }, 49 | 50 | { 51 | name: "GeogToWGS84GeoKey5.tif", 52 | GTModelTypeGeoKey: 2, 53 | GTRasterTypeGeoKey: 1, 54 | GeogAngularUnitsGeoKey: 9102, 55 | GeogEllipsoidGeoKey: 7004, 56 | GeogGeodeticDatumGeoKey: 32767, 57 | GeogTOWGS84GeoKey: [598.1, 73.7, 418.2, 0.202, 0.045, -2.455, 6.7], 58 | GeographicTypeGeoKey: 32767, 59 | proj4: "+proj=longlat +ellps=bessel +towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7 +no_defs", 60 | }, 61 | 62 | { 63 | name: "ntf_nord.tif", 64 | GTModelTypeGeoKey: 1, 65 | GTRasterTypeGeoKey: 1, 66 | ProjectedCSTypeGeoKey: 27591, 67 | proj4: "+proj=lcc +lat_1=49.50000000000001 +lat_0=49.50000000000001 +lon_0=0 +k_0=0.999877341 +x_0=600000 +y_0=200000 +a=6378249.2 +b=6356515 +towgs84=-168,-60,320,0,0,0,0 +pm=paris +units=m +no_defs", 68 | }, 69 | 70 | { 71 | name: "srtm_44_01_tiff.tif", 72 | GTModelTypeGeoKey: 2, 73 | GTRasterTypeGeoKey: 1, 74 | GeogAngularUnitsGeoKey: 9102, 75 | GeogCitationGeoKey: "GCS Name = Comm. des Poids et Mesures 1799|Datum = unknown|Ellipsoid = CPM|Primem = Greenwich|", 76 | GeogEllipsoidGeoKey: 32767, 77 | GeogGeodeticDatumGeoKey: 32767, 78 | GeogInvFlatteningGeoKey: 334.29, 79 | GeogPrimeMeridianLongGeoKey: 0, 80 | GeogSemiMajorAxisGeoKey: 6375738.7, 81 | GeographicTypeGeoKey: 32767, 82 | proj4: "+proj=longlat +a=6375738.7 +b=6356666.221912113 +no_defs", 83 | }, 84 | 85 | { 86 | name: "usa.tif", 87 | GTModelTypeGeoKey: 1, 88 | GTRasterTypeGeoKey: 1, 89 | PCSCitationGeoKey: "UTM Zone 17 N with WGS84", 90 | ProjectedCSTypeGeoKey: 32617, 91 | proj4: "+proj=utm +zone=17 +datum=WGS84 +units=m +no_defs", 92 | }, 93 | 94 | { 95 | name: "From #2", 96 | GTModelTypeGeoKey: 1, 97 | GTRasterTypeGeoKey: 1, 98 | GeogEllipsoidGeoKey: 7030, 99 | GeogGeodeticDatumGeoKey: 6326, 100 | PCSCitationGeoKey: "Transverse Mercator; WGS84; WGS84", 101 | ProjCoordTransGeoKey: 1, 102 | ProjFalseEastingGeoKey: 500000, 103 | ProjFalseNorthingGeoKey: -5300000, 104 | ProjLinearUnitsGeoKey: 9001, 105 | ProjNatOriginLatGeoKey: 0, 106 | ProjNatOriginLongGeoKey: 19, 107 | ProjScaleAtNatOriginGeoKey: 0.9993, 108 | ProjectedCSTypeGeoKey: 32767, 109 | ProjectionGeoKey: 32767, 110 | proj4: "+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +datum=WGS84 +units=m +no_defs", 111 | }, 112 | 113 | { 114 | name: "From #3", 115 | GTCitationGeoKey: "UTM Zone 32, Northern Hemisphere", 116 | GTModelTypeGeoKey: 1, 117 | GTRasterTypeGeoKey: 1, 118 | GeogAngularUnitsGeoKey: 9102, 119 | GeogCitationGeoKey: "GCS Name = GRS 1980(IUGG, 1980)|Datum = unknown|Ellipsoid = GRS80|Primem = Greenwich|", 120 | GeogEllipsoidGeoKey: 32767, 121 | GeogGeodeticDatumGeoKey: 32767, 122 | GeogInvFlatteningGeoKey: 298.257222101, 123 | GeogPrimeMeridianLongGeoKey: 0, 124 | GeogSemiMajorAxisGeoKey: 6378137, 125 | GeogTOWGS84GeoKey: [ 0, 0, 0 ], 126 | GeographicTypeGeoKey: 32767, 127 | ProjLinearUnitsGeoKey: 9001, 128 | ProjectedCSTypeGeoKey: 32767, 129 | ProjectionGeoKey: 16032, 130 | proj4: "+proj=tmerc +lat_0=0 +lon_0=9 +k_0=0.9996 +x_0=500000 +y_0=0 +a=6378137 +b=6356752.314140356 +towgs84=0,0,0 +no_defs" 131 | }, 132 | 133 | { 134 | name: "From #4", 135 | GTModelTypeGeoKey: 1, 136 | GTRasterTypeGeoKey: 1, 137 | GeographicTypeGeoKey: 4326, 138 | ProjectedCSTypeGeoKey: 32634, 139 | proj4: "+proj=longlat +datum=WGS84 +no_defs" 140 | }, 141 | 142 | { 143 | name: "Some example DEM with VerticalCSTypeGeoKey changed", 144 | GTModelTypeGeoKey: 1, 145 | GTRasterTypeGeoKey: 2, 146 | GTCitationGeoKey: 'WGS 84 / UTM zone 32N + EGM2008 geoid height', 147 | GeogCitationGeoKey: 'WGS 84', 148 | GeogAngularUnitsGeoKey: 9102, 149 | ProjectedCSTypeGeoKey: 32632, 150 | ProjLinearUnitsGeoKey: 9001, 151 | VerticalCSTypeGeoKey: 8051, 152 | proj4: "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs", 153 | }, 154 | { 155 | name: "From #7", 156 | GTModelTypeGeoKey: 1, 157 | GTRasterTypeGeoKey: 1, 158 | GTCitationGeoKey: "WGS 84 / Pseudo-Mercator", 159 | GeogCitationGeoKey: "WGS 84", 160 | GeogAngularUnitsGeoKey: 9102, 161 | ProjectedCSTypeGeoKey: 3857, 162 | ProjLinearUnitsGeoKey: 9001, 163 | proj4: "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs", 164 | } 165 | ]; 166 | 167 | // Test runner 168 | for (let key of testKeys) { 169 | let proj = toProj.toProj4(key); 170 | let str = `-------------------------------------------- 171 | Testing: ${key.name} 172 | Original string: ${key.proj4} 173 | Generated string: ${proj.proj4} 174 | X multiplier: ${proj.coordinatesConversionParameters.x} 175 | Y multiplier: ${proj.coordinatesConversionParameters.y} 176 | Z multiplier: ${proj.coordinatesConversionParameters.z} 177 | Units: ${proj.coordinatesUnits} 178 | Is GCS: ${proj.isGCS ? "Yes" : "No"}` 179 | let errStr = ""; 180 | for (let error in proj.errors) 181 | errStr += "\n\t" + error + ":\t\t" + proj.errors[error]; 182 | if (errStr !== "") 183 | str += "\nErrors:" + errStr; 184 | console.log(str); 185 | } -------------------------------------------------------------------------------- /EPSG/CRSWorker.js: -------------------------------------------------------------------------------- 1 | /* This script generates GCS from epsg.io. Can be used standalone or in a worker. */ 2 | 3 | /* 4 | Some EPSG GCS are using 3D cartesian coordinate systems (see https://en.wikipedia.org/wiki/Geographic_coordinate_system#3D_Cartesian_coordinates and https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_ECEF_to_geodetic_coordinates). 5 | 6 | There's no way to map images (2D planes) to such systems, that's why we won't support such GCS. 7 | 8 | I don't know, Why GeoTIFF lists these GCS as supported. 9 | 10 | GeoTIFF also contains ModelTiepointTag which can be used to specify XYZ coordinates of a set of points. However: 11 | 1. It's unclear whether Z represents height or distance from the Earth's center. 12 | 2. "this third dimension is provided in anticipation of future support for 3D digital elevation models and vertical coordinate systems". 13 | So, for now, there's no standard use for this tag. 14 | */ 15 | 16 | const datums = require("./data/GeogGeodeticDatumGeoKey.js"); 17 | const conversions = require("./data/ProjectionGeoKey.js"); 18 | const additionalCrs = require("./data/AdditionalCRS.js"); 19 | const forEach = require("./forEachEntryInEPSG.js"); 20 | const csNameToObj = require("./utils/csNameToObj.js"); 21 | const csUomToMultiplier = require("./utils/csUomToMultiplier.js"); 22 | const verticalCS = require("./data/VerticalCS.js"); 23 | 24 | function getObjectFromProjString(proj) { 25 | if (!proj) 26 | return {} 27 | 28 | const pairs = proj.split(" "); 29 | const obj = {}; 30 | 31 | for (const pair of pairs) { 32 | const [key, value] = pair.split("="); 33 | obj[key] = value ?? null; 34 | } 35 | 36 | return obj; 37 | } 38 | 39 | // These CS have messed up description, but they're ok without transformation 40 | const CS_IGNORE_ORIENTATION = { 41 | 1035: true, 42 | 1036: true, 43 | 1037: true, 44 | 1038: true, 45 | 4499: true, 46 | 4463: true, 47 | 4464: true, 48 | 4465: true, 49 | 4466: true, 50 | 4467: true, 51 | 4468: true, 52 | 4469: true, 53 | 4470: true, 54 | 6500: true, 55 | } 56 | 57 | const orientationLettersStr = "nsewud"; 58 | const ORIENTATION_LETTERS = {}; 59 | 60 | for (const letter of orientationLettersStr) 61 | ORIENTATION_LETTERS[letter] = true; 62 | 63 | forEach(` 64 | SELECT crs.coord_ref_sys_code AS id, 65 | crs.coord_ref_sys_kind as type, 66 | cs.coord_sys_name as cs_name, 67 | crs.cmpd_horizcrs_code AS compound_horizontal_crs, 68 | crs.cmpd_vertcrs_code AS compound_vertical_crs, 69 | crs.datum_code AS datum, 70 | crs.base_crs_code AS base_crs, 71 | crs.projection_conv_code AS conversion, 72 | cs.coord_sys_code AS cs_id, 73 | base_crs_data.datum_code AS base_datum, 74 | base_crs_data.coord_sys_code AS base_crs_cs_code, 75 | vertical_crs_data.coord_sys_code AS vertical_cs 76 | FROM epsg.epsg_coordinatereferencesystem as crs 77 | LEFT JOIN epsg.epsg_coordinatereferencesystem base_crs_data ON crs.base_crs_code = base_crs_data.coord_ref_sys_code 78 | LEFT JOIN epsg.epsg_coordinatesystem cs ON cs.coord_sys_code = crs.coord_sys_code 79 | LEFT JOIN epsg.epsg_coordinatereferencesystem vertical_crs_data ON crs.cmpd_vertcrs_code = vertical_crs_data.coord_ref_sys_code 80 | WHERE crs.coord_ref_sys_kind NOT LIKE 'engineering' 81 | -- Make compound CRS and derived CRS come last, so we can reference previously fetched CRS 82 | ORDER BY crs.coord_ref_sys_kind DESC, crs.base_crs_code DESC 83 | `, async (result, fetchedCRS) => { 84 | 85 | // Due to sorting, compound CRS comes last 86 | if (result.type === "compound") { 87 | const horizontalCRS = fetchedCRS[result.compound_horizontal_crs.toString()]; 88 | const z = verticalCS[result.vertical_cs.toString()]; 89 | 90 | if (!horizontalCRS || !z) 91 | return; 92 | 93 | if (typeof horizontalCRS === "string") { 94 | return { 95 | p: horizontalCRS, 96 | x: 1, 97 | y: 1, 98 | z, 99 | } 100 | } 101 | 102 | return { ...horizontalCRS, z }; 103 | } 104 | 105 | // Return multiplier for vertical CRS 106 | if (result.type === "vertical") { 107 | return verticalCS[result.cs_id?.toString()] || verticalCS[result.base_crs_cs_code?.toString()]; 108 | } 109 | 110 | let conversion = conversions[result.conversion + ""]; 111 | 112 | if (!conversion) { 113 | if (result.type === "projected") 114 | return; 115 | else if (result.type === "geocentric") 116 | conversion = "geocent"; 117 | else 118 | conversion = "longlat"; 119 | } 120 | 121 | const datum = datums[result.datum] || datums[result.base_datum]; 122 | 123 | // Datum is always present, but I like to check anyway 124 | if (!datum) 125 | return; 126 | 127 | const conversionKeys = getObjectFromProjString("+proj=" + conversion); 128 | const datumKeys = getObjectFromProjString(datum); 129 | 130 | for (const key in datumKeys) { 131 | conversionKeys[key] = datumKeys[key]; 132 | } 133 | 134 | // Exceptions 135 | 136 | if (result.cs_id === 4468) { 137 | const value = parseFloat(conversionKeys["+lat_0"]); 138 | conversionKeys["+lat_0"] = (isNaN(value) ? 0 : value) + 90; // Not sure if it must be added or replaced 139 | } 140 | 141 | // Merge keys into a string 142 | 143 | let projStr = ""; 144 | for (const key in conversionKeys) { 145 | projStr += key; 146 | const value = conversionKeys[key]; 147 | 148 | if (value !== null) { 149 | projStr += "=" + value; 150 | } 151 | 152 | projStr += " "; 153 | } 154 | 155 | projStr = projStr.substring(0, projStr.length - 1); 156 | 157 | if (!result.cs_name) 158 | return projStr; 159 | 160 | // Get orientation, i.e. +axis parameter 161 | 162 | const cs = csNameToObj(result.cs_name); 163 | let orientation = ""; 164 | let isOrientationValid = !!cs.orientations; 165 | 166 | if (cs.orientations) { 167 | for (let direction of cs.orientations) { 168 | const firstLetter = direction[0]; 169 | 170 | if (!ORIENTATION_LETTERS[firstLetter]) { 171 | if (!CS_IGNORE_ORIENTATION[result.cs_id]) 172 | return; 173 | 174 | isOrientationValid = false; 175 | break; 176 | } 177 | 178 | orientation += firstLetter; 179 | } 180 | } 181 | 182 | // Validate orientation further 183 | if (isOrientationValid) { 184 | const letters = {}; 185 | 186 | for (const letter of orientation) { 187 | letters[letter] = (letters[letter] || -1) + 1; 188 | 189 | if (letters[letter] > 1) { 190 | isOrientationValid = false; 191 | 192 | if (CS_IGNORE_ORIENTATION[result.cs_id]) 193 | break; 194 | else 195 | return; 196 | } 197 | } 198 | } 199 | 200 | const isVertical = result.type === "vertical"; 201 | 202 | let uomsNames = { 203 | x: cs.uom[0], 204 | y: cs.uom[1], 205 | z: cs.uom[isVertical ? 0 : 2], 206 | }; 207 | 208 | const isGeographic3d = result.type === "geographic 3D"; 209 | const isGeocentric = result.type === "geocentric"; 210 | 211 | if (!isGeographic3d && !isGeocentric) 212 | delete uomsNames.z; 213 | 214 | if (!uomsNames.y) 215 | uomsNames.y = uomsNames.x; 216 | 217 | if (!uomsNames.z) { 218 | if (isGeographic3d || isVertical) 219 | return; 220 | 221 | if (isGeocentric) 222 | uomsNames.z = uomsNames.x; 223 | } 224 | 225 | let uoms = {}; 226 | let axes = ["x", "y", "z"]; 227 | let isAngle = false; 228 | 229 | for (let axis of axes) { 230 | if (!uomsNames[axis]) 231 | continue; 232 | 233 | const multiplier = csUomToMultiplier(uomsNames[axis]); 234 | 235 | if (!multiplier.m) 236 | return; 237 | 238 | isAngle = isAngle || multiplier.isAngle; 239 | uoms[axis] = multiplier.m; 240 | } 241 | 242 | if (orientation && isOrientationValid) 243 | projStr += " +axis=" + orientation; 244 | 245 | if (!isAngle && uoms.x == uoms.y && uoms.x) { 246 | if (uoms.x !== 1) 247 | projStr += " +to_meter=" + uoms.x; 248 | } else if (uoms.x || uoms.y) { 249 | return { 250 | p: projStr, 251 | ...uoms, 252 | } 253 | } 254 | 255 | return projStr; 256 | }, "CRS", `/** 257 | * Maps EPSG CRS to their proj4 definitions. Should be a base for Proj4 string. 258 | * Corresponding geokeys are GeographicTypeGeoKey and ProjectedCSTypeGeoKey. 259 | * @type {Object} 260 | */`, (obj) => { 261 | for (const key in additionalCrs) { 262 | if (!obj[key]) 263 | obj[key] = additionalCrs[key] 264 | } 265 | }); -------------------------------------------------------------------------------- /EPSG/data/KnownDatums.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Maps known EPSG datums to their transformation parameters. Proj4 parameter is "+towgs84". 3 | * 4 | * This module should be used only by generators. For all datums, use GeogGeodeticDatumGeoKey.js file. 5 | * 6 | * Source for this data: https://gis-lab.info/qa/mapinfo_to_wkt_proj4.html 7 | * Data has been taken from MapInfo by the authors of this article 8 | * 9 | * I've put everything together and tried my best to map datums' names to EPSG codes. If something's wrong, feel free to create an issue. 10 | * 11 | * @type {Object} 12 | */ 13 | module.exports = { 14 | 15 | //--------------// 16 | // 3 parameters // 17 | //--------------// 18 | 19 | // Mapped as: 20 | // X shift,Y shift,Z shift // Name 21 | 22 | "6201": "-162,-12,206", // Adindan 23 | "6205": "-43,-163,45", // Afgooye 24 | "6209": "-143,-90,-294", // Arc1950 25 | "6210": "-160,-8,-300", // Arc1960 26 | "6712": "-207,107,52", // AscensionIsland1958 27 | "6710": "-320,550,-494", // AstroDOS71/4 28 | "6202": "-133,-48,148", // AustralianGeodetic1966 29 | "6203": "-134,-48,149", // AustralianGeodetic1984 30 | "6714": "-127,-769,472", // Bellevue(IGN) 31 | "6216": "-73,213,296", // Bermuda1957 32 | "6218": "307,304,-318", // Bogota 1975 33 | "6802": "307,304,-318", // Bogota 1975 (Bogota) 34 | "6221": "-148,136,90", // CampoInchauspe 35 | "6222": "-136,-108,-292", // Cape 36 | "6717": "-2,150,181", // CapeCanaveral 37 | "6223": "-263,6,431", // Carthage 38 | "6672": "175,-38,113", // Chatham1971 39 | "6224": "-134,229,-29", // ChuaAstro 40 | "1074": "-206,172,-6", // CorregoAlegre 1961 41 | "6225": "-206,172,-6", // CorregoAlegre 1970-72 42 | "6813": "-377,681,-50", // Djakarta(Batavia) 43 | "6719": "211,147,111", // EasterIsland1967 44 | "6230": "-87,-98,-121", // European1950 45 | "6668": "-86,-98,-119", // European1979 46 | "6685": "-133,-321,50", // GandajikaBase 47 | "6036": "0,0,0", // GRS67 48 | "6019": "0,0,0", // GRS80 49 | "6675": "-100,-248,259", // Guam1963 50 | "6254": "16,196,93", // HitoXVIII1963 51 | "6658": "-73,46,-86", // Hjorsey1955 52 | "6738": "-156,-271,-189", // HongKong1963 53 | "6236": "-634,-549,-201", // Hu-Tzu-Shan 54 | "6300": "506,-122,611", // Ireland1965 55 | "6725": "191,-77,-204", // JohnstonIsland1961 56 | "6244": "-97,787,86", // Kandawala 57 | "6251": "-90,40,88", // Liberia1964 58 | "6253": "-133,-77,-51", // Luzon(Philippines) 59 | "6256": "41,-220,-134", // Mahe1971 60 | "6262": "639,405,60", // Massawa 61 | "6261": "31,146,47", // Merchich 62 | "6727": "912,-58,1227", // MidwayAstro1961 63 | "6263": "-92,-93,122", // Minna 64 | "6271": "-2,374,172", // Naparima 1972 65 | "6158": "-2,374,172", // Naparima 1955 66 | "6268": "-8,160,176", // NAD27(Michigan) 67 | "1211": "0,0,0", // NAD83 68 | "6152": "0,0,0", // NAD83 69 | "6140": "0,0,0", // NAD83 70 | "6759": "0,0,0", // NAD83 71 | "1212": "0,0,0", // NAD83 72 | "1133": "0,0,0", // NAD83 73 | "1118": "0,0,0", // NAD83 74 | "1117": "0,0,0", // NAD83 75 | "1116": "0,0,0", // NAD83 76 | "6135": "61,-285,-181", // OldHawaiian 77 | "1147": "-346,-1,224", // Oman 78 | "5101": "375,-111,431", // Ordnance Datum Newlyn 79 | "1164": "375,-111,431", // Ordnance Datum Newlyn (Offshore) 80 | "6728": "-307,-92,127", // PicodelasNieves 81 | "6729": "185,165,42", // PitcairnAstro1967 82 | "6139": "11,72,-101", // PuertoRico 83 | "6614": "-128,-283,22", // QatarNational 84 | "6194": "164,138,-189", // Qornoq 85 | "6626": "94,-948,-1262", // Reunion 86 | "6806": "-225,-65,9", // Rome1940 87 | "6730": "170,42,84", // Santo(DOS) 88 | "6292": "-355,16,74", // SapperHill1943 89 | "6293": "616,97,-251", // Schwarzeck 90 | "6618": "-57,1,-41", // SouthAmerican1969 91 | "6185": "-499,-249,314", // SoutheastBase 92 | "1102": "-499,-249,314", // SoutheastBase 93 | "6663": "-499,-249,314", // SoutheastBase 94 | "1104": "-104,167,-38", // SouthwestBase 95 | "6665": "-104,167,-38", // SouthwestBase 96 | "6298": "-689,691,-46", // Timbalai1948 97 | "6301": "-128,481,664", // Tokyo 98 | "6734": "-632,438,-609", // TristanAstro1968 99 | "6731": "51,391,-36", // VitiLevu1916 100 | "6732": "101,52,-39", // Wake-Eniwetok1960 101 | "6043": "0,8,10", // WGS72 102 | "6030": "0,0,0", // WGS84 103 | "6309": "-155,171,37", // Yacare 104 | "6311": "-265,120,-358", // Zanderij 105 | "6231": "-83,-96,-113", // European1987 106 | "6169": "-115,118,426", // AmericanSamoa 107 | "6601": "-270,13,62", // AntiguaIslandAstro1943 108 | "6713": "-79,-129,145", // AyabelleLighthouse 109 | "6219": "-384,664,-48", // BukitRimpah 110 | "6155": "-83,37,124", // Dabola 111 | "6736": "260,12,-147", // DeceptionIsland 112 | "6183": "-104,167,-38", // GraciosaBaseSW1948 113 | "6255": "-333,-222,114", // HeratNorth 114 | "6239": "217,823,299", // Indian1954 115 | "6131": "198,881,317", // Indian1960 116 | "6240": "210,814,289", // Indian1975 117 | "6238": "-24,-15,5",// Indonesian1974 118 | "6735": "647,1777,-1124", // KusaieAstro1951 119 | "6250": "-130,29,364", // Leigon 120 | "6604": "174,359,365", // MontserratIsl.Astro1958 121 | "6266": "-74,-130,42", // M'Poraloko 122 | "6307": "-186,-93,310", // NorthSahara1959 123 | "6620": "-106,-129,165", // Point58 124 | "6615": "-499,-249,314", // PortoSanto1936 125 | "6616": "-289,-124,60", // SelvagemGrande1938 126 | "6175": "-88,4,101",// SierraLeone1960 127 | "6810": "-189,-242,-91", // TananariveObservatory1925 128 | "6297": "-189,-242,-91", // TananariveObservatory1925 129 | "6304": "-73,-247,227", // Voirol1874 130 | "6811": "-73,-247,227", // Voirol1874 131 | "6214": "-31.4,144.3,81.2", // Beijing 1954 132 | 133 | // These datums are either not in EPSG or their index is unknown. Left here in case someone will need them. 134 | 135 | // -150 -251 -2", // AinelAbd1970 136 | // -491 -22 435", // Anna1Astro1965 137 | // 145 75 -272", // AstroBeacon "E" 138 | // 114 -116 -333", // AstroB4SorolAtoll 139 | // 124 -234 -25", // AstronomicStation1952 140 | // 298 -304 -375", // CantonAstro1966 141 | // 230 -199 -752", // DOS1968 142 | // 84 -22 209", // GeodeticDatum1949 143 | // 252 -209 -751", // GUX1Astro 144 | // 214 836 303", // Indian(Thailand/Vietnam) 145 | // 289 734 257", // Indian(Bangladesh) 146 | // 208 -435 -229", // ISTS073Astro1969 147 | // 145 -187 103", // KerguelenIsland 148 | // -11 851 5", // Kertau1948 149 | // 42 124 147", // L.C.5Astro 150 | // -133 -79 -72", // Luzon(MindanaoIsland) 151 | // -289 -124 60", // MarcoAstro 152 | // -247 -148 369", // Nahrwan(MasirahIsland) 153 | // -249 -156 381", // Nahrwan(Un.ArabEmirates) 154 | // -231 -196 482", // Nahrwan(SaudiArabia) 155 | // -8 160 176", // NAD27(ContinentalUS) 156 | // -5 135 172", // NAD27(Alaska) 157 | // -4 154 178", // NAD27(Bahamas) 158 | // 1 140 165", // NAD27(SanSalvador) 159 | // -10 158 187", // NAD27(Canada) 160 | // 0 125 201", // NAD27(CanalZone) 161 | // -7 152 178", // NAD27(Caribbean) 162 | // 0 125 194", // NAD27(CentralAmerica) 163 | // -9 152 178", // NAD27(Cuba) 164 | // 11 114 195", // NAD27(Greenland) 165 | // -12 130 190", // NAD27(Mexico) 166 | // -425 -169 81", // Observatorio1966 167 | // -130 110 -13", // OldEgyptian 168 | // -288 175 -376", // ProvisionalSouthAmerican 169 | // -203 141 53", // SaoBraz 170 | // 0 0 0", // WGS60 171 | // 0 0 0", // WGS66 172 | // -168 -60 320", // NTF(Greenwichmeridian) 173 | // 593 26 478", // Netherlands7004 174 | // 81 120 129", // BelgiumHayford 175 | // -1 15 1", // NWGL10 176 | // 498 -36 568", // RT90(Sweden) 177 | // -303 -62 105", // Lisboa(DLx) 178 | // -223 110 37", // Melrica1973(D73) 179 | // 0 0 0", // EUREF89 180 | // 0 0 0", // GDA94 181 | // 0 0 0", // NZGD2000 182 | // 374 150 588", // Estonia1937 183 | // -7 215 225", // FortThomas1955 184 | // 682 -203 480", // Hermannskogel 185 | // 283 682 231", // Indian(Pakistan) 186 | // -794 119 -298", // ISTS061Astro1968 187 | // -425 -169 81", // ObservatorioMeteor.1939 188 | // -148 51 -291", // PointeNoire1948 189 | // 589 76 480", // S-JTSK 190 | // -123 -206 219", // Voirol1960 191 | // 0 0 0", // Hartbeesthoek94 192 | // 0 0 0", // ATS77 193 | // 0 0 0", // JGD2000 194 | // -199.87 74.79 246.62", // HGRS87 195 | 196 | //--------------// 197 | // 7 parameters // 198 | //--------------// 199 | 200 | // Mapped as: 201 | // X shift,Y shift,Z shift,X rotation,Y rotation,Z rotation // Name 202 | 203 | "6284": "24,-123,-94,-0.02,0.25,0.13,1.1", // Pulkovo 1942 204 | "6272": "59.47,-5.04,187.44,-0.47,0.1,-1.024,-4.5993,0", // New Zealand Geodetic Datum 1949 205 | "6124": "419.3836,99.3335,591.3451,-0.850389,-1.817277,7.862238,-0.99496", // Sweden (RT 90) 206 | "6610": "24,-123,-94,-0.02,-0.25,0.13,1.1", // Xian 1980 207 | 208 | // For following datums same information as above applies 209 | 210 | // 582 105 414 -1.04 -0.35 3.08 8.3, // DHDN (Potsdam/Rauenberg) 211 | // -168 -60 320 0 0 0 0, // NTF (Paris meridian) 212 | // 660.077 13.551 369.344 0.804816 0.577692 0.952236 5.66, // CH 1903 (Switzerland) 213 | // -56 75.77 15.31 -0.37 -0.2 -0.21 -1.01, // HD72 (Hungarian Datum of 1972) 214 | // -134.73 -110.92 -292.66 0 0 0 1, // Cape (South Africa) 215 | // -117.763 -51.51 139.061 -0.292 -0.443 -0.277 -0.191, // Australia National (AGD84) 216 | // -129.193 -41.212 130.73 -0.246 -0.374 -0.329 -2.955" // Australia A.C.T. (AGD66) 217 | // -120.271 -64.543 161.632 -0.2175 0.0672 0.1291 2.4985, // Australia Tasmania (AGD66) 218 | // -119.353 -48.301 139.484 -0.415 -0.26 -0.437 -0.613, // Australia Victoria/NSW (AGD66) 219 | // -1.08 -0.27 -0.9 0 0 -0.16 -0.12, // Russia PZ90 220 | // 23.92 -141.27 -80.9 0 -0.35 -0.82 -0.12, // Russia SK42 221 | // 24.82 -131.21 -82.66 0 0 -0.16 -0.12, // Russia SK95 222 | // -146.414 507.337 680.507 0 0 0 0, // Tokyo97 223 | // -96.062 -82.428 -121.754 -4.801 -0.345 1.376 1.496, // KKJ 224 | // -40.59527 -18.54979 -69.33956 -2.508 -1.8319 2.6114 -4.2991, // Lithuanian Pulkovo 1942 225 | // -99.059 53.322 -112.486 -0.419 0.83 -1.885 0.999999, // Belgian 1972 7 parameter 226 | 227 | // These ones were taken from geodesy library (https://github.com/chrisveness/geodesy/blob/33d1bf53c069cd7dd83c6bf8531f5f3e0955c16e/latlon-ellipsoidal-datum.js#L54) 228 | // epsg.io doesn't seem to have those for the moment being, but I'll leave them anyway 229 | "1311": "89.5,93.8,123.1,-1.2,0.0,0.0,0.156", // ED50 230 | "1149": "0,0,0,0,0,0,0", // ETRS89 231 | "1954": "-482.530,130.596,-564.557,-8.150,1.042,0.214,0.631", // Irl1975 232 | "1314": "-446.448,125.157,-542.060, 20.4894,-0.1502, -0.2470,-0.8421", // OSGB36 233 | 234 | } 235 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | // This file was generated by "jsdoc -t node_modules/@otris/jsdoc-tsd ." command and edited manually to fix errors. Don't regenerate this file, unless there're API changes. 2 | 3 | /** 4 | * This library converts geokeys to Proj4 string and contains following functions: 5 | * 6 | * 1. {@link module:geokeysToProj4.toProj4} Does actual conversion 7 | * 1. {@link module:geokeysToProj4.convertCoordinates} Converts coordinates to use with proj4 8 | * 9 | * In general, you want to: 10 | * 1. Read geokeys. 11 | * 1. Pass them to `geokeysToProj4.toProj4()` (let's call returned object `projObj`). 12 | * 1. Pass `projObj.proj4` (which is Proj4 string) to proj4js. 13 | * 1. Convert pixel coordinates to CRS coordinates (let's call them `crsX` and `crsY`). 14 | * 1. Convert CRS coordinates to usable units *(in most cases, meters, but GeoTIFF allows speed, angular speed, and scale)*: `geokeysToProj4(crsX, crsY, projObj.coordinatesConversionParameters)`. 15 | * 1. The returned object contains X, Y, Z coordinates. which are ready to be projected with proj4js. So project them. Of course, you can alter this workflow to use this library with any other (presumably, server-side) software. 16 | */ 17 | declare module "geokeysToProj4"; 18 | 19 | /** 20 | * Geokeys. If you're working with `geotiff` library, this is result of `image.getGeoKeys()`. 21 | */ 22 | export interface GeoKeys { 23 | /** 24 | * See GeoTIFF docs for more information 25 | */ 26 | GeographicTypeGeoKey: number; 27 | /** 28 | * See GeoTIFF docs for more information 29 | */ 30 | GeogGeodeticDatumGeoKey: number; 31 | /** 32 | * See GeoTIFF docs for more information 33 | */ 34 | GeogPrimeMeridianGeoKey: number; 35 | /** 36 | * See GeoTIFF docs for more information 37 | */ 38 | GeogLinearUnitsGeoKey: number; 39 | /** 40 | * See GeoTIFF docs for more information 41 | */ 42 | GeogLinearUnitSizeGeoKey: number; 43 | /** 44 | * See GeoTIFF docs for more information 45 | */ 46 | GeogAngularUnitsGeoKey: number; 47 | /** 48 | * See GeoTIFF docs for more information 49 | */ 50 | GeogAngularUnitSizeGeoKey: number; 51 | /** 52 | * See GeoTIFF docs for more information 53 | */ 54 | GeogEllipsoidGeoKey: number; 55 | /** 56 | * See GeoTIFF docs for more information 57 | */ 58 | GeogSemiMajorAxisGeoKey: number; 59 | /** 60 | * See GeoTIFF docs for more information 61 | */ 62 | GeogSemiMinorAxisGeoKey: number; 63 | /** 64 | * See GeoTIFF docs for more information 65 | */ 66 | GeogInvFlatteningGeoKey: number; 67 | /** 68 | * See GeoTIFF docs for more information 69 | */ 70 | GeogPrimeMeridianLongGeoKey: number; 71 | /** 72 | * See GeoTIFF docs for more information 73 | */ 74 | ProjectedCSTypeGeoKey: number; 75 | /** 76 | * See GeoTIFF docs for more information 77 | */ 78 | ProjectionGeoKey: number; 79 | /** 80 | * See GeoTIFF docs for more information 81 | */ 82 | ProjCoordTransGeoKey: number; 83 | /** 84 | * See GeoTIFF docs for more information 85 | */ 86 | ProjLinearUnitsGeoKey: number; 87 | /** 88 | * See GeoTIFF docs for more information 89 | */ 90 | ProjLinearUnitSizeGeoKey: number; 91 | /** 92 | * See GeoTIFF docs for more information 93 | */ 94 | ProjStdParallel1GeoKey: number; 95 | /** 96 | * See GeoTIFF docs for more information 97 | */ 98 | ProjStdParallel2GeoKey: number; 99 | /** 100 | * See GeoTIFF docs for more information 101 | */ 102 | ProjNatOriginLongGeoKey: number; 103 | /** 104 | * See GeoTIFF docs for more information 105 | */ 106 | ProjNatOriginLatGeoKey: number; 107 | /** 108 | * See GeoTIFF docs for more information 109 | */ 110 | ProjFalseEastingGeoKey: number; 111 | /** 112 | * See GeoTIFF docs for more information 113 | */ 114 | ProjFalseNorthingGeoKey: number; 115 | /** 116 | * See GeoTIFF docs for more information 117 | */ 118 | ProjFalseOriginLongGeoKey: number; 119 | /** 120 | * See GeoTIFF docs for more information 121 | */ 122 | ProjFalseOriginLatGeoKey: number; 123 | /** 124 | * See GeoTIFF docs for more information 125 | */ 126 | ProjFalseOriginEastingGeoKey: number; 127 | /** 128 | * See GeoTIFF docs for more information 129 | */ 130 | ProjFalseOriginNorthingGeoKey: number; 131 | /** 132 | * See GeoTIFF docs for more information 133 | */ 134 | ProjCenterLongGeoKey: number; 135 | /** 136 | * See GeoTIFF docs for more information 137 | */ 138 | ProjCenterLatGeoKey: number; 139 | /** 140 | * See GeoTIFF docs for more information 141 | */ 142 | ProjCenterEastingGeoKey: number; 143 | /** 144 | * See GeoTIFF docs for more information 145 | */ 146 | ProjCenterNorthingGeoKey: number; 147 | /** 148 | * See GeoTIFF docs for more information 149 | */ 150 | ProjScaleAtNatOriginGeoKey: number; 151 | /** 152 | * See GeoTIFF docs for more information 153 | */ 154 | ProjScaleAtCenterGeoKey: number; 155 | /** 156 | * See GeoTIFF docs for more information 157 | */ 158 | ProjAzimuthAngleGeoKey: number; 159 | /** 160 | * See GeoTIFF docs for more information 161 | */ 162 | ProjStraightVertPoleLongGeoKey: number; 163 | /** 164 | * See GeoTIFF docs for more information 165 | */ 166 | VerticalGeoKey: number; 167 | /** 168 | * See GeoTIFF docs for more information 169 | */ 170 | VerticalUnitsGeoKey: number; 171 | /** 172 | * Datum to WGS transformation parameters, unofficial key 173 | */ 174 | GeogTOWGS84GeoKey: number[]; 175 | } 176 | 177 | /** 178 | * Errors that have occurred during conversion. 179 | * 180 | * Apart from listed properties, there's properties that named after geokeys with `NotSupported` suffix, i.e. `ProjFalseOriginLongGeoKeyNotSupported`. Values are EPSG codes assigned to those keys. These errors mean that the specified EPSG code is either not supported by this library, or it's new and hasn't been added yet. If it's the latter, please, create an issue at https://github.com/matafokka/geotiff-geokeys-to-proj4 181 | * 182 | * If an error has not occurred, it won't be present in this object. 183 | * 184 | * How to process these errors: 185 | * 186 | * 1. If it's your program's user's GeoTIFF, show an error message. 187 | * 1. If it's your GeoTIFF, fix it in a GIS. 188 | * 1. If you're sure that file is fine or want to discuss it, please, create an issue at https://github.com/matafokka/geotiff-geokeys-to-proj4 189 | */ 190 | export interface ConversionErrors { 191 | /** 192 | * `true` when both `GeographicTypeGeoKey` and `ProjectedCSTypeGeoKey` geokeys are set. In this case, `GeographicTypeGeoKey` is used. The cause of this error is broken geokeys. 193 | */ 194 | bothGCSAndPCSAreSet: boolean; 195 | /** 196 | * Specified CRS can't be represented as Proj4 string or it's new and hasn't been added to this library. Value is EPSG code of specified CRS. 197 | */ 198 | CRSNotSupported: number; 199 | /** 200 | * Geokey `GeogLinearUnitsGeoKey` is set to user-defined, but user hasn't specified `GeogLinearUnitSizeGeoKey`. In this case, every other key using this one assumed to be using meters. The cause of this error is broken geokeys. 201 | */ 202 | GeogLinearUnitSizeGeoKeyNotDefined: number; 203 | /** 204 | * Geokey `GeogAngularUnitsGeoKey` is set to user-defined, but user hasn't specified `GeogAngularUnitSizeGeoKey`. In this case, every other key using this one assumed to be using degrees. The cause of this error is broken geokeys. 205 | */ 206 | GeogAngularUnitSizeGeoKeyNotDefined: number; 207 | /** 208 | * Geokey `ProjLinearUnitsGeoKey` is set to user-defined, but user hasn't specified `ProjLinearUnitSizeGeoKey`. In this case, every other key using this one assumed to be using meters. The cause of this error is broken geokeys. 209 | */ 210 | ProjLinearUnitSizeGeoKeyNotDefined: number; 211 | /** 212 | * Conversion specified in `ProjectionGeoKey` is not supported by this library. Value is EPSG conversion code. 213 | */ 214 | conversionNotSupported: number; 215 | /** 216 | * Transformation specified in `ProjCoordTransGeoKey` is not supported by this library. Value is projection code. See http://geotiff.maptools.org/spec/geotiff6.html#6.3.3.3 for more information. 217 | */ 218 | coordinateTransformationNotSupported: number; 219 | /** 220 | * Vertical CS specified in `VerticalCSTypeGeoKey` is not supported by this library. Value is EPSG CS code. 221 | */ 222 | verticalCsNotSupported: number; 223 | /** 224 | * Vertical CS specified in `VerticalUnitsGeoKey` is not supported by this library. Value is EPSG uom code. 225 | */ 226 | verticalCsUnitsNotSupported: number; 227 | /** 228 | * Vertical datums are not supported by this library. If vertical CRS is user-defined, and `VerticalDatumGeoKey` is set, this error will be reported. Value is EPSG datum code. 229 | */ 230 | verticalDatumsNotSupported: number; 231 | } 232 | 233 | /** 234 | * Returned projection parameters 235 | */ 236 | export interface ProjectionParameters { 237 | /** 238 | * Proj4 string 239 | */ 240 | proj4: string; 241 | /** 242 | * If true, coordinates should be converted by using {@link module:geokeysToProj4.convertCoordinates} before passing to proj4js 243 | */ 244 | shouldConvertCoordinates: boolean; 245 | /** 246 | * Parameters to pass to {@link module:geokeysToProj4.convertCoordinates} 247 | */ 248 | coordinatesConversionParameters: CoordinateConversionParameters; 249 | /** 250 | * Multiply X coordinate by this parameter to convert it to standard unit 251 | */ 252 | "coordinatesConversionParameters.x": number; 253 | /** 254 | * Multiply Y coordinate by this parameter to convert it to standard unit 255 | */ 256 | "coordinatesConversionParameters.y": number; 257 | /** 258 | * Coordinates units after conversion. EPSG defines speed, angular speed and scale as linear units, and GeoTIFF relies on EPSG. So there's a chance that coordinates will represent something's different from distance (in case of PCS). Note: GCS will always use degrees; if PCS uses angles, radians will be used. 259 | */ 260 | coordinatesUnits: "metre" | "metre per second" | "second" | "radian" | "radian per second" | "scale" | "scale per second" | "degree"; 261 | /** 262 | * If `true`, geographic (either 2D or 3D) CRS is used. 263 | */ 264 | isGCS: boolean; 265 | /** 266 | * Errors that have occurred while processing geokeys. If no error has occurred, there will be an empty object. 267 | */ 268 | errors: ConversionErrors; 269 | } 270 | 271 | /** 272 | * Represents a point. X and Y coordinates are not necessarily cartesian coordinates (might be lon/lat, depends on CRS) and not in this order (if axes has been swapped by GeoTIFF). 273 | */ 274 | export interface Point { 275 | /** 276 | * X coordinate (coordinate of a first axis of CRS) of a point 277 | */ 278 | x: number; 279 | /** 280 | * Y coordinate (coordinate of a second axis of CRS) of a point 281 | */ 282 | y: number; 283 | /** 284 | * Z coordinate (coordinate of a third axis of CRS) of a point, i.e. transformed pixel value. Always points up. 285 | */ 286 | z: number; 287 | } 288 | 289 | /** 290 | * Parameters to pass to {@link module:geokeysToProj4.convertCoordinates} or to convert coordinates manually 291 | */ 292 | export interface CoordinateConversionParameters { 293 | 294 | /** 295 | * Multiply X coordinate by this parameter to convert it to standard units (meters or degrees) 296 | */ 297 | x: number, 298 | 299 | /** 300 | * Multiply Y coordinate by this parameter to convert it to standard units (meters or degrees) 301 | */ 302 | y: number, 303 | 304 | /** 305 | * Multiply Z coordinate (pixel value) by this parameter to convert it to standard units (meters) 306 | */ 307 | z: number, 308 | } 309 | 310 | /** 311 | * Converts GeoTIFFs geokeys to Proj4 string 312 | * 313 | * @param geoKeys {GeoKeys} Object where keys are geokeys (named exactly as in GeoTIFF specification) and values are, well, their values. 314 | */ 315 | export function toProj4(geoKeys: GeoKeys): ProjectionParameters; 316 | 317 | /** 318 | * Converts given coordinates to standard ones (i.e. meters or degrees). 319 | * 320 | * Basically, a short way to multiply `x, y, z` by `parameters.x`, `parameters.y` and `parameters.z` respectively. 321 | * 322 | * It does NOT accept image coordinates! Convert image coordinates to projection coordinates first (by multiplying image coordinates by `image.getResolution()` and adding coordinates of a top left corner) and then pass converted coordinates to this function. 323 | * 324 | * @param x {number} X coordinate 325 | * @param y {number} Y coordinate 326 | * @param z {number} Pixel value, i.e. Z coordinate 327 | * @param parameters {Object} getProjectionParameters().coordinatesConversionParameters 328 | * @return {module:geokeysToProj4.Point} Converted coordinates 329 | */ 330 | export function convertCoordinates(x: number, y: number, z: number, parameters: Object): Point; 331 | 332 | export default { toProj4, convertCoordinates }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geotiff-geokeys-to-proj4 2 | 3 | This library converts GeoTIFF's geokeys to Proj4 string, so you can [consume](#why-do-i-need-it) your images. 4 | 5 | Intended to be used with [geotiff.js](https://github.com/geotiffjs/geotiff.js/) and [proj4js](https://github.com/proj4js/proj4js), it's basically a glue between these libraries, but it can be used with alternatives. 6 | 7 | Designed for both frontend and backend. Supports ES3+ environments *(any browser from 2000 year)*. Size is ~1.46 Mb *(despite what npm says, it counts both sources and bundle)*. 8 | 9 | Grab it from npm: `npm install geotiff-geokeys-to-proj4`. 10 | 11 | ## Looking for maintainers! 12 | EPSG updates their database once in a month, these updates needs to be integrated into this library. For now, there's no one to perform this task. If you want to help, please, see [how to update the database](#manually-updating-from-epsg-database), create an issue to let me know that we've got a maintainer, and submit pull requests with the updated files. 13 | 14 | # [Docs](https://matafokka.github.io/geotiff-geokeys-to-proj4/module-geokeysToProj4.html) 15 | 16 | # Demo & example 17 | 18 | [GeoTIFF 3D DEM Viewer](https://matafokka.github.io/geotiff-3d-dem-viewer) demonstrates how to read GeoTIFF files and display them in [CesiumJS](https://cesium.com/) as 3D terrain. Check the [source code](https://github.com/matafokka/geotiff-3d-dem-viewer) for more. Files that'll interest you the most: [initial image setup](https://github.com/matafokka/geotiff-3d-dem-viewer/blob/master/src/components/Menu.tsx), [where all interesting stuff is](https://github.com/matafokka/geotiff-3d-dem-viewer/blob/master/src/etc/GeoTIFFManager.ts). 19 | 20 | Still, be sure to check out usage example below! 21 | 22 | # Usage 23 | 24 | ## Importing this library 25 | 26 | In **Node.js** or with **bundler**: `const geokeysToProj4 = require("geotiff-geokeys-to-proj4");` 27 | 28 | In **TypeScript**: `import geokeysToProj4 from "geotiff-geokeys-to-proj4";` or `import { toProj4, convertCoordinates } from "geotiff-geokeys-to-proj4";` 29 | 30 | In a **browser**: `` - after that you'll have `geokeysToProj4` global variable. 31 | 32 | Please note that ESM module is not transpiled to ES3. Use a bundler to transpile your app to whatever ES version you need. 33 | 34 | ## General usage 35 | 36 | In general, you want to: 37 | 38 | 1. Read geokeys. 39 | 1. Pass them to `geokeysToProj4.toProj4()` (let's call returned object `projObj`). 40 | 1. Pass `projObj.proj4` (which is Proj4 string) to proj4js. 41 | 1. Convert pixel coordinates and pixel value *(Z coordinate; DEM or geocentric CRS only; for other files, use any number, it doesn't matter)* to CRS coordinates (let's call them `crsX`, `crsY` and `crsZ`). 42 | 1. Convert CRS coordinates to usable units *(in most cases, meters or degrees, but GeoTIFF allows speed, angular speed, and scale)*: `geokeysToProj4(crsX, crsY, crsZ, projObj.coordinatesConversionParameters)`. 43 | 1. The returned object will contain `x, y, z` coordinates which are ready to be projected with proj4js. So project them. 44 | 45 | Of course, you can alter this workflow to use this library with any other (presumably, server-side) software. 46 | 47 | Let's demonstrate everything from start to finish on an example with [geotiff.js](https://github.com/geotiffjs/geotiff.js/) and [proj4js](https://github.com/proj4js/proj4js): 48 | 49 | ```js 50 | // Import all the stuff 51 | const geotiff = require("geotiff"); // geotiff.js 52 | const proj4 = require("proj4"); // proj4js 53 | const geokeysToProj4 = require("geotiff-geokeys-to-proj4"); // This library 54 | 55 | // Let's wrap our example in a function 56 | async function workWithGeoTIFF(blob) { 57 | // Read image. See geotiff.js docs on what all of that means. 58 | const tiff = await geotiff.fromBlob(blob); // Read blob 59 | const imageCount = await tiff.getImageCount(); // Get image count 60 | 61 | // Work with each image in a file 62 | for (let i = 0; i < imageCount; i++) { 63 | const image = await tiff.getImage(i); // Get image instance 64 | const geoKeys = image.getGeoKeys(); // Get geokeys 65 | const projObj = geokeysToProj4.toProj4(geoKeys); // Convert geokeys to proj4 string 66 | // The function above returns an object where proj4 property is a Proj4 string and coordinatesConversionParameters is conversion parameters which we'll use later 67 | const projection = proj4(projObj.proj4, "WGS84"); // Project our GeoTIFF to WGS84 68 | 69 | // Now you may want to deal with errors. Unfortunately, errors are unavoidable, but in most cases, you can warn the user or just continue on. 70 | // All occurred errors will be in projObj.errors object. See the docs for more information: 71 | // https://matafokka.github.io/geotiff-geokeys-to-proj4/module-geokeysToProj4.html#.ConversionErrors__anchor 72 | 73 | // Work with pixels 74 | // For looping over pixels 75 | const width = image.getWidth(); 76 | const height = image.getHeight(); 77 | // Pixel dimensions for converting image coordinates to source CRS coordinates 78 | const [originX, originY] = image.getOrigin(); 79 | const [xSize, ySize] = image.getResolution(); 80 | 81 | // Read rows 82 | for (let y = 0; y < height; y++) { 83 | // Read one row of pixels. Easier to deal with coordinates, takes less RAM. 84 | const raster = await image.readRasters({window: [0, y, width, y + 1]}); 85 | const color0 = raster[0]; // Raster is a TypedArray where elements are colors and their elements are pixel values of that color 86 | 87 | // Read columns. Since we're reading full row, we can replace color0.length with width, but I find color0.length more explicit. 88 | for (let x = 0; i < color0.length; x++) { 89 | 90 | // Convert current pixel's coordinates to CRS by: 91 | // 1. Multiplying current coordinates by pixel size which will result in distance from top-left corner in CRS units. 92 | // 2. Adding this value to top-left corner coordinates which will result in "global" coordinates in CRS units. 93 | // This will work because image is transformed by Affine Transformation which preserves parallelism. 94 | // Warning: this logic works only for source CRS, target CRS might screw up parallel lines, so pixel dimensions will not be constant! 95 | const crsX = originX + x * xSize; 96 | const crsY = originY + y * ySize; 97 | 98 | // DEM or geocentric CRS only: Z coordinate is pixel value. You may want to use another band or a combination of bands as Z coordinate. 99 | // For other files, you may use any number instead, it doesn't matter. 100 | const crsZ = color0[i]; 101 | 102 | // Check if coordinates are already in meters (or other "standard" units). If not, convert them. Either: 103 | 104 | // 1. Use convertCoordinates(): 105 | let point; 106 | 107 | if (projObj.shouldConvertCoordinates) // You can remove this condition, convertCoordinates() will work just fine in any case. Just a bit of time saving when dealing with large files. 108 | point = geokeysToProj4.convertCoordinates(crsX, crsY, crsZ, projObj.coordinatesConversionParameters); 109 | else 110 | point = { x: crsX, y: crsY, z: crsZ }; 111 | 112 | // 2. Just multiply manually to speed up execution by removing function calls and conditions: 113 | point = { 114 | x: crsX * projObj.coordinatesConversionParameters.x, 115 | y: crsY * projObj.coordinatesConversionParameters.y, 116 | z: crsZ * projObj.coordinatesConversionParameters.z, 117 | } 118 | 119 | let projectedPoint = projection.forward(point); // Project these coordinates 120 | // Work with projected coordinates... 121 | } 122 | } 123 | } 124 | } 125 | ``` 126 | 127 | # Known issues 128 | 129 | I don't know which geokeys should take precedence over which. I did what seems to be logical, but I might be wrong. If you know anything about it, please, create an issue and describe whether I'm wrong (and how to fix it) or right (so I'll remove this text). 130 | 131 | Vertical CRS which use local depth are not supported because reference points are needed. Following has been excluded: 132 | 133 | 1. Vertical CS: 1049 and 1050. 134 | 2. Vertical CRS: 8378 and 8897. 135 | 136 | Vertical datums are not supported at all because mappings are needed. If you have at least some mappings, please, let me know by creating an issue. 137 | 138 | # Manually updating from EPSG database 139 | 140 | Unfortunately, [epsg.org](https://epsg.org) doesn't provide public access to their database. You need to register an account, and only then you'll be able to download database. Since all of that is for the users and not for bots, both client and server might change in the future, so there's no point in writing self-update script. 141 | 142 | To update: 143 | 144 | 1. Clone this repo. 145 | 1. Run `npm install` on cloned repo. 146 | 1. Set up [PostgreSQL](https://www.postgresql.org/) server *(other RDBMS are not supported)*. Default configuration should be fine, just create a user and a database for that user. 147 | 1. Head over to [here](https://epsg.org/download-dataset.html), create an account (if you don't have one) and download PostgreSQL scripts. 148 | 1. Extract downloaded scripts to `postgres_prep` directory. 149 | 1. Run `npm run update-all` to update everything and rebuilds the project. See arguments below. **Warning: this script utilizes `epsg` schema and will erase it completely! It's hardcoded and can't be changed.** To avoid data loss, create a separate database solely to run this script. 150 | 151 | There're actually two scripts: `update-all` which has been described above and `update-existing` which will update project files from existing database. Both of these scripts accepts following arguments: 152 | 153 | ``` 154 | --host - PostgreSQL host - Defaults to "localhost" 155 | --port - PostgreSQL port - Defaults to "5432" 156 | --database - Database containing "epsg" schema - Defaults to "postgres" 157 | --user - PostgreSQL database user - Defaults to "user" 158 | --password - Password for the user - Defaults to "12345" 159 | ``` 160 | 161 | Arguments passed to npm scripts in following manner: `npm run [script name] -- [script arguments]`, for example: `npm run update-all -- --user myuser --password mypassword`. Note the `--` separator, it should always present when using this library's scripts. 162 | 163 | # FAQ 164 | 165 | ## Why do I need it? 166 | 167 | Every GeoTIFF is bound to some kind of Coordinate Reference System (CRS) which in combination with georeferencing data defines where pixel coordinates are on the Earth. These CRS are quite different from what you can find, let's say, in Leaflet or OpenLayers which uses WGS *(which is CRS too)* by default. 168 | 169 | So you need to convert image coordinates from one CRS to another. [proj4js](https://github.com/proj4js/proj4js) is the best tool to do that. You need to supply an input and output CRS *(which Proj4 calls a projection; yes, terminology is quite confusing)* to it in form of a string. 170 | 171 | For output, you can specify whatever your software is using. But input is defined by geokeys *(information embedded into GeoTIFF file)*. Geokeys are really hard to handle. This library will create an input Proj4 string for you. 172 | 173 | How to perform all of that is described in [example](#general-usage) above. 174 | 175 | Without these procedures, you'll get wrong results. 176 | 177 | ## How is it different from [epsg-index](https://github.com/derhuerst/epsg-index)? 178 | 179 | epsg-index only provides projections definitions, GeoTIFF uses more than that. 180 | 181 | ## How does it compare to already existing and battle-proven libraries such as GDAL? 182 | 183 | Pros: 184 | 185 | 1. Can be used in a browser. There're no alternatives for now except for compiling another library to WebAssembly. Even then this library wins because it can run in ES3 environment (any browser from 2000 year). 186 | 1. Easier to use in Node since this library is written in pure JS. Non-JS libraries requires a wrapper or use of CLI. 187 | 188 | Cons: 189 | 190 | 1. Libraries such as GDAL are, in fact, already battle-proven. Since this library is new, I'm the sole developer for now, and I'm not a cartographer, there might be some bugs. 191 | 1. Since there're no maintainers to update database, more popular libraries might have newer data, though, it shouldn't make big difference. 192 | 193 | **To summarize:** use this library for a frontend, but existing libraries with a wrapper might be better for a backend; 194 | 195 | ## The example above can be put in a function that takes a callback. Why didn't you do that? 196 | 197 | Because: 198 | 199 | 1. It's not the goal of this library. 200 | 1. It'll slow down reading which is already quite slow. 201 | 1. You'll still need to understand how all this stuff works and be able to modify the example when needed. 202 | 203 | ## This library produces wrong results! 204 | 205 | This library only maps geokeys to their Proj4 definitions and builds a final Proj4 string. It doesn't perform any projections. 206 | 207 | If you've encountered a bug, please, take a look at Proj4 string first and compare it to one generated by a professional GIS. If something is fundamentally wrong, it's the issue of this library. Otherwise, there's something wrong with Proj4. 208 | 209 | If you're comparing results with what [epsg.io](https://epsg.io) says, note that while [epsg.io](https://epsg.io) is mostly right, it's not an official data source. For example, [epsg.io](https://epsg.io) maps CRS `21780` to `+proj=somerc`, but the right projection seems to be `+proj=omerc`. If you're not sure how to verify which Proj4 string is correct, feel free to message me anyway. 210 | 211 | Please, don't report redundant parameters *(for example, `+a` and `+b` that are the same as `+ellps` provides)* as a bug. This behavior simplifies development, increases performance by not making useless comparisons and ensures that the right parameters are used. 212 | 213 | Missing `+units` is also not a bug, you're converting coordinates to meters by using `geokeysToProj4.convertCoordinates()`. 214 | 215 | ## What are the sources for Proj4 strings? 216 | 217 | Official database from epsg.org is the main data source. Data is enriched by some [additional sources](EPSG/data/AdditionalCRS.js). [epsg.io](https://epsg.io) is used to selectively check if Proj4 strings are correct. 218 | 219 | # Contributing 220 | 221 | You can contribute by solving [described issues](#known-issues) or by [maintaining the database](#looking-for-maintainers). 222 | 223 | # Related projects 224 | 225 | 1. [geotiff.js](https://github.com/geotiffjs/geotiff.js/) is a library that can read GeoTIFF images. 226 | 1. [proj4js](https://github.com/proj4js/proj4js) is a port of Proj4 to JS. 227 | 1. [epsg-index](https://github.com/derhuerst/epsg-index) is a machine-readable index of all EPSG coordinate reference systems. 228 | 1. [epsg.io](https://epsg.io) is a website that provides all the EPSG stuff mostly in human-readable form, and an API to access it. 229 | 1. [geokeys-to-proj4js](https://github.com/GeoTIFF/geokeys-to-proj4js) is a project with the same goal but far from to be finished, and it looks abandoned. 230 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | // Polyfills 2 | require("core-js/stable/object/keys"); 3 | require("core-js/stable/string/trim"); 4 | 5 | const CRS = require("./EPSG/data/CRS.js"); 6 | const Units = require("./EPSG/data/Units.js"); 7 | const ProjCoordTransGeoKey = require("./EPSG/data/ProjCoordTransGeoKey.js"); 8 | const ProjectionGeoKey = require("./EPSG/data/ProjectionGeoKey.js"); 9 | const PCSKeys = require("./EPSG/data/PCSKeys.js"); 10 | const override = require("./EPSG/data/Overrides.js"); 11 | 12 | const geodeticKeysToCopy = [ 13 | { 14 | names: ["GeodeticDatumGeoKey", "GeogGeodeticDatumGeoKey"], // Newer keys come first 15 | obj: require("./EPSG/data/GeogGeodeticDatumGeoKey.js"), 16 | }, 17 | { 18 | names: ["PrimeMeridianGeoKey", "GeogPrimeMeridianGeoKey"], 19 | obj: require("./EPSG/data/GeogPrimeMeridianGeoKey.js"), 20 | }, 21 | { 22 | names: ["EllipsoidGeoKey", "GeogEllipsoidGeoKey"], 23 | obj: require("./EPSG/data/GeogEllipsoidGeoKey.js"), 24 | }, 25 | ]; 26 | 27 | /** 28 | * Represents user-defined value 29 | * @type {number} 30 | * @private 31 | */ 32 | const userDefined = 32767; 33 | 34 | /** 35 | * Order in which tokens should be written to final string to make it look nice 36 | * @type {string[]} 37 | * @private 38 | */ 39 | const tokensOrder = ["+proj", "+lat_0", "+lon_0", "+lat_1", "+lat_ts", "+lon_1", "+lat_2", "+lon_2", "+k_0", "+x_0", "+y_0", "+ellps", "+a", "+b", "+pm", "+towgs84", "+approx"]; 40 | 41 | /** 42 | * Parses given argument as float and returns its fixed value 43 | * 44 | * @ignore 45 | * @param n {*} Number to fix 46 | * @returns {number|*} Fixed number or original value if it can't be parsed as float 47 | */ 48 | const toFixed = (n) => { 49 | if (isNaN(n)) 50 | return n; 51 | return parseFloat(parseFloat(n).toFixed(12)); 52 | } 53 | 54 | /** 55 | * Geokeys. If you're working with `geotiff` library, this is result of `image.getGeoKeys()`. 56 | * @typedef {Object} module:geokeysToProj4.GeoKeys 57 | * @property {number} GeodeticCRSGeoKey See GeoTIFF docs for more information 58 | * @property {number} GeographicTypeGeoKey See GeoTIFF docs for more information 59 | * @property {number} GeodeticDatumGeoKey See GeoTIFF docs for more information 60 | * @property {number} GeogGeodeticDatumGeoKey See GeoTIFF docs for more information 61 | * @property {number} PrimeMeridianGeoKey See GeoTIFF docs for more information 62 | * @property {number} GeogPrimeMeridianGeoKey See GeoTIFF docs for more information 63 | * @property {number} GeogLinearUnitsGeoKey See GeoTIFF docs for more information 64 | * @property {number} GeogLinearUnitSizeGeoKey See GeoTIFF docs for more information 65 | * @property {number} GeogAngularUnitsGeoKey See GeoTIFF docs for more information 66 | * @property {number} GeogAngularUnitSizeGeoKey See GeoTIFF docs for more information 67 | * @property {number} GeogEllipsoidGeoKey See GeoTIFF docs for more information 68 | * @property {number} EllipsoidSemiMajorAxisGeoKey See GeoTIFF docs for more information 69 | * @property {number} GeogSemiMajorAxisGeoKey See GeoTIFF docs for more information 70 | * @property {number} EllipsoidSemiMinorAxisGeoKey See GeoTIFF docs for more information 71 | * @property {number} GeogSemiMinorAxisGeoKey See GeoTIFF docs for more information 72 | * @property {number} EllipsoidInvFlatteningGeoKey See GeoTIFF docs for more information 73 | * @property {number} GeogInvFlatteningGeoKey See GeoTIFF docs for more information 74 | * @property {number} PrimeMeridianLongitudeGeoKey See GeoTIFF docs for more information 75 | * @property {number} PrimeMeridianLongitudeGeoKey See GeoTIFF docs for more information 76 | * @property {number} GeogPrimeMeridianLongGeoKey See GeoTIFF docs for more information 77 | * @property {number} ProjectedCRSGeoKey See GeoTIFF docs for more information 78 | * @property {number} ProjectedCSTypeGeoKey See GeoTIFF docs for more information 79 | * @property {number} ProjectionGeoKey See GeoTIFF docs for more information 80 | * @property {number} ProjMethodGeoKey See GeoTIFF docs for more information 81 | * @property {number} ProjCoordTransGeoKey See GeoTIFF docs for more information 82 | * @property {number} ProjLinearUnitsGeoKey See GeoTIFF docs for more information 83 | * @property {number} ProjLinearUnitSizeGeoKey See GeoTIFF docs for more information 84 | * @property {number} ProjStdParallel1GeoKey See GeoTIFF docs for more information 85 | * @property {number} ProjStdParallel2GeoKey See GeoTIFF docs for more information 86 | * @property {number} ProjNatOriginLongGeoKey See GeoTIFF docs for more information 87 | * @property {number} ProjNatOriginLatGeoKey See GeoTIFF docs for more information 88 | * @property {number} ProjFalseEastingGeoKey See GeoTIFF docs for more information 89 | * @property {number} ProjFalseNorthingGeoKey See GeoTIFF docs for more information 90 | * @property {number} ProjFalseOriginLongGeoKey See GeoTIFF docs for more information 91 | * @property {number} ProjFalseOriginLatGeoKey See GeoTIFF docs for more information 92 | * @property {number} ProjFalseOriginEastingGeoKey See GeoTIFF docs for more information 93 | * @property {number} ProjFalseOriginNorthingGeoKey See GeoTIFF docs for more information 94 | * @property {number} ProjCenterLongGeoKey See GeoTIFF docs for more information 95 | * @property {number} ProjCenterLatGeoKey See GeoTIFF docs for more information 96 | * @property {number} ProjCenterEastingGeoKey See GeoTIFF docs for more information 97 | * @property {number} ProjCenterNorthingGeoKey See GeoTIFF docs for more information 98 | * @property {number} ProjScaleAtNatOriginGeoKey See GeoTIFF docs for more information 99 | * @property {number} ProjScaleAtCenterGeoKey See GeoTIFF docs for more information 100 | * @property {number} ProjAzimuthAngleGeoKey See GeoTIFF docs for more information 101 | * @property {number} ProjStraightVertPoleLongGeoKey See GeoTIFF docs for more information 102 | * @property {number} VerticalGeoKey See GeoTIFF docs for more information 103 | * @property {number} VerticalCSTypeGeoKey See GeoTIFF docs for more information 104 | * @property {number} VerticalUnitsGeoKey See GeoTIFF docs for more information 105 | * @property {number[]} GeogTOWGS84GeoKey Datum to WGS transformation parameters, unofficial key 106 | */ 107 | 108 | /** 109 | * Errors that have occurred during conversion. 110 | * 111 | * Apart from listed properties, there's properties that named after geokeys with `NotSupported` suffix, i.e. `ProjFalseOriginLongGeoKeyNotSupported`. Values are EPSG codes assigned to those keys. These errors mean that the specified EPSG code is either not supported by this library, or it's new and hasn't been added yet. If it's the latter, please, create an issue at https://github.com/matafokka/geotiff-geokeys-to-proj4 112 | * 113 | * If an error has not occurred, it won't be present in this object. 114 | * 115 | * How to process these errors: 116 | * 117 | * 1. If it's your program's user's GeoTIFF, show an error message. 118 | * 1. If it's your GeoTIFF, fix it in a GIS. 119 | * 1. If you're sure that file is fine or want to discuss it, please, create an issue at https://github.com/matafokka/geotiff-geokeys-to-proj4 120 | * 121 | * @typedef {Object} module:geokeysToProj4.ConversionErrors 122 | * @property {boolean} bothGCSAndPCSAreSet `true` When both `GeodeticCRSGeoKey` (or `GeographicTypeGeoKey`) and `ProjectedCRSGeoKey` (or `ProjectedCSTypeGeoKey`) geokeys are set. In this case, `GeographicTypeGeoKey` is used. The cause of this error is broken geokeys. 123 | * @property {number} CRSNotSupported Specified CRS can't be represented as Proj4 string or it's new and hasn't been added to this library. Value is EPSG code of specified CRS. 124 | * @property {number} GeogLinearUnitSizeGeoKeyNotDefined Geokey `GeogLinearUnitsGeoKey` is set to user-defined, but user hasn't specified `GeogLinearUnitSizeGeoKey`. In this case, every other key using this one assumed to be using meters. The cause of this error is broken geokeys. 125 | * @property {number} GeogAngularUnitSizeGeoKeyNotDefined Geokey `GeogAngularUnitsGeoKey` is set to user-defined, but user hasn't specified `GeogAngularUnitSizeGeoKey`. In this case, every other key using this one assumed to be using degrees. The cause of this error is broken geokeys. 126 | * @property {number} ProjLinearUnitSizeGeoKeyNotDefined Geokey `ProjLinearUnitsGeoKey` is set to user-defined, but user hasn't specified `ProjLinearUnitSizeGeoKey`. In this case, every other key using this one assumed to be using meters. The cause of this error is broken geokeys. 127 | * @property {number} conversionNotSupported Conversion specified in `ProjectionGeoKey` is not supported by this library. Value is EPSG conversion code. 128 | * @property {number} coordinateTransformationNotSupported Transformation specified in `ProjMethodGeoKey` (or `ProjCoordTransGeoKey`) is not supported by this library. Value is projection code. See http://geotiff.maptools.org/spec/geotiff6.html#6.3.3.3 for more information. 129 | * @property {number} verticalCsNotSupported Vertical CS specified in `VerticalGeoKey` (or `VerticalCSTypeGeoKey`) is not supported by this library. Value is EPSG CS code. 130 | * @property {number} verticalCsUnitsNotSupported Vertical CS specified in `VerticalUnitsGeoKey` is not supported by this library. Value is EPSG uom code. 131 | * @property {number} verticalDatumsNotSupported Vertical datums are not supported by this library. If vertical CRS is user-defined, and `VerticalDatumGeoKey` is set, this error will be reported. Value is EPSG datum code. 132 | */ 133 | 134 | /** 135 | * Parameters to pass to {@link module:geokeysToProj4.convertCoordinates} or to convert coordinates manually 136 | * @typedef {Object} module:geokeysToProj4.CoordinateConversionParameters 137 | * @property {number} x Multiply X coordinate by this parameter to convert it to standard units (meters or degrees) 138 | * @property {number} y Multiply Y coordinate by this parameter to convert it to standard units (meters or degrees) 139 | * @property {number} z Multiply Z coordinate (pixel value) by this parameter to convert it to standard units (meters) 140 | */ 141 | 142 | /** 143 | * Returned projection parameters 144 | * @typedef {Object} module:geokeysToProj4.ProjectionParameters 145 | * @property {string} proj4 Proj4 string 146 | * @property {boolean} shouldConvertCoordinates If true, coordinates should be converted by using {@link module:geokeysToProj4.convertCoordinates} before passing to proj4js 147 | * @property {CoordinateConversionParameters} coordinatesConversionParameters Parameters to pass to {@link module:geokeysToProj4.convertCoordinates} 148 | * @property {"metre"|"metre per second"|"second"|"radian"|"radian per second"|"scale"|"scale per second"|"degree"} coordinatesUnits Coordinates units after conversion. EPSG defines speed, angular speed and scale as linear units, and GeoTIFF relies on EPSG. So there's a chance that coordinates will represent something's different from distance (in case of PCS). Note: GCS will always use degrees; if PCS uses angles, radians will be used. 149 | * @property {boolean} isGCS If `true`, geographic (either 2D or 3D) CRS is used. 150 | * @property {module:geokeysToProj4.ConversionErrors} errors Errors that have occurred while processing geokeys. If no error has occurred, there will be an empty object. 151 | */ 152 | 153 | /** 154 | * Represents a point. X and Y coordinates are not necessarily cartesian coordinates (might be lon/lat, depends on CRS) and not in this order (if axes has been swapped by GeoTIFF). 155 | * @typedef {Object} module:geokeysToProj4.Point 156 | * @property {number} x X coordinate (coordinate of a first axis of CRS) of a point 157 | * @property {number} y Y coordinate (coordinate of a second axis of CRS) of a point 158 | * @property {number} z Z coordinate (coordinate of a third axis of CRS) of a point, i.e. transformed pixel value. Always points up. 159 | */ 160 | 161 | /** 162 | * This library converts geokeys to Proj4 string and contains following functions: 163 | * 1. {@link module:geokeysToProj4.toProj4} Does actual conversion 164 | * 1. {@link module:geokeysToProj4.convertCoordinates} Converts coordinates to use with proj4 165 | * 166 | * In general, you want to: 167 | * 1. Read geokeys. 168 | * 1. Pass them to `geokeysToProj4.toProj4()` (let's call returned object `projObj`). 169 | * 1. Pass `projObj.proj4` (Proj4 string) to proj4js. 170 | * 1. Convert pixel coordinates to CRS coordinates (let's call them `crsX` and `crsY`). 171 | * 1. Convert CRS coordinates to usable units *(in most cases, meters, but GeoTIFF allows speed, angular speed, and scale)*: `geokeysToProj4(crsX, crsY, projObj.coordinatesConversionParameters)`. 172 | * 1. The returned object contains X, Y, Z coordinates. which are ready to be projected with proj4js. So project them. 173 | * 174 | * Of course, you can alter this workflow to use this library with any other (presumably, server-side) software. 175 | * 176 | * @module geokeysToProj4 177 | */ 178 | module.exports = { 179 | /** 180 | * Converts GeoTIFFs geokeys to Proj4 string 181 | * @param geoKeys {GeoKeys} Object where keys are geokeys (named exactly as in GeoTIFF specification) and values are, well, their values. 182 | * @return {module:geokeysToProj4.ProjectionParameters} Projection parameters 183 | */ 184 | toProj4: function (geoKeys) { 185 | 186 | ///////////////////////// 187 | // Read base CRS // 188 | ///////////////////////// 189 | 190 | let proj = "", x = 1, y = 1, z = 1, errors = {}; 191 | 192 | // First, get CRS, both geographic and projected 193 | const geographicCode = geoKeys.GeodeticCRSGeoKey || geoKeys.GeographicTypeGeoKey; 194 | const projectedCode = geoKeys.ProjectedCRSGeoKey || geoKeys.ProjectedCSTypeGeoKey; 195 | 196 | if (geographicCode && projectedCode) 197 | errors.bothGCSAndPCSAreSet = true; 198 | 199 | let crsKey = geographicCode || projectedCode; 200 | if (crsKey) { 201 | let crs = CRS[crsKey + ""]; 202 | 203 | // Numbers are multipliers from vertical CRS 204 | if (crs && typeof crs !== "number") { 205 | if (typeof crs === "string") 206 | proj = crs; 207 | else { 208 | proj = crs.p; 209 | x = crs.x; 210 | y = crs.y; 211 | z = crs.z || z; 212 | } 213 | } else if (crsKey !== userDefined) 214 | errors.CRSNotSupported = crsKey; 215 | } 216 | 217 | ///////////////////////// 218 | // Read vertical CS // 219 | ///////////////////////// 220 | 221 | const verticalCode = geoKeys.VerticalGeoKey || geoKeys.VerticalCSTypeGeoKey; 222 | 223 | if (verticalCode && verticalCode !== userDefined) { 224 | let verticalCs = CRS[verticalCode + ""]; // Yes, that's CRS, not CS. Either vertical CRS or geographic 3D CRS may be set. 225 | 226 | if (typeof verticalCs === "number") 227 | z = verticalCs; 228 | else if (verticalCs.z) 229 | verticalCs = verticalCs.z; 230 | else 231 | errors.verticalCsNotSupported = verticalCode; 232 | } else if (geoKeys.VerticalUnitsGeoKey) { 233 | const newZ = Units[geoKeys.VerticalUnitsGeoKey]; 234 | z = newZ || z; 235 | 236 | if (!newZ) 237 | errors.verticalCsUnitsNotSupported = geoKeys.VerticalUnitsGeoKey; 238 | 239 | if (geoKeys.VerticalDatumGeoKey) 240 | errors.verticalDatumsNotSupported = geoKeys.VerticalDatumGeoKey; 241 | } 242 | 243 | if (!proj) 244 | proj = "+proj=longlat"; // If GeoTIFF uses PCS, string rebuilding will override +proj 245 | 246 | ///////////////////////// 247 | // Copy geodetic keys // 248 | ///////////////////////// 249 | 250 | for (const key of geodeticKeysToCopy) { 251 | for (const name of key.names) { 252 | const value = geoKeys[name]; 253 | 254 | if (value) { 255 | const keyValue = key.obj[value + ""]; 256 | 257 | if (keyValue !== undefined) { 258 | proj += " " + keyValue; 259 | continue; 260 | } 261 | } 262 | } 263 | } 264 | 265 | // All other geokeys will override ones provided by keys above 266 | 267 | ///////////////////////// 268 | // Read units // 269 | ///////////////////////// 270 | 271 | let units = { 272 | GeogLinearUnitsGeoKey: 1, 273 | GeogAngularUnitsGeoKey: 1, 274 | ProjLinearUnitsGeoKey: 1, 275 | } 276 | 277 | let unitDefs = {}; // Values are booleans, true means that GeoTIFF redefines units 278 | 279 | for (let name in units) { 280 | let m, key = geoKeys[name]; 281 | 282 | if (!key) 283 | continue; 284 | 285 | if (key === userDefined) { 286 | let splitAt = name.length - 7, // I.e., "GeogLinearUnitsGeoKey" will be split to "GeogLinearUnit" and "sGeoKey" 287 | sizeKeyName = name.substr(0, splitAt) + "SizeGeoKey", 288 | size = geoKeys[sizeKeyName]; 289 | if (size) 290 | m = size; 291 | else 292 | errors[sizeKeyName + "NotDefined"] = true; 293 | } else if (key) 294 | m = Units[key.toString()]?.m; 295 | 296 | if (!m) { 297 | m = 1; 298 | errors[name + "NotSupported"] = key; // This EPSG key doesn't exist, assuming meters or degrees 299 | } else { 300 | unitDefs[name] = true; 301 | if (name === "GeogAngularUnitsGeoKey") 302 | m *= 180 / Math.PI; // Radians are angular base units 303 | } 304 | units[name] = m; 305 | } 306 | 307 | ///////////////////////// 308 | // Read axes // 309 | ///////////////////////// 310 | 311 | let a = (geoKeys.EllipsoidSemiMajorAxisGeoKey || geoKeys.GeogSemiMajorAxisGeoKey || 0) * units.GeogLinearUnitsGeoKey; 312 | let b = (geoKeys.EllipsoidSemiMinorAxisGeoKey || geoKeys.GeogSemiMinorAxisGeoKey || 0) * units.GeogLinearUnitsGeoKey; 313 | const invFlattening = geoKeys.EllipsoidInvFlatteningGeoKey || geoKeys.GeogInvFlatteningGeoKey; 314 | 315 | if (invFlattening && a) // Can't calculate semi minor axis if semi major axis is missing 316 | b = a - a / invFlattening; 317 | 318 | if (a) 319 | proj += " +a=" + a; 320 | 321 | if (!b && proj.indexOf("+b") === -1) 322 | b = a; 323 | 324 | if (b) 325 | proj += " +b=" + b; 326 | 327 | // Get prime meridian 328 | const pm = geoKeys.PrimeMeridianLongitudeGeoKey || geoKeys.GeogPrimeMeridianLongGeoKey; 329 | if (pm) 330 | proj += " +pm=" + (pm * units.GeogAngularUnitsGeoKey); 331 | 332 | // To WGS key 333 | if (geoKeys.GeogTOWGS84GeoKey) 334 | proj += " +towgs84=" + geoKeys.GeogTOWGS84GeoKey.join(); 335 | 336 | ///////////////////////// 337 | // PCS // 338 | ///////////////////////// 339 | 340 | // This key despite its name defines conversion -- a method (and its parameters) which converts coordinates. The basic example of it is a projection. 341 | if (geoKeys.ProjectionGeoKey && geoKeys.ProjectionGeoKey !== userDefined) { 342 | let conversion = ProjectionGeoKey[geoKeys.ProjectionGeoKey + ""]; 343 | if (conversion) 344 | proj += " +proj=" + conversion; 345 | else 346 | errors.conversionNotSupported = geoKeys.ProjectionGeoKey; 347 | } 348 | 349 | let objects = ["o1", "o2", "o3"]; 350 | for (let name of objects) { 351 | let object = PCSKeys[name]; 352 | for (let key in object) { 353 | if (!object.hasOwnProperty(key)) 354 | continue; 355 | 356 | let keyValue = geoKeys[key]; 357 | if (keyValue === undefined) 358 | continue; 359 | 360 | // Get key definition and units 361 | let keyDef = object[key], m; 362 | if (keyDef.u === 1) 363 | m = units.GeogAngularUnitsGeoKey; 364 | else if (keyDef.u === 2) 365 | m = units.ProjLinearUnitsGeoKey; 366 | else 367 | m = 1; 368 | 369 | keyValue *= m; 370 | proj += ` +${keyDef.p}=${keyValue}`; 371 | } 372 | } 373 | 374 | // This key should take precedence over all other keys 375 | const transformKey = geoKeys.ProjMethodGeoKey || geoKeys.ProjCoordTransGeoKey; 376 | 377 | if (transformKey && transformKey !== userDefined) { 378 | let projName = ProjCoordTransGeoKey[transformKey + ""]; 379 | if (projName) 380 | proj += " +proj=" + projName; 381 | else 382 | errors.coordinateTransformationNotSupported = transformKey; 383 | } 384 | 385 | // Gosh, everybody seems to suggest to add +no_defs to avoid errors caused by default values. Let's follow this suggestion. 386 | proj += " +no_defs"; 387 | 388 | ///////////////////////// 389 | // String processing // 390 | ///////////////////////// 391 | 392 | // Tokenize string 393 | 394 | let keyValues = proj.split(" "); 395 | let tokens = {}; 396 | for (let kv of keyValues) { 397 | let kvArr = kv.trim().split("="); 398 | if (kvArr.length === 1) 399 | tokens[kvArr[0]] = null; 400 | else 401 | tokens[kvArr[0].trim()] = kvArr[1].trim(); 402 | } 403 | 404 | override(tokens, geoKeys); // Apply all necessary overrides 405 | 406 | // Build final string 407 | 408 | proj = ""; 409 | let tokenArrays = [tokensOrder, Object.keys(tokens)]; 410 | let processedTokens = {}; 411 | 412 | for (let arr of tokenArrays) { 413 | for (let token of arr) { 414 | if (!(token in tokens) || processedTokens[token]) 415 | continue; 416 | 417 | proj += token; 418 | let tokenValue = tokens[token]; 419 | if (tokenValue !== null) 420 | proj += "=" + toFixed(tokenValue); 421 | 422 | proj += " "; 423 | processedTokens[token] = true; 424 | } 425 | } 426 | 427 | // Find out which units to use 428 | 429 | let isGCS = (tokens["+proj"] === "longlat"), coordUnits; 430 | if (isGCS) { 431 | coordUnits = "degree"; 432 | if (unitDefs.GeogAngularUnitsGeoKey) { 433 | x = units.GeogAngularUnitsGeoKey; 434 | y = units.GeogAngularUnitsGeoKey; 435 | } 436 | } else { 437 | coordUnits = "metre"; 438 | if (unitDefs.ProjLinearUnitsGeoKey) { 439 | let m; 440 | if (typeof units.ProjLinearUnitsGeoKey === "number") 441 | m = units.ProjLinearUnitsGeoKey; 442 | else { 443 | m = units.ProjLinearUnitsGeoKey.m; 444 | coordUnits = units.ProjLinearUnitsGeoKey.t; 445 | } 446 | x = m; 447 | y = m; 448 | } 449 | } 450 | 451 | x = toFixed(x); 452 | y = toFixed(y); 453 | z = toFixed(z); 454 | 455 | return { 456 | proj4: proj, 457 | coordinatesConversionParameters: { x, y, z }, 458 | shouldConvertCoordinates: (x !== 1 || y !== 1 || z !== 1), 459 | coordinatesUnits: coordUnits, 460 | isGCS: isGCS, 461 | errors: errors, 462 | } 463 | }, 464 | 465 | /** 466 | * Converts given coordinates to standard ones (i.e. meters or degrees). 467 | * 468 | * Basically, a short way to multiply `x, y, z` by `parameters.x`, `parameters.y` and `parameters.z` respectively. 469 | * 470 | * It does NOT accept image coordinates! Convert image coordinates to projection coordinates first (by multiplying image coordinates by `image.getResolution()` and adding coordinates of a top left corner) and then pass converted coordinates to this function. 471 | * 472 | * @param x {number} X coordinate 473 | * @param y {number} Y coordinate 474 | * @param z {number} Pixel value, i.e. Z coordinate. If you don't use DEM, pass any number. 475 | * @param parameters {Object} getProjectionParameters().coordinatesConversionParameters 476 | * @return {module:geokeysToProj4.Point} Converted coordinates 477 | */ 478 | convertCoordinates: function (x, y, z, parameters) { 479 | return { 480 | x: x * parameters.x, 481 | y: y * parameters.y, 482 | z: z * parameters.z, 483 | } 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /EPSG/data/GeogGeodeticDatumGeoKey.js: -------------------------------------------------------------------------------- 1 | 2 | // WARNING: This file has been generated automatically 3 | 4 | /** 5 | * Maps EPSG datums to their proj4 definition. Append values directly to Proj4 string. 6 | * @type {Object} 7 | */ 8 | module.exports = {"1024":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","1025":"+ellps=GRS67 +a=6378160 +b=6356774.719195306 +pm=0","1026":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1029":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1031":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","1032":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1033":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1034":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1035":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1036":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1037":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1038":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1041":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1042":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1043":"+a=6378137 +b=6356752.314140356 +pm=0","1044":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1045":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","1046":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1047":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1048":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","1052":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","1053":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0","1055":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=-17.666666666666668","1056":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1057":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1058":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1060":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1061":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1062":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1063":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1064":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1065":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1066":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1067":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1068":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1069":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1070":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","1071":"+a=6378293.645208759 +b=6356617.987679838 +pm=0","1072":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","1073":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1074":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-206,172,-6","1075":"+ellps=GRS67 +a=6378160 +b=6356774.719195306 +pm=0","1076":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1077":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","1078":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1081":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","1095":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1100":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1111":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0","1112":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1113":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1114":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1115":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1116":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","1117":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","1118":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","1120":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1128":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1132":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1133":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","1135":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","1136":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1137":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","1138":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","1139":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","1141":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1142":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1143":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1144":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1145":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1147":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=-346,-1,224","1152":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1153":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1154":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1155":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1156":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1157":"+ellps=PZ90 +a=6378136 +b=6356751.361745712 +pm=0","1158":"+ellps=PZ90 +a=6378136 +b=6356751.361745712 +pm=0","1159":"+ellps=GSK2011 +a=6378136.5 +b=6356751.757955603 +pm=0","1160":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1165":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1166":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1167":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1168":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1173":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1174":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1178":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1179":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1180":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1181":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1182":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1183":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1184":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1185":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1186":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1187":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1188":"+a=6376045 +b=6355477.112903226 +pm=-17.666666666666668","1189":"+a=6376045 +b=6355477.112903226 +pm=-17.666666666666668","1191":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1192":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1193":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1194":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1195":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1196":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1197":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1198":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1201":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","1204":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1206":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1207":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1208":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1209":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1211":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","1212":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","1214":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1217":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","1218":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1220":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1221":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1223":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1225":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1227":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1228":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1229":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1230":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1231":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1232":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1233":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1234":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1235":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1236":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1237":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1238":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1239":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1240":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1241":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1242":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1243":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1244":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1245":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1246":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1247":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1248":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1249":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1251":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1252":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1253":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1254":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1257":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1258":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1259":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1263":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1264":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1266":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1268":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1271":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1272":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1273":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1286":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1289":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1291":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1293":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1295":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1304":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1305":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1308":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1309":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1310":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1311":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=89.5,93.8,123.1,-1.2,0.0,0.0,0.156","1312":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1313":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1314":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=-446.448,125.157,-542.060, 20.4894,-0.1502, -0.2470,-0.8421","1315":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1317":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1319":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1320":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1321":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1322":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1324":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1327":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1329":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1332":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1333":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1334":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1335":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1336":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1337":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1338":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1339":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1340":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1341":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1342":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1343":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1344":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1345":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1346":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","1347":"+a=6377019.27 +b=6355762.5391 +pm=0","1348":"+a=6377019.27 +b=6355762.5391 +pm=0","1349":"+a=6377019.27 +b=6355762.5391 +pm=0","1350":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","1351":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1352":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1353":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1355":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1356":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1357":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1358":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1359":"+a=6378273 +b=6356889.449 +pm=0","1360":"+a=6371228 +b=6371228 +pm=0","1365":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1366":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1367":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","1382":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","1383":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6001":"+ellps=airy +a=6377563.396 +b=6356256.909237285 +pm=0","6002":"+ellps=mod_airy +a=6377340.189 +b=6356034.447938534 +pm=0","6003":"+ellps=aust_SA +a=6378160 +b=6356774.719195306 +pm=0","6004":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6005":"+ellps=bessel +a=6377492.018 +b=6356173.508712696 +pm=0","6006":"+ellps=bess_nam +a=6377483.865280419 +b=6356165.383245807 +pm=0","6007":"+a=6378293.645208759 +b=6356617.987679838 +pm=0","6008":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6009":"+ellps=clrk66 +a=6378450.047548896 +b=6356826.621488444 +pm=0","6010":"+ellps=clrk80 +a=6378300.789 +b=6356566.435 +pm=0","6011":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6012":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6013":"+ellps=clrk80 +a=6378249.145 +b=6356514.966398753 +pm=0","6014":"+ellps=clrk80 +a=6378249.2 +b=6356514.996941779 +pm=0","6015":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0","6016":"+ellps=evrst30 +a=6377298.556 +b=6356097.550300896 +pm=0","6018":"+ellps=evrst30 +a=6377304.063 +b=6356103.038993155 +pm=0","6019":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","6020":"+ellps=helmert +a=6378200 +b=6356818.169627891 +pm=0","6021":"+a=6378160 +b=6356774.50408554 +pm=0","6022":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6024":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6025":"+ellps=NWL9D +a=6378145 +b=6356759.769488684 +pm=0","6027":"+ellps=plessis +a=6376523 +b=6355862.933255573 +pm=0","6028":"+a=6378298.3 +b=6356657.142669562 +pm=0","6029":"+a=6378300 +b=6356751.689189189 +pm=0","6030":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0 +towgs84=0,0,0","6031":"+a=6378137 +b=6356752.314245179 +pm=0","6032":"+a=6378136.2 +b=6356751.516927429 +pm=0","6033":"+a=6378136.3 +b=6356751.616592146 +pm=0","6034":"+ellps=clrk80 +a=6378249.144808011 +b=6356514.966204134 +pm=0","6035":"+ellps=sphere +a=6371000 +b=6371000 +pm=0","6036":"+ellps=GRS67 +a=6378160 +b=6356774.516090714 +pm=0 +towgs84=0,0,0","6041":"+a=6378135 +b=6356750.304921594 +pm=0","6042":"+a=6377299.36559538 +b=6356098.359005156 +pm=0","6043":"+ellps=WGS72 +a=6378135 +b=6356750.520016094 +pm=0 +towgs84=0,8,10","6044":"+ellps=evrst30 +a=6377301.243 +b=6356100.230165385 +pm=0","6045":"+ellps=evrst30 +a=6377299.151 +b=6356098.145120132 +pm=0","6047":"+ellps=GRS80 +a=6371007 +b=6371007 +pm=0","6052":"+ellps=clrk66 +a=6370997 +b=6370997 +pm=0","6053":"+a=6371228 +b=6371228 +pm=0","6054":"+a=6378273 +b=6356889.449 +pm=0","6055":"+a=6378137 +b=6378137 +pm=0","6120":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6121":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6122":"+a=6378135 +b=6356750.304921594 +pm=0","6123":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6124":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0 +towgs84=419.3836,99.3335,591.3451,-0.850389,-1.817277,7.862238,-0.99496","6125":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6126":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6127":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6128":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6129":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6130":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6131":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0 +towgs84=198,881,317","6132":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6133":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6134":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6135":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0 +towgs84=61,-285,-181","6136":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6137":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6138":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6139":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0 +towgs84=11,72,-101","6140":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","6141":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6142":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6143":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6144":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0","6145":"+ellps=evrst30 +a=6377301.243 +b=6356100.230165385 +pm=0","6146":"+ellps=evrst30 +a=6377299.151 +b=6356098.145120132 +pm=0","6147":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6148":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6149":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6150":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6151":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6152":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","6153":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6154":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6155":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0 +towgs84=-83,37,124","6156":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6157":"+a=6378293.645208759 +b=6356617.987679838 +pm=0","6158":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-2,374,172","6159":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6160":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6161":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6162":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6163":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6164":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6165":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6166":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6167":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6168":"+a=6378300 +b=6356751.689189189 +pm=0","6169":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0 +towgs84=-115,118,426","6170":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6171":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6172":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6173":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6174":"+a=6378300 +b=6356751.689189189 +pm=0","6175":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-88,4,101","6176":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6178":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6179":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6180":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6181":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6182":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6183":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-104,167,-38","6184":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6185":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-499,-249,314","6188":"+ellps=airy +a=6377563.396 +b=6356256.909237285 +pm=0","6189":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6190":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6191":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6192":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6193":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6194":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=164,138,-189","6195":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6196":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6197":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6198":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6199":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6200":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6201":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-162,-12,206","6202":"+ellps=aust_SA +a=6378160 +b=6356774.719195306 +pm=0 +towgs84=-133,-48,148","6203":"+ellps=aust_SA +a=6378160 +b=6356774.719195306 +pm=0 +towgs84=-134,-48,149","6204":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6205":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0 +towgs84=-43,-163,45","6206":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6207":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6208":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6209":"+ellps=clrk80 +a=6378249.145 +b=6356514.966398753 +pm=0 +towgs84=-143,-90,-294","6210":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-160,-8,-300","6211":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6212":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6213":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6214":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0 +towgs84=-31.4,144.3,81.2","6215":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6216":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0 +towgs84=-73,213,296","6218":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=307,304,-318","6219":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0 +towgs84=-384,664,-48","6220":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6221":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-148,136,90","6222":"+ellps=clrk80 +a=6378249.145 +b=6356514.966398753 +pm=0 +towgs84=-136,-108,-292","6223":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0 +towgs84=-263,6,431","6224":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-134,229,-29","6225":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-206,172,-6","6226":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6227":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6228":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6229":"+ellps=helmert +a=6378200 +b=6356818.169627891 +pm=0","6230":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-87,-98,-121","6231":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-83,-96,-113","6232":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6233":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6234":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6235":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6236":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-634,-549,-201","6237":"+ellps=GRS67 +a=6378160 +b=6356774.516090714 +pm=0","6238":"+a=6378160 +b=6356774.50408554 +pm=0 +towgs84=-24,-15,5","6239":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0 +towgs84=217,823,299","6240":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0 +towgs84=210,814,289","6241":"+ellps=clrk80 +a=6378249.144808011 +b=6356514.966204134 +pm=0","6242":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6243":"+a=6377299.36559538 +b=6356098.359005156 +pm=0","6244":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0 +towgs84=-97,787,86","6245":"+ellps=evrst30 +a=6377304.063 +b=6356103.038993155 +pm=0","6246":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6247":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6248":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6249":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6250":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-130,29,364","6251":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-90,40,88","6252":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6253":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0 +towgs84=-133,-77,-51","6254":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=16,196,93","6255":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-333,-222,114","6256":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=41,-220,-134","6257":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6258":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6259":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6260":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6261":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0 +towgs84=31,146,47","6262":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0 +towgs84=639,405,60","6263":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-92,-93,122","6264":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6265":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6266":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0 +towgs84=-74,-130,42","6267":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6268":"+ellps=clrk66 +a=6378450.047548896 +b=6356826.621488444 +pm=0 +towgs84=-8,160,176","6269":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6270":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6271":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-2,374,172","6272":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=59.47,-5.04,187.44,-0.47,0.1,-1.024,-4.5993,0","6273":"+ellps=bessel +a=6377492.018 +b=6356173.508712696 +pm=0","6274":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6275":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6276":"+ellps=NWL9D +a=6378145 +b=6356759.769488684 +pm=0","6277":"+ellps=airy +a=6377563.396 +b=6356256.909237285 +pm=0","6278":"+ellps=airy +a=6377563.396 +b=6356256.909237285 +pm=0","6279":"+ellps=airy +a=6377563.396 +b=6356256.909237285 +pm=0","6280":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6281":"+ellps=clrk80 +a=6378300.789 +b=6356566.435 +pm=0","6282":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6283":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6284":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0 +towgs84=24,-123,-94,-0.02,0.25,0.13,1.1","6285":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6286":"+ellps=helmert +a=6378200 +b=6356818.169627891 +pm=0","6287":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6288":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6289":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6291":"+ellps=GRS67 +a=6378160 +b=6356774.516090714 +pm=0","6292":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-355,16,74","6293":"+ellps=bess_nam +a=6377483.865280419 +b=6356165.383245807 +pm=0 +towgs84=616,97,-251","6294":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6295":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6296":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6297":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-189,-242,-91","6298":"+ellps=evrst30 +a=6377298.556 +b=6356097.550300896 +pm=0 +towgs84=-689,691,-46","6299":"+ellps=mod_airy +a=6377340.189 +b=6356034.447938534 +pm=0","6300":"+ellps=mod_airy +a=6377340.189 +b=6356034.447938534 +pm=0 +towgs84=506,-122,611","6301":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0 +towgs84=-128,481,664","6302":"+a=6378293.645208759 +b=6356617.987679838 +pm=0","6303":"+ellps=helmert +a=6378200 +b=6356818.169627891 +pm=0","6304":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0 +towgs84=-73,-247,227","6306":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6307":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-186,-93,310","6308":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6309":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-155,171,37","6310":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6311":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-265,120,-358","6312":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6313":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6314":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6315":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6316":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6317":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6318":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6319":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6322":"+ellps=WGS72 +a=6378135 +b=6356750.520016094 +pm=0","6324":"+ellps=WGS72 +a=6378135 +b=6356750.520016094 +pm=0","6326":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6600":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6601":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-270,13,62","6602":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6603":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6604":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=174,359,365","6605":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6606":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6607":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6608":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6609":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6610":"+a=6378140 +b=6356755.288157528 +pm=0 +towgs84=24,-123,-94,-0.02,-0.25,0.13,1.1","6611":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6612":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6613":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6614":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-128,-283,22","6615":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-499,-249,314","6616":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-289,-124,60","6618":"+ellps=GRS67 +a=6378160 +b=6356774.719195306 +pm=0 +towgs84=-57,1,-41","6619":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6620":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-106,-129,165","6621":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6622":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6623":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6624":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6625":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6626":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=94,-948,-1262","6627":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6628":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6629":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6630":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6631":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6632":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6633":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6634":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6635":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6636":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6637":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6638":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6639":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6640":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6641":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6642":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6643":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6644":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6645":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6646":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6647":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6648":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6649":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6650":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6651":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6652":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6653":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6654":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6655":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6656":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6657":"+a=6377019.27 +b=6355762.5391 +pm=0","6658":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-73,46,-86","6659":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6660":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6661":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6663":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-499,-249,314","6664":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6665":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-104,167,-38","6666":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6667":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6668":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-86,-98,-119","6670":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6671":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=0","6672":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=175,-38,113","6673":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6674":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6675":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0 +towgs84=-100,-248,259","6676":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6677":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6678":"+ellps=krass +a=6378245 +b=6356863.018773047 +pm=0","6679":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6680":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6681":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6682":"+ellps=evrst30 +a=6377276.345 +b=6356075.413140239 +pm=0","6683":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6684":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6685":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-133,-321,50","6686":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6687":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6688":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6689":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6690":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6691":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6692":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6693":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6694":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6695":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6696":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6697":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6698":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6699":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6700":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6701":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6702":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6703":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6704":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6705":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6706":"+ellps=helmert +a=6378200 +b=6356818.169627891 +pm=0","6707":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6708":"+ellps=aust_SA +a=6378160 +b=6356774.719195306 +pm=0","6709":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6710":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-320,550,-494","6711":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6712":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-207,107,52","6713":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=-79,-129,145","6714":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-127,-769,472","6715":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6716":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6717":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0 +towgs84=-2,150,181","6718":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6719":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=211,147,111","6720":"+ellps=WGS72 +a=6378135 +b=6356750.520016094 +pm=0","6721":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6722":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6723":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6724":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6725":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=191,-77,-204","6726":"+ellps=clrk66 +a=6378206.4 +b=6356583.8 +pm=0","6727":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=912,-58,1227","6728":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-307,-92,127","6729":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=185,165,42","6730":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=170,42,84","6731":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=51,391,-36","6732":"+ellps=hough +a=6378270 +b=6356794.343434343 +pm=0 +towgs84=101,52,-39","6733":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6734":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=-632,438,-609","6735":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0 +towgs84=647,1777,-1124","6736":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0 +towgs84=260,12,-147","6737":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6738":"+a=6378293.645208759 +b=6356617.987679838 +pm=0 +towgs84=-156,-271,-189","6739":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6740":"+ellps=PZ90 +a=6378136 +b=6356751.361745712 +pm=0","6741":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6742":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6743":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6744":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=0","6745":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6746":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=0","6747":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6748":"+ellps=clrk80 +a=6378306.3696 +b=6356571.996 +pm=0","6749":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6750":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6751":"+ellps=evrst69 +a=6377295.664 +b=6356094.667915204 +pm=0","6752":"+ellps=clrk80 +a=6378306.3696 +b=6356571.996 +pm=0","6753":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6754":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=0","6755":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6756":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6757":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6758":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6759":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0 +towgs84=0,0,0","6760":"+ellps=NWL9D +a=6378145 +b=6356759.769488684 +pm=0","6761":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6762":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6763":"+ellps=WGS84 +a=6378137 +b=6356752.314245179 +pm=0","6764":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6765":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6801":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=7.439583333333333","6802":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=-74.08091666666667 +towgs84=307,304,-318","6803":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=-9.131906111111112","6804":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=106.80771944444444","6805":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=-17.666666666666668","6806":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=12.452333333333332 +towgs84=-225,-65,9","6807":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=2.337229169999998","6808":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=106.80771944444444","6809":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=4.3679749999999995","6810":"+ellps=intl +a=6378388 +b=6356911.9461279465 +pm=2.337229169999998 +towgs84=-189,-242,-91","6811":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=2.337229169999998 +towgs84=-73,-247,227","6813":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=106.80771944444444 +towgs84=-377,681,-50","6814":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=18.05827777777778","6815":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=23.716337499999998","6816":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=2.337229169999998","6817":"+ellps=bessel +a=6377492.018 +b=6356173.508712696 +pm=10.722916666666666","6818":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=-17.666666666666668","6819":"+ellps=clrk80 +a=6378249.145 +b=6356514.8695497755 +pm=2.337229169999998","6820":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=106.80771944444444","6821":"+ellps=clrk80ign +a=6378249.2 +b=6356515 +pm=2.337229169999998","6896":"+ellps=GRS80 +a=6378137 +b=6356752.314140356 +pm=0","6901":"+ellps=plessis +a=6376523 +b=6355862.933255573 +pm=2.3372083333333333","6902":"+ellps=plessis +a=6376523 +b=6355862.933255573 +pm=2.337229169999998","6903":"+a=6378298.3 +b=6356657.142669562 +pm=-3.6873750000000003","6904":"+ellps=bessel +a=6377397.155 +b=6356078.962818189 +pm=-9.131906111111112"} --------------------------------------------------------------------------------