Compare commits

...

41 Commits

Author SHA1 Message Date
AJ ONeal
7a10f5b562 rebrand 2018-04-10 09:57:02 -06:00
AJ ONeal
e7ecb48449 auto-update banner 2016-12-30 02:41:07 -07:00
AJ ONeal
441c693bad auto-update ad 2016-12-30 02:23:27 -07:00
AJ ONeal
e67bb19828 Update README.md 2016-11-25 10:39:15 -07:00
AJ ONeal
df6d38b5ad v1.2.9 2016-08-17 23:35:26 -06:00
AJ ONeal
3a962c1956 further cleanup and testing for #16 2016-08-17 23:35:20 -06:00
AJ ONeal
5495dd0a53 Merge pull request #16 from lopper/master
support converting from HilbertQuadkey to s2Cell.
2016-08-17 23:02:59 -06:00
Patrick
d28f0a6d3f add support to convert from HilbertQuadkey to s2Cell. 2016-08-16 22:08:21 -07:00
Patrick
a4e97ba0a9 fix issue with FromLatLng so that it supports latLng cooridinates that involve 0. 2016-08-16 22:00:34 -07:00
AJ ONeal
0cc25036e5 v1.2.8 2016-08-16 13:19:30 -06:00
AJ ONeal
a11cd4e920 merge #14 and #15 2016-08-16 13:18:33 -06:00
AJ ONeal
42a9eaf1fc Merge pull request #15 from breadboardllc/module-exporting-fixes
Fix export so it works with `module.exports` & `window`
2016-08-16 13:10:27 -06:00
David Murdoch
4e631500ea Don't overwrite L if it exists in export scope
This makes the module.exports fix more inline with previous behavior.
2016-08-16 14:56:14 -04:00
David Murdoch
8aa84ebf93 Fix export so it works w/ module.exports & window
Fixes Issue #13.

This also fixes a similar issue where this library is used via browserify/webpack in the browser (or other environments where `window` is defined). Previously, `var S2 = require("s1-geometry").S2;`, would result in `window.S2` being defined but `S2` remaining undefined. This changes the behavior to prefer exporting via `module.exports`, if available, and falling back to `window` (or whatever `this` is in the module's imported scope --  which is usually `window`). Additionally, this keeps the library from polluting the global scope unnecessarily.
2016-08-16 14:52:15 -04:00
Jason Hunter
9fdcc00739 flip module.exports and window check for this lib to work on nw.js and electron per Issue #13 2016-08-14 22:39:50 -04:00
AJ ONeal
7180db1be6 explicit br 2016-08-03 18:00:49 -06:00
AJ ONeal
c6ee1ee508 update formatting 2016-08-03 17:59:48 -06:00
AJ ONeal
4249d3bfa6 add orientation 2016-08-03 17:54:58 -06:00
AJ ONeal
60d01395ef add orientation 2016-08-03 17:53:38 -06:00
AJ ONeal
2e7b12e212 add more of api 2016-08-03 17:48:24 -06:00
AJ ONeal
1a9c95e2c6 bump 2016-08-03 17:44:03 -06:00
AJ ONeal
b3f5bbd27f Merge branch 'master' of github.com:Daplie/s2-geometry.js 2016-08-03 17:43:49 -06:00
AJ ONeal
f96e16c6bf update docs and aliases 2016-08-03 17:43:35 -06:00
AJ ONeal
d1f19ef616 Update README.md 2016-08-03 16:56:56 -06:00
AJ ONeal
191e83fe40 Update README.md 2016-08-03 16:56:09 -06:00
AJ ONeal
4c4154a288 update table 2016-08-03 16:54:02 -06:00
AJ ONeal
be9112f2ad add table with faces 2016-08-03 16:48:49 -06:00
AJ ONeal
bb664af25b special test case for @vekexasia #1 2016-08-03 10:27:24 -06:00
AJ ONeal
98fa79b6bd special test case for @Skeec for #11 2016-08-03 09:24:49 -06:00
AJ ONeal
d5524f7b74 test for @DVassilev on #8 and minor updates 2016-08-03 09:05:23 -06:00
AJ ONeal
afe681240b test against golang/geo/s2 2016-08-03 01:20:48 -06:00
AJ ONeal
197e07a605 bump 2016-08-03 00:33:32 -06:00
AJ ONeal
60186d007f fix #8 #6 2016-08-03 00:33:20 -06:00
AJ ONeal
b7f7c6df1c fix #9 2016-08-03 00:23:22 -06:00
AJ ONeal
a1da97514e bump 2016-08-03 00:04:51 -06:00
AJ ONeal
68274eb677 use correct starting square for face #6 #8 #9 2016-08-03 00:04:26 -06:00
AJ ONeal
e04b0b0ecd move to Daplie 2016-07-30 11:06:23 -04:00
AJ ONeal
4a7b8819c9 bump 2016-07-30 00:51:34 -04:00
AJ ONeal
c0c80ca0f8 show xyz value also 2016-07-29 13:50:20 -04:00
AJ ONeal
c094e1def7 add step-by-step value debugging 2016-07-29 13:49:32 -04:00
AJ ONeal
d18e4906c5 export some internals for debugging 2016-07-29 13:48:41 -04:00
14 changed files with 2684 additions and 100 deletions

View File

@ -1,7 +1,7 @@
ISC License (ISC)
Copyright (c) 2012-2016, Jon Atkins <github@jonatkins.com>
Copyright (c) 2016, AJ ONeal <coolaj86@gmail.com>
Copyright (c) 2016, AJ ONeal <aj@daplie.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

153
README.md
View File

@ -1,10 +1,86 @@
s2-geometry (JavaScript/ES5.1)
======================
| Sponsored by [ppl](https://ppl.family)
A pure JavaScript/ES5.1 port of Google/Niantic's S2 Geometry library (as used by **Ingress**, **Pokemon GO**)
Currently contains basic support for S2Cell
<table>
<tr>
<td></td>
<td>
Face 2
<br>
Orientation A
<a href="http://i.imgur.com/SODO4bT.jpg" target="_face2"><img src="http://i.imgur.com/SODO4bTt.jpg" title="Face 2" alt="Face 2"></a>
<br>
The North Pole<br>(and Canada / Europe)
</td>
<td></td>
</tr>
<tr>
<td>
Face 0
<br>
Orientation A
<a href="http://i.imgur.com/dLI5Zd1.jpg" target="_face0"><img src="http://i.imgur.com/dLI5Zd1t.jpg" title="Face 0" alt="Face 0"></a>
<br>
Africa
</td>
<td>
Face 1
<br>
Orientation D
<a href="http://i.imgur.com/duTLDTV.jpg" target="_face1"><img src="http://i.imgur.com/duTLDTVt.jpg" title="Face 1" alt="Face 1"></a>
<br>
Asia
</td>
<td>
Face 3
<br>
Orientation D
<a href="http://i.imgur.com/6Ho35Tc.jpg" target="_face3"><img src="http://i.imgur.com/6Ho35Tct.jpg" title="Face 3" alt="Face 3"></a>
<br>
Nothing<br>(and Australia)
</td>
<td>
Face 4
<br>
Orientation A
<a href="http://i.imgur.com/3IBAfqj.jpg" target="_face4"><img src="http://i.imgur.com/3IBAfqjt.jpg" title="Face 4" alt="Face 4"></a>
<br>
The Americas<br>(and Provo, UT)
</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>
Face 5
<br>
Orientation D
<a href="http://i.imgur.com/HZCBvgy.jpg" target="_face5"><img src="http://i.imgur.com/HZCBvgyt.jpg" title="Face 5" alt="Face 5"></a>
<br>
Antarctica
</td>
</tr>
</table>
Where is this being used?
---------------------
@ -16,15 +92,57 @@ Simple Examples
---------------
```javascript
'use strict';
var S2 = require('s2-geometry').S2;
var lat = 40.2574448;
var lng = -111.7089464;
var level = 15;
var latlng = { lat: 40.2574448, lng: -111.7089464 };
var cell = S2.S2Cell.FromLatLng(latlng, level);
cell.getNeighbors(); // [ cellLeft, cellDown, cellRight, cellUp ]
cell.getLatLng(); // { lat: 40.2574448, lng: -111.7089464 }
var key = cell.toHilbertQuadkey();
//
// Convert from Lat / Lng
//
var key = S2.latLngToKey(lat, lng, level);
// '4/032212303102210'
//
// Convert between Hilbert Curve Quadtree Key and S2 Cell Id
//
var id = S2.keyToId(key);
// '9749618446378729472'
var key = S2.idToKey(id);
// '9749618446378729472'
//
// Convert between Quadkey and Id
//
var latlng = S2.keyToLatLng(key);
var latlng = S2.idToLatLng(id);
//
// Neighbors
//
var neighbors = S2.latLngToNeighborKeys(lat, lng, level);
// [ keyLeft, keyDown, keyRight, keyUp ]
//
// Previous, Next, and Step
//
var nextKey = S2.nextKey(key);
var prevKey = S2.prevKey(key);
var backTenKeys = S2.stepKey(key, -10);
```
Previous and Next
@ -37,14 +155,16 @@ You can get the previous and next S2CellId from any given Key:
3. Convert the Key to an Id (uint64 string)
```javascript
var key = S2.latLngToKey(40.2574448, -111.7089464); // '4/032212303102210'
var id = S2.toId(key); // '9749618446378729472'
var key = S2.latLngToKey(40.2574448, -111.7089464, 15); // '4/032212303102210'
var id = S2.keyToId(key); // '9749618446378729472'
var nextKey = S2.nextKey(key);
var nextId = S2.toId(nextKey);
var nextId = S2.keyToId(nextKey);
var prevKey = S2.prevKey(key);
var prevId = S2.toId(prevKey);
var prevId = S2.keyToId(prevKey);
var backTenKeys = S2.stepKey(key, -10);
// See it
console.log(prevKey); // '4/032212303102203'
@ -53,7 +173,7 @@ console.log(nextKey); // '4/032212303102211'
console.log(nextId);
```
convert Cell Id to Quadkey
convert Cell Id to Hilbert Curve Quad Tree
------------------
Convert from base 10 (decimal) `S2 Cell Id` to base 4 `quadkey` (aka hilbert curve quadtree id)
@ -69,7 +189,7 @@ var face = parts[0]; // 4
var position = parts[1]; // '032212303102210';
var level = '032212303102210'.length; // 15
var cellId = S2.fromFacePosLevel(face, position, level);
var cellId = S2.facePosLevelToId(face, position, level);
console.log(cellId);
```
@ -83,7 +203,16 @@ Example '9749618446378729472' becomes '4/032212303102210'
var cellId = '9749618446378729472';
var hilbertQuadkey = S2.toHilbertQuadkey(cellId);
var hilbertQuadkey = S2.idToKey(cellId);
console.log(hilbertQuadkey);
```
Convert Key and Id to LatLng
---------------------
```javascript
var latlng = S2.keyToLatLng('4/032212303102210');
var latlng = S2.idToLatLng('9749618446378729472');
```

View File

@ -34,5 +34,6 @@
"bower_components",
"test",
"tests"
]
],
"version": "1.2.9"
}

View File

@ -1,6 +1,6 @@
{
"name": "s2-geometry",
"version": "1.2.2",
"version": "1.2.10",
"description": "A pure JavaScript/ES5.1 port of Google/Niantic's S2 Geometry library (used by Ingress, Pokemon GO)",
"main": "src/s2geometry.js",
"scripts": {
@ -8,7 +8,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/coolaj86/s2-geometry-javascript.git"
"url": "git+https://git.coolaj86.com/coolaj86/s2-geometry.js.git"
},
"keywords": [
"s2",
@ -28,11 +28,11 @@
"lng"
],
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "ISC",
"license": "(MIT or Apache-2 or ISC)",
"bugs": {
"url": "https://github.com/coolaj86/s2-geometry-javascript/issues"
"url": "https://git.coolaj86.com/coolaj86/s2-geometry.js/issues"
},
"homepage": "https://github.com/coolaj86/s2-geometry-javascript#readme",
"homepage": "https://git.coolaj86.com/coolaj86/s2-geometry.js#readme",
"dependencies": {
"long": "^3.2.0"
}

View File

@ -25,9 +25,29 @@
(function (exports) {
'use strict';
var S2 = exports.S2 = {};
var S2 = exports.S2 = { L: {} };
var LatLngToXYZ = function(latLng) {
S2.L.LatLng = function (/*Number*/ rawLat, /*Number*/ rawLng, /*Boolean*/ noWrap) {
var lat = parseFloat(rawLat, 10);
var lng = parseFloat(rawLng, 10);
if (isNaN(lat) || isNaN(lng)) {
throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
}
if (noWrap !== true) {
lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90
lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180); // wrap longtitude into -180..180
}
return { lat: lat, lng: lng };
};
S2.L.LatLng.DEG_TO_RAD = Math.PI / 180;
S2.L.LatLng.RAD_TO_DEG = 180 / Math.PI;
/*
S2.LatLngToXYZ = function(latLng) {
// http://stackoverflow.com/questions/8981943/lat-long-to-x-y-z-position-in-js-not-working
var lat = latLng.lat;
var lon = latLng.lng;
@ -48,8 +68,19 @@ var LatLngToXYZ = function(latLng) {
, rad * sinLat
];
};
*/
S2.LatLngToXYZ = function(latLng) {
var d2r = S2.L.LatLng.DEG_TO_RAD;
var XYZToLatLng = function(xyz) {
var phi = latLng.lat*d2r;
var theta = latLng.lng*d2r;
var cosphi = Math.cos(phi);
return [Math.cos(theta)*cosphi, Math.sin(theta)*cosphi, Math.sin(phi)];
};
S2.XYZToLatLng = function(xyz) {
var r2d = S2.L.LatLng.RAD_TO_DEG;
var lat = Math.atan2(xyz[2], Math.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]));
@ -96,7 +127,7 @@ var faceXYZToUV = function(face,xyz) {
var XYZToFaceUV = function(xyz) {
S2.XYZToFaceUV = function(xyz) {
var face = largestAbsComponent(xyz);
if (xyz[face] < 0) {
@ -108,7 +139,7 @@ var XYZToFaceUV = function(xyz) {
return [face, uv];
};
var FaceUVToXYZ = function(face,uv) {
S2.FaceUVToXYZ = function(face,uv) {
var u = uv[0];
var v = uv[1];
@ -131,7 +162,7 @@ var singleSTtoUV = function(st) {
}
};
var STToUV = function(st) {
S2.STToUV = function(st) {
return [singleSTtoUV(st[0]), singleSTtoUV(st[1])];
};
@ -143,12 +174,12 @@ var singleUVtoST = function(uv) {
return 1 - 0.5 * Math.sqrt (1 - 3*uv);
}
};
var UVToST = function(uv) {
S2.UVToST = function(uv) {
return [singleUVtoST(uv[0]), singleUVtoST(uv[1])];
};
var STToIJ = function(st,order) {
S2.STToIJ = function(st,order) {
var maxSize = (1<<order);
var singleSTtoIJ = function(st) {
@ -160,7 +191,7 @@ var STToIJ = function(st,order) {
};
var IJToST = function(ij,order,offsets) {
S2.IJToST = function(ij,order,offsets) {
var maxSize = (1<<order);
return [
@ -169,12 +200,36 @@ var IJToST = function(ij,order,offsets) {
];
};
var rotateAndFlipQuadrant = function(n, point, rx, ry)
{
var newX, newY;
if(ry == 0)
{
if(rx == 1){
point.x = n - 1 - point.x;
point.y = n - 1 - point.y
}
var x = point.x;
point.x = point.y
point.y = x;
}
}
// hilbert space-filling curve
// based on http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
// note: rather then calculating the final integer hilbert position, we just return the list of quads
// this ensures no precision issues whth large orders (S3 cell IDs use up to 30), and is more
// convenient for pulling out the individual bits as needed later
var pointToHilbertQuadList = function(x,y,order) {
var pointToHilbertQuadList = function(x,y,order,face) {
var hilbertMap = {
'a': [ [0,'d'], [1,'a'], [3,'b'], [2,'a'] ],
'b': [ [2,'b'], [1,'b'], [3,'a'], [0,'c'] ],
@ -182,7 +237,10 @@ var pointToHilbertQuadList = function(x,y,order) {
'd': [ [0,'a'], [3,'c'], [1,'d'], [2,'d'] ]
};
var currentSquare='a';
if ('number' !== typeof face) {
console.warn(new Error("called pointToHilbertQuadList without face value, defaulting to '0'").stack);
}
var currentSquare = (face % 2) ? 'd' : 'a';
var positions = [];
for (var i=order-1; i>=0; i--) {
@ -206,19 +264,82 @@ var pointToHilbertQuadList = function(x,y,order) {
S2.S2Cell = function(){};
S2.S2Cell.FromHilbertQuadKey = function(hilbertQuadkey) {
var parts = hilbertQuadkey.split('/');
var face = parseInt(parts[0]);
var position = parts[1];
var maxLevel = position.length;
var point = {
x : 0,
y: 0
};
var i;
var level;
var bit;
var rx, ry;
var val;
for(i = maxLevel - 1; i >= 0; i--) {
level = maxLevel - i;
bit = position[i];
rx = 0;
ry = 0;
if (bit === '1') {
ry = 1;
}
else if (bit === '2') {
rx = 1;
ry = 1;
}
else if (bit === '3') {
rx = 1;
}
val = Math.pow(2, level - 1);
rotateAndFlipQuadrant(val, point, rx, ry);
point.x += val * rx;
point.y += val * ry;
}
if (face % 2 === 1) {
var t = point.x;
point.x = point.y;
point.y = t;
}
return S2.S2Cell.FromFaceIJ(parseInt(face), [point.x, point.y], level);
};
//static method to construct
S2.S2Cell.FromLatLng = function(latLng, level) {
if ((!latLng.lat && latLng.lat !== 0) || (!latLng.lng && latLng.lng !== 0)) {
throw new Error("Pass { lat: lat, lng: lng } to S2.S2Cell.FromLatLng");
}
var xyz = S2.LatLngToXYZ(latLng);
var xyz = LatLngToXYZ(latLng);
var faceuv = S2.XYZToFaceUV(xyz);
var st = S2.UVToST(faceuv[1]);
var faceuv = XYZToFaceUV(xyz);
var st = UVToST(faceuv[1]);
var ij = STToIJ(st,level);
var ij = S2.STToIJ(st,level);
return S2.S2Cell.FromFaceIJ (faceuv[0], ij, level);
};
/*
S2.faceIjLevelToXyz = function (face, ij, level) {
var st = S2.IJToST(ij, level, [0.5, 0.5]);
var uv = S2.STToUV(st);
var xyz = S2.FaceUVToXYZ(face, uv);
return S2.XYZToLatLng(xyz);
return xyz;
};
*/
S2.S2Cell.FromFaceIJ = function(face,ij,level) {
var cell = new S2.S2Cell();
cell.face = face;
@ -234,11 +355,11 @@ S2.S2Cell.prototype.toString = function() {
};
S2.S2Cell.prototype.getLatLng = function() {
var st = IJToST(this.ij,this.level, [0.5,0.5]);
var uv = STToUV(st);
var xyz = FaceUVToXYZ(this.face, uv);
var st = S2.IJToST(this.ij,this.level, [0.5,0.5]);
var uv = S2.STToUV(st);
var xyz = S2.FaceUVToXYZ(this.face, uv);
return XYZToLatLng(xyz);
return S2.XYZToLatLng(xyz);
};
S2.S2Cell.prototype.getCornerLatLngs = function() {
@ -251,27 +372,32 @@ S2.S2Cell.prototype.getCornerLatLngs = function() {
];
for (var i=0; i<4; i++) {
var st = IJToST(this.ij, this.level, offsets[i]);
var uv = STToUV(st);
var xyz = FaceUVToXYZ(this.face, uv);
var st = S2.IJToST(this.ij, this.level, offsets[i]);
var uv = S2.STToUV(st);
var xyz = S2.FaceUVToXYZ(this.face, uv);
result.push ( XYZToLatLng(xyz) );
result.push ( S2.XYZToLatLng(xyz) );
}
return result;
};
S2.S2Cell.prototype.getFaceAndQuads = function () {
var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level);
var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face);
return [this.face,quads];
};
S2.S2Cell.prototype.toHilbertQuadkey = function () {
var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level);
var quads = pointToHilbertQuadList(this.ij[0], this.ij[1], this.level, this.face);
return this.face.toString(10) + '/' + quads.join('');
};
S2.latLngToNeighborKeys = S2.S2Cell.latLngToNeighborKeys = function (lat, lng, level) {
return S2.S2Cell.FromLatLng({ lat: lat, lng: lng }, level).getNeighbors().map(function (cell) {
return cell.toHilbertQuadkey();
});
};
S2.S2Cell.prototype.getNeighbors = function() {
var fromFaceIJWrap = function(face,ij,level) {
@ -284,14 +410,14 @@ S2.S2Cell.prototype.getNeighbors = function() {
// with the assumption that they're only a little past the borders we can just take the points as
// just beyond the cube face, project to XYZ, then re-create FaceUV from the XYZ vector
var st = IJToST(ij,level,[0.5,0.5]);
var uv = STToUV(st);
var xyz = FaceUVToXYZ(face,uv);
var faceuv = XYZToFaceUV(xyz);
var st = S2.IJToST(ij,level,[0.5,0.5]);
var uv = S2.STToUV(st);
var xyz = S2.FaceUVToXYZ(face,uv);
var faceuv = S2.XYZToFaceUV(xyz);
face = faceuv[0];
uv = faceuv[1];
st = UVToST(uv);
ij = STToIJ(st,level);
st = S2.UVToST(uv);
ij = S2.STToIJ(st,level);
return S2.S2Cell.FromFaceIJ (face, ij, level);
}
};
@ -318,7 +444,7 @@ S2.FACE_BITS = 3;
S2.MAX_LEVEL = 30;
S2.POS_BITS = (2 * S2.MAX_LEVEL) + 1; // 61 (60 bits of data, 1 bit lsb marker)
S2.fromFacePosLevel = function (faceN, posS, levelN) {
S2.facePosLevelToId = S2.S2Cell.facePosLevelToId = S2.fromFacePosLevel = function (faceN, posS, levelN) {
var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
var faceB;
var posB;
@ -354,13 +480,19 @@ S2.fromFacePosLevel = function (faceN, posS, levelN) {
return Long.fromString(bin, true, 2).toString(10);
};
S2.toId = S2.toCellId = S2.fromKey = function (key) {
S2.keyToId = S2.S2Cell.keyToId
= S2.toId = S2.toCellId = S2.fromKey
= function (key) {
var parts = key.split('/');
return S2.fromFacePosLevel(parts[0], parts[1], parts[1].length);
};
S2.toKey = S2.fromId = S2.fromCellId = S2.toHilbertQuadkey = function (idS) {
S2.idToKey = S2.S2Cell.idToKey
= S2.S2Cell.toKey = S2.toKey
= S2.fromId = S2.fromCellId
= S2.S2Cell.toHilbertQuadkey = S2.toHilbertQuadkey
= function (idS) {
var Long = exports.dcodeIO && exports.dcodeIO.Long || require('long');
var bin = Long.fromString(idS, true, 10).toString(2);
@ -387,7 +519,21 @@ S2.toKey = S2.fromId = S2.fromCellId = S2.toHilbertQuadkey = function (idS) {
return faceS + '/' + posS;
};
S2.latLngToKey = S2.latLngToQuadkey = function (lat, lng, level) {
S2.keyToLatLng = S2.S2Cell.keyToLatLng = function (key) {
var cell2 = S2.S2Cell.FromHilbertQuadKey(key);
return cell2.getLatLng();
};
S2.idToLatLng = S2.S2Cell.idToLatLng = function (id) {
var key = S2.idToKey(id);
return S2.keyToLatLng(key);
};
S2.S2Cell.latLngToKey = S2.latLngToKey
= S2.latLngToQuadkey = function (lat, lng, level) {
if (isNaN(level) || level < 1 || level > 30) {
throw new Error("'level' is not a number between 1 and 30 (but it should be)");
}
// TODO
//
// S2.idToLatLng(id)
@ -437,45 +583,12 @@ S2.stepKey = function (key, num) {
return faceS + '/' + otherS;
};
S2.prevKey = function (key) {
S2.S2Cell.prevKey = S2.prevKey = function (key) {
return S2.stepKey(key, -1);
};
S2.nextKey = function (key) {
S2.S2Cell.nextKey = S2.nextKey = function (key) {
return S2.stepKey(key, 1);
};
})('undefined' !== typeof window ? window : module.exports);
(function (exports) {
'use strict';
// Adapted from Leafletjs https://searchcode.com/codesearch/view/42525008/
var L = {};
var S2 = exports.S2;
if (!exports.L) {
exports.L = L;
}
S2.L = L;
L.LatLng = function (/*Number*/ rawLat, /*Number*/ rawLng, /*Boolean*/ noWrap) {
var lat = parseFloat(rawLat, 10);
var lng = parseFloat(rawLng, 10);
if (isNaN(lat) || isNaN(lng)) {
throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')');
}
if (noWrap !== true) {
lat = Math.max(Math.min(lat, 90), -90); // clamp latitude into -90..90
lng = (lng + 180) % 360 + ((lng < -180 || lng === 180) ? 180 : -180); // wrap longtitude into -180..180
}
return { lat: lat, lng: lng };
};
L.LatLng.DEG_TO_RAD = Math.PI / 180;
L.LatLng.RAD_TO_DEG = 180 / Math.PI;
})('undefined' !== typeof window ? window : module.exports);
})('undefined' !== typeof module ? module.exports : window);

View File

@ -52,6 +52,7 @@ tests.forEach(function (loc) {
loc.lat = s2nId.toLatLng().toString().split(',')[0];
loc.lng = s2nId.toLatLng().toString().split(',')[1];
loc.level = s2nId.level(); // always 15
loc.point = s2nId.toPoint()// .toArray();
//console.log(JSON.stringify(loc, null, ' '));
@ -60,16 +61,24 @@ tests.forEach(function (loc) {
var key2 = S2.toKey(id);
var id2 = S2.toId(key2);
if (loc.key !== key || loc.id !== id || loc.key !== key2 || loc.id !== id2) {
if (loc.key !== key || loc.id !== id) {
console.error("Error testing " + loc.name + " @ " + loc.lat + ',' + loc.lng);
console.error("Calculated/Expected:");
console.error(id, ':', loc.id);
console.error(key, " : ", loc.key);
console.error(id2, ':', loc.id);
console.error(key2, " : ", loc.key);
console.error(loc.point.x(), loc.point.y(), loc.point.z());
console.error(Long.fromString(id, true, 10).toString(2));
console.error(Long.fromString(loc.id, true, 10).toString(2));
throw new Error('Test Failed');
}
if (loc.key !== key2 || loc.id !== id2) {
console.error("Error testing " + loc.name + " @ " + loc.lat + ',' + loc.lng);
console.error("Secondary Key / ID conversion failed: Calculated/Expected:");
console.error(id2, ':', loc.id);
console.error(key2, " : ", loc.key);
throw new Error('Test Failed');
}
});

79
tests/debug.js Normal file
View File

@ -0,0 +1,79 @@
'use strict';
//var oS2 = require('./s2geometry.old.js').S2;
var jS2 = require('../src/s2geometry.js').S2;
var nS2 = require('s2geometry-node');
var lat = -43.525166;
var lng = 172.655096;
//var id = '8678661352471920640';
var tests = [
[ -13.846153846153854, -41.53846153846155 ] // face 0
, [ -13.846153846153854, 96.92307692307692 ] // face 1
, [ 41.53846153846153, -124.61538461538463 ] // face 2
, [ -152.30769230769232, 41.53846153846153 ] // face 3
, [ -152.30769230769232, 69.23076923076923 ] // face 4
, [ -124.61538461538463, -69.23076923076924 ] // face 5
];
var tests = [
[ -3.9832738, 12.6825449, 'The Congo (Africa)' ]
, [ 19.0827696, 72.7407752, 'Mumbai, India (Asia)' ]
, [ 68.5207533, -74.9563282, 'Greenland (North Pole)' ]
, [ -1.573289, -158.957890, 'The Pacific Ocean (nowhere)' ]
, [ 40.2573137, -111.7091177, 'Provo, UT, USA (Americas)' ]
, [ -46.362972,-73.7431064, 'Antarctica (South Pole)' ]
];
var bugReports = [
// https://github.com/jonatkins/s2-geometry-javascript/issues/12
[ -6.120097, 106.846511, '@Skeec' ]
// https://github.com/coolaj86/s2-geometry-javascript/issues/8#issuecomment-237204759
, [ -33.87347601637759, 151.1954084522501 ]
// https://github.com/Daplie/s2-geometry.js/issues/1
, [ 45.74528835847731, 12.5516625, '@vekexasia' ]
];
tests.concat(bugReports).forEach(function (pair, i) {
var lat = pair[0];
var lng = pair[1];
var comment = pair[2] && ('(' + pair[2] + ')') || '';
console.log('');
console.log('');
if (i < 6) {
console.log('FACE', i);
}
console.log('Lat/Lng', '=', lat + ',' + lng, comment);
//
// Lat / Lng to XYZ
//
var nS2LatLng = new nS2.S2LatLng(lat, lng).toPoint();
//var nXyz = [ nS2LatLng.x(), nS2LatLng.y(), nS2LatLng.z() ];
//var jXyz = jS2.LatLngToXYZ({ lat: lat, lng: lng });
/*
console.log('');
console.log('XYZ');
console.log('=', nXyz);
console.log('j', jXyz);
*/
var nCell = new nS2.S2CellId(nS2LatLng).parent(15);
var jCell = jS2.S2Cell.FromLatLng({ lat: lat, lng: lng }, 15);
/*
console.log('');
console.log('F,IJ,L');
console.log('j', jCell.toString());
*/
var nKey = nCell.toString();
var jQuad = jCell.getFaceAndQuads();
var jKey = jQuad[0] + '/' + jQuad[1].join('');
console.log('Quadkey');
console.log('=', nKey);
console.log('j', jKey, "(" + jS2.toId(jKey) + ")");
});

32
tests/exhaustive.js Normal file
View File

@ -0,0 +1,32 @@
'use strict';
var jS2 = require('../src/s2geometry.js').S2;
var nS2 = require('s2geometry-node');
var x, y;
function checkReal(lat, lng) {
var nS2LatLng = new nS2.S2LatLng(lat, lng).toPoint();
var nCell = new nS2.S2CellId(nS2LatLng).parent(15);
var jCell = jS2.S2Cell.FromLatLng({ lat: lat, lng: lng }, 15);
var nKey = nCell.toString();
var jQuad = jCell.getFaceAndQuads();
var jKey = jQuad[0] + '/' + jQuad[1].join('');
if (nKey !== jKey) {
console.log('');
console.log('Quadkey');
console.log('=', nKey);
console.log('j', jKey);
throw new Error("values didn't match expected");
}
}
console.log('Exhaustive check of about 518,400 random lat,lng coordinates of the earth (about every 0.5°)');
console.log('(this will take several seconds)');
for (x = -180; x <= 180; x += (0 + Math.random())) {
for (y = -180; y <= 180; y += (0 + Math.random())) {
checkReal(x, y);
}
}
console.log('PASS');

File diff suppressed because it is too large Load Diff

36
tests/hilbert-to-cell.js Normal file
View File

@ -0,0 +1,36 @@
'use strict';
var S2 = require('../src/s2geometry.js').S2;
for(var level = 1; level <= 20; level++) {
var success = 0;
var total = 0;
for (var x = -180.0; x < 180; x += 0.5) {
for (var y = -180.0; y < 180; y += 0.5) {
var latlng = { lat: x, lng: y };
var cell = S2.S2Cell.FromLatLng(latlng, level);
var quadKey = cell.toHilbertQuadkey();
var cell2 = S2.S2Cell.FromHilbertQuadKey(quadKey);
if(cell.face != cell2.face ||
cell.ij[0] != cell2.ij[0] ||
cell.ij[1] != cell2.ij[1] ||
cell.level != cell2.level)
{
/*console.log({
cell: cell,
cell2: cell2})*/
}
else
{
success++;
}
total++;
// check equal
}
}
console.log("level:" + level + "\t total:" + total + "\t success:" + success);
}

33
tests/js-vs-go.js Normal file
View File

@ -0,0 +1,33 @@
'use strict';
var tests = require('./generated-locations.json');
var jS2 = require('../src/s2geometry.js').S2;
function checkReal(loc) {
var jCell = jS2.S2Cell.FromLatLng({ lat: loc.lat, lng: loc.lng }, 15);
var jQuad = jCell.getFaceAndQuads();
var jKey = jQuad[0] + '/' + jQuad[1].join('');
if (loc.quadkey !== jKey) {
loc.badFace = jCell.face;
loc.badI = jCell.ij[0];
loc.badJ = jCell.ij[1];
loc.badQuad = jKey;
loc.badId = jS2.toId(jKey);
console.log(JSON.stringify(loc, null, ' ') + ',');
console.log('');
console.log('Lat/Lng:', loc.lat, loc.lng);
console.log('');
console.log('I, J:');
console.log('=', loc.i, loc.j);
console.log('j', jCell.ij.join(', '));
console.log('');
console.log('Quadkey');
console.log('=', loc.quadkey);
console.log('j', jKey);
throw new Error("values didn't match expected");
}
}
tests.forEach(checkReal);
console.log('PASS');

48
tests/key-id-to-latlng.js Normal file
View File

@ -0,0 +1,48 @@
'use strict';
var S2 = require('../src/s2geometry.js').S2;
var x, y;
var count = 0;
function refCheck() {
var refKey = '4/032212303102210';
var latlng = {
'lat': 40.2574448
, 'lng': -111.7089464
};
var key = S2.latLngToKey(latlng.lat, latlng.lng, 15);
if (key !== refKey) {
throw new Error("reference doesn't match");
}
var latlng1 = S2.keyToLatLng('4/032212303102210');
var key1 = S2.latLngToKey(latlng1.lat, latlng1.lng, 15);
if (key1 !== refKey) {
throw new Error("reference 1 doesn't match");
}
var latlng2 = S2.idToLatLng('9749618446378729472');
var key2 = S2.latLngToKey(latlng2.lat, latlng2.lng, 15);
if (key2 !== refKey) {
throw new Error("reference 2 doesn't match");
}
}
function checkReal(lat, lng) {
var key = S2.latLngToKey(lat, lng, 15);
var latlng = S2.keyToLatLng(key);
var key2 = S2.latLngToKey(latlng.lat, latlng.lng, 15);
if (key !== key2) {
throw new Error("keys do not match", latlng, key, key2);
}
}
for (x = -180; x <= 180; x += (0 + Math.random())) {
for (y = -180; y <= 180; y += (0 + Math.random())) {
count += 1;
checkReal(x, y);
}
}
console.log('PASS:', count, 'random locations without any key mismatches');

View File

@ -1,4 +1,4 @@
var S2 = require('s2geometry-node');
var s2n = require('s2geometry-node');
// Provo, UT (Center St)
//var lat = 40.2574448;
@ -16,8 +16,8 @@ var S2 = require('s2geometry-node');
var lat = -43.5261282;
var lng = 172.6561085;
var s2latlng = new S2.S2LatLng(lat, lng);
var cellId = new S2.S2CellId(s2latlng).parent(15);
var s2nlatlng = new s2n.S2LatLng(lat, lng);
var cellId = new s2n.S2CellId(s2nlatlng).parent(15);
var cell;
var walk = [];
@ -47,6 +47,6 @@ walk.forEach(function (parts) {
var i = parts[0];
var cellId = parts[1];
cell = new S2.S2Cell(cellId);
cell = new s2n.S2Cell(cellId);
console.log(i, cell.face(), cellId.id(), cellId.toString(), cellId.toLatLng().toString(), cellId.level());
});

51
tests/simple.js Normal file
View File

@ -0,0 +1,51 @@
'use strict';
var S2 = require('../src/s2geometry.js').S2;
var lat = 40.2574448;
var lng = -111.7089464;
var level = 15;
//
// Convert from Lat / Lng
//
var key = S2.latLngToKey(lat, lng, level);
console.log(key);
// '4/032212303102210'
//
// Convert between Hilbert Curve Quadtree Key and S2 Cell Id
//
var id = S2.keyToId(key);
console.log(id);
// '9749618446378729472'
var key = S2.idToKey(id);
console.log(key);
// '9749618446378729472'
//
// Neighbors
//
var neighbors = S2.latLngToNeighborKeys(lat, lng, level);
console.log(neighbors);
// [ keyLeft, keyDown, keyRight, keyUp ]
//
// Previous, Next, and Step
//
var nextKey = S2.nextKey(key);
console.log(nextKey);
var prevKey = S2.prevKey(key);
console.log(prevKey);
var backTenKeys = S2.stepKey(key, -10);
console.log(backTenKeys);