@takram/three-geospatial 0.0.1-alpha.1

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.
Files changed (53) hide show
  1. package/README.md +15 -0
  2. package/build/index.cjs +43 -0
  3. package/build/index.js +932 -0
  4. package/build/r3f.cjs +1 -0
  5. package/build/r3f.js +38 -0
  6. package/build/shared.cjs +1 -0
  7. package/build/shared.js +198 -0
  8. package/package.json +42 -0
  9. package/src/ArrayBufferLoader.ts +35 -0
  10. package/src/DataLoader.ts +114 -0
  11. package/src/Ellipsoid.ts +128 -0
  12. package/src/EllipsoidGeometry.ts +107 -0
  13. package/src/Geodetic.ts +160 -0
  14. package/src/PointOfView.ts +169 -0
  15. package/src/Rectangle.ts +97 -0
  16. package/src/TileCoordinate.test.ts +38 -0
  17. package/src/TileCoordinate.ts +112 -0
  18. package/src/TilingScheme.test.ts +63 -0
  19. package/src/TilingScheme.ts +76 -0
  20. package/src/TypedArrayLoader.ts +53 -0
  21. package/src/assertions.ts +13 -0
  22. package/src/bufferGeometry.ts +62 -0
  23. package/src/helpers/projectOnEllipsoidSurface.ts +72 -0
  24. package/src/index.ts +25 -0
  25. package/src/math.ts +41 -0
  26. package/src/r3f/EastNorthUpFrame.tsx +52 -0
  27. package/src/r3f/EllipsoidMesh.tsx +36 -0
  28. package/src/r3f/index.ts +2 -0
  29. package/src/shaders/depth.glsl +15 -0
  30. package/src/shaders/packing.glsl +20 -0
  31. package/src/shaders/transform.glsl +12 -0
  32. package/src/typedArray.ts +76 -0
  33. package/src/types.ts +54 -0
  34. package/types/ArrayBufferLoader.d.ts +5 -0
  35. package/types/DataLoader.d.ts +67 -0
  36. package/types/Ellipsoid.d.ts +18 -0
  37. package/types/EllipsoidGeometry.d.ts +13 -0
  38. package/types/Geodetic.d.ts +37 -0
  39. package/types/PointOfView.d.ts +20 -0
  40. package/types/Rectangle.d.ts +27 -0
  41. package/types/TileCoordinate.d.ts +21 -0
  42. package/types/TilingScheme.d.ts +21 -0
  43. package/types/TypedArrayLoader.d.ts +16 -0
  44. package/types/assertions.d.ts +4 -0
  45. package/types/bufferGeometry.d.ts +5 -0
  46. package/types/helpers/projectOnEllipsoidSurface.d.ts +6 -0
  47. package/types/index.d.ts +18 -0
  48. package/types/math.d.ts +13 -0
  49. package/types/r3f/EastNorthUpFrame.d.ts +15 -0
  50. package/types/r3f/EllipsoidMesh.d.ts +13 -0
  51. package/types/r3f/index.d.ts +2 -0
  52. package/types/typedArray.d.ts +10 -0
  53. package/types/types.d.ts +22 -0
package/build/r3f.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),i=require("react"),l=require("three"),n=require("./shared.cjs"),f=require("@react-three/fiber"),m=new l.Matrix4,d=new n.Geodetic,E=new l.Vector3;class h extends l.Group{set(t,e,r,o=n.Ellipsoid.WGS84){const s=d.set(t,e,r).toECEF(E);o.getEastNorthUpFrame(s,m).decompose(this.position,this.quaternion,this.scale)}}const x=i.forwardRef(function({longitude:t,latitude:e,height:r=0,ellipsoid:o=n.Ellipsoid.WGS84,children:s},p){const a=i.useMemo(()=>new h,[]);return i.useEffect(()=>{a.set(t,e,r,o)},[t,e,r,o,a]),u.jsx("primitive",{ref:p,object:a,children:s})});function G(c){return t=>{c.forEach(e=>{typeof e=="function"?e(t):e!=null&&(e.current=t)})}}const S=i.forwardRef(function({args:t,children:e,...r},o){const s=i.useRef(null);return f.extend({EllipsoidGeometry:n.EllipsoidGeometry}),u.jsxs("mesh",{ref:G([s,o]),...r,children:[u.jsx("ellipsoidGeometry",{args:t}),e]})});exports.EastNorthUpFrame=x;exports.EllipsoidMesh=S;
package/build/r3f.js ADDED
@@ -0,0 +1,38 @@
1
+ import { jsx as a, jsxs as f } from "react/jsx-runtime";
2
+ import { forwardRef as m, useMemo as u, useEffect as l, useRef as E } from "react";
3
+ import { Group as h, Matrix4 as d, Vector3 as x } from "three";
4
+ import { E as p, G, a as w } from "./shared.js";
5
+ import { extend as F } from "@react-three/fiber";
6
+ const S = /* @__PURE__ */ new d(), N = /* @__PURE__ */ new G(), U = /* @__PURE__ */ new x();
7
+ class j extends h {
8
+ set(o, t, r, e = p.WGS84) {
9
+ const s = N.set(o, t, r).toECEF(U);
10
+ e.getEastNorthUpFrame(s, S).decompose(this.position, this.quaternion, this.scale);
11
+ }
12
+ }
13
+ const g = /* @__PURE__ */ m(function({ longitude: o, latitude: t, height: r = 0, ellipsoid: e = p.WGS84, children: s }, n) {
14
+ const c = u(() => new j(), []);
15
+ return l(() => {
16
+ c.set(o, t, r, e);
17
+ }, [o, t, r, e, c]), /* @__PURE__ */ a("primitive", { ref: n, object: c, children: s });
18
+ });
19
+ function y(i) {
20
+ return (o) => {
21
+ i.forEach((t) => {
22
+ typeof t == "function" ? t(o) : t != null && (t.current = o);
23
+ });
24
+ };
25
+ }
26
+ const q = /* @__PURE__ */ m(
27
+ function({ args: o, children: t, ...r }, e) {
28
+ const s = E(null);
29
+ return F({ EllipsoidGeometry: w }), /* @__PURE__ */ f("mesh", { ref: y([s, e]), ...r, children: [
30
+ /* @__PURE__ */ a("ellipsoidGeometry", { args: o }),
31
+ t
32
+ ] });
33
+ }
34
+ );
35
+ export {
36
+ g as EastNorthUpFrame,
37
+ q as EllipsoidMesh
38
+ };
@@ -0,0 +1 @@
1
+ "use strict";var P=Object.defineProperty;var j=(p,t,e)=>t in p?P(p,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):p[t]=e;var g=(p,t,e)=>j(p,typeof t!="symbol"?t+"":t,e);const s=require("three"),W=new s.Vector3;function G(p,t,e=new s.Vector3,r){const{x:i,y:n,z:c}=p,o=t.x,a=t.y,y=t.z,d=i*i*o,u=n*n*a,m=c*c*y,l=d+u+m,h=Math.sqrt(1/l);if(!Number.isFinite(h))return;const f=W.copy(p).multiplyScalar(h);if(l<((r==null?void 0:r.centerTolerance)??.1))return e.copy(f);const A=f.multiply(t).multiplyScalar(2);let M=(1-h)*p.length()/(A.length()/2),z=0,w,x,V,E;do{M-=z,w=1/(1+M*o),x=1/(1+M*a),V=1/(1+M*y);const v=w*w,L=x*x,T=V*V,B=v*w,C=L*x,D=T*V;E=d*v+u*L+m*T-1,z=E/((d*B*o+u*C*a+m*D*y)*-2)}while(Math.abs(E)>1e-12);return e.set(i*w,n*x,c*V)}const b=new s.Vector3,U=new s.Vector3,O=new s.Vector3,S=class S{constructor(t,e,r){g(this,"radii");this.radii=new s.Vector3(t,e,r)}get minimumRadius(){return Math.min(this.radii.x,this.radii.y,this.radii.z)}get maximumRadius(){return Math.max(this.radii.x,this.radii.y,this.radii.z)}reciprocalRadii(t=new s.Vector3){const{x:e,y:r,z:i}=this.radii;return t.set(1/e,1/r,1/i)}reciprocalRadiiSquared(t=new s.Vector3){const{x:e,y:r,z:i}=this.radii;return t.set(1/e**2,1/r**2,1/i**2)}projectOnSurface(t,e=new s.Vector3,r){return G(t,this.reciprocalRadiiSquared(),e,r)}getSurfaceNormal(t,e=new s.Vector3){return e.multiplyVectors(this.reciprocalRadiiSquared(b),t).normalize()}getEastNorthUpVectors(t,e=new s.Vector3,r=new s.Vector3,i=new s.Vector3){this.getSurfaceNormal(t,i),e.set(-t.y,t.x,0).normalize(),r.crossVectors(i,e).normalize()}getEastNorthUpFrame(t,e=new s.Matrix4){const r=b,i=U,n=O;return this.getEastNorthUpVectors(t,r,i,n),e.makeBasis(r,i,n).setPosition(t)}getIntersection(t,e=new s.Vector3){const r=this.reciprocalRadii(b),i=U.copy(r).multiply(t.origin),n=O.copy(r).multiply(t.direction),c=i.lengthSq(),o=n.lengthSq(),a=i.dot(n),y=a**2-o*(c-1);if(c===1)return e.copy(t.origin);if(c>1){if(a>=0||y<0)return;const d=Math.sqrt(y),u=(-a-d)/o,m=(-a+d)/o;return t.at(Math.min(u,m),e)}if(c<1){const d=a**2-o*(c-1),u=Math.sqrt(d),m=(-a+u)/o;return t.at(m,e)}if(a<0)return t.at(-a/o,e)}getOsculatingSphereCenter(t,e,r=new s.Vector3){const i=this.radii.x**2,n=b.set(t.x/i,t.y/i,t.z/this.radii.z**2).normalize();return r.copy(n.multiplyScalar(-e).add(t))}};g(S,"WGS84",new S(6378137,6378137,6356752314245179e-9));let q=S;class $ extends s.BufferGeometry{constructor(e=new s.Vector3(1,1,1),r=32,i=16){super();g(this,"type","EllipsoidGeometry");g(this,"parameters");this.parameters={radii:e,longitudeSegments:r,latitudeSegments:i},r=Math.max(3,Math.floor(r)),i=Math.max(2,Math.floor(i));const n=(r+1)*(i+1),c=new s.Vector3,o=new s.Vector3,a=new Float32Array(n*3),y=new Float32Array(n*3),d=new Float32Array(n*2),u=[],m=[];for(let l=0,h=0,f=0,A=0;l<=i;++l){const M=[],z=l/i,w=z*Math.PI;let x=0;l===0?x=.5/r:l===i&&(x=-.5/r);for(let V=0;V<=r;++V,h+=3,f+=2,++A){const E=V/r,v=E*Math.PI*2;c.x=e.x*Math.cos(v)*Math.sin(w),c.y=e.y*Math.sin(v)*Math.sin(w),c.z=e.z*Math.cos(w),a[h]=c.x,a[h+1]=c.y,a[h+2]=c.z,o.copy(c).normalize(),y[h]=o.x,y[h+1]=o.y,y[h+2]=o.z,d[f]=E+x,d[f+1]=1-z,M.push(A)}u.push(M)}for(let l=0;l<i;++l)for(let h=0;h<r;++h){const f=u[l][h+1],A=u[l][h],M=u[l+1][h],z=u[l+1][h+1];l!==0&&m.push(f,A,z),l!==i-1&&m.push(A,M,z)}this.setIndex(m),this.setAttribute("position",new s.BufferAttribute(a,3)),this.setAttribute("normal",new s.BufferAttribute(y,3)),this.setAttribute("uv",new s.BufferAttribute(d,2))}copy(e){return super.copy(e),this.parameters={...e.parameters},this}}const N=new s.Vector3,R=new s.Vector3,I=class I{constructor(t=0,e=0,r=0){this.longitude=t,this.latitude=e,this.height=r}set(t,e,r){return this.longitude=t,this.latitude=e,r!=null&&(this.height=r),this}clone(){return new I(this.longitude,this.latitude,this.height)}copy(t){return this.longitude=t.longitude,this.latitude=t.latitude,this.height=t.height,this}equals(t){return t.longitude===this.longitude&&t.latitude===this.latitude&&t.height===this.height}setLongitude(t){return this.longitude=t,this}setLatitude(t){return this.latitude=t,this}setHeight(t){return this.height=t,this}normalize(){return this.longitude<I.MIN_LONGITUDE&&(this.longitude+=Math.PI*2),this}setFromECEF(t,e){const i=((e==null?void 0:e.ellipsoid)??q.WGS84).reciprocalRadiiSquared(N),n=G(t,i,R,e);if(n==null)throw new Error(`Could not project position to ellipsoid surface: ${t.toArray()}`);const c=N.multiplyVectors(n,i).normalize();this.longitude=Math.atan2(c.y,c.x),this.latitude=Math.asin(c.z);const o=N.subVectors(t,n);return this.height=Math.sign(o.dot(t))*o.length(),this}toECEF(t=new s.Vector3,e){const r=(e==null?void 0:e.ellipsoid)??q.WGS84,i=N.multiplyVectors(r.radii,r.radii),n=Math.cos(this.latitude),c=R.set(n*Math.cos(this.longitude),n*Math.sin(this.longitude),Math.sin(this.latitude)).normalize();return t.multiplyVectors(i,c),t.divideScalar(Math.sqrt(c.dot(t))).add(c.multiplyScalar(this.height))}fromArray(t,e=0){return this.longitude=t[e],this.latitude=t[e+1],this.height=t[e+2],this}toArray(t=[],e=0){return t[e]=this.longitude,t[e+1]=this.latitude,t[e+2]=this.height,t}*[Symbol.iterator](){yield this.longitude,yield this.latitude,yield this.height}};g(I,"MIN_LONGITUDE",-Math.PI),g(I,"MAX_LONGITUDE",Math.PI),g(I,"MIN_LATITUDE",-Math.PI/2),g(I,"MAX_LATITUDE",Math.PI/2);let F=I;exports.Ellipsoid=q;exports.EllipsoidGeometry=$;exports.Geodetic=F;
@@ -0,0 +1,198 @@
1
+ var B = Object.defineProperty;
2
+ var W = (p, t, i) => t in p ? B(p, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : p[t] = i;
3
+ var z = (p, t, i) => W(p, typeof t != "symbol" ? t + "" : t, i);
4
+ import { Vector3 as c, Matrix4 as $, BufferGeometry as Q, BufferAttribute as F } from "three";
5
+ const X = /* @__PURE__ */ new c();
6
+ function D(p, t, i = new c(), r) {
7
+ const { x: s, y: n, z: e } = p, a = t.x, o = t.y, y = t.z, d = s * s * a, u = n * n * o, m = e * e * y, l = d + u + m, h = Math.sqrt(1 / l);
8
+ if (!Number.isFinite(h))
9
+ return;
10
+ const f = X.copy(p).multiplyScalar(h);
11
+ if (l < ((r == null ? void 0 : r.centerTolerance) ?? 0.1))
12
+ return i.copy(f);
13
+ const E = f.multiply(t).multiplyScalar(2);
14
+ let M = (1 - h) * p.length() / (E.length() / 2), I = 0, w, x, g, A;
15
+ do {
16
+ M -= I, w = 1 / (1 + M * a), x = 1 / (1 + M * o), g = 1 / (1 + M * y);
17
+ const q = w * w, L = x * x, T = g * g, G = q * w, P = L * x, j = T * g;
18
+ A = d * q + u * L + m * T - 1, I = A / ((d * G * a + u * P * o + m * j * y) * -2);
19
+ } while (Math.abs(A) > 1e-12);
20
+ return i.set(s * w, n * x, e * g);
21
+ }
22
+ const N = /* @__PURE__ */ new c(), U = /* @__PURE__ */ new c(), O = /* @__PURE__ */ new c(), V = class V {
23
+ constructor(t, i, r) {
24
+ z(this, "radii");
25
+ this.radii = new c(t, i, r);
26
+ }
27
+ get minimumRadius() {
28
+ return Math.min(this.radii.x, this.radii.y, this.radii.z);
29
+ }
30
+ get maximumRadius() {
31
+ return Math.max(this.radii.x, this.radii.y, this.radii.z);
32
+ }
33
+ reciprocalRadii(t = new c()) {
34
+ const { x: i, y: r, z: s } = this.radii;
35
+ return t.set(1 / i, 1 / r, 1 / s);
36
+ }
37
+ reciprocalRadiiSquared(t = new c()) {
38
+ const { x: i, y: r, z: s } = this.radii;
39
+ return t.set(1 / i ** 2, 1 / r ** 2, 1 / s ** 2);
40
+ }
41
+ projectOnSurface(t, i = new c(), r) {
42
+ return D(
43
+ t,
44
+ this.reciprocalRadiiSquared(),
45
+ i,
46
+ r
47
+ );
48
+ }
49
+ getSurfaceNormal(t, i = new c()) {
50
+ return i.multiplyVectors(this.reciprocalRadiiSquared(N), t).normalize();
51
+ }
52
+ getEastNorthUpVectors(t, i = new c(), r = new c(), s = new c()) {
53
+ this.getSurfaceNormal(t, s), i.set(-t.y, t.x, 0).normalize(), r.crossVectors(s, i).normalize();
54
+ }
55
+ getEastNorthUpFrame(t, i = new $()) {
56
+ const r = N, s = U, n = O;
57
+ return this.getEastNorthUpVectors(t, r, s, n), i.makeBasis(r, s, n).setPosition(t);
58
+ }
59
+ getIntersection(t, i = new c()) {
60
+ const r = this.reciprocalRadii(N), s = U.copy(r).multiply(t.origin), n = O.copy(r).multiply(t.direction), e = s.lengthSq(), a = n.lengthSq(), o = s.dot(n), y = o ** 2 - a * (e - 1);
61
+ if (e === 1)
62
+ return i.copy(t.origin);
63
+ if (e > 1) {
64
+ if (o >= 0 || y < 0)
65
+ return;
66
+ const d = Math.sqrt(y), u = (-o - d) / a, m = (-o + d) / a;
67
+ return t.at(Math.min(u, m), i);
68
+ }
69
+ if (e < 1) {
70
+ const d = o ** 2 - a * (e - 1), u = Math.sqrt(d), m = (-o + u) / a;
71
+ return t.at(m, i);
72
+ }
73
+ if (o < 0)
74
+ return t.at(-o / a, i);
75
+ }
76
+ getOsculatingSphereCenter(t, i, r = new c()) {
77
+ const s = this.radii.x ** 2, n = N.set(
78
+ t.x / s,
79
+ t.y / s,
80
+ t.z / this.radii.z ** 2
81
+ ).normalize();
82
+ return r.copy(n.multiplyScalar(-i).add(t));
83
+ }
84
+ };
85
+ z(V, "WGS84", /* @__PURE__ */ new V(
86
+ 6378137,
87
+ 6378137,
88
+ 6356752314245179e-9
89
+ ));
90
+ let b = V;
91
+ class J extends Q {
92
+ constructor(i = new c(1, 1, 1), r = 32, s = 16) {
93
+ super();
94
+ z(this, "type", "EllipsoidGeometry");
95
+ z(this, "parameters");
96
+ this.parameters = {
97
+ radii: i,
98
+ longitudeSegments: r,
99
+ latitudeSegments: s
100
+ }, r = Math.max(3, Math.floor(r)), s = Math.max(2, Math.floor(s));
101
+ const n = (r + 1) * (s + 1), e = new c(), a = new c(), o = new Float32Array(n * 3), y = new Float32Array(n * 3), d = new Float32Array(n * 2), u = [], m = [];
102
+ for (let l = 0, h = 0, f = 0, E = 0; l <= s; ++l) {
103
+ const M = [], I = l / s, w = I * Math.PI;
104
+ let x = 0;
105
+ l === 0 ? x = 0.5 / r : l === s && (x = -0.5 / r);
106
+ for (let g = 0; g <= r; ++g, h += 3, f += 2, ++E) {
107
+ const A = g / r, q = A * Math.PI * 2;
108
+ e.x = i.x * Math.cos(q) * Math.sin(w), e.y = i.y * Math.sin(q) * Math.sin(w), e.z = i.z * Math.cos(w), o[h] = e.x, o[h + 1] = e.y, o[h + 2] = e.z, a.copy(e).normalize(), y[h] = a.x, y[h + 1] = a.y, y[h + 2] = a.z, d[f] = A + x, d[f + 1] = 1 - I, M.push(E);
109
+ }
110
+ u.push(M);
111
+ }
112
+ for (let l = 0; l < s; ++l)
113
+ for (let h = 0; h < r; ++h) {
114
+ const f = u[l][h + 1], E = u[l][h], M = u[l + 1][h], I = u[l + 1][h + 1];
115
+ l !== 0 && m.push(f, E, I), l !== s - 1 && m.push(E, M, I);
116
+ }
117
+ this.setIndex(m), this.setAttribute("position", new F(o, 3)), this.setAttribute("normal", new F(y, 3)), this.setAttribute("uv", new F(d, 2));
118
+ }
119
+ copy(i) {
120
+ return super.copy(i), this.parameters = { ...i.parameters }, this;
121
+ }
122
+ }
123
+ const S = /* @__PURE__ */ new c(), R = /* @__PURE__ */ new c(), v = class v {
124
+ constructor(t = 0, i = 0, r = 0) {
125
+ this.longitude = t, this.latitude = i, this.height = r;
126
+ }
127
+ set(t, i, r) {
128
+ return this.longitude = t, this.latitude = i, r != null && (this.height = r), this;
129
+ }
130
+ clone() {
131
+ return new v(this.longitude, this.latitude, this.height);
132
+ }
133
+ copy(t) {
134
+ return this.longitude = t.longitude, this.latitude = t.latitude, this.height = t.height, this;
135
+ }
136
+ equals(t) {
137
+ return t.longitude === this.longitude && t.latitude === this.latitude && t.height === this.height;
138
+ }
139
+ setLongitude(t) {
140
+ return this.longitude = t, this;
141
+ }
142
+ setLatitude(t) {
143
+ return this.latitude = t, this;
144
+ }
145
+ setHeight(t) {
146
+ return this.height = t, this;
147
+ }
148
+ normalize() {
149
+ return this.longitude < v.MIN_LONGITUDE && (this.longitude += Math.PI * 2), this;
150
+ }
151
+ // See: https://en.wikipedia.org/wiki/Geographic_coordinate_conversion
152
+ // Reference: https://github.com/CesiumGS/cesium/blob/1.122/packages/engine/Source/Core/Geodetic.js#L119
153
+ setFromECEF(t, i) {
154
+ const s = ((i == null ? void 0 : i.ellipsoid) ?? b.WGS84).reciprocalRadiiSquared(S), n = D(
155
+ t,
156
+ s,
157
+ R,
158
+ i
159
+ );
160
+ if (n == null)
161
+ throw new Error(
162
+ `Could not project position to ellipsoid surface: ${t.toArray()}`
163
+ );
164
+ const e = S.multiplyVectors(n, s).normalize();
165
+ this.longitude = Math.atan2(e.y, e.x), this.latitude = Math.asin(e.z);
166
+ const a = S.subVectors(t, n);
167
+ return this.height = Math.sign(a.dot(t)) * a.length(), this;
168
+ }
169
+ // See: https://en.wikipedia.org/wiki/Geographic_coordinate_conversion
170
+ // Reference: https://github.com/CesiumGS/cesium/blob/1.122/packages/engine/Source/Core/Cartesian3.js#L916
171
+ toECEF(t = new c(), i) {
172
+ const r = (i == null ? void 0 : i.ellipsoid) ?? b.WGS84, s = S.multiplyVectors(
173
+ r.radii,
174
+ r.radii
175
+ ), n = Math.cos(this.latitude), e = R.set(
176
+ n * Math.cos(this.longitude),
177
+ n * Math.sin(this.longitude),
178
+ Math.sin(this.latitude)
179
+ ).normalize();
180
+ return t.multiplyVectors(s, e), t.divideScalar(Math.sqrt(e.dot(t))).add(e.multiplyScalar(this.height));
181
+ }
182
+ fromArray(t, i = 0) {
183
+ return this.longitude = t[i], this.latitude = t[i + 1], this.height = t[i + 2], this;
184
+ }
185
+ toArray(t = [], i = 0) {
186
+ return t[i] = this.longitude, t[i + 1] = this.latitude, t[i + 2] = this.height, t;
187
+ }
188
+ *[Symbol.iterator]() {
189
+ yield this.longitude, yield this.latitude, yield this.height;
190
+ }
191
+ };
192
+ z(v, "MIN_LONGITUDE", -Math.PI), z(v, "MAX_LONGITUDE", Math.PI), z(v, "MIN_LATITUDE", -Math.PI / 2), z(v, "MAX_LATITUDE", Math.PI / 2);
193
+ let C = v;
194
+ export {
195
+ b as E,
196
+ C as G,
197
+ J as a
198
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@takram/three-geospatial",
3
+ "version": "0.0.1-alpha.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./build/index.js",
9
+ "require": "./build/index.cjs",
10
+ "types": "./types/index.d.ts"
11
+ },
12
+ "./r3f": {
13
+ "import": "./build/r3f.js",
14
+ "require": "./build/r3f.cjs",
15
+ "types": "./types/r3f/index.d.ts"
16
+ }
17
+ },
18
+ "main": "./build/index.cjs",
19
+ "module": "./build/index.js",
20
+ "types": "./types/index.d.ts",
21
+ "files": [
22
+ "build",
23
+ "src",
24
+ "types",
25
+ "package.json",
26
+ "README.md"
27
+ ],
28
+ "dependencies": {
29
+ "lodash-es": "^4.17.21",
30
+ "react-merge-refs": "^2.1.1",
31
+ "tiny-invariant": "^1.3.3",
32
+ "type-fest": "^4.28.0"
33
+ },
34
+ "peerDependencies": {
35
+ "@react-three/fiber": ">=8.17.10",
36
+ "react": ">=18.0",
37
+ "three": ">=0.170.0"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }
@@ -0,0 +1,35 @@
1
+ import { FileLoader, Loader } from 'three'
2
+ import invariant from 'tiny-invariant'
3
+
4
+ export class ArrayBufferLoader extends Loader<ArrayBuffer> {
5
+ override load(
6
+ url: string,
7
+ onLoad: (data: ArrayBuffer) => void,
8
+ onProgress?: (event: ProgressEvent) => void,
9
+ onError?: (error: unknown) => void
10
+ ): void {
11
+ const loader = new FileLoader(this.manager)
12
+ loader.setResponseType('arraybuffer')
13
+ loader.setRequestHeader(this.requestHeader)
14
+ loader.setPath(this.path)
15
+ loader.setWithCredentials(this.withCredentials)
16
+ loader.load(
17
+ url,
18
+ arrayBuffer => {
19
+ invariant(arrayBuffer instanceof ArrayBuffer)
20
+ try {
21
+ onLoad(arrayBuffer)
22
+ } catch (error) {
23
+ if (onError != null) {
24
+ onError(error)
25
+ } else {
26
+ console.error(error)
27
+ }
28
+ this.manager.itemError(url)
29
+ }
30
+ },
31
+ onProgress,
32
+ onError
33
+ )
34
+ }
35
+ }
@@ -0,0 +1,114 @@
1
+ import {
2
+ ClampToEdgeWrapping,
3
+ Data3DTexture,
4
+ DataTexture,
5
+ FloatType,
6
+ LinearFilter,
7
+ Loader,
8
+ RGBAFormat,
9
+ type Texture,
10
+ type TypedArray
11
+ } from 'three'
12
+ import { type Class } from 'type-fest'
13
+
14
+ import {
15
+ Float32ArrayLoader,
16
+ Int16ArrayLoader,
17
+ Uint16ArrayLoader,
18
+ type TypedArrayLoader
19
+ } from './TypedArrayLoader'
20
+ import { type Callable } from './types'
21
+
22
+ export interface ImageSize {
23
+ width: number
24
+ height: number
25
+ depth?: number
26
+ }
27
+
28
+ export type DataTextureParameters = Omit<
29
+ Partial<{
30
+ [K in keyof Texture as Texture[K] extends Callable ? never : K]: Texture[K]
31
+ }>,
32
+ 'image'
33
+ >
34
+
35
+ const defaultDataTextureParameter = {
36
+ format: RGBAFormat,
37
+ wrapS: ClampToEdgeWrapping,
38
+ wrapT: ClampToEdgeWrapping,
39
+ minFilter: LinearFilter,
40
+ magFilter: LinearFilter
41
+ } satisfies DataTextureParameters
42
+
43
+ export abstract class DataLoader<
44
+ T extends DataTexture | Data3DTexture,
45
+ U extends TypedArray
46
+ > extends Loader<T> {
47
+ abstract readonly Texture: Class<T>
48
+ abstract readonly TypedArrayLoader: Class<TypedArrayLoader<U>>
49
+
50
+ readonly parameters?: DataTextureParameters
51
+
52
+ override load(
53
+ url: string,
54
+ onLoad: (data: T) => void,
55
+ onProgress?: (event: ProgressEvent) => void,
56
+ onError?: (error: unknown) => void
57
+ ): void {
58
+ const texture = new this.Texture()
59
+ const loader = new this.TypedArrayLoader(this.manager)
60
+ loader.setRequestHeader(this.requestHeader)
61
+ loader.setPath(this.path)
62
+ loader.setWithCredentials(this.withCredentials)
63
+ loader.load(
64
+ url,
65
+ array => {
66
+ texture.image.data = array as typeof texture.image.data
67
+ Object.assign(texture, this.parameters)
68
+ texture.needsUpdate = true
69
+ onLoad(texture)
70
+ },
71
+ onProgress,
72
+ onError
73
+ )
74
+ }
75
+ }
76
+
77
+ export class Int16Data2DLoader extends DataLoader<DataTexture, Int16Array> {
78
+ readonly Texture = DataTexture
79
+ readonly TypedArrayLoader = Int16ArrayLoader
80
+ readonly parameters = {
81
+ ...defaultDataTextureParameter,
82
+ type: FloatType
83
+ } satisfies DataTextureParameters
84
+ }
85
+
86
+ export class Uint16Data2DLoader extends DataLoader<DataTexture, Uint16Array> {
87
+ readonly Texture = DataTexture
88
+ readonly TypedArrayLoader = Uint16ArrayLoader
89
+ readonly parameters = {
90
+ ...defaultDataTextureParameter,
91
+ type: FloatType
92
+ } satisfies DataTextureParameters
93
+ }
94
+
95
+ export class Float32Data2DLoader extends DataLoader<DataTexture, Float32Array> {
96
+ readonly Texture = DataTexture
97
+ readonly TypedArrayLoader = Float32ArrayLoader
98
+ readonly parameters = {
99
+ ...defaultDataTextureParameter,
100
+ type: FloatType
101
+ } satisfies DataTextureParameters
102
+ }
103
+
104
+ export class Float32Data3DLoader extends DataLoader<
105
+ Data3DTexture,
106
+ Float32Array
107
+ > {
108
+ readonly Texture = Data3DTexture
109
+ readonly TypedArrayLoader = Float32ArrayLoader
110
+ readonly parameters = {
111
+ ...defaultDataTextureParameter,
112
+ type: FloatType
113
+ } satisfies DataTextureParameters
114
+ }
@@ -0,0 +1,128 @@
1
+ import { Matrix4, Vector3, type Ray } from 'three'
2
+
3
+ import {
4
+ projectOnEllipsoidSurface,
5
+ type ProjectOnEllipsoidSurfaceOptions
6
+ } from './helpers/projectOnEllipsoidSurface'
7
+
8
+ const vectorScratch1 = /*#__PURE__*/ new Vector3()
9
+ const vectorScratch2 = /*#__PURE__*/ new Vector3()
10
+ const vectorScratch3 = /*#__PURE__*/ new Vector3()
11
+
12
+ export class Ellipsoid {
13
+ static readonly WGS84 = /*#__PURE__*/ new Ellipsoid(
14
+ 6378137,
15
+ 6378137,
16
+ 6356752.3142451793
17
+ )
18
+
19
+ readonly radii: Vector3
20
+
21
+ constructor(x: number, y: number, z: number) {
22
+ this.radii = new Vector3(x, y, z)
23
+ }
24
+
25
+ get minimumRadius(): number {
26
+ return Math.min(this.radii.x, this.radii.y, this.radii.z)
27
+ }
28
+
29
+ get maximumRadius(): number {
30
+ return Math.max(this.radii.x, this.radii.y, this.radii.z)
31
+ }
32
+
33
+ reciprocalRadii(result = new Vector3()): Vector3 {
34
+ const { x, y, z } = this.radii
35
+ return result.set(1 / x, 1 / y, 1 / z)
36
+ }
37
+
38
+ reciprocalRadiiSquared(result = new Vector3()): Vector3 {
39
+ const { x, y, z } = this.radii
40
+ return result.set(1 / x ** 2, 1 / y ** 2, 1 / z ** 2)
41
+ }
42
+
43
+ projectOnSurface(
44
+ position: Vector3,
45
+ result = new Vector3(),
46
+ options?: ProjectOnEllipsoidSurfaceOptions
47
+ ): Vector3 | undefined {
48
+ return projectOnEllipsoidSurface(
49
+ position,
50
+ this.reciprocalRadiiSquared(),
51
+ result,
52
+ options
53
+ )
54
+ }
55
+
56
+ getSurfaceNormal(position: Vector3, result = new Vector3()): Vector3 {
57
+ return result
58
+ .multiplyVectors(this.reciprocalRadiiSquared(vectorScratch1), position)
59
+ .normalize()
60
+ }
61
+
62
+ getEastNorthUpVectors(
63
+ position: Vector3,
64
+ east = new Vector3(),
65
+ north = new Vector3(),
66
+ up = new Vector3()
67
+ ): void {
68
+ this.getSurfaceNormal(position, up)
69
+ east.set(-position.y, position.x, 0).normalize()
70
+ north.crossVectors(up, east).normalize()
71
+ }
72
+
73
+ getEastNorthUpFrame(position: Vector3, result = new Matrix4()): Matrix4 {
74
+ const east = vectorScratch1
75
+ const north = vectorScratch2
76
+ const up = vectorScratch3
77
+ this.getEastNorthUpVectors(position, east, north, up)
78
+ return result.makeBasis(east, north, up).setPosition(position)
79
+ }
80
+
81
+ getIntersection(ray: Ray, result = new Vector3()): Vector3 | undefined {
82
+ const reciprocalRadii = this.reciprocalRadii(vectorScratch1)
83
+ const p = vectorScratch2.copy(reciprocalRadii).multiply(ray.origin)
84
+ const d = vectorScratch3.copy(reciprocalRadii).multiply(ray.direction)
85
+ const p2 = p.lengthSq()
86
+ const d2 = d.lengthSq()
87
+ const pd = p.dot(d)
88
+ const discriminant = pd ** 2 - d2 * (p2 - 1)
89
+ if (p2 === 1) {
90
+ return result.copy(ray.origin)
91
+ }
92
+ if (p2 > 1) {
93
+ if (pd >= 0 || discriminant < 0) {
94
+ return // No intersection
95
+ }
96
+ const Q = Math.sqrt(discriminant)
97
+ const t1 = (-pd - Q) / d2
98
+ const t2 = (-pd + Q) / d2
99
+ return ray.at(Math.min(t1, t2), result)
100
+ }
101
+ if (p2 < 1) {
102
+ const discriminant = pd ** 2 - d2 * (p2 - 1)
103
+ const Q = Math.sqrt(discriminant)
104
+ const t = (-pd + Q) / d2
105
+ return ray.at(t, result) // Backface of the ellipsoid
106
+ }
107
+ if (pd < 0) {
108
+ return ray.at(-pd / d2, result) // Backface of the ellipsoid
109
+ }
110
+ // No intersection
111
+ }
112
+
113
+ getOsculatingSphereCenter(
114
+ surfacePosition: Vector3,
115
+ radius: number,
116
+ result = new Vector3()
117
+ ): Vector3 {
118
+ const xySquared = this.radii.x ** 2
119
+ const normal = vectorScratch1
120
+ .set(
121
+ surfacePosition.x / xySquared,
122
+ surfacePosition.y / xySquared,
123
+ surfacePosition.z / this.radii.z ** 2
124
+ )
125
+ .normalize()
126
+ return result.copy(normal.multiplyScalar(-radius).add(surfacePosition))
127
+ }
128
+ }
@@ -0,0 +1,107 @@
1
+ import { BufferAttribute, BufferGeometry, Vector3 } from 'three'
2
+
3
+ export interface EllipsoidGeometryParameters {
4
+ radii: Vector3
5
+ longitudeSegments?: number
6
+ latitudeSegments?: number
7
+ }
8
+
9
+ export class EllipsoidGeometry extends BufferGeometry {
10
+ readonly type = 'EllipsoidGeometry'
11
+
12
+ parameters: EllipsoidGeometryParameters
13
+
14
+ constructor(
15
+ radii = new Vector3(1, 1, 1),
16
+ longitudeSegments = 32,
17
+ latitudeSegments = 16
18
+ ) {
19
+ super()
20
+ this.parameters = {
21
+ radii,
22
+ longitudeSegments,
23
+ latitudeSegments
24
+ }
25
+
26
+ longitudeSegments = Math.max(3, Math.floor(longitudeSegments))
27
+ latitudeSegments = Math.max(2, Math.floor(latitudeSegments))
28
+
29
+ const elementCount = (longitudeSegments + 1) * (latitudeSegments + 1)
30
+ const vertex = new Vector3()
31
+ const normal = new Vector3()
32
+ const vertices = new Float32Array(elementCount * 3)
33
+ const normals = new Float32Array(elementCount * 3)
34
+ const uvs = new Float32Array(elementCount * 2)
35
+ const grid: number[][] = []
36
+ const indices: number[] = []
37
+
38
+ // Vertices, normals and UVs
39
+ for (
40
+ let y = 0, vertexIndex = 0, uvIndex = 0, rowIndex = 0;
41
+ y <= latitudeSegments;
42
+ ++y
43
+ ) {
44
+ const rowIndices = []
45
+ const v = y / latitudeSegments
46
+ const phi = v * Math.PI
47
+
48
+ // Special case for the poles
49
+ let uOffset = 0
50
+ if (y === 0) {
51
+ uOffset = 0.5 / longitudeSegments
52
+ } else if (y === latitudeSegments) {
53
+ uOffset = -0.5 / longitudeSegments
54
+ }
55
+
56
+ for (
57
+ let x = 0;
58
+ x <= longitudeSegments;
59
+ ++x, vertexIndex += 3, uvIndex += 2, ++rowIndex
60
+ ) {
61
+ const u = x / longitudeSegments
62
+ const theta = u * Math.PI * 2
63
+ vertex.x = radii.x * Math.cos(theta) * Math.sin(phi)
64
+ vertex.y = radii.y * Math.sin(theta) * Math.sin(phi)
65
+ vertex.z = radii.z * Math.cos(phi)
66
+ vertices[vertexIndex] = vertex.x
67
+ vertices[vertexIndex + 1] = vertex.y
68
+ vertices[vertexIndex + 2] = vertex.z
69
+ normal.copy(vertex).normalize()
70
+ normals[vertexIndex] = normal.x
71
+ normals[vertexIndex + 1] = normal.y
72
+ normals[vertexIndex + 2] = normal.z
73
+ uvs[uvIndex] = u + uOffset
74
+ uvs[uvIndex + 1] = 1 - v
75
+ rowIndices.push(rowIndex)
76
+ }
77
+ grid.push(rowIndices)
78
+ }
79
+
80
+ // Indices
81
+ for (let y = 0; y < latitudeSegments; ++y) {
82
+ for (let x = 0; x < longitudeSegments; ++x) {
83
+ const a = grid[y][x + 1]
84
+ const b = grid[y][x]
85
+ const c = grid[y + 1][x]
86
+ const d = grid[y + 1][x + 1]
87
+ if (y !== 0) {
88
+ indices.push(a, b, d)
89
+ }
90
+ if (y !== latitudeSegments - 1) {
91
+ indices.push(b, c, d)
92
+ }
93
+ }
94
+ }
95
+
96
+ this.setIndex(indices)
97
+ this.setAttribute('position', new BufferAttribute(vertices, 3))
98
+ this.setAttribute('normal', new BufferAttribute(normals, 3))
99
+ this.setAttribute('uv', new BufferAttribute(uvs, 2))
100
+ }
101
+
102
+ copy(source: EllipsoidGeometry): this {
103
+ super.copy(source)
104
+ this.parameters = { ...source.parameters }
105
+ return this
106
+ }
107
+ }