diff --git a/src/s2geometry.js b/src/s2geometry.js new file mode 100644 index 0000000..68831fd --- /dev/null +++ b/src/s2geometry.js @@ -0,0 +1,307 @@ +/// S2 Geometry functions +// the regional scoreboard is based on a level 6 S2 Cell +// - https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view?pli=1#slide=id.i22 +// at the time of writing there's no actual API for the intel map to retrieve scoreboard data, +// but it's still useful to plot the score cells on the intel map + + +// the S2 geometry is based on projecting the earth sphere onto a cube, with some scaling of face coordinates to +// keep things close to approximate equal area for adjacent cells +// to convert a lat,lng into a cell id: +// - convert lat,lng to x,y,z +// - convert x,y,z into face,u,v +// - u,v scaled to s,t with quadratic formula +// - s,t converted to integer i,j offsets +// - i,j converted to a position along a Hubbert space-filling curve +// - combine face,position to get the cell id + +//NOTE: compared to the google S2 geometry library, we vary from their code in the following ways +// - cell IDs: they combine face and the hilbert curve position into a single 64 bit number. this gives efficient space +// and speed. javascript doesn't have appropriate data types, and speed is not cricical, so we use +// as [face,[bitpair,bitpair,...]] instead +// - i,j: they always use 30 bits, adjusting as needed. we use 0 to (1< temp[1]) { + if (temp[0] > temp[2]) { + return 0; + } else { + return 2; + } + } else { + if (temp[1] > temp[2]) { + return 1; + } else { + return 2; + } + } + +}; + +var faceXYZToUV = function(face,xyz) { + var u,v; + + switch (face) { + case 0: u = xyz[1]/xyz[0]; v = xyz[2]/xyz[0]; break; + case 1: u = -xyz[0]/xyz[1]; v = xyz[2]/xyz[1]; break; + case 2: u = -xyz[0]/xyz[2]; v = -xyz[1]/xyz[2]; break; + case 3: u = xyz[2]/xyz[0]; v = xyz[1]/xyz[0]; break; + case 4: u = xyz[2]/xyz[1]; v = -xyz[0]/xyz[1]; break; + case 5: u = -xyz[1]/xyz[2]; v = -xyz[0]/xyz[2]; break; + default: throw {error: 'Invalid face'}; break; + } + + return [u,v]; +} + + + + +var XYZToFaceUV = function(xyz) { + var face = largestAbsComponent(xyz); + + if (xyz[face] < 0) { + face += 3; + } + + uv = faceXYZToUV (face,xyz); + + return [face, uv]; +}; + +var FaceUVToXYZ = function(face,uv) { + var u = uv[0]; + var v = uv[1]; + + switch (face) { + case 0: return [ 1, u, v]; + case 1: return [-u, 1, v]; + case 2: return [-u,-v, 1]; + case 3: return [-1,-v,-u]; + case 4: return [ v,-1,-u]; + case 5: return [ v, u,-1]; + default: throw {error: 'Invalid face'}; + } +}; + + +var STToUV = function(st) { + var singleSTtoUV = function(st) { + if (st >= 0.5) { + return (1/3.0) * (4*st*st - 1); + } else { + return (1/3.0) * (1 - (4*(1-st)*(1-st))); + } + } + + return [singleSTtoUV(st[0]), singleSTtoUV(st[1])]; +}; + + + +var UVToST = function(uv) { + var singleUVtoST = function(uv) { + if (uv >= 0) { + return 0.5 * Math.sqrt (1 + 3*uv); + } else { + return 1 - 0.5 * Math.sqrt (1 - 3*uv); + } + } + + return [singleUVtoST(uv[0]), singleUVtoST(uv[1])]; +}; + + +var STToIJ = function(st,order) { + var maxSize = (1<=0; i--) { + + var mask = 1<=0 && ij[1]>=0 && ij[0]