Last active
September 6, 2022 21:37
-
-
Save mate-h/d8230faa4a93dc4ee13b73e8afc08c0e to your computer and use it in GitHub Desktop.
Generates a grid over planet earth for displaying with Three.js for any map projection using proj4js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import proj4 from "proj4"; | |
import * as THREE from "three"; | |
// BC Albers | |
proj4.defs( | |
"EPSG:3005", | |
"+proj=aea +lat_0=45 +lon_0=-126 +lat_1=50 +lat_2=58.5 +x_0=1000000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs" | |
); | |
// approximate radius of the earth in megameters (sphere) | |
const earthRadius = 6360; | |
class GlobeProjection { | |
// fundctions below assume spherical earth with Z pointing to north pole | |
earthToLngLat(earth: THREE.Vector3) { | |
const lng = Math.atan2(earth.y, earth.x); | |
const lat = Math.asin(earth.z / earthRadius); | |
// convert to degrees | |
return [lng * (180 / Math.PI), lat * (180 / Math.PI)]; | |
} | |
lngLatToEarth(lngLat: [number, number]) { | |
// convert to radians | |
const lng = lngLat[0] * (Math.PI / 180); | |
const lat = lngLat[1] * (Math.PI / 180); | |
const x = earthRadius * Math.cos(lat) * Math.cos(lng); | |
const z = earthRadius * Math.sin(lat); | |
const y = earthRadius * Math.cos(lat) * Math.sin(lng); | |
return new THREE.Vector3(x, y, z); | |
} | |
getTiles(zoomLevel: number) { | |
const points: THREE.Vector3[] = []; | |
if (zoomLevel === 0) { | |
return points; | |
} | |
const sectionCount = Math.pow(2, zoomLevel); | |
// distribute tiles on sphere | |
const projections = { | |
epsg3857: { | |
name: "EPSG:3857", | |
bounds: [ | |
[-180.0, -85.06], | |
[180.0, 85.06], | |
], | |
}, | |
epsg3005: { | |
name: "EPSG:3005", | |
bounds: [ | |
[-172.54, 23.81], | |
[-47.74, 86.46], | |
], | |
}, | |
epsg4269: { | |
name: "EPSG:4269", | |
bounds: [ | |
[-172.54, 23.81], | |
[-47.74, 86.46], | |
], | |
}, | |
}; | |
const projName = projections.epsg3857.name; | |
const projBounds = projections.epsg3857.bounds; | |
const minBounds = proj4("EPSG:4326", projName, projBounds[0]); | |
const maxBounds = proj4("EPSG:4326", projName, projBounds[1]); | |
const projSampleX = | |
(maxBounds[0] - minBounds[0]) / (projBounds[1][0] - projBounds[0][0]); | |
const projSampleY = | |
(maxBounds[1] - minBounds[1]) / (projBounds[1][1] - projBounds[0][1]); | |
const w = maxBounds[0] - minBounds[0]; | |
const h = maxBounds[1] - minBounds[1]; | |
for (let i = 0; i < sectionCount; i++) { | |
for (let j = 0; j < sectionCount; j++) { | |
// top left | |
let mx = (i / sectionCount) * 2 * w - w; | |
let my = (j / sectionCount) * 2 * h - h; | |
// const [lng, lat] = proj4(projName, "EPSG:4326", [mx, my]); | |
// bottom right | |
let mx2 = ((i + 1) / sectionCount) * 2 * w - w; | |
let my2 = ((j + 1) / sectionCount) * 2 * h - h; | |
// const [lng2, lat2] = proj4(projName, "EPSG:4326", [mx, my]); | |
const topLeft = [mx, my]; | |
const bottomRight = [mx2, my2]; | |
const topRight = [mx2, my]; | |
const bottomLeft = [mx, my2]; | |
const edges = [topLeft, topRight, bottomRight, bottomLeft]; | |
// resample each edge | |
for (let k = 0; k < edges.length; k++) { | |
const point = edges[k]; | |
const nextPoint = edges[(k + 1) % edges.length]; | |
const [lng, lat] = point; | |
const [lng2, lat2] = nextPoint; | |
let latCount = Math.ceil((lat2 - lat) / projSampleX); | |
let lngCount = Math.ceil((lng2 - lng) / projSampleY); | |
let count = Math.max(latCount, lngCount); | |
for (let k = 0; k < count; k++) { | |
let xi = THREE.MathUtils.lerp(lat, lat2, k / count); | |
let yi = THREE.MathUtils.lerp(lng, lng2, k / count); | |
let [lngi, lati] = proj4(projName, "EPSG:4326", [xi, yi]); | |
let earth = this.lngLatToEarth([lngi, lati]); | |
points.push(earth); | |
// second point | |
xi = THREE.MathUtils.lerp(lat, lat2, (k + 1) / count); | |
yi = THREE.MathUtils.lerp(lng, lng2, (k + 1) / count); | |
[lngi, lati] = proj4(projName, "EPSG:4326", [xi, yi]); | |
earth = this.lngLatToEarth([lngi, lati]); | |
points.push(earth); | |
} | |
// rectangle | |
// points.push(topLeft); | |
// points.push(topRight); | |
// points.push(bottomRight); | |
// points.push(bottomLeft); | |
// points.push(topLeft); | |
} | |
} | |
} | |
return points; | |
} | |
} |
Author
mate-h
commented
Sep 6, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment