@trackunit/geo-json-utils 1.14.42 → 1.14.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -1380,7 +1380,7 @@ const geoJsonPolygonDifference = (subject, clips) => {
1380
1380
  // infinity; clamping keeps the round-trip finite for shapes near the poles.
1381
1381
  const WEB_MERCATOR_MAX_LAT = 85.05112878;
1382
1382
  const DEG_TO_RAD$1 = Math.PI / 180;
1383
- const RAD_TO_DEG = 180 / Math.PI;
1383
+ const RAD_TO_DEG$1 = 180 / Math.PI;
1384
1384
  /**
1385
1385
  * Project a `[lng, lat]` position to Web Mercator metres (EPSG:3857).
1386
1386
  *
@@ -1399,8 +1399,8 @@ const projectLngLatToWebMercator = (position) => {
1399
1399
  /** Inverse of `projectLngLatToWebMercator`: Web Mercator metres back to `[lng, lat]`. */
1400
1400
  const unprojectWebMercatorToLngLat = (position) => {
1401
1401
  const [x, y] = position;
1402
- const lng = (x / EARTH_RADIUS) * RAD_TO_DEG;
1403
- const lat = (2 * Math.atan(Math.exp(y / EARTH_RADIUS)) - Math.PI / 2) * RAD_TO_DEG;
1402
+ const lng = (x / EARTH_RADIUS) * RAD_TO_DEG$1;
1403
+ const lat = (2 * Math.atan(Math.exp(y / EARTH_RADIUS)) - Math.PI / 2) * RAD_TO_DEG$1;
1404
1404
  return [lng, lat];
1405
1405
  };
1406
1406
  const mapPolygonalCoordinates = (geometry, project) => {
@@ -1800,6 +1800,57 @@ const tuGeoJsonRectangularBoxPolygonSchema = zod.z
1800
1800
  });
1801
1801
 
1802
1802
  const DEG_TO_RAD = Math.PI / 180;
1803
+ const RAD_TO_DEG = 180 / Math.PI;
1804
+ /**
1805
+ * Web Mercator is undefined at the poles; tile services clamp latitude to this
1806
+ * value (the latitude where the projection becomes square). Projecting beyond it
1807
+ * produces unbounded y, so we clamp before projecting.
1808
+ */
1809
+ const MAX_WEB_MERCATOR_LAT = 85.05112878;
1810
+ /**
1811
+ * Inner projection that accepts a pre-computed `worldSize` (= tileSize * 2^zoom).
1812
+ *
1813
+ * Exposed so hot loops that project dozens of points per frame (e.g. edge-label
1814
+ * layout) can hoist `Math.pow(2, zoom)` out of the per-vertex work. Most callers
1815
+ * should prefer the zoom-based {@link lngLatToWebMercatorPx} wrapper.
1816
+ */
1817
+ const lngLatToMercatorPxWS = (lng, lat, worldSize) => {
1818
+ const clampedLat = Math.max(-MAX_WEB_MERCATOR_LAT, Math.min(MAX_WEB_MERCATOR_LAT, lat));
1819
+ const x = ((lng + 180) / 360) * worldSize;
1820
+ const latRad = clampedLat * DEG_TO_RAD;
1821
+ const y = (0.5 - Math.log(Math.tan(Math.PI / 4 + latRad / 2)) / (2 * Math.PI)) * worldSize;
1822
+ return [x, y];
1823
+ };
1824
+ /**
1825
+ * Inverse of {@link lngLatToMercatorPxWS}: map a pixel coordinate back to
1826
+ * [lng, lat] given a pre-computed `worldSize`.
1827
+ */
1828
+ const mercatorPxToLngLatWS = (px, py, worldSize) => {
1829
+ const lng = (px / worldSize) * 360 - 180;
1830
+ const lat = Math.atan(Math.sinh(Math.PI * (1 - (2 * py) / worldSize))) * RAD_TO_DEG;
1831
+ return [lng, lat];
1832
+ };
1833
+ /**
1834
+ * Project a [lng, lat] coordinate to absolute Web Mercator pixel space at the
1835
+ * given zoom. This is the same projection formula map adapters use internally,
1836
+ * so pixel-space measurements derived here agree with the rendered map.
1837
+ */
1838
+ const lngLatToWebMercatorPx = (lng, lat, zoom, tileSize = 256) => lngLatToMercatorPxWS(lng, lat, tileSize * Math.pow(2, zoom));
1839
+ /**
1840
+ * Inverse of {@link lngLatToWebMercatorPx}: map an absolute Web Mercator pixel
1841
+ * coordinate back to [lng, lat] at the given zoom.
1842
+ */
1843
+ const webMercatorPxToLngLat = (px, py, zoom, tileSize = 256) => mercatorPxToLngLatWS(px, py, tileSize * Math.pow(2, zoom));
1844
+ /**
1845
+ * Convert a pixel distance to latitude degrees at a given zoom level,
1846
+ * accounting for Web Mercator latitude distortion (latitude pixels scale by
1847
+ * sec(lat)).
1848
+ */
1849
+ const pixelsToLatDegrees = (pixels, zoom, latDeg, tileSize = 256) => {
1850
+ const pxPerDeg = (tileSize * Math.pow(2, zoom)) / 360;
1851
+ const secLat = 1 / Math.cos(latDeg * DEG_TO_RAD);
1852
+ return pixels / (pxPerDeg * secLat);
1853
+ };
1803
1854
  /**
1804
1855
  * Compute the pixel length of a line segment at a given zoom level,
1805
1856
  * accounting for Web Mercator latitude distortion.
@@ -1871,8 +1922,12 @@ exports.isFullyContainedInGeoJsonPolygon = isFullyContainedInGeoJsonPolygon;
1871
1922
  exports.isGeoJsonPointInPolygon = isGeoJsonPointInPolygon;
1872
1923
  exports.isGeoJsonPositionInLinearRing = isGeoJsonPositionInLinearRing;
1873
1924
  exports.isPositionInsideRing = isPositionInsideRing;
1925
+ exports.lngLatToMercatorPxWS = lngLatToMercatorPxWS;
1926
+ exports.lngLatToWebMercatorPx = lngLatToWebMercatorPx;
1927
+ exports.mercatorPxToLngLatWS = mercatorPxToLngLatWS;
1874
1928
  exports.normalizeLongitudes = normalizeLongitudes;
1875
1929
  exports.padGeoJsonBbox = padGeoJsonBbox;
1930
+ exports.pixelsToLatDegrees = pixelsToLatDegrees;
1876
1931
  exports.projectLngLatToWebMercator = projectLngLatToWebMercator;
1877
1932
  exports.projectPolygonalToWebMercator = projectPolygonalToWebMercator;
1878
1933
  exports.scaleGeoJsonBbox = scaleGeoJsonBbox;
@@ -1889,3 +1944,4 @@ exports.validateBbox = validateBbox;
1889
1944
  exports.validateBboxWithFallback = validateBboxWithFallback;
1890
1945
  exports.validateFeatureCollection = validateFeatureCollection;
1891
1946
  exports.validatePosition = validatePosition;
1947
+ exports.webMercatorPxToLngLat = webMercatorPxToLngLat;
package/index.esm.js CHANGED
@@ -1378,7 +1378,7 @@ const geoJsonPolygonDifference = (subject, clips) => {
1378
1378
  // infinity; clamping keeps the round-trip finite for shapes near the poles.
1379
1379
  const WEB_MERCATOR_MAX_LAT = 85.05112878;
1380
1380
  const DEG_TO_RAD$1 = Math.PI / 180;
1381
- const RAD_TO_DEG = 180 / Math.PI;
1381
+ const RAD_TO_DEG$1 = 180 / Math.PI;
1382
1382
  /**
1383
1383
  * Project a `[lng, lat]` position to Web Mercator metres (EPSG:3857).
1384
1384
  *
@@ -1397,8 +1397,8 @@ const projectLngLatToWebMercator = (position) => {
1397
1397
  /** Inverse of `projectLngLatToWebMercator`: Web Mercator metres back to `[lng, lat]`. */
1398
1398
  const unprojectWebMercatorToLngLat = (position) => {
1399
1399
  const [x, y] = position;
1400
- const lng = (x / EARTH_RADIUS) * RAD_TO_DEG;
1401
- const lat = (2 * Math.atan(Math.exp(y / EARTH_RADIUS)) - Math.PI / 2) * RAD_TO_DEG;
1400
+ const lng = (x / EARTH_RADIUS) * RAD_TO_DEG$1;
1401
+ const lat = (2 * Math.atan(Math.exp(y / EARTH_RADIUS)) - Math.PI / 2) * RAD_TO_DEG$1;
1402
1402
  return [lng, lat];
1403
1403
  };
1404
1404
  const mapPolygonalCoordinates = (geometry, project) => {
@@ -1798,6 +1798,57 @@ const tuGeoJsonRectangularBoxPolygonSchema = z
1798
1798
  });
1799
1799
 
1800
1800
  const DEG_TO_RAD = Math.PI / 180;
1801
+ const RAD_TO_DEG = 180 / Math.PI;
1802
+ /**
1803
+ * Web Mercator is undefined at the poles; tile services clamp latitude to this
1804
+ * value (the latitude where the projection becomes square). Projecting beyond it
1805
+ * produces unbounded y, so we clamp before projecting.
1806
+ */
1807
+ const MAX_WEB_MERCATOR_LAT = 85.05112878;
1808
+ /**
1809
+ * Inner projection that accepts a pre-computed `worldSize` (= tileSize * 2^zoom).
1810
+ *
1811
+ * Exposed so hot loops that project dozens of points per frame (e.g. edge-label
1812
+ * layout) can hoist `Math.pow(2, zoom)` out of the per-vertex work. Most callers
1813
+ * should prefer the zoom-based {@link lngLatToWebMercatorPx} wrapper.
1814
+ */
1815
+ const lngLatToMercatorPxWS = (lng, lat, worldSize) => {
1816
+ const clampedLat = Math.max(-MAX_WEB_MERCATOR_LAT, Math.min(MAX_WEB_MERCATOR_LAT, lat));
1817
+ const x = ((lng + 180) / 360) * worldSize;
1818
+ const latRad = clampedLat * DEG_TO_RAD;
1819
+ const y = (0.5 - Math.log(Math.tan(Math.PI / 4 + latRad / 2)) / (2 * Math.PI)) * worldSize;
1820
+ return [x, y];
1821
+ };
1822
+ /**
1823
+ * Inverse of {@link lngLatToMercatorPxWS}: map a pixel coordinate back to
1824
+ * [lng, lat] given a pre-computed `worldSize`.
1825
+ */
1826
+ const mercatorPxToLngLatWS = (px, py, worldSize) => {
1827
+ const lng = (px / worldSize) * 360 - 180;
1828
+ const lat = Math.atan(Math.sinh(Math.PI * (1 - (2 * py) / worldSize))) * RAD_TO_DEG;
1829
+ return [lng, lat];
1830
+ };
1831
+ /**
1832
+ * Project a [lng, lat] coordinate to absolute Web Mercator pixel space at the
1833
+ * given zoom. This is the same projection formula map adapters use internally,
1834
+ * so pixel-space measurements derived here agree with the rendered map.
1835
+ */
1836
+ const lngLatToWebMercatorPx = (lng, lat, zoom, tileSize = 256) => lngLatToMercatorPxWS(lng, lat, tileSize * Math.pow(2, zoom));
1837
+ /**
1838
+ * Inverse of {@link lngLatToWebMercatorPx}: map an absolute Web Mercator pixel
1839
+ * coordinate back to [lng, lat] at the given zoom.
1840
+ */
1841
+ const webMercatorPxToLngLat = (px, py, zoom, tileSize = 256) => mercatorPxToLngLatWS(px, py, tileSize * Math.pow(2, zoom));
1842
+ /**
1843
+ * Convert a pixel distance to latitude degrees at a given zoom level,
1844
+ * accounting for Web Mercator latitude distortion (latitude pixels scale by
1845
+ * sec(lat)).
1846
+ */
1847
+ const pixelsToLatDegrees = (pixels, zoom, latDeg, tileSize = 256) => {
1848
+ const pxPerDeg = (tileSize * Math.pow(2, zoom)) / 360;
1849
+ const secLat = 1 / Math.cos(latDeg * DEG_TO_RAD);
1850
+ return pixels / (pxPerDeg * secLat);
1851
+ };
1801
1852
  /**
1802
1853
  * Compute the pixel length of a line segment at a given zoom level,
1803
1854
  * accounting for Web Mercator latitude distortion.
@@ -1824,4 +1875,4 @@ const edgePixelLength = (x0, y0, x1, y1, zoom, midLatDeg, tileSize = 256) => {
1824
1875
  return Math.sqrt(dxPx * dxPx + dyPx * dyPx);
1825
1876
  };
1826
1877
 
1827
- export { EARTH_RADIUS, EMPTY_FEATURE_COLLECTION, boundingBoxCrossesMeridian, checkCrossesMeridian, computeGeometryCentroid, coordinatesToStandardFormat, denormalizeLongitude, distanceToGeoJsonPolygonBoundary, edgePixelLength, extractEdges, extractFirstPointCoordinate, extractPositionsFromGeometry, geoJsonBboxSchema, geoJsonFeatureCollectionSchema, geoJsonFeatureSchema, geoJsonGeometryCollectionSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonDifference, geoJsonPolygonSchema, geoJsonPosition2dSchema, geoJsonPositionSchema, getBboxFromGeoJsonPolygon, getBoundingBoxFromGeoJsonBbox, getBoundingBoxFromGeoJsonPolygon, getExtremeGeoJsonPointFromPolygon, getGeoJsonPolygonFromBoundingBox, getGeoJsonPolygonIntersection, getMinMaxLongitudes, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, isBboxInsideFeatureCollection, isFullyContainedInGeoJsonGeometry, isFullyContainedInGeoJsonPolygon, isGeoJsonPointInPolygon, isGeoJsonPositionInLinearRing, isPositionInsideRing, normalizeLongitudes, padGeoJsonBbox, projectLngLatToWebMercator, projectPolygonalToWebMercator, scaleGeoJsonBbox, splitPolygonAtAntimeridian, splitPolygonWithHolesAtAntimeridian, toFeatureCollection, toPosition2d, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unprojectPolygonalFromWebMercator, unprojectWebMercatorToLngLat, validateBbox, validateBboxWithFallback, validateFeatureCollection, validatePosition };
1878
+ export { EARTH_RADIUS, EMPTY_FEATURE_COLLECTION, boundingBoxCrossesMeridian, checkCrossesMeridian, computeGeometryCentroid, coordinatesToStandardFormat, denormalizeLongitude, distanceToGeoJsonPolygonBoundary, edgePixelLength, extractEdges, extractFirstPointCoordinate, extractPositionsFromGeometry, geoJsonBboxSchema, geoJsonFeatureCollectionSchema, geoJsonFeatureSchema, geoJsonGeometryCollectionSchema, geoJsonGeometrySchema, geoJsonLineStringSchema, geoJsonLinearRingSchema, geoJsonMultiLineStringSchema, geoJsonMultiPointSchema, geoJsonMultiPolygonSchema, geoJsonPointSchema, geoJsonPolygonDifference, geoJsonPolygonSchema, geoJsonPosition2dSchema, geoJsonPositionSchema, getBboxFromGeoJsonPolygon, getBoundingBoxFromGeoJsonBbox, getBoundingBoxFromGeoJsonPolygon, getExtremeGeoJsonPointFromPolygon, getGeoJsonPolygonFromBoundingBox, getGeoJsonPolygonIntersection, getMinMaxLongitudes, getMultipleCoordinatesFromGeoJsonObject, getPointCoordinateFromGeoJsonObject, getPointCoordinateFromGeoJsonPoint, getPolygonFromBbox, getPolygonFromPointAndRadius, isBboxInsideFeatureCollection, isFullyContainedInGeoJsonGeometry, isFullyContainedInGeoJsonPolygon, isGeoJsonPointInPolygon, isGeoJsonPositionInLinearRing, isPositionInsideRing, lngLatToMercatorPxWS, lngLatToWebMercatorPx, mercatorPxToLngLatWS, normalizeLongitudes, padGeoJsonBbox, pixelsToLatDegrees, projectLngLatToWebMercator, projectPolygonalToWebMercator, scaleGeoJsonBbox, splitPolygonAtAntimeridian, splitPolygonWithHolesAtAntimeridian, toFeatureCollection, toPosition2d, tuGeoJsonPointRadiusSchema, tuGeoJsonPolygonNoHolesSchema, tuGeoJsonRectangularBoxPolygonSchema, unprojectPolygonalFromWebMercator, unprojectWebMercatorToLngLat, validateBbox, validateBboxWithFallback, validateFeatureCollection, validatePosition, webMercatorPxToLngLat };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/geo-json-utils",
3
- "version": "1.14.42",
3
+ "version": "1.14.43",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -1,3 +1,33 @@
1
+ /**
2
+ * Inner projection that accepts a pre-computed `worldSize` (= tileSize * 2^zoom).
3
+ *
4
+ * Exposed so hot loops that project dozens of points per frame (e.g. edge-label
5
+ * layout) can hoist `Math.pow(2, zoom)` out of the per-vertex work. Most callers
6
+ * should prefer the zoom-based {@link lngLatToWebMercatorPx} wrapper.
7
+ */
8
+ export declare const lngLatToMercatorPxWS: (lng: number, lat: number, worldSize: number) => readonly [number, number];
9
+ /**
10
+ * Inverse of {@link lngLatToMercatorPxWS}: map a pixel coordinate back to
11
+ * [lng, lat] given a pre-computed `worldSize`.
12
+ */
13
+ export declare const mercatorPxToLngLatWS: (px: number, py: number, worldSize: number) => readonly [number, number];
14
+ /**
15
+ * Project a [lng, lat] coordinate to absolute Web Mercator pixel space at the
16
+ * given zoom. This is the same projection formula map adapters use internally,
17
+ * so pixel-space measurements derived here agree with the rendered map.
18
+ */
19
+ export declare const lngLatToWebMercatorPx: (lng: number, lat: number, zoom: number, tileSize?: number) => readonly [number, number];
20
+ /**
21
+ * Inverse of {@link lngLatToWebMercatorPx}: map an absolute Web Mercator pixel
22
+ * coordinate back to [lng, lat] at the given zoom.
23
+ */
24
+ export declare const webMercatorPxToLngLat: (px: number, py: number, zoom: number, tileSize?: number) => readonly [number, number];
25
+ /**
26
+ * Convert a pixel distance to latitude degrees at a given zoom level,
27
+ * accounting for Web Mercator latitude distortion (latitude pixels scale by
28
+ * sec(lat)).
29
+ */
30
+ export declare const pixelsToLatDegrees: (pixels: number, zoom: number, latDeg: number, tileSize?: number) => number;
1
31
  /**
2
32
  * Compute the pixel length of a line segment at a given zoom level,
3
33
  * accounting for Web Mercator latitude distortion.