Skip to content

Add new function proj4ToProj4 to turf/projection#3046

Open
BenPortner wants to merge 2 commits into
Turfjs:masterfrom
BenPortner:proj4
Open

Add new function proj4ToProj4 to turf/projection#3046
BenPortner wants to merge 2 commits into
Turfjs:masterfrom
BenPortner:proj4

Conversation

@BenPortner

Copy link
Copy Markdown

This PR adds a new function proj4ToProj4 to the turf-projection package. The function allows conversions from an arbitrary input projection to an arbitrary output projection, as long as it is implemented in Proj4js. Before, only conversions from WGS84 to Mercator and vice-versa were possible.

Example use:

  import { proj4ToProj4 } from "@turf/projection";

  const coord = [10, 40];
  const millerProj4 = "+proj=mill +R_A +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs";
  const millerCoord = proj4ToProj4(coord, "WGS84", millerProj4);

This is not a breaking change. Both of the original toMercator and toWgs84 functions are left untouched (however, it might be worthwhile removing them, as they are easily replaced by proj4ToProj4). Proj4js has been added as a project dependency (it had already been a dev dependency before).

I've read the steps for preparing a pull request. I've added appropriate tests for the new function (currently testing only WGS84->Mercator and Mercator->WGS84, based on the already existing test files). All tests are ok. The docs have not yet been updated to include the new function because I first want to see if this sparks your interest at all.

@smallsaucepan

Copy link
Copy Markdown
Contributor

Thanks for putting this together @BenPortner. Overall would be supportive of this. Though the spec says geojson is WSG84 by default, seems reasonable if we offer projection services to one alternative (mercator) we should offer it to others.

Couple of questions ...

  1. Does proj4 offer type safety for the projection names? Or can we only be as specific as "string"?
  2. Can you please evaluate how adding proj4 as a runtime dependency changes bundle sizes? The size of packages/turf/turf.min.js would be a good first check
  3. I'm not 100% sure proj4 will be compatible with ESM projects? https://arethetypeswrong.github.io/?p=proj4%402.20.8 Do you have any practical insights there?

@BenPortner

Copy link
Copy Markdown
Author

Hello @smallsaucepan,

  1. Does proj4 offer type safety for the projection names? Or can we only be as specific as "string"?

proj4 really just expects strings. The string can be a pre-defined name or a projection definition. proj4 exports a type ProjectDefinition however, I am not sure how we can use this as a safety. Maybe we could mention it in the docs for user orientation?

Pre-defined names (from the docs):

‘EPSG:4326’, which has the following alias: ‘WGS84’
‘EPSG:4269’
‘EPSG:3857’, which has the following aliases: ‘EPSG:3785’, ‘GOOGLE’, ‘EPSG:900913’, ‘EPSG:102113’
EPSG:32601 to EPSG:32660 (WGS84 / UTM zones 1 to 60 North)
EPSG:32701 to EPSG:32760 (WGS84 / UTM zones 1 to 60 South)
EPSG:5041 (WGS 84 / UPS North (E,N))
EPSG:5042 (WGS 84 / UPS South (E,N))

Projection definition type:

export type ProjectionDefinition = {
    title: string;
    projName?: string;
    ellps?: string;
    datum?: import("./Proj.js").DatumDefinition;
    datumName?: string;
    rf?: number;
    lat0?: number;
    lat1?: number;
    lat2?: number;
    lat_ts?: number;
    long0?: number;
    long1?: number;
    long2?: number;
    alpha?: number;
    longc?: number;
    x0?: number;
    y0?: number;
    k0?: number;
    a?: number;
    b?: number;
    R_A?: true;
    zone?: number;
    utmSouth?: true;
    datum_params?: string | Array<number>;
    to_meter?: number;
    units?: string;
    from_greenwich?: number;
    datumCode?: string;
    nadgrids?: string;
    axis?: string;
    sphere?: boolean;
    rectified_grid_angle?: number;
    approx?: boolean;
    over?: boolean;
    projStr?: string;
    inverse: <T extends import("./core").TemplateCoordinates>(coordinates: T, enforceAxis?: boolean) => T;
    forward: <T extends import("./core").TemplateCoordinates>(coordinates: T, enforceAxis?: boolean) => T;
};
  1. Can you please evaluate how adding proj4 as a runtime dependency changes bundle sizes? The size of packages/turf/turf.min.js would be a good first check

That would be a plus of 23% on turf.min.js:
Without: 543 KB
With: 669 KB

  1. I'm not 100% sure proj4 will be compatible with ESM projects? https://arethetypeswrong.github.io/?p=proj4%402.20.8 Do you have any practical insights there?

I setup a local ESM project with proj4 and it works just fine:

package.json:

{
  "name": "deleteme",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "description": "Minimal Node.js ESM project using proj4",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js"
  },
  "dependencies": {
    "proj4": "^2.20.4"
  }
}

index.js:

import proj4 from "proj4";

// Convert from WGS84 (lon/lat) to Web Mercator
const wgs84 = "EPSG:4326";
const webMercator = "EPSG:3857";
const coordinate = [-74.006, 40.7128];

const projected = proj4(wgs84, webMercator, coordinate);

console.log("Input (EPSG:4326):", coordinate);
console.log("Output (EPSG:3857):", projected);

Console:

C:\Users\BNPR\code\deleteme> npm run start  

> deleteme@1.0.0 start
> node src/index.js

Input (EPSG:4326): [ -74.006, 40.7128 ]
Output (EPSG:3857): [ -8238310.235647004, 4970071.579142423 ]

@mfedderly

Copy link
Copy Markdown
Collaborator

👋 Thanks for putting this together.

I'm kind of curious what your use-case is for having Turf as an intermediary for proj4 instead of using it directly, perhaps leveraging coordEach in your own code to iterate each position. I like the utility of supporting arbitrary conversions, but it is also ~40kb into the @turf/turf turf.min.js bundle we ship to browsers.

@BenPortner

Copy link
Copy Markdown
Author

Hi @mfedderly,

Legitimate question! My use case is here: https://github.com/BenPortner/geojson-atlas
I create choropleth maps from Natural Earth geojson data. It's fine to use Web Mercator for a lot of regions but for a good map of Europe for example, Web Mercator is not ideal because it inflates the size of Scandinavia significantly (see geojson 1 vs geojson 2).

Of course I can use Turfjs and Proj4 separately, but it would be more convenient to have them integrated, especially since Turfjs already has a projection module. The current module seems somewhat underpowered. Since I did the integration for my project, I thought I'd share it with the world. Of course I leave it up to you to decide if the increase in package size is acceptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants