diff --git a/README.md b/README.md index 51012a5..f06031a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ s2-geometry (JavaScript/ES5.1) ====================== -A pure JavaScript/ES5.1 port of Google/Niantic's S2 Geometry library (used by Ingress, Pokemon GO) +A pure JavaScript/ES5.1 port of Google/Niantic's S2 Geometry library (used by **Ingress**, **Pokemon GO**) Currently contains basic support for S2Cell @@ -17,3 +17,38 @@ cell.getNeighbors(); // [ cellLeft, cellDown, cellRight, cellUp ] cell.getLatLng(); // { lat: 40.2574448, lng: -111.7089464 } ``` + +convert Cell Id to Quadkey +------------------ + +Convert from base 10 (decimal) `S2 Cell Id` to base 4 `quadkey` (aka hilbert curve quadtree id) + +Example '4/032212303102210' becomes '9749618446378729472' + +``` +'use strict'; + +var quadkey = '4/032212303102210' +var parts = quadkey.split('/'); +var face = parts[0]; // 4 +var position = parts[1]; // '032212303102210'; +var level = '032212303102210'.length; // 15 + +var cellId = S2.fromFacePosLevel(face, position, level); + +console.log(cellId); +``` + +Convert from hilbert quadtree id to s2 cell id: + +Example '9749618446378729472' becomes '4/032212303102210' + +``` +'use strict'; + +var cellId = '9749618446378729472'; + +var hilbertQuadkey = S2.toHilbertQuadkey(cellId); + +console.log(hilbertQuadkey); +``` diff --git a/package.json b/package.json index 317328d..0ba7b45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "s2-geometry", - "version": "1.0.0", + "version": "1.1.0", "description": "A pure JavaScript/ES5.1 port of Google/Niantic's S2 Geometry library (used by Ingress, Pokemon GO)", "main": "src/s2geometry.js", "scripts": { @@ -32,5 +32,8 @@ "bugs": { "url": "https://github.com/coolaj86/s2-geometry-javascript/issues" }, - "homepage": "https://github.com/coolaj86/s2-geometry-javascript#readme" + "homepage": "https://github.com/coolaj86/s2-geometry-javascript#readme", + "dependencies": { + "long": "^3.2.0" + } } diff --git a/src/s2geometry.js b/src/s2geometry.js index a25a2ba..313f1ad 100644 --- a/src/s2geometry.js +++ b/src/s2geometry.js @@ -298,6 +298,58 @@ S2.S2Cell.prototype.getNeighbors = function() { }; +// +// Functional Style +// +S2.FACE_BITS = 3; +S2.MAX_LEVEL = 30; +S2.POS_BITS = (2 * S2.MAX_LEVEL) + 1; + +S2.fromFacePosLevel = function (faceN, posS, levelN) { + var Long = require('long'); + + if (!levelN) { + levelN = posS.length; + } + if (posS.length > levelN) { + posS = posS.substr(0, levelN); + } + + var posB = Long.fromString(posS, true, 4).toString(2); + while (posB.length < (2 * levelN)) { + posB = '0' + posB; + } + var bin = Long.fromString(faceN.toString(10), true, 10).toString(2); + bin += posB; + bin += '1'; + while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) { + bin += '0'; + } + + return Long.fromString(bin, true, 2).toString(); +}; + +S2.toHilbertQuadkey = function (idS) { + var Long = require('long'); + var bin = Long.fromString(idS, true, 10).toString(2); + var lsbIndex = bin.lastIndexOf('1'); + + // substr(start, len) + // substring(start, end) + var faceB = bin.substr(0, 3); + // posB will always be a multiple of 2 (or it's invalid) + var posB = bin.substring(4, lsbIndex); + var levelN = posB.length / 2; + + var faceS = Long.fromString(faceB, true, 2).toString(10); + var posS = Long.fromString(posB, true, 2).toString(4); + + while (posS.length < levelN) { + posS = '0' + posS; + } + + return faceS + '/' + posS; +}; })('undefined' !== typeof window ? window : module.exports); diff --git a/tests/ids.js b/tests/ids.js new file mode 100644 index 0000000..f9e2fd9 --- /dev/null +++ b/tests/ids.js @@ -0,0 +1,9 @@ +'use strict'; + +var S2 = require('../src/s2geometry').S2; + +var cellId = S2.fromFacePosLevel(4, '032212303102210'); +var hilbertQuadkey = S2.toHilbertQuadkey('9749618446378729472'); + +console.log(cellId, '9749618446378729472' === cellId); +console.log(hilbertQuadkey, '4/032212303102210' === hilbertQuadkey);