Skip to content

Instantly share code, notes, and snippets.

@mate-h
Last active September 6, 2022 21:37
Show Gist options
  • Save mate-h/d8230faa4a93dc4ee13b73e8afc08c0e to your computer and use it in GitHub Desktop.
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
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;
}
}
@mate-h
Copy link
Author

mate-h commented Sep 6, 2022

Screenshot 2022-09-06 at 15 37 22

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