diff --git a/lib/src/s2cell_id.dart b/lib/src/s2cell_id.dart index 88283ef..d68e228 100644 --- a/lib/src/s2cell_id.dart +++ b/lib/src/s2cell_id.dart @@ -23,6 +23,7 @@ import 's2coords.dart'; import 's2coords_impl.dart'; import 's2point.dart'; export 's2point.dart'; +import 's1angle.dart'; import 's2latlng.dart'; import 'util/bits/bits.dart'; @@ -79,6 +80,70 @@ int _lsbForLevel(int level) { class S2CellId { S2CellId(this._id); + + S2LatLng latLng() { + S2Point p = this.rawPoint(); + return S2LatLng(_latitude(p), _longitude(p)); + } + + List faceSiTi() { + _maybeInit(); + List res = faceIJOrientation(); + int delta = 0; + if (this.isLeaf()) { + delta = 1; + } else if (((res[1] ^ (id >> 2)) & 1) != 0) { + delta = 2; + } + return [res[0], 2 * res[1] + delta, 2 * res[2] + delta]; + } + + + List faceIJOrientation() { + int face = this.face(); + int orientation = face & kSwapMask; + int nbits = _kMaxLevel - 7 * _kLookupBits; + + int i = 0; + int j = 0; + for (var k = 7; k >= 0; k--) { + orientation += ((id >> (k * 2 * _kLookupBits + 1)) & ((1 << (2 * nbits)) - 1)) << 2; + orientation = _lookupIJ[orientation]; + i += (orientation >> (_kLookupBits + 2)) << (k * _kLookupBits); + j += ((orientation >> 2) & ((1 << _kLookupBits) - 1)) << (k * _kLookupBits); + orientation &= (kSwapMask | kInvertMask); + nbits = _kLookupBits; + } + + if (((id & -id) & 0x1111111111111110) != 0) { + orientation ^= kSwapMask; + } + + return [face, i, j, orientation]; + } + + int face() { + return this.id >> _kPosBits; + } + + bool isLeaf() { + return (id & 1) != 0; + } + + S2Point rawPoint() { + List res = this.faceSiTi(); + return faceUVToXYZ( + res[0], + R2Point( + stToUV((0.5 / _kMaxSize) * res[1]), + stToUV((0.5 / _kMaxSize) * res[2]), + ), + ); + } + + S1Angle _latitude(S2Point p) => S1Angle.fromRadians(atan2(p.z, sqrt(p.x * p.x + p.y * p.y))); + + S1Angle _longitude(S2Point p) => S1Angle.fromRadians(atan2(p.y, p.x)); S2CellId.fromPoint(S2Point p) { // ok diff --git a/test/s2geometry_test.dart b/test/s2geometry_test.dart index 8afeae5..3209f3d 100644 --- a/test/s2geometry_test.dart +++ b/test/s2geometry_test.dart @@ -3,6 +3,17 @@ import 'package:test/test.dart'; void main() { group('S2CellId', () { + test('S2CellID.latlng', () { + var ll = S2CellId(5185027895138811904).latLng(); + var expectedLL = + S2LatLng.fromDegrees(45.78147499434792, 4.799570770239867); + expect(ll.lat.degrees.round(), expectedLL.lat.degrees.round()); + + var ll2 = S2CellId(226402483348101380).latLng(); + var expectedLL2 = + S2LatLng.fromDegrees(-32.32163872204854, -3.232390648508235); + expect(ll2.lat.degrees.round(), expectedLL2.lat.degrees.round()); + }); test('S2CellId.fromLatLng', () { expect( new S2CellId.fromLatLng( @@ -76,24 +87,42 @@ void main() { 0xb112966aaaaaaaab); }); test('S2CellId.parent', () { - expect(new S2CellId(0x47a1cbd595522b39).parent(), new S2CellId(0x47a1cbd595522b39).immediateParent()); - expect(new S2CellId(0x47a1cbd595522b39).parent(29), new S2CellId(0x47a1cbd595522b39).immediateParent()); - expect(new S2CellId(0x47a1cbd595522b39).parent(28), new S2CellId(0x47a1cbd595522b39).immediateParent().immediateParent()); - expect(new S2CellId(0x47a1cbd595522b39).parent(28).id, 0x47a1cbd595522b30); + expect(new S2CellId(0x47a1cbd595522b39).parent(), + new S2CellId(0x47a1cbd595522b39).immediateParent()); + expect(new S2CellId(0x47a1cbd595522b39).parent(29), + new S2CellId(0x47a1cbd595522b39).immediateParent()); + expect(new S2CellId(0x47a1cbd595522b39).parent(28), + new S2CellId(0x47a1cbd595522b39).immediateParent().immediateParent()); + expect( + new S2CellId(0x47a1cbd595522b39).parent(28).id, 0x47a1cbd595522b30); expect(new S2CellId(0x47a1cbd595522b39).parent(13).level, 13); }); test('S2CellId.operator', () { - expect(new S2CellId(0xf7a1cbd595522b39) > new S2CellId(0x07a1cbd595522b39), isTrue); - expect(new S2CellId(0xf7a1cbd595522b39) > new S2CellId(0xe7a1cbd595522b39), isTrue); - expect(new S2CellId(0x17a1cbd595522b39) > new S2CellId(0x07a1cbd595522b39), isTrue); - expect(new S2CellId(0xf7a1cbd595522b39) < new S2CellId(0x07a1cbd595522b39), isFalse); - expect(new S2CellId(0xf7a1cbd595522b39) < new S2CellId(0xe7a1cbd595522b39), isFalse); - expect(new S2CellId(0x17a1cbd595522b39) < new S2CellId(0x07a1cbd595522b39), isFalse); + expect( + new S2CellId(0xf7a1cbd595522b39) > new S2CellId(0x07a1cbd595522b39), + isTrue); + expect( + new S2CellId(0xf7a1cbd595522b39) > new S2CellId(0xe7a1cbd595522b39), + isTrue); + expect( + new S2CellId(0x17a1cbd595522b39) > new S2CellId(0x07a1cbd595522b39), + isTrue); + expect( + new S2CellId(0xf7a1cbd595522b39) < new S2CellId(0x07a1cbd595522b39), + isFalse); + expect( + new S2CellId(0xf7a1cbd595522b39) < new S2CellId(0xe7a1cbd595522b39), + isFalse); + expect( + new S2CellId(0x17a1cbd595522b39) < new S2CellId(0x07a1cbd595522b39), + isFalse); }); test('S2CellId.toToken', () { expect(new S2CellId(0x47a1cbd595522b39).toToken(), "47a1cbd595522b39"); - expect(new S2CellId(0x47a1cbd595522b39).parent(29).toToken(), "47a1cbd595522b3c"); - expect(new S2CellId(0x47a1cbd595522b39).parent(28).toToken(), "47a1cbd595522b3"); + expect(new S2CellId(0x47a1cbd595522b39).parent(29).toToken(), + "47a1cbd595522b3c"); + expect(new S2CellId(0x47a1cbd595522b39).parent(28).toToken(), + "47a1cbd595522b3"); }); }); }