diff --git a/Gopkg.lock b/Gopkg.lock index 1ddea21c..3d653b30 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -133,6 +133,34 @@ pruneopts = "" revision = "0b12d6b5" +[[projects]] + branch = "master" + digest = "1:75dddee0eb82002b5aff6937fdf6d544b85322d2414524a521768fe4b4e5ed3d" + name = "github.com/mmcloughlin/geohash" + packages = ["."] + pruneopts = "" + revision = "f7f2bcae3294530249c63fcb6fb6d5e83eee4e73" + +[[projects]] + digest = "1:f04a78a43f55f089c919beee8ec4a1495dee1bd271548da2cb44bf44699a6a61" + name = "github.com/nats-io/go-nats" + packages = [ + ".", + "encoders/builtin", + "util", + ] + pruneopts = "" + revision = "fb0396ee0bdb8018b0fef30d6d1de798ce99cd05" + version = "v1.6.0" + +[[projects]] + digest = "1:be61e8224b84064109eaba8157cbb4bbe6ca12443e182b6624fdfa1c0dcf53d9" + name = "github.com/nats-io/nuid" + packages = ["."] + pruneopts = "" + revision = "289cccf02c178dc782430d534e3c1f5b72af807f" + version = "v1.0.0" + [[projects]] branch = "master" digest = "1:30f72985e574101b71666d6e601e7564bd02d95164da59ca17363ad194137969" @@ -197,6 +225,18 @@ pruneopts = "" revision = "b67b1b8c1658cb01502801c14e33c61e6c4cbb95" +[[projects]] + branch = "master" + digest = "1:157eb52179752d4d88f1049aa6c3e4954d6796af5f6bcd54b5ab40f8637805df" + name = "github.com/tidwall/geojson" + packages = [ + ".", + "geo", + "geometry", + ] + pruneopts = "" + revision = "8ff3ef500c61617c9f325603cf40863ca7086a1d" + [[projects]] digest = "1:211773b67c5594aa92b1e8389c59558fa4927614507ea38237265e00c0ba6b81" name = "github.com/tidwall/gjson" @@ -229,6 +269,14 @@ pruneopts = "" revision = "1731857f09b1f38450e2c12409748407822dc6be" +[[projects]] + branch = "master" + digest = "1:7eed51dcae60e95dbde54662594ef90a7cbf3b7e3f0de32f84f0213b695967ff" + name = "github.com/tidwall/pretty" + packages = ["."] + pruneopts = "" + revision = "65a9db5fad5105a89e17f38adcc9878685be6d78" + [[projects]] branch = "master" digest = "1:630381558bc538e831db8468dd0dc2702d81789f79b8ddf665eeebc729e2a055" @@ -394,13 +442,18 @@ "github.com/eclipse/paho.mqtt.golang", "github.com/garyburd/redigo/redis", "github.com/golang/protobuf/proto", + "github.com/mmcloughlin/geohash", + "github.com/nats-io/go-nats", "github.com/peterh/liner", "github.com/streadway/amqp", "github.com/tidwall/boxtree/d2", "github.com/tidwall/btree", "github.com/tidwall/buntdb", + "github.com/tidwall/geojson", + "github.com/tidwall/geojson/geometry", "github.com/tidwall/gjson", "github.com/tidwall/lotsa", + "github.com/tidwall/match", "github.com/tidwall/redbench", "github.com/tidwall/redcon", "github.com/tidwall/resp", diff --git a/Gopkg.toml b/Gopkg.toml index b5843b77..fd732b74 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -20,7 +20,11 @@ # name = "github.com/x/y" # version = "2.4.0" -required = ["github.com/tidwall/lotsa"] +required = [ + "github.com/tidwall/lotsa", + "github.com/mmcloughlin/geohash", + "github.com/tidwall/geojson" +] [[constraint]] branch = "master" @@ -50,10 +54,6 @@ required = ["github.com/tidwall/lotsa"] branch = "master" name = "github.com/streadway/amqp" -[[constraint]] - name = "github.com/stretchr/testify" - version = "1.1.4" - [[constraint]] branch = "master" name = "github.com/tidwall/btree" @@ -82,10 +82,6 @@ required = ["github.com/tidwall/lotsa"] name = "github.com/tidwall/sjson" version = "1.0.0" -[[constraint]] - branch = "master" - name = "github.com/tidwall/tinyqueue" - [[constraint]] branch = "master" name = "golang.org/x/crypto" diff --git a/README.md b/README.md index 50b4ffff..20916a88 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

Tile38

@@ -14,11 +14,11 @@ Tile38 is an open source (MIT licensed), in-memory geolocation data store, spati

This README is quick start document. You can find detailed documentation at http://tile38.com.

-Nearby -Within -Intersects -Geofencing -Roaming Geofences +Nearby +Within +Intersects +Geofencing +Roaming Geofences

## Features @@ -134,19 +134,19 @@ To set a field when an object already exists: Tile38 has support to search for objects and points that are within or intersects other objects. All object types can be searched including Polygons, MultiPolygons, GeometryCollections, etc. -Search Within +Search Within #### Within WITHIN searches a collection for objects that are fully contained inside a specified bounding area.
-Search Intersects +Search Intersects #### Intersects INTERSECTS searches a collection for objects that intersect a specified bounding area.
-Search Nearby +Search Nearby #### Nearby NEARBY searches a collection for objects that intersect a specified radius. @@ -167,7 +167,7 @@ NEARBY searches a collection for objects that intersect a specified radius. ## Geofencing -Geofence animation +Geofence animation A geofence is a virtual boundary that can detect when an object enters or exits the area. This boundary can be a radius, bounding box, or a polygon. Tile38 can turn any standard search into a geofence monitor by adding the FENCE keyword to the search. *Tile38 also allows for [Webhooks](http://tile38.com/commands/sethook) to be assigned to Geofences.* diff --git a/build.sh b/build.sh index acac4e68..dee75a7c 100755 --- a/build.sh +++ b/build.sh @@ -161,7 +161,7 @@ if [ "$NOLINK" != "1" ]; then fi # generate the core package -pkg/core/gen.sh +core/gen.sh # build and store objects into original directory. CGO_ENABLED=0 go build -ldflags "$LDFLAGS -extldflags '-static'" -o "$OD/tile38-server" cmd/tile38-server/*.go @@ -178,7 +178,7 @@ if [ "$1" == "test" ]; then } trap testend EXIT cd tests && go test && cd .. - go test $(go list ./... | grep -v /vendor/ | grep -v /tests) + go test $(go list ./... | grep -v /vendor/ | grep -v /tests | grep -v /pkg/geojson_bak) fi # cover if requested diff --git a/cmd/tile38-benchmark/main.go b/cmd/tile38-benchmark/main.go index 1c9c75bd..772568cc 100644 --- a/cmd/tile38-benchmark/main.go +++ b/cmd/tile38-benchmark/main.go @@ -11,7 +11,7 @@ import ( "time" "github.com/tidwall/redbench" - "github.com/tidwall/tile38/pkg/core" + "github.com/tidwall/tile38/core" ) var ( diff --git a/cmd/tile38-cli/main.go b/cmd/tile38-cli/main.go index 9e4e87fd..4d227c83 100644 --- a/cmd/tile38-cli/main.go +++ b/cmd/tile38-cli/main.go @@ -16,8 +16,8 @@ import ( "github.com/peterh/liner" "github.com/tidwall/gjson" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/client" - "github.com/tidwall/tile38/pkg/core" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/client" ) func userHomeDir() string { diff --git a/cmd/tile38-server/main.go b/cmd/tile38-server/main.go index 0f3c6149..99118d52 100644 --- a/cmd/tile38-server/main.go +++ b/cmd/tile38-server/main.go @@ -15,14 +15,12 @@ import ( "sync" "syscall" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/controller" + "github.com/tidwall/tile38/internal/hservice" + "github.com/tidwall/tile38/internal/log" "golang.org/x/net/context" - "google.golang.org/grpc" - - "github.com/tidwall/tile38/pkg/controller" - "github.com/tidwall/tile38/pkg/core" - "github.com/tidwall/tile38/pkg/hservice" - "github.com/tidwall/tile38/pkg/log" ) var ( diff --git a/pkg/core/commands.go b/core/commands.go similarity index 100% rename from pkg/core/commands.go rename to core/commands.go diff --git a/pkg/core/commands.json b/core/commands.json similarity index 100% rename from pkg/core/commands.json rename to core/commands.json diff --git a/pkg/core/commands_gen.go b/core/commands_gen.go similarity index 100% rename from pkg/core/commands_gen.go rename to core/commands_gen.go diff --git a/pkg/core/commands_test.go b/core/commands_test.go similarity index 100% rename from pkg/core/commands_test.go rename to core/commands_test.go diff --git a/pkg/core/gen.sh b/core/gen.sh similarity index 100% rename from pkg/core/gen.sh rename to core/gen.sh diff --git a/pkg/core/options.go b/core/options.go similarity index 100% rename from pkg/core/options.go rename to core/options.go diff --git a/pkg/core/version.go b/core/version.go similarity index 100% rename from pkg/core/version.go rename to core/version.go diff --git a/pkg/assets/geofence.gif b/internal/assets/geofence.gif similarity index 100% rename from pkg/assets/geofence.gif rename to internal/assets/geofence.gif diff --git a/pkg/assets/logo1500.png b/internal/assets/logo1500.png similarity index 100% rename from pkg/assets/logo1500.png rename to internal/assets/logo1500.png diff --git a/pkg/assets/logo200.png b/internal/assets/logo200.png similarity index 100% rename from pkg/assets/logo200.png rename to internal/assets/logo200.png diff --git a/pkg/assets/roaming.gif b/internal/assets/roaming.gif similarity index 100% rename from pkg/assets/roaming.gif rename to internal/assets/roaming.gif diff --git a/pkg/assets/search-intersects.png b/internal/assets/search-intersects.png similarity index 100% rename from pkg/assets/search-intersects.png rename to internal/assets/search-intersects.png diff --git a/pkg/assets/search-nearby.png b/internal/assets/search-nearby.png similarity index 100% rename from pkg/assets/search-nearby.png rename to internal/assets/search-nearby.png diff --git a/pkg/assets/search-plain.png b/internal/assets/search-plain.png similarity index 100% rename from pkg/assets/search-plain.png rename to internal/assets/search-plain.png diff --git a/pkg/assets/search-within.png b/internal/assets/search-within.png similarity index 100% rename from pkg/assets/search-within.png rename to internal/assets/search-within.png diff --git a/pkg/assets/sparse-1.png b/internal/assets/sparse-1.png similarity index 100% rename from pkg/assets/sparse-1.png rename to internal/assets/sparse-1.png diff --git a/pkg/assets/sparse-2.png b/internal/assets/sparse-2.png similarity index 100% rename from pkg/assets/sparse-2.png rename to internal/assets/sparse-2.png diff --git a/pkg/assets/sparse-3.png b/internal/assets/sparse-3.png similarity index 100% rename from pkg/assets/sparse-3.png rename to internal/assets/sparse-3.png diff --git a/pkg/assets/sparse-4.png b/internal/assets/sparse-4.png similarity index 100% rename from pkg/assets/sparse-4.png rename to internal/assets/sparse-4.png diff --git a/pkg/assets/sparse-5.png b/internal/assets/sparse-5.png similarity index 100% rename from pkg/assets/sparse-5.png rename to internal/assets/sparse-5.png diff --git a/pkg/assets/sparse-6.png b/internal/assets/sparse-6.png similarity index 100% rename from pkg/assets/sparse-6.png rename to internal/assets/sparse-6.png diff --git a/pkg/assets/sparse-none.png b/internal/assets/sparse-none.png similarity index 100% rename from pkg/assets/sparse-none.png rename to internal/assets/sparse-none.png diff --git a/pkg/bing/bing.go b/internal/bing/bing.go similarity index 100% rename from pkg/bing/bing.go rename to internal/bing/bing.go diff --git a/pkg/bing/bing_test.go b/internal/bing/bing_test.go similarity index 100% rename from pkg/bing/bing_test.go rename to internal/bing/bing_test.go diff --git a/pkg/bing/ext.go b/internal/bing/ext.go similarity index 100% rename from pkg/bing/ext.go rename to internal/bing/ext.go diff --git a/pkg/bing/ext_test.go b/internal/bing/ext_test.go similarity index 100% rename from pkg/bing/ext_test.go rename to internal/bing/ext_test.go diff --git a/pkg/client/README.md b/internal/client/README.md similarity index 100% rename from pkg/client/README.md rename to internal/client/README.md diff --git a/pkg/client/conn.go b/internal/client/conn.go similarity index 100% rename from pkg/client/conn.go rename to internal/client/conn.go diff --git a/pkg/client/conn_test.go b/internal/client/conn_test.go similarity index 100% rename from pkg/client/conn_test.go rename to internal/client/conn_test.go diff --git a/pkg/client/helper.go b/internal/client/helper.go similarity index 100% rename from pkg/client/helper.go rename to internal/client/helper.go diff --git a/pkg/client/pool.go b/internal/client/pool.go similarity index 100% rename from pkg/client/pool.go rename to internal/client/pool.go diff --git a/pkg/client/pool_test.go b/internal/client/pool_test.go similarity index 100% rename from pkg/client/pool_test.go rename to internal/client/pool_test.go diff --git a/internal/clip/clip.go b/internal/clip/clip.go new file mode 100644 index 00000000..92a88a46 --- /dev/null +++ b/internal/clip/clip.go @@ -0,0 +1,142 @@ +package clip + +import ( + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" +) + +// Clip clips the contents of a geojson object and return +func Clip(obj geojson.Object, clipper geojson.Object) (clipped geojson.Object) { + switch obj := obj.(type) { + case *geojson.Point: + return clipPoint(obj, clipper) + case *geojson.Rect: + return clipRect(obj, clipper) + case *geojson.LineString: + return clipLineString(obj, clipper) + case *geojson.Polygon: + return clipPolygon(obj, clipper) + case *geojson.Feature: + return clipFeature(obj, clipper) + case geojson.Collection: + return clipCollection(obj, clipper) + } + return obj +} + +// clipSegment is Cohen-Sutherland Line Clipping +// https://www.cs.helsinki.fi/group/goa/viewing/leikkaus/lineClip.html +func clipSegment(seg geometry.Segment, rect geometry.Rect) ( + res geometry.Segment, rejected bool, +) { + startCode := getCode(rect, seg.A) + endCode := getCode(rect, seg.B) + if (startCode | endCode) == 0 { + // trivially accept + res = seg + } else if (startCode & endCode) != 0 { + // trivially reject + rejected = true + } else if startCode != 0 { + // start is outside. get new start. + newStart := intersect(rect, startCode, seg.A, seg.B) + res, rejected = + clipSegment(geometry.Segment{A: newStart, B: seg.B}, rect) + } else { + // end is outside. get new end. + newEnd := intersect(rect, endCode, seg.A, seg.B) + res, rejected = clipSegment(geometry.Segment{A: seg.A, B: newEnd}, rect) + } + return +} + +// clipRing is Sutherland-Hodgman Polygon Clipping +// https://www.cs.helsinki.fi/group/goa/viewing/leikkaus/intro2.html +func clipRing(ring []geometry.Point, bbox geometry.Rect) ( + resRing []geometry.Point, +) { + if len(ring) < 4 { + // under 4 elements this is not a polygon ring! + return + } + var edge uint8 + var inside, prevInside bool + var prev geometry.Point + for edge = 1; edge <= 8; edge *= 2 { + prev = ring[len(ring)-2] + prevInside = (getCode(bbox, prev) & edge) == 0 + for _, p := range ring { + inside = (getCode(bbox, p) & edge) == 0 + if prevInside && inside { + // Staying inside + resRing = append(resRing, p) + } else if prevInside && !inside { + // Leaving + resRing = append(resRing, intersect(bbox, edge, prev, p)) + } else if !prevInside && inside { + // Entering + resRing = append(resRing, intersect(bbox, edge, prev, p)) + resRing = append(resRing, p) + } else { + // Staying outside + } + prev, prevInside = p, inside + } + if len(resRing) > 0 && resRing[0] != resRing[len(resRing)-1] { + resRing = append(resRing, resRing[0]) + } + ring, resRing = resRing, []geometry.Point{} + if len(ring) == 0 { + break + } + } + resRing = ring + return +} + +func getCode(bbox geometry.Rect, point geometry.Point) (code uint8) { + code = 0 + + if point.X < bbox.Min.X { + code |= 1 // left + } else if point.X > bbox.Max.X { + code |= 2 // right + } + + if point.Y < bbox.Min.Y { + code |= 4 // bottom + } else if point.Y > bbox.Max.Y { + code |= 8 // top + } + + return +} + +func intersect(bbox geometry.Rect, code uint8, start, end geometry.Point) ( + new geometry.Point, +) { + if (code & 8) != 0 { // top + new = geometry.Point{ + X: start.X + (end.X-start.X)*(bbox.Max.Y-start.Y)/(end.Y-start.Y), + Y: bbox.Max.Y, + } + } else if (code & 4) != 0 { // bottom + new = geometry.Point{ + X: start.X + (end.X-start.X)*(bbox.Min.Y-start.Y)/(end.Y-start.Y), + Y: bbox.Min.Y, + } + } else if (code & 2) != 0 { //right + new = geometry.Point{ + X: bbox.Max.X, + Y: start.Y + (end.Y-start.Y)*(bbox.Max.X-start.X)/(end.X-start.X), + } + } else if (code & 1) != 0 { // left + new = geometry.Point{ + X: bbox.Min.X, + Y: start.Y + (end.Y-start.Y)*(bbox.Min.X-start.X)/(end.X-start.X), + } + } else { // should not call intersect with the zero code + } + + return +} diff --git a/internal/clip/clip_test.go b/internal/clip/clip_test.go new file mode 100644 index 00000000..dbe562b1 --- /dev/null +++ b/internal/clip/clip_test.go @@ -0,0 +1,88 @@ +package clip + +import ( + "testing" + + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" +) + +func LO(points []geometry.Point) *geojson.LineString { + return geojson.NewLineString(geometry.NewLine(points, geometry.DefaultIndex)) +} + +func RO(minX, minY, maxX, maxY float64) *geojson.Rect { + return geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: 1.5, Y: 0.5}, + Max: geometry.Point{X: 2.5, Y: 1.8}, + }) +} + +func PPO(exterior []geometry.Point, holes [][]geometry.Point) *geojson.Polygon { + return geojson.NewPolygon(geometry.NewPoly(exterior, holes, geometry.DefaultIndex)) +} + +func TestClipLineStringSimple(t *testing.T) { + ls := LO([]geometry.Point{ + {X: 1, Y: 1}, + {X: 2, Y: 2}, + {X: 3, Y: 1}}) + clipped := Clip(ls, RO(1.5, 0.5, 2.5, 1.8)) + cl, ok := clipped.(*geojson.MultiLineString) + if !ok { + t.Fatal("wrong type") + } + if len(cl.Children()) != 2 { + t.Fatal("result must have two parts in MultiString") + } +} + +func TestClipPolygonSimple(t *testing.T) { + exterior := []geometry.Point{ + {X: 2, Y: 2}, + {X: 1, Y: 2}, + {X: 1.5, Y: 1.5}, + {X: 1, Y: 1}, + {X: 2, Y: 1}, + {X: 2, Y: 2}, + } + holes := [][]geometry.Point{ + []geometry.Point{ + {X: 1.9, Y: 1.9}, + {X: 1.2, Y: 1.9}, + {X: 1.45, Y: 1.65}, + {X: 1.9, Y: 1.5}, + {X: 1.9, Y: 1.9}, + }, + } + polygon := PPO(exterior, holes) + clipped := Clip(polygon, RO(1.3, 1.3, 1.4, 2.15)) + cp, ok := clipped.(*geojson.Polygon) + if !ok { + t.Fatal("wrong type") + } + if !cp.Base().Exterior.Empty() && len(cp.Base().Holes) != 1 { + t.Fatal("result must have two parts in Polygon") + } +} + +// func TestClipLineString(t *testing.T) { +// featuresJSON := ` +// {"type": "FeatureCollection","features": [ +// {"type": "Feature","properties":{},"geometry": {"type": "LineString","coordinates": [[-71.46537780761717,42.594290856363344],[-71.37714385986328,42.600861802789524],[-71.37508392333984,42.538156868495555],[-71.43756866455078,42.535374141307415],[-71.44683837890625,42.466018925787495],[-71.334228515625,42.465005871175755],[-71.32736206054688,42.52424199254517]]}}, +// {"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.49284362792969,42.527784255084676],[-71.35791778564453,42.527784255084676],[-71.35791778564453,42.61096959812047],[-71.49284362792969,42.61096959812047],[-71.49284362792969,42.527784255084676]]]}}, +// {"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.47396087646484,42.48247876554176],[-71.30744934082031,42.48247876554176],[-71.30744934082031,42.576596402826894],[-71.47396087646484,42.576596402826894],[-71.47396087646484,42.48247876554176]]]}}, +// {"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.33491516113281,42.613496290695196],[-71.29920959472656,42.613496290695196],[-71.29920959472656,42.643556064374536],[-71.33491516113281,42.643556064374536],[-71.33491516113281,42.613496290695196]]]}}, +// {"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.37130737304686,42.530061317794775],[-71.3287353515625,42.530061317794775],[-71.3287353515625,42.60414701616359],[-71.37130737304686,42.60414701616359],[-71.37130737304686,42.530061317794775]]]}}, +// {"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.52889251708984,42.564460160624115],[-71.45713806152342,42.54043355305221],[-71.53266906738281,42.49969365675931],[-71.36547088623047,42.508552415528634],[-71.43962860107422,42.58999409368092],[-71.52889251708984,42.564460160624115]]]}}, +// {"type": "Feature","properties": {},"geometry": {"type": "Point","coordinates": [-71.33079528808594,42.55940269610327]}}, +// {"type": "Feature","properties": {},"geometry": {"type": "Point","coordinates": [-71.27208709716797,42.53107331902133]}} +// ]} +// ` +// rectJSON := `{"type": "Feature","properties": {},"geometry": {"type": "Polygon","coordinates": [[[-71.44065856933594,42.51740991900762],[-71.29131317138672,42.51740991900762],[-71.29131317138672,42.62663343969058],[-71.44065856933594,42.62663343969058],[-71.44065856933594,42.51740991900762]]]}}` +// features := expectJSON(t, featuresJSON, nil) +// rect := expectJSON(t, rectJSON, nil) +// clipped := features.Clipped(rect) +// println(clipped.String()) + +// } diff --git a/internal/clip/collection.go b/internal/clip/collection.go new file mode 100644 index 00000000..dc713a52 --- /dev/null +++ b/internal/clip/collection.go @@ -0,0 +1,20 @@ +package clip + +import "github.com/tidwall/geojson" + +func clipCollection( + collection geojson.Collection, clipper geojson.Object, +) geojson.Object { + var features []geojson.Object + for _, feature := range collection.Children() { + feature = Clip(feature, clipper) + if feature.Empty() { + continue + } + if _, ok := feature.(*geojson.Feature); !ok { + feature = geojson.NewFeature(feature, "") + } + features = append(features, feature) + } + return geojson.NewFeatureCollection(features) +} diff --git a/internal/clip/feature.go b/internal/clip/feature.go new file mode 100644 index 00000000..2e50f506 --- /dev/null +++ b/internal/clip/feature.go @@ -0,0 +1,13 @@ +package clip + +import "github.com/tidwall/geojson" + +func clipFeature( + feature *geojson.Feature, clipper geojson.Object, +) geojson.Object { + newFeature := Clip(feature.Base(), clipper) + if _, ok := newFeature.(*geojson.Feature); !ok { + newFeature = geojson.NewFeature(newFeature, feature.Members()) + } + return newFeature +} diff --git a/internal/clip/linestring.go b/internal/clip/linestring.go new file mode 100644 index 00000000..211d375e --- /dev/null +++ b/internal/clip/linestring.go @@ -0,0 +1,43 @@ +package clip + +import ( + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" +) + +func clipLineString( + lineString *geojson.LineString, clipper geojson.Object, +) geojson.Object { + bbox := clipper.Rect() + var newPoints [][]geometry.Point + var clipped geometry.Segment + var rejected bool + var line []geometry.Point + base := lineString.Base() + nSegments := base.NumSegments() + for i := 0; i < nSegments; i++ { + clipped, rejected = clipSegment(base.SegmentAt(i), bbox) + if rejected { + continue + } + if len(line) > 0 && line[len(line)-1] != clipped.A { + newPoints = append(newPoints, line) + line = []geometry.Point{clipped.A} + } else if len(line) == 0 { + line = append(line, clipped.A) + } + line = append(line, clipped.B) + } + if len(line) > 0 { + newPoints = append(newPoints, line) + } + var children []*geometry.Line + for _, points := range newPoints { + children = append(children, + geometry.NewLine(points, geometry.DefaultIndex)) + } + if len(children) == 1 { + return geojson.NewLineString(children[0]) + } + return geojson.NewMultiLineString(children) +} diff --git a/internal/clip/point.go b/internal/clip/point.go new file mode 100644 index 00000000..cf6de5c0 --- /dev/null +++ b/internal/clip/point.go @@ -0,0 +1,10 @@ +package clip + +import "github.com/tidwall/geojson" + +func clipPoint(point *geojson.Point, clipper geojson.Object) geojson.Object { + if point.IntersectsRect(clipper.Rect()) { + return point + } + return geojson.NewMultiPoint(nil) +} diff --git a/internal/clip/polygon.go b/internal/clip/polygon.go new file mode 100644 index 00000000..1d7849e4 --- /dev/null +++ b/internal/clip/polygon.go @@ -0,0 +1,39 @@ +package clip + +import ( + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" +) + +func clipPolygon( + polygon *geojson.Polygon, clipper geojson.Object, +) geojson.Object { + rect := clipper.Rect() + var newPoints [][]geometry.Point + base := polygon.Base() + rings := []geometry.Ring{base.Exterior} + rings = append(rings, base.Holes...) + for _, ring := range rings { + ringPoints := make([]geometry.Point, ring.NumPoints()) + for i := 0; i < len(ringPoints); i++ { + ringPoints[i] = ring.PointAt(i) + } + newPoints = append(newPoints, clipRing(ringPoints, rect)) + } + + var exterior []geometry.Point + var holes [][]geometry.Point + if len(newPoints) > 0 { + exterior = newPoints[0] + } + if len(newPoints) > 1 { + holes = newPoints[1:] + } + newPoly := geojson.NewPolygon( + geometry.NewPoly(exterior, holes, geometry.DefaultIndex), + ) + if newPoly.Empty() { + return geojson.NewMultiPolygon(nil) + } + return polygon +} diff --git a/internal/clip/rect.go b/internal/clip/rect.go new file mode 100644 index 00000000..3568d2fc --- /dev/null +++ b/internal/clip/rect.go @@ -0,0 +1,17 @@ +package clip + +import ( + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" +) + +func clipRect(rect *geojson.Rect, clipper geojson.Object) geojson.Object { + base := rect.Base() + points := make([]geometry.Point, base.NumPoints()) + for i := 0; i < len(points); i++ { + points[i] = base.PointAt(i) + } + poly := geometry.NewPoly(points, nil, geometry.DefaultIndex) + gPoly := geojson.NewPolygon(poly) + return Clip(gPoly, clipper) +} diff --git a/internal/collection/collection.go b/internal/collection/collection.go new file mode 100644 index 00000000..86bc4a41 --- /dev/null +++ b/internal/collection/collection.go @@ -0,0 +1,570 @@ +package collection + +import ( + "github.com/tidwall/boxtree/d2" + "github.com/tidwall/btree" + "github.com/tidwall/tile38/internal/ds" + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" +) + +type itemT struct { + id string + obj geojson.Object +} + +func (item *itemT) Less(other btree.Item, ctx interface{}) bool { + value1 := item.obj.String() + value2 := other.(*itemT).obj.String() + if value1 < value2 { + return true + } + if value1 > value2 { + return false + } + // the values match so we'll compare IDs, which are always unique. + return item.id < other.(*itemT).id +} + +// Collection represents a collection of geojson objects. +type Collection struct { + items ds.BTree // items sorted by keys + index d2.BoxTree // items geospatially indexed + values *btree.BTree // items sorted by value+key + fieldMap map[string]int + fieldValues map[string][]float64 + weight int + points int + objects int // geometry count + nobjects int // non-geometry count +} + +var counter uint64 + +// New creates an empty collection +func New() *Collection { + col := &Collection{ + values: btree.New(16, nil), + fieldMap: make(map[string]int), + } + return col +} + +func (c *Collection) setFieldValues(id string, values []float64) { + if c.fieldValues == nil { + c.fieldValues = make(map[string][]float64) + } + c.fieldValues[id] = values +} + +func (c *Collection) getFieldValues(id string) (values []float64) { + if c.fieldValues == nil { + return nil + } + return c.fieldValues[id] +} +func (c *Collection) deleteFieldValues(id string) { + if c.fieldValues != nil { + delete(c.fieldValues, id) + } +} + +// Count returns the number of objects in collection. +func (c *Collection) Count() int { + return c.objects + c.nobjects +} + +// StringCount returns the number of string values. +func (c *Collection) StringCount() int { + return c.nobjects +} + +// PointCount returns the number of points (lat/lon coordinates) in collection. +func (c *Collection) PointCount() int { + return c.points +} + +// TotalWeight calculates the in-memory cost of the collection in bytes. +func (c *Collection) TotalWeight() int { + return c.weight +} + +// Bounds returns the bounds of all the items in the collection. +func (c *Collection) Bounds() (minX, minY, maxX, maxY float64) { + min, max := c.index.Bounds() + if len(min) >= 2 && len(max) >= 2 { + return min[0], min[1], max[0], max[1] + } + return +} + +func objIsSpatial(obj geojson.Object) bool { + _, ok := obj.(geojson.Spatial) + return ok +} + +func (c *Collection) objWeight(item *itemT) int { + var weight int + if objIsSpatial(item.obj) { + weight = item.obj.NumPoints() * 16 + } else { + weight = len(item.obj.String()) + } + return weight + len(c.getFieldValues(item.id))*8 + len(item.id) +} + +func (c *Collection) indexDelete(item *itemT) { + if !item.obj.Empty() { + rect := item.obj.Rect() + c.index.Delete( + []float64{rect.Min.X, rect.Min.Y}, + []float64{rect.Max.X, rect.Max.Y}, + item) + } +} + +func (c *Collection) indexInsert(item *itemT) { + if !item.obj.Empty() { + rect := item.obj.Rect() + c.index.Insert( + []float64{rect.Min.X, rect.Min.Y}, + []float64{rect.Max.X, rect.Max.Y}, + item) + } +} + +// Set adds or replaces an object in the collection and returns the fields +// array. If an item with the same id is already in the collection then the +// new item will adopt the old item's fields. +// The fields argument is optional. +// The return values are the old object, the old fields, and the new fields +func (c *Collection) Set( + id string, obj geojson.Object, fields []string, values []float64, +) ( + oldObject geojson.Object, oldFields []float64, newFields []float64, +) { + newItem := &itemT{id: id, obj: obj} + + // add the new item to main btree and remove the old one if needed + oldItem, ok := c.items.Set(id, newItem) + if ok { + oldItem := oldItem.(*itemT) + // the old item was removed, now let's remove it from the rtree/btree. + if objIsSpatial(oldItem.obj) { + c.indexDelete(oldItem) + c.objects-- + } else { + c.values.Delete(oldItem) + c.nobjects-- + } + + // decrement the point count + c.points -= oldItem.obj.NumPoints() + + // decrement the weights + c.weight -= c.objWeight(oldItem) + + // references + oldObject = oldItem.obj + oldFields = c.getFieldValues(id) + newFields = oldFields + } + // insert the new item into the rtree or strings tree. + if objIsSpatial(newItem.obj) { + c.indexInsert(newItem) + c.objects++ + } else { + c.values.ReplaceOrInsert(newItem) + c.nobjects++ + } + + // increment the point count + c.points += newItem.obj.NumPoints() + + // add the new weights + c.weight += c.objWeight(newItem) + + if fields == nil { + if len(values) > 0 { + // directly set the field values, update weight + c.weight -= len(newFields) * 8 + newFields = values + c.setFieldValues(id, newFields) + c.weight += len(newFields) * 8 + } + } else { + // map field name to value + for i, field := range fields { + c.setField(newItem, field, values[i]) + } + newFields = c.getFieldValues(id) + } + return oldObject, oldFields, newFields +} + +// Delete removes an object and returns it. +// If the object does not exist then the 'ok' return value will be false. +func (c *Collection) Delete(id string) ( + obj geojson.Object, fields []float64, ok bool, +) { + oldItemV, ok := c.items.Delete(id) + if !ok { + return nil, nil, false + } + oldItem := oldItemV.(*itemT) + if objIsSpatial(oldItem.obj) { + if !oldItem.obj.Empty() { + c.indexDelete(oldItem) + } + c.objects-- + } else { + c.values.Delete(oldItem) + c.nobjects-- + } + c.weight -= c.objWeight(oldItem) + c.points -= oldItem.obj.NumPoints() + + fields = c.getFieldValues(id) + c.deleteFieldValues(id) + return oldItem.obj, fields, true +} + +// Get returns an object. +// If the object does not exist then the 'ok' return value will be false. +func (c *Collection) Get(id string) ( + obj geojson.Object, fields []float64, ok bool, +) { + itemV, ok := c.items.Get(id) + if !ok { + return nil, nil, false + } + item := itemV.(*itemT) + return item.obj, c.getFieldValues(id), true +} + +// SetField set a field value for an object and returns that object. +// If the object does not exist then the 'ok' return value will be false. +func (c *Collection) SetField(id, field string, value float64) ( + obj geojson.Object, fields []float64, updated bool, ok bool, +) { + itemV, ok := c.items.Get(id) + if !ok { + return nil, nil, false, false + } + item := itemV.(*itemT) + updated = c.setField(item, field, value) + return item.obj, c.getFieldValues(id), updated, true +} + +// SetFields is similar to SetField, just setting multiple fields at once +func (c *Collection) SetFields( + id string, inFields []string, inValues []float64, +) (obj geojson.Object, fields []float64, updatedCount int, ok bool) { + itemV, ok := c.items.Get(id) + if !ok { + return nil, nil, 0, false + } + item := itemV.(*itemT) + for idx, field := range inFields { + if c.setField(item, field, inValues[idx]) { + updatedCount++ + } + } + return item.obj, c.getFieldValues(id), updatedCount, true +} + +func (c *Collection) setField(item *itemT, field string, value float64) ( + updated bool, +) { + idx, ok := c.fieldMap[field] + if !ok { + idx = len(c.fieldMap) + c.fieldMap[field] = idx + } + fields := c.getFieldValues(item.id) + c.weight -= len(fields) * 8 + for idx >= len(fields) { + fields = append(fields, 0) + } + c.weight += len(fields) * 8 + ovalue := fields[idx] + fields[idx] = value + c.setFieldValues(item.id, fields) + return ovalue != value +} + +// FieldMap return a maps of the field names. +func (c *Collection) FieldMap() map[string]int { + return c.fieldMap +} + +// FieldArr return an array representation of the field names. +func (c *Collection) FieldArr() []string { + arr := make([]string, len(c.fieldMap)) + for field, i := range c.fieldMap { + arr[i] = field + } + return arr +} + +// Scan iterates though the collection ids. +func (c *Collection) Scan(desc bool, + iterator func(id string, obj geojson.Object, fields []float64) bool, +) bool { + var keepon = true + iter := func(key string, value interface{}) bool { + iitm := value.(*itemT) + keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id)) + return keepon + } + if desc { + c.items.Reverse(iter) + } else { + c.items.Scan(iter) + } + return keepon +} + +// ScanRange iterates though the collection starting with specified id. +func (c *Collection) ScanRange(start, end string, desc bool, + iterator func(id string, obj geojson.Object, fields []float64) bool, +) bool { + var keepon = true + iter := func(key string, value interface{}) bool { + if !desc { + if key >= end { + return false + } + } else { + if key <= end { + return false + } + } + iitm := value.(*itemT) + keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id)) + return keepon + } + + if desc { + c.items.Descend(start, iter) + } else { + c.items.Ascend(start, iter) + } + return keepon +} + +// SearchValues iterates though the collection values. +func (c *Collection) SearchValues(desc bool, + iterator func(id string, obj geojson.Object, fields []float64) bool, +) bool { + var keepon = true + iter := func(item btree.Item) bool { + iitm := item.(*itemT) + keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id)) + return keepon + } + if desc { + c.values.Descend(iter) + } else { + c.values.Ascend(iter) + } + return keepon +} + +// SearchValuesRange iterates though the collection values. +func (c *Collection) SearchValuesRange(start, end string, desc bool, + iterator func(id string, obj geojson.Object, fields []float64) bool, +) bool { + var keepon = true + iter := func(item btree.Item) bool { + iitm := item.(*itemT) + keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id)) + return keepon + } + if desc { + c.values.DescendRange(&itemT{obj: String(start)}, + &itemT{obj: String(end)}, iter) + } else { + c.values.AscendRange(&itemT{obj: String(start)}, + &itemT{obj: String(end)}, iter) + } + return keepon +} + +// ScanGreaterOrEqual iterates though the collection starting with specified id. +func (c *Collection) ScanGreaterOrEqual(id string, desc bool, + iterator func(id string, obj geojson.Object, fields []float64) bool, +) bool { + var keepon = true + iter := func(key string, value interface{}) bool { + iitm := value.(*itemT) + keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id)) + return keepon + } + if desc { + c.items.Descend(id, iter) + } else { + c.items.Ascend(id, iter) + } + return keepon +} + +func (c *Collection) geoSearch( + rect geometry.Rect, + iter func(id string, obj geojson.Object, fields []float64) bool, +) bool { + alive := true + c.index.Search( + []float64{rect.Min.X, rect.Min.Y}, + []float64{rect.Max.X, rect.Max.Y}, + func(_, _ []float64, itemv interface{}) bool { + item := itemv.(*itemT) + alive = iter(item.id, item.obj, c.getFieldValues(item.id)) + return alive + }, + ) + return alive +} + +func (c *Collection) geoSparse( + obj geojson.Object, sparse uint8, + iter func(id string, obj geojson.Object, fields []float64) (match, ok bool), +) bool { + matches := make(map[string]bool) + alive := true + c.geoSparseInner(obj.Rect(), sparse, + func(id string, o geojson.Object, fields []float64) ( + match, ok bool, + ) { + ok = true + if !matches[id] { + match, ok = iter(id, o, fields) + if match { + matches[id] = true + } + } + return match, ok + }, + ) + return alive +} +func (c *Collection) geoSparseInner( + rect geometry.Rect, sparse uint8, + iter func(id string, obj geojson.Object, fields []float64) (match, ok bool), +) bool { + if sparse > 0 { + w := rect.Max.X - rect.Min.X + h := rect.Max.Y - rect.Min.Y + quads := [4]geometry.Rect{ + geometry.Rect{ + Min: geometry.Point{X: rect.Min.X, Y: rect.Min.Y + h/2}, + Max: geometry.Point{X: rect.Min.X + w/2, Y: rect.Max.Y}, + }, + geometry.Rect{ + Min: geometry.Point{X: rect.Min.X + w/2, Y: rect.Min.Y + h/2}, + Max: geometry.Point{X: rect.Max.X, Y: rect.Max.Y}, + }, + geometry.Rect{ + Min: geometry.Point{X: rect.Min.X, Y: rect.Min.Y}, + Max: geometry.Point{X: rect.Min.X + w/2, Y: rect.Min.Y + h/2}, + }, + geometry.Rect{ + Min: geometry.Point{X: rect.Min.X + w/2, Y: rect.Min.Y}, + Max: geometry.Point{X: rect.Max.X, Y: rect.Min.Y + h/2}, + }, + } + for _, quad := range quads { + if !c.geoSparseInner(quad, sparse-1, iter) { + return false + } + } + return true + } + alive := true + c.geoSearch(rect, + func(id string, obj geojson.Object, fields []float64) bool { + match, ok := iter(id, obj, fields) + if !ok { + alive = false + return false + } + return !match + }, + ) + return alive +} + +// Within returns all object that are fully contained within an object or +// bounding box. Set obj to nil in order to use the bounding box. +func (c *Collection) Within( + obj geojson.Object, sparse uint8, + iter func(id string, obj geojson.Object, fields []float64) bool, +) bool { + if sparse > 0 { + return c.geoSparse(obj, sparse, + func(id string, o geojson.Object, fields []float64) ( + match, ok bool, + ) { + if match = o.Within(obj); match { + ok = iter(id, o, fields) + } + return match, ok + }, + ) + } + return c.geoSearch(obj.Rect(), + func(id string, o geojson.Object, fields []float64) bool { + if o.Within(obj) { + return iter(id, o, fields) + } + return true + }, + ) +} + +// Intersects returns all object that are intersect an object or bounding box. +// Set obj to nil in order to use the bounding box. +func (c *Collection) Intersects( + obj geojson.Object, sparse uint8, + iter func(id string, obj geojson.Object, fields []float64) bool, +) bool { + if sparse > 0 { + return c.geoSparse(obj, sparse, + func(id string, o geojson.Object, fields []float64) ( + match, ok bool, + ) { + if match = o.Intersects(obj); match { + ok = iter(id, o, fields) + } + return match, ok + }, + ) + } + return c.geoSearch(obj.Rect(), + func(id string, o geojson.Object, fields []float64) bool { + if o.Intersects(obj) { + return iter(id, o, fields) + } + return true + }, + ) +} + +// Nearby returns the nearest neighbors +func (c *Collection) Nearby( + target geojson.Object, + iter func(id string, obj geojson.Object, fields []float64) bool, +) bool { + alive := true + center := target.Center() + c.index.Nearby( + []float64{center.X, center.Y}, + []float64{center.X, center.Y}, + func(_, _ []float64, itemv interface{}) bool { + item := itemv.(*itemT) + alive = iter(item.id, item.obj, c.getFieldValues(item.id)) + return alive + }, + ) + return alive +} diff --git a/internal/collection/collection_test.go b/internal/collection/collection_test.go new file mode 100644 index 00000000..123ad202 --- /dev/null +++ b/internal/collection/collection_test.go @@ -0,0 +1,721 @@ +package collection + +import ( + "fmt" + "math/rand" + "reflect" + "strconv" + "testing" + "time" + + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" +) + +func PO(x, y float64) *geojson.Point { + return geojson.NewPoint(geometry.Point{X: x, Y: y}) +} + +func init() { + seed := time.Now().UnixNano() + println(seed) + rand.Seed(seed) +} + +func expect(t testing.TB, expect bool) { + t.Helper() + if !expect { + t.Fatal("not what you expected") + } +} + +func bounds(c *Collection) geometry.Rect { + minX, minY, maxX, maxY := c.Bounds() + return geometry.Rect{ + Min: geometry.Point{X: minX, Y: minY}, + Max: geometry.Point{X: maxX, Y: maxY}, + } +} + +func TestCollectionNewCollection(t *testing.T) { + const numItems = 10000 + objs := make(map[string]geojson.Object) + c := New() + for i := 0; i < numItems; i++ { + id := strconv.FormatInt(int64(i), 10) + var obj geojson.Object + obj = PO(rand.Float64()*360-180, rand.Float64()*180-90) + objs[id] = obj + c.Set(id, obj, nil, nil) + } + count := 0 + bbox := geometry.Rect{ + Min: geometry.Point{X: -180, Y: -90}, + Max: geometry.Point{X: 180, Y: 90}, + } + c.geoSearch(bbox, func(id string, obj geojson.Object, field []float64) bool { + count++ + return true + }) + if count != len(objs) { + t.Fatalf("count = %d, expect %d", count, len(objs)) + } + count = c.Count() + if count != len(objs) { + t.Fatalf("c.Count() = %d, expect %d", count, len(objs)) + } + testCollectionVerifyContents(t, c, objs) +} + +func TestCollectionSet(t *testing.T) { + t.Run("AddString", func(t *testing.T) { + c := New() + str1 := String("hello") + oldObject, oldFields, newFields := c.Set("str", str1, nil, nil) + expect(t, oldObject == nil) + expect(t, len(oldFields) == 0) + expect(t, len(newFields) == 0) + }) + t.Run("UpdateString", func(t *testing.T) { + c := New() + str1 := String("hello") + str2 := String("world") + oldObject, oldFields, newFields := c.Set("str", str1, nil, nil) + expect(t, oldObject == nil) + expect(t, len(oldFields) == 0) + expect(t, len(newFields) == 0) + oldObject, oldFields, newFields = c.Set("str", str2, nil, nil) + expect(t, oldObject == str1) + expect(t, len(oldFields) == 0) + expect(t, len(newFields) == 0) + }) + t.Run("AddPoint", func(t *testing.T) { + c := New() + point1 := PO(-112.1, 33.1) + oldObject, oldFields, newFields := c.Set("point", point1, nil, nil) + expect(t, oldObject == nil) + expect(t, len(oldFields) == 0) + expect(t, len(newFields) == 0) + }) + t.Run("UpdatePoint", func(t *testing.T) { + c := New() + point1 := PO(-112.1, 33.1) + point2 := PO(-112.2, 33.2) + oldObject, oldFields, newFields := c.Set("point", point1, nil, nil) + expect(t, oldObject == nil) + expect(t, len(oldFields) == 0) + expect(t, len(newFields) == 0) + oldObject, oldFields, newFields = c.Set("point", point2, nil, nil) + expect(t, oldObject == point1) + expect(t, len(oldFields) == 0) + expect(t, len(newFields) == 0) + }) + t.Run("Fields", func(t *testing.T) { + c := New() + str1 := String("hello") + fNames := []string{"a", "b", "c"} + fValues := []float64{1, 2, 3} + oldObj, oldFlds, newFlds := c.Set("str", str1, fNames, fValues) + expect(t, oldObj == nil) + expect(t, len(oldFlds) == 0) + expect(t, reflect.DeepEqual(newFlds, fValues)) + str2 := String("hello") + fNames = []string{"d", "e", "f"} + fValues = []float64{4, 5, 6} + oldObj, oldFlds, newFlds = c.Set("str", str2, fNames, fValues) + expect(t, oldObj == str1) + expect(t, reflect.DeepEqual(oldFlds, []float64{1, 2, 3})) + expect(t, reflect.DeepEqual(newFlds, []float64{1, 2, 3, 4, 5, 6})) + fValues = []float64{7, 8, 9, 10, 11, 12} + oldObj, oldFlds, newFlds = c.Set("str", str1, nil, fValues) + expect(t, oldObj == str2) + expect(t, reflect.DeepEqual(oldFlds, []float64{1, 2, 3, 4, 5, 6})) + expect(t, reflect.DeepEqual(newFlds, []float64{7, 8, 9, 10, 11, 12})) + }) + t.Run("Delete", func(t *testing.T) { + c := New() + + c.Set("1", String("1"), nil, nil) + c.Set("2", String("2"), nil, nil) + c.Set("3", PO(1, 2), nil, nil) + + expect(t, c.Count() == 3) + expect(t, c.StringCount() == 2) + expect(t, c.PointCount() == 1) + expect(t, bounds(c) == geometry.Rect{ + Min: geometry.Point{X: 1, Y: 2}, + Max: geometry.Point{X: 1, Y: 2}}) + var v geojson.Object + var ok bool + var flds []float64 + var updated bool + var updateCount int + + v, _, ok = c.Delete("2") + expect(t, v.String() == "2") + expect(t, ok) + expect(t, c.Count() == 2) + expect(t, c.StringCount() == 1) + expect(t, c.PointCount() == 1) + + v, _, ok = c.Delete("1") + expect(t, v.String() == "1") + expect(t, ok) + expect(t, c.Count() == 1) + expect(t, c.StringCount() == 0) + expect(t, c.PointCount() == 1) + + expect(t, len(c.FieldMap()) == 0) + + v, flds, updated, ok = c.SetField("3", "hello", 123) + expect(t, ok) + expect(t, reflect.DeepEqual(flds, []float64{123})) + expect(t, updated) + expect(t, c.FieldMap()["hello"] == 0) + + v, flds, updated, ok = c.SetField("3", "hello", 1234) + expect(t, ok) + expect(t, reflect.DeepEqual(flds, []float64{1234})) + expect(t, updated) + + v, flds, updated, ok = c.SetField("3", "hello", 1234) + expect(t, ok) + expect(t, reflect.DeepEqual(flds, []float64{1234})) + expect(t, !updated) + + v, flds, updateCount, ok = c.SetFields("3", + []string{"planet", "world"}, []float64{55, 66}) + expect(t, ok) + expect(t, reflect.DeepEqual(flds, []float64{1234, 55, 66})) + expect(t, updateCount == 2) + expect(t, c.FieldMap()["hello"] == 0) + expect(t, c.FieldMap()["planet"] == 1) + expect(t, c.FieldMap()["world"] == 2) + + v, _, ok = c.Delete("3") + expect(t, v.String() == `{"type":"Point","coordinates":[1,2]}`) + expect(t, ok) + expect(t, c.Count() == 0) + expect(t, c.StringCount() == 0) + expect(t, c.PointCount() == 0) + v, _, ok = c.Delete("3") + expect(t, v == nil) + expect(t, !ok) + expect(t, c.Count() == 0) + expect(t, bounds(c) == geometry.Rect{}) + v, _, ok = c.Get("3") + expect(t, v == nil) + expect(t, !ok) + _, _, _, ok = c.SetField("3", "hello", 123) + expect(t, !ok) + _, _, _, ok = c.SetFields("3", []string{"hello"}, []float64{123}) + expect(t, !ok) + expect(t, c.TotalWeight() == 0) + expect(t, c.FieldMap()["hello"] == 0) + expect(t, c.FieldMap()["planet"] == 1) + expect(t, c.FieldMap()["world"] == 2) + expect(t, reflect.DeepEqual( + c.FieldArr(), []string{"hello", "planet", "world"}), + ) + }) +} + +func TestCollectionScan(t *testing.T) { + N := 256 + c := New() + for _, i := range rand.Perm(N) { + id := fmt.Sprintf("%04d", i) + c.Set(id, String(id), []string{"ex"}, []float64{float64(i)}) + } + var n int + var prevID string + c.Scan(false, func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, id > prevID) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[0]))) + n++ + prevID = id + return true + }) + expect(t, n == c.Count()) + n = 0 + c.Scan(true, func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, id < prevID) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[0]))) + n++ + prevID = id + return true + }) + expect(t, n == c.Count()) + + n = 0 + c.ScanRange("0060", "0070", false, + func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, id > prevID) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[0]))) + n++ + prevID = id + return true + }) + expect(t, n == 10) + + n = 0 + c.ScanRange("0070", "0060", true, + func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, id < prevID) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[0]))) + n++ + prevID = id + return true + }) + expect(t, n == 10) + + n = 0 + c.ScanGreaterOrEqual("0070", true, + func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, id < prevID) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[0]))) + n++ + prevID = id + return true + }) + expect(t, n == 71) + + n = 0 + c.ScanGreaterOrEqual("0070", false, + func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, id > prevID) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[0]))) + n++ + prevID = id + return true + }) + expect(t, n == c.Count()-70) + +} + +func TestCollectionSearch(t *testing.T) { + N := 256 + c := New() + for i, j := range rand.Perm(N) { + id := fmt.Sprintf("%04d", j) + ex := fmt.Sprintf("%04d", i) + c.Set(id, String(ex), []string{"i", "j"}, + []float64{float64(i), float64(j)}) + } + var n int + var prevValue string + c.SearchValues(false, func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, obj.String() > prevValue) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[1]))) + n++ + prevValue = obj.String() + return true + }) + expect(t, n == c.Count()) + n = 0 + c.SearchValues(true, func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, obj.String() < prevValue) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[1]))) + n++ + prevValue = obj.String() + return true + }) + expect(t, n == c.Count()) + + n = 0 + c.SearchValuesRange("0060", "0070", false, + func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, obj.String() > prevValue) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[1]))) + n++ + prevValue = obj.String() + return true + }) + expect(t, n == 10) + + n = 0 + c.SearchValuesRange("0070", "0060", true, + func(id string, obj geojson.Object, fields []float64) bool { + if n > 0 { + expect(t, obj.String() < prevValue) + } + expect(t, id == fmt.Sprintf("%04d", int(fields[1]))) + n++ + prevValue = obj.String() + return true + }) + expect(t, n == 10) +} + +func TestCollectionWeight(t *testing.T) { + c := New() + c.Set("1", String("1"), nil, nil) + expect(t, c.TotalWeight() > 0) + c.Delete("1") + expect(t, c.TotalWeight() == 0) + c.Set("1", String("1"), + []string{"a", "b", "c"}, + []float64{1, 2, 3}, + ) + expect(t, c.TotalWeight() > 0) + c.Delete("1") + expect(t, c.TotalWeight() == 0) + c.Set("1", String("1"), + []string{"a", "b", "c"}, + []float64{1, 2, 3}, + ) + c.Set("2", String("2"), + []string{"d", "e", "f"}, + []float64{4, 5, 6}, + ) + c.Set("1", String("1"), + []string{"d", "e", "f"}, + []float64{4, 5, 6}, + ) + c.Delete("1") + c.Delete("2") + expect(t, c.TotalWeight() == 0) +} + +func TestSpatialSearch(t *testing.T) { + json := ` + {"type":"FeatureCollection","features":[ + {"type":"Feature","id":"p1","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4743041992187,42.51867517417283]}}, + {"type":"Feature","id":"p2","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4056396484375,42.50197174319114]}}, + {"type":"Feature","id":"p3","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4619445800781,42.49437779897246]}}, + {"type":"Feature","id":"p4","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4337921142578,42.53891577257117]}}, + {"type":"Feature","id":"r1","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Polygon","coordinates":[[[-71.4279556274414,42.48804880765346],[-71.37439727783203,42.48804880765346],[-71.37439727783203,42.52322988064187],[-71.4279556274414,42.52322988064187],[-71.4279556274414,42.48804880765346]]]}}, + {"type":"Feature","id":"r2","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Polygon","coordinates":[[[-71.4825439453125,42.53588010092859],[-71.45027160644531,42.53588010092859],[-71.45027160644531,42.55839115400447],[-71.4825439453125,42.55839115400447],[-71.4825439453125,42.53588010092859]]]}}, + {"type":"Feature","id":"r3","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Polygon","coordinates": [[[-71.4111328125,42.53512115995963],[-71.3833236694336,42.53512115995963],[-71.3833236694336,42.54953946116446],[-71.4111328125,42.54953946116446],[-71.4111328125,42.53512115995963]]]}}, + {"type":"Feature","id":"q1","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-71.55258178710938,42.51361399979923],[-71.42074584960938,42.51361399979923],[-71.42074584960938,42.59100512331456],[-71.55258178710938,42.59100512331456],[-71.55258178710938,42.51361399979923]]]}}, + {"type":"Feature","id":"q2","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-71.52992248535156,42.48121277771616],[-71.36375427246092,42.48121277771616],[-71.36375427246092,42.57786045892046],[-71.52992248535156,42.57786045892046],[-71.52992248535156,42.48121277771616]]]}}, + {"type":"Feature","id":"q3","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-71.49490356445312,42.56673588590953],[-71.52236938476562,42.47462922809497],[-71.42898559570312,42.464499337722344],[-71.43241882324219,42.522217752342236],[-71.37954711914061,42.56420729713456],[-71.49490356445312,42.56673588590953]]]}}, + {"type":"Feature","id":"q4","properties":{},"geometry":{"type":"Point","coordinates": [-71.46366119384766,42.54043355305221]}} + ]} + ` + p1, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p1"]`).Raw, nil) + p2, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p2"]`).Raw, nil) + p3, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p3"]`).Raw, nil) + p4, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p4"]`).Raw, nil) + r1, _ := geojson.Parse(gjson.Get(json, `features.#[id=="r1"]`).Raw, nil) + r2, _ := geojson.Parse(gjson.Get(json, `features.#[id=="r2"]`).Raw, nil) + r3, _ := geojson.Parse(gjson.Get(json, `features.#[id=="r3"]`).Raw, nil) + q1, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q1"]`).Raw, nil) + q2, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q2"]`).Raw, nil) + q3, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q3"]`).Raw, nil) + q4, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q4"]`).Raw, nil) + + c := New() + c.Set("p1", p1, nil, nil) + c.Set("p2", p2, nil, nil) + c.Set("p3", p3, nil, nil) + c.Set("p4", p4, nil, nil) + c.Set("r1", r1, nil, nil) + c.Set("r2", r2, nil, nil) + c.Set("r3", r3, nil, nil) + + var n int + + n = 0 + c.Within(q1, 0, + func(id string, obj geojson.Object, fields []float64) bool { + n++ + return true + }, + ) + expect(t, n == 3) + + n = 0 + c.Within(q2, 0, + func(id string, obj geojson.Object, fields []float64) bool { + n++ + return true + }, + ) + expect(t, n == 7) + + n = 0 + c.Within(q3, 0, + func(id string, obj geojson.Object, fields []float64) bool { + n++ + return true + }, + ) + expect(t, n == 4) + + n = 0 + c.Intersects(q1, 0, + func(_ string, _ geojson.Object, _ []float64) bool { + n++ + return true + }, + ) + expect(t, n == 4) + + n = 0 + c.Intersects(q2, 0, + func(_ string, _ geojson.Object, _ []float64) bool { + n++ + return true + }, + ) + expect(t, n == 7) + + n = 0 + c.Intersects(q3, 0, + func(_ string, _ geojson.Object, _ []float64) bool { + n++ + return true + }, + ) + expect(t, n == 5) + + n = 0 + c.Intersects(q3, 0, + func(_ string, _ geojson.Object, _ []float64) bool { + n++ + return n <= 1 + }, + ) + expect(t, n == 2) + + var items []geojson.Object + exitems := []geojson.Object{ + r2, p1, p4, r1, p3, r3, p2, + } + c.Nearby(q4, + func(id string, obj geojson.Object, fields []float64) bool { + items = append(items, obj) + return true + }, + ) + expect(t, len(items) == 7) + expect(t, reflect.DeepEqual(items, exitems)) +} + +func TestCollectionSparse(t *testing.T) { + rect := geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: -71.598930, Y: 42.4586739}, + Max: geometry.Point{X: -71.37302, Y: 42.607937}, + }) + N := 10000 + c := New() + r := rect.Rect() + for i := 0; i < N; i++ { + x := (r.Max.X-r.Min.X)*rand.Float64() + r.Min.X + y := (r.Max.Y-r.Min.Y)*rand.Float64() + r.Min.Y + point := PO(x, y) + c.Set(fmt.Sprintf("%d", i), point, nil, nil) + } + var n int + n = 0 + c.Within(rect, 1, + func(id string, obj geojson.Object, fields []float64) bool { + n++ + return true + }, + ) + expect(t, n == 4) + + n = 0 + c.Within(rect, 2, + func(id string, obj geojson.Object, fields []float64) bool { + n++ + return true + }, + ) + expect(t, n == 16) + + n = 0 + c.Within(rect, 3, + func(id string, obj geojson.Object, fields []float64) bool { + n++ + return true + }, + ) + expect(t, n == 64) + + n = 0 + c.Within(rect, 3, + func(id string, obj geojson.Object, fields []float64) bool { + n++ + return n <= 30 + }, + ) + expect(t, n == 31) + + n = 0 + c.Intersects(rect, 3, + func(id string, _ geojson.Object, _ []float64) bool { + n++ + return true + }, + ) + expect(t, n == 64) + + n = 0 + c.Intersects(rect, 3, + func(id string, _ geojson.Object, _ []float64) bool { + n++ + return n <= 30 + }, + ) + expect(t, n == 31) + +} + +func testCollectionVerifyContents(t *testing.T, c *Collection, objs map[string]geojson.Object) { + for id, o2 := range objs { + o1, _, ok := c.Get(id) + if !ok { + t.Fatalf("ok[%s] = false, expect true", id) + } + j1 := string(o1.AppendJSON(nil)) + j2 := string(o2.AppendJSON(nil)) + if j1 != j2 { + t.Fatalf("j1 == %s, expect %s", j1, j2) + } + } +} + +func TestManyCollections(t *testing.T) { + colsM := make(map[string]*Collection) + cols := 100 + objs := 1000 + k := 0 + for i := 0; i < cols; i++ { + key := strconv.FormatInt(int64(i), 10) + for j := 0; j < objs; j++ { + id := strconv.FormatInt(int64(j), 10) + p := geometry.Point{ + X: rand.Float64()*360 - 180, + Y: rand.Float64()*180 - 90, + } + obj := geojson.Object(PO(p.X, p.Y)) + col, ok := colsM[key] + if !ok { + col = New() + colsM[key] = col + } + col.Set(id, obj, nil, nil) + k++ + } + } + + col := colsM["13"] + //println(col.Count()) + bbox := geometry.Rect{ + Min: geometry.Point{X: -180, Y: 30}, + Max: geometry.Point{X: 34, Y: 100}, + } + col.geoSearch(bbox, func(id string, obj geojson.Object, fields []float64) bool { + //println(id) + return true + }) +} + +type testPointItem struct { + id string + object geojson.Object +} + +func BenchmarkInsert(t *testing.B) { + rand.Seed(time.Now().UnixNano()) + items := make([]testPointItem, t.N) + for i := 0; i < t.N; i++ { + items[i] = testPointItem{ + fmt.Sprintf("%d", i), + PO(rand.Float64()*360-180, rand.Float64()*180-90), + } + } + col := New() + t.ResetTimer() + for i := 0; i < t.N; i++ { + col.Set(items[i].id, items[i].object, nil, nil) + } +} + +func BenchmarkReplace(t *testing.B) { + rand.Seed(time.Now().UnixNano()) + items := make([]testPointItem, t.N) + for i := 0; i < t.N; i++ { + items[i] = testPointItem{ + fmt.Sprintf("%d", i), + PO(rand.Float64()*360-180, rand.Float64()*180-90), + } + } + col := New() + for i := 0; i < t.N; i++ { + col.Set(items[i].id, items[i].object, nil, nil) + } + t.ResetTimer() + for _, i := range rand.Perm(t.N) { + o, _, _ := col.Set(items[i].id, items[i].object, nil, nil) + if o != items[i].object { + t.Fatal("shoot!") + } + } +} + +func BenchmarkGet(t *testing.B) { + rand.Seed(time.Now().UnixNano()) + items := make([]testPointItem, t.N) + for i := 0; i < t.N; i++ { + items[i] = testPointItem{ + fmt.Sprintf("%d", i), + PO(rand.Float64()*360-180, rand.Float64()*180-90), + } + } + col := New() + for i := 0; i < t.N; i++ { + col.Set(items[i].id, items[i].object, nil, nil) + } + t.ResetTimer() + for _, i := range rand.Perm(t.N) { + o, _, _ := col.Get(items[i].id) + if o != items[i].object { + t.Fatal("shoot!") + } + } +} + +func BenchmarkRemove(t *testing.B) { + rand.Seed(time.Now().UnixNano()) + items := make([]testPointItem, t.N) + for i := 0; i < t.N; i++ { + items[i] = testPointItem{ + fmt.Sprintf("%d", i), + PO(rand.Float64()*360-180, rand.Float64()*180-90), + } + } + col := New() + for i := 0; i < t.N; i++ { + col.Set(items[i].id, items[i].object, nil, nil) + } + t.ResetTimer() + for _, i := range rand.Perm(t.N) { + o, _, _ := col.Delete(items[i].id) + if o != items[i].object { + t.Fatal("shoot!") + } + } +} diff --git a/internal/collection/string.go b/internal/collection/string.go new file mode 100644 index 00000000..63e24931 --- /dev/null +++ b/internal/collection/string.go @@ -0,0 +1,79 @@ +package collection + +import ( + "encoding/json" + + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" +) + +// String ... +type String string + +var _ geojson.Object = String("") + +// Spatial ... +func (s String) Spatial() geojson.Spatial { + return geojson.EmptySpatial{} +} + +// ForEach ... +func (s String) ForEach(iter func(geom geojson.Object) bool) bool { + return iter(s) +} + +// Empty ... +func (s String) Empty() bool { + return true +} + +// Rect ... +func (s String) Rect() geometry.Rect { + return geometry.Rect{} +} + +// Center ... +func (s String) Center() geometry.Point { + return geometry.Point{} +} + +// AppendJSON ... +func (s String) AppendJSON(dst []byte) []byte { + data, _ := json.Marshal(string(s)) + return append(dst, data...) +} + +// String ... +func (s String) String() string { + return string(s) +} + +// JSON ... +func (s String) JSON() string { + return string(s.AppendJSON(nil)) +} + +// Within ... +func (s String) Within(obj geojson.Object) bool { + return false +} + +// Contains ... +func (s String) Contains(obj geojson.Object) bool { + return false +} + +// Intersects ... +func (s String) Intersects(obj geojson.Object) bool { + return false +} + +// NumPoints ... +func (s String) NumPoints() int { + return 0 +} + +// Distance ... +func (s String) Distance(obj geojson.Object) float64 { + return 0 +} diff --git a/pkg/controller/aof.go b/internal/controller/aof.go similarity index 99% rename from pkg/controller/aof.go rename to internal/controller/aof.go index a54ff443..fa248e74 100644 --- a/pkg/controller/aof.go +++ b/internal/controller/aof.go @@ -14,8 +14,8 @@ import ( "github.com/tidwall/buntdb" "github.com/tidwall/redcon" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) // AsyncHooks indicates that the hooks should happen in the background. diff --git a/pkg/controller/aofmigrate.go b/internal/controller/aofmigrate.go similarity index 98% rename from pkg/controller/aofmigrate.go rename to internal/controller/aofmigrate.go index 31fe3d98..2171df23 100644 --- a/pkg/controller/aofmigrate.go +++ b/internal/controller/aofmigrate.go @@ -10,7 +10,7 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/log" + "github.com/tidwall/tile38/internal/log" ) var errCorruptedAOF = errors.New("corrupted aof file") diff --git a/pkg/controller/aofshrink.go b/internal/controller/aofshrink.go similarity index 91% rename from pkg/controller/aofshrink.go rename to internal/controller/aofshrink.go index cd661c60..e7a3eff6 100644 --- a/pkg/controller/aofshrink.go +++ b/internal/controller/aofshrink.go @@ -8,10 +8,10 @@ import ( "strings" "time" - "github.com/tidwall/tile38/pkg/collection" - "github.com/tidwall/tile38/pkg/core" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/log" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/collection" + "github.com/tidwall/geojson" + "github.com/tidwall/tile38/internal/log" ) const maxkeys = 8 @@ -127,19 +127,12 @@ func (c *Controller) aofshrink() { } } } - switch obj := obj.(type) { - default: - if obj.IsGeometry() { - values = append(values, "object") - values = append(values, obj.JSON()) - } else { - values = append(values, "string") - values = append(values, obj.String()) - } - case geojson.SimplePoint: - values = append(values, "point") - values = append(values, strconv.FormatFloat(obj.Y, 'f', -1, 64)) - values = append(values, strconv.FormatFloat(obj.X, 'f', -1, 64)) + if objIsSpatial(obj) { + values = append(values, "object") + values = append(values, string(obj.AppendJSON(nil))) + } else { + values = append(values, "string") + values = append(values, obj.String()) } // append the values to the aof buffer diff --git a/pkg/controller/atomic.go b/internal/controller/atomic.go similarity index 100% rename from pkg/controller/atomic.go rename to internal/controller/atomic.go diff --git a/pkg/controller/atomic_test.go b/internal/controller/atomic_test.go similarity index 100% rename from pkg/controller/atomic_test.go rename to internal/controller/atomic_test.go diff --git a/pkg/controller/bson.go b/internal/controller/bson.go similarity index 100% rename from pkg/controller/bson.go rename to internal/controller/bson.go diff --git a/pkg/controller/checksum.go b/internal/controller/checksum.go similarity index 98% rename from pkg/controller/checksum.go rename to internal/controller/checksum.go index ea4217b8..246c7b3c 100644 --- a/pkg/controller/checksum.go +++ b/internal/controller/checksum.go @@ -9,8 +9,8 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/core" - "github.com/tidwall/tile38/pkg/log" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/log" ) // checksum performs a simple md5 checksum on the aof file diff --git a/pkg/controller/client.go b/internal/controller/client.go similarity index 99% rename from pkg/controller/client.go rename to internal/controller/client.go index bf373609..ef7806d1 100644 --- a/pkg/controller/client.go +++ b/internal/controller/client.go @@ -9,7 +9,7 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/server" ) // Conn represents a simple resp connection. diff --git a/pkg/controller/config.go b/internal/controller/config.go similarity index 99% rename from pkg/controller/config.go rename to internal/controller/config.go index 17c97f99..2ee82cfa 100644 --- a/pkg/controller/config.go +++ b/internal/controller/config.go @@ -12,8 +12,8 @@ import ( "github.com/tidwall/gjson" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/server" ) const ( @@ -21,6 +21,7 @@ const ( defaultProtectedMode = "yes" ) +// Config keys const ( FollowHost = "follow_host" FollowPort = "follow_port" diff --git a/pkg/controller/controller.go b/internal/controller/controller.go similarity index 98% rename from pkg/controller/controller.go rename to internal/controller/controller.go index 504fe186..ae70971f 100644 --- a/pkg/controller/controller.go +++ b/internal/controller/controller.go @@ -18,14 +18,14 @@ import ( "github.com/tidwall/btree" "github.com/tidwall/buntdb" + "github.com/tidwall/geojson" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/collection" - "github.com/tidwall/tile38/pkg/core" - "github.com/tidwall/tile38/pkg/endpoint" - "github.com/tidwall/tile38/pkg/expire" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/collection" + "github.com/tidwall/tile38/internal/endpoint" + "github.com/tidwall/tile38/internal/expire" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) var errOOM = errors.New("OOM command not allowed when used memory > 'maxmemory'") @@ -121,6 +121,8 @@ type Controller struct { func ListenAndServe(host string, port int, dir string, http bool) error { return ListenAndServeEx(host, port, dir, nil, http) } + +// ListenAndServeEx ... func ListenAndServeEx(host string, port int, dir string, ln *net.Listener, http bool) error { if core.AppendFileName == "" { core.AppendFileName = path.Join(dir, "appendonly.aof") @@ -155,8 +157,8 @@ func ListenAndServeEx(host string, port int, dir string, ln *net.Listener, http } } c.epc = endpoint.NewManager(c) - c.luascripts = c.NewScriptMap() - c.luapool = c.NewPool() + c.luascripts = c.newScriptMap() + c.luapool = c.newPool() defer c.luapool.Shutdown() if err := os.MkdirAll(dir, 0700); err != nil { diff --git a/pkg/controller/crud.go b/internal/controller/crud.go similarity index 92% rename from pkg/controller/crud.go rename to internal/controller/crud.go index a77a1478..65611bf1 100644 --- a/pkg/controller/crud.go +++ b/internal/controller/crud.go @@ -7,13 +7,14 @@ import ( "strings" "time" + "github.com/mmcloughlin/geohash" "github.com/tidwall/btree" + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/collection" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/geojson/geohash" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/collection" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/server" ) type fvt struct { @@ -76,10 +77,13 @@ func (c *Controller) cmdBounds(msg *server.Message) (resp.Value, error) { } minX, minY, maxX, maxY := col.Bounds() - bbox := geojson.New2DBBox(minX, minY, maxX, maxY) + bbox := geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: minX, Y: minY}, + Max: geometry.Point{X: maxX, Y: maxY}, + }) if msg.OutputType == server.JSON { buf.WriteString(`,"bounds":`) - buf.WriteString(bbox.ExternalJSON()) + buf.WriteString(string(bbox.AppendJSON(nil))) } else { vals = append(vals, resp.ArrayValue([]resp.Value{ resp.ArrayValue([]resp.Value{ @@ -182,21 +186,25 @@ func (c *Controller) cmdGet(msg *server.Message) (resp.Value, error) { case "object": if msg.OutputType == server.JSON { buf.WriteString(`,"object":`) - buf.WriteString(o.JSON()) + buf.WriteString(string(o.AppendJSON(nil))) } else { vals = append(vals, resp.StringValue(o.String())) } case "point": - point := o.CalculatedPoint() if msg.OutputType == server.JSON { buf.WriteString(`,"point":`) - buf.WriteString(point.ExternalJSON()) + buf.Write(appendJSONSimplePoint(nil, o)) } else { - if point.Z != 0 { + point := o.Center() + var z float64 + if gPoint, ok := o.(*geojson.Point); ok { + z = gPoint.Z() + } + if z != 0 { vals = append(vals, resp.ArrayValue([]resp.Value{ resp.StringValue(strconv.FormatFloat(point.Y, 'f', -1, 64)), resp.StringValue(strconv.FormatFloat(point.X, 'f', -1, 64)), - resp.StringValue(strconv.FormatFloat(point.Z, 'f', -1, 64)), + resp.StringValue(strconv.FormatFloat(z, 'f', -1, 64)), })) } else { vals = append(vals, resp.ArrayValue([]resp.Value{ @@ -216,21 +224,19 @@ func (c *Controller) cmdGet(msg *server.Message) (resp.Value, error) { if err != nil || precision < 1 || precision > 64 { return server.NOMessage, errInvalidArgument(sprecision) } - p, err := o.Geohash(int(precision)) - if err != nil { - return server.NOMessage, err - } + center := o.Center() + p := geohash.EncodeWithPrecision(center.Y, center.X, uint(precision)) if msg.OutputType == server.JSON { buf.WriteString(`"` + p + `"`) } else { vals = append(vals, resp.StringValue(p)) } case "bounds": - bbox := o.CalculatedBBox() if msg.OutputType == server.JSON { buf.WriteString(`,"bounds":`) - buf.WriteString(bbox.ExternalJSON()) + buf.Write(appendJSONSimpleBounds(nil, o)) } else { + bbox := o.Rect() vals = append(vals, resp.ArrayValue([]resp.Value{ resp.ArrayValue([]resp.Value{ resp.FloatValue(bbox.Min.Y), @@ -582,7 +588,7 @@ func (c *Controller) parseSetArgs(vs []resp.Value) ( err = errInvalidNumberOfArguments return } - d.obj = geojson.String(str) + d.obj = collection.String(str) case lcb(typ, "point"): var slat, slon, sz string if vs, slat, ok = tokenval(vs); !ok || slat == "" { @@ -595,36 +601,36 @@ func (c *Controller) parseSetArgs(vs []resp.Value) ( } vs, sz, ok = tokenval(vs) if !ok || sz == "" { - var sp geojson.SimplePoint - sp.Y, err = strconv.ParseFloat(slat, 64) + var x, y float64 + y, err = strconv.ParseFloat(slat, 64) if err != nil { err = errInvalidArgument(slat) return } - sp.X, err = strconv.ParseFloat(slon, 64) + x, err = strconv.ParseFloat(slon, 64) if err != nil { err = errInvalidArgument(slon) return } - d.obj = sp + d.obj = geojson.NewPoint(geometry.Point{X: x, Y: y}) } else { - var sp geojson.Point - sp.Coordinates.Y, err = strconv.ParseFloat(slat, 64) + var x, y, z float64 + y, err = strconv.ParseFloat(slat, 64) if err != nil { err = errInvalidArgument(slat) return } - sp.Coordinates.X, err = strconv.ParseFloat(slon, 64) + x, err = strconv.ParseFloat(slon, 64) if err != nil { err = errInvalidArgument(slon) return } - sp.Coordinates.Z, err = strconv.ParseFloat(sz, 64) + z, err = strconv.ParseFloat(sz, 64) if err != nil { err = errInvalidArgument(sz) return } - d.obj = sp + d.obj = geojson.NewPointZ(geometry.Point{X: x, Y: y}, z) } case lcb(typ, "bounds"): var sminlat, sminlon, smaxlat, smaxlon string @@ -665,40 +671,25 @@ func (c *Controller) parseSetArgs(vs []resp.Value) ( err = errInvalidArgument(smaxlon) return } - g := geojson.Polygon{ - Coordinates: [][]geojson.Position{ - { - {X: minlon, Y: minlat, Z: 0}, - {X: minlon, Y: maxlat, Z: 0}, - {X: maxlon, Y: maxlat, Z: 0}, - {X: maxlon, Y: minlat, Z: 0}, - {X: minlon, Y: minlat, Z: 0}, - }, - }, - } - d.obj = g + d.obj = geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: minlon, Y: minlat}, + Max: geometry.Point{X: maxlon, Y: maxlat}, + }) case lcb(typ, "hash"): - var sp geojson.SimplePoint var shash string if vs, shash, ok = tokenval(vs); !ok || shash == "" { err = errInvalidNumberOfArguments return } - var lat, lon float64 - lat, lon, err = geohash.Decode(shash) - if err != nil { - return - } - sp.X = lon - sp.Y = lat - d.obj = sp + lat, lon := geohash.Decode(shash) + d.obj = geojson.NewPoint(geometry.Point{X: lon, Y: lat}) case lcb(typ, "object"): var object string if vs, object, ok = tokenval(vs); !ok || object == "" { err = errInvalidNumberOfArguments return } - d.obj, err = geojson.ObjectJSON(object) + d.obj, err = geojson.Parse(object, nil) if err != nil { return } @@ -832,7 +823,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta var fields []string var values []float64 var xx bool - var updated_count int + var updateCount int d, fields, values, xx, err = c.parseFSetArgs(vs) col := c.getCol(d.key) @@ -841,7 +832,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta return } var ok bool - d.obj, d.fields, updated_count, ok = col.SetFields(d.id, fields, values) + d.obj, d.fields, updateCount, ok = col.SetFields(d.id, fields, values) if !(ok || xx) { err = errIDNotFound return @@ -849,7 +840,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta if ok { d.command = "fset" d.timestamp = time.Now() - d.updated = updated_count > 0 + d.updated = updateCount > 0 fmap := col.FieldMap() d.fmap = make(map[string]int) for key, idx := range fmap { @@ -861,7 +852,7 @@ func (c *Controller) cmdFset(msg *server.Message) (res resp.Value, d commandDeta case server.JSON: res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Now().Sub(start).String() + "\"}") case server.RESP: - res = resp.IntegerValue(updated_count) + res = resp.IntegerValue(updateCount) } return } diff --git a/pkg/controller/dev.go b/internal/controller/dev.go similarity index 97% rename from pkg/controller/dev.go rename to internal/controller/dev.go index 498df6af..008388bf 100644 --- a/pkg/controller/dev.go +++ b/internal/controller/dev.go @@ -10,8 +10,8 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) // MASSINSERT num_keys num_points [minx miny maxx maxy] diff --git a/pkg/controller/expire.go b/internal/controller/expire.go similarity index 98% rename from pkg/controller/expire.go rename to internal/controller/expire.go index ec569719..8d27e1a8 100644 --- a/pkg/controller/expire.go +++ b/internal/controller/expire.go @@ -7,7 +7,7 @@ import ( "github.com/tidwall/btree" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/server" ) type exitem struct { diff --git a/pkg/controller/fence.go b/internal/controller/fence.go similarity index 78% rename from pkg/controller/fence.go rename to internal/controller/fence.go index f671b6ab..b4d3aa44 100644 --- a/pkg/controller/fence.go +++ b/internal/controller/fence.go @@ -5,10 +5,11 @@ import ( "strconv" "time" + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" "github.com/tidwall/gjson" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/server" ) // FenceMatch executes a fence match returns back json messages for fence detection. @@ -44,6 +45,12 @@ func appendHookDetails(b []byte, hookName string, metas []FenceMeta) []byte { } return b } + +func objIsSpatial(obj geojson.Object) bool { + _, ok := obj.(geojson.Spatial) + return ok +} + func hookJSONString(hookName string, metas []FenceMeta) string { return string(appendHookDetails(nil, hookName, metas)) } @@ -63,7 +70,7 @@ func fenceMatch( return nil } } - if details.obj == nil || !details.obj.IsGeometry() { + if details.obj == nil || !objIsSpatial(details.obj) { return nil } if details.command == "fset" { @@ -120,12 +127,11 @@ func fenceMatch( // Maybe the old object and new object create a line that crosses the fence. // Must detect for that possibility. if details.oldObj != nil { - ls := geojson.LineString{ - Coordinates: []geojson.Position{ - details.oldObj.CalculatedPoint(), - details.obj.CalculatedPoint(), - }, - } + ls := geojson.NewLineString(geometry.NewLine( + []geometry.Point{ + details.oldObj.Center(), + details.obj.Center(), + }, geometry.DefaultIndex)) temp := false if fence.cmd == "within" { // because we are testing if the line croses the area we need to use @@ -165,7 +171,7 @@ func fenceMatch( sw.mu.Lock() var distance float64 if fence.distance { - distance = details.obj.CalculatedPoint().DistanceTo(geojson.Position{X: fence.lon, Y: fence.lat, Z: 0}) + distance = details.obj.Distance(fence.obj) } sw.fmap = details.fmap sw.fullFields = true @@ -260,7 +266,7 @@ func extendRoamMessage( nmsg = append(nmsg, `,"id":`...) nmsg = appendJSONString(nmsg, match.id) nmsg = append(nmsg, `,"object":`...) - nmsg = append(nmsg, match.obj.JSON()...) + nmsg = match.obj.AppendJSON(nmsg) nmsg = append(nmsg, `,"meters":`...) nmsg = strconv.AppendFloat(nmsg, math.Floor(match.meters*1000)/1000, 'f', -1, 64) @@ -270,9 +276,11 @@ func extendRoamMessage( if col != nil { obj, _, ok := col.Get(match.id) if ok { - nmsg = append(nmsg, - `{"id":`+jsonString(match.id)+ - `,"self":true,"object":`+obj.JSON()+`}`...) + nmsg = append(nmsg, `{"id":`...) + nmsg = appendJSONString(nmsg, match.id) + nmsg = append(nmsg, `,"self":true,"object":`...) + nmsg = obj.AppendJSON(nmsg) + nmsg = append(nmsg, '}') } pattern := match.id + fence.roam.scan iterator := func( @@ -282,9 +290,11 @@ func extendRoamMessage( return true } if matched, _ := glob.Match(pattern, oid); matched { - nmsg = append(nmsg, - `,{"id":`+jsonString(oid)+ - `,"object":`+o.JSON()+`}`...) + nmsg = append(nmsg, `,{"id":`...) + nmsg = appendJSONString(nmsg, oid) + nmsg = append(nmsg, `,"object":`...) + nmsg = o.AppendJSON(nmsg) + nmsg = append(nmsg, '}') } return true } @@ -323,34 +333,22 @@ func makemsg( } func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool { - if obj == nil { + gobj, _ := obj.(geojson.Object) + if gobj == nil { return false } if fence.roam.on { // we need to check this object against return false } - - if fence.cmd == "nearby" { - return obj.Nearby(geojson.Position{X: fence.lon, Y: fence.lat, Z: 0}, fence.meters) - } - if fence.cmd == "within" { - if fence.o != nil { - return obj.Within(fence.o) - } - return obj.WithinBBox(geojson.BBox{ - Min: geojson.Position{X: fence.minLon, Y: fence.minLat, Z: 0}, - Max: geojson.Position{X: fence.maxLon, Y: fence.maxLat, Z: 0}, - }) - } - if fence.cmd == "intersects" { - if fence.o != nil { - return obj.Intersects(fence.o) - } - return obj.IntersectsBBox(geojson.BBox{ - Min: geojson.Position{X: fence.minLon, Y: fence.minLat, Z: 0}, - Max: geojson.Position{X: fence.maxLon, Y: fence.maxLat, Z: 0}, - }) + switch fence.cmd { + case "nearby": + // nearby is an INTERSECT on a Circle + return gobj.Intersects(fence.obj) + case "within": + return gobj.Within(fence.obj) + case "intersects": + return gobj.Intersects(fence.obj) } return false } @@ -359,57 +357,58 @@ func fenceMatchRoam( c *Controller, fence *liveFenceSwitches, tkey, tid string, obj geojson.Object, ) (nearbys, faraways []roamMatch) { + col := c.getCol(fence.roam.key) if col == nil { return } - p := obj.CalculatedPoint() prevNearbys := fence.roam.nearbys[tid] var newNearbys map[string]bool - col.Nearby(0, p.Y, p.X, fence.roam.meters, math.Inf(-1), math.Inf(+1), - func(id string, obj geojson.Object, fields []float64) bool { - if c.hasExpired(fence.roam.key, id) { - return true - } - var idMatch bool - if id == tid { - return true // skip self - } - if fence.roam.pattern { - idMatch, _ = glob.Match(fence.roam.id, id) - } else { - idMatch = fence.roam.id == id - } - if !idMatch { - return true - } - if newNearbys == nil { - newNearbys = make(map[string]bool) - } - newNearbys[id] = true - prev := prevNearbys[id] - if prev { - delete(prevNearbys, id) - } - match := roamMatch{ - id: id, - obj: obj, - meters: obj.CalculatedPoint().DistanceTo(p), - } - if !prev || !fence.nodwell { - // brand new "nearby" - nearbys = append(nearbys, match) - } + col.Intersects(obj, 0, func( + id string, obj2 geojson.Object, fields []float64, + ) bool { + if c.hasExpired(fence.roam.key, id) { return true - }, - ) + } + var idMatch bool + if id == tid { + return true // skip self + } + if fence.roam.pattern { + idMatch, _ = glob.Match(fence.roam.id, id) + } else { + idMatch = fence.roam.id == id + } + if !idMatch { + return true + } + if newNearbys == nil { + newNearbys = make(map[string]bool) + } + newNearbys[id] = true + prev := prevNearbys[id] + if prev { + delete(prevNearbys, id) + } + match := roamMatch{ + id: id, + obj: obj2, + meters: obj.Distance(obj2), + } + if !prev || !fence.nodwell { + // brand new "nearby" + nearbys = append(nearbys, match) + } + return true + }) for id := range prevNearbys { - obj, _, ok := col.Get(id) + obj2, _, ok := col.Get(id) if ok && !c.hasExpired(fence.roam.key, id) { faraways = append(faraways, roamMatch{ - id: id, obj: obj, - meters: obj.CalculatedPoint().DistanceTo(p), + id: id, + obj: obj2, + meters: obj.Distance(obj2), }) } } diff --git a/pkg/controller/follow.go b/internal/controller/follow.go similarity index 97% rename from pkg/controller/follow.go rename to internal/controller/follow.go index b69f980f..873b9bbf 100644 --- a/pkg/controller/follow.go +++ b/internal/controller/follow.go @@ -10,9 +10,9 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/core" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) var errNoLongerFollowing = errors.New("no longer following") diff --git a/pkg/controller/hooks.go b/internal/controller/hooks.go similarity index 98% rename from pkg/controller/hooks.go rename to internal/controller/hooks.go index 94e671fa..5ae165f0 100644 --- a/pkg/controller/hooks.go +++ b/internal/controller/hooks.go @@ -11,10 +11,10 @@ import ( "github.com/tidwall/buntdb" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/endpoint" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/endpoint" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) var hookLogSetDefaults = &buntdb.SetOptions{ diff --git a/pkg/controller/json.go b/internal/controller/json.go similarity index 83% rename from pkg/controller/json.go rename to internal/controller/json.go index abf31537..93234263 100644 --- a/pkg/controller/json.go +++ b/internal/controller/json.go @@ -7,12 +7,12 @@ import ( "strings" "time" + "github.com/tidwall/geojson" "github.com/tidwall/gjson" "github.com/tidwall/resp" "github.com/tidwall/sjson" - "github.com/tidwall/tile38/pkg/collection" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/collection" + "github.com/tidwall/tile38/internal/server" ) func appendJSONString(b []byte, s string) []byte { @@ -41,6 +41,37 @@ func jsonString(s string) string { b[len(b)-1] = '"' return string(b) } +func appendJSONSimpleBounds(dst []byte, o geojson.Object) []byte { + bbox := o.Rect() + dst = append(dst, `{"sw":{"lat":`...) + dst = strconv.AppendFloat(dst, bbox.Min.Y, 'f', -1, 64) + dst = append(dst, `,"lon":`...) + dst = strconv.AppendFloat(dst, bbox.Min.X, 'f', -1, 64) + dst = append(dst, `},"ne":{"lat":`...) + dst = strconv.AppendFloat(dst, bbox.Max.Y, 'f', -1, 64) + dst = append(dst, `,"lon":`...) + dst = strconv.AppendFloat(dst, bbox.Max.X, 'f', -1, 64) + dst = append(dst, `}}`...) + return dst +} + +func appendJSONSimplePoint(dst []byte, o geojson.Object) []byte { + point := o.Center() + var z float64 + if gPoint, ok := o.(*geojson.Point); ok { + z = gPoint.Z() + } + dst = append(dst, `{"lat":`...) + dst = strconv.AppendFloat(dst, point.Y, 'f', -1, 64) + dst = append(dst, `,"lon":`...) + dst = strconv.AppendFloat(dst, point.X, 'f', -1, 64) + if z != 0 { + dst = append(dst, `,"z":`...) + dst = strconv.AppendFloat(dst, z, 'f', -1, 64) + } + dst = append(dst, '}') + return dst +} func appendJSONTimeFormat(b []byte, t time.Time) []byte { b = append(b, '"') @@ -174,9 +205,7 @@ func (c *Controller) cmdJset(msg *server.Message) (res resp.Value, d commandDeta var geoobj bool o, _, ok := col.Get(id) if ok { - if _, ok := o.(geojson.String); !ok { - geoobj = true - } + geoobj = objIsSpatial(o) json = o.String() } if raw { @@ -208,7 +237,7 @@ func (c *Controller) cmdJset(msg *server.Message) (res resp.Value, d commandDeta d.key = key d.id = id - d.obj = geojson.String(json) + d.obj = collection.String(json) d.timestamp = time.Now() d.updated = true @@ -248,9 +277,7 @@ func (c *Controller) cmdJdel(msg *server.Message) (res resp.Value, d commandDeta var geoobj bool o, _, ok := col.Get(id) if ok { - if _, ok := o.(geojson.String); !ok { - geoobj = true - } + geoobj = objIsSpatial(o) json = o.String() } njson, err := sjson.Delete(json, path) @@ -282,7 +309,7 @@ func (c *Controller) cmdJdel(msg *server.Message) (res resp.Value, d commandDeta d.key = key d.id = id - d.obj = geojson.String(json) + d.obj = collection.String(json) d.timestamp = time.Now() d.updated = true diff --git a/pkg/controller/json_test.go b/internal/controller/json_test.go similarity index 100% rename from pkg/controller/json_test.go rename to internal/controller/json_test.go diff --git a/pkg/controller/keys.go b/internal/controller/keys.go similarity index 95% rename from pkg/controller/keys.go rename to internal/controller/keys.go index 98ad0376..3e835c5a 100644 --- a/pkg/controller/keys.go +++ b/internal/controller/keys.go @@ -7,8 +7,8 @@ import ( "github.com/tidwall/btree" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/server" ) func (c *Controller) cmdKeys(msg *server.Message) (res resp.Value, err error) { diff --git a/pkg/controller/live.go b/internal/controller/live.go similarity index 97% rename from pkg/controller/live.go rename to internal/controller/live.go index 2843b093..8d678372 100644 --- a/pkg/controller/live.go +++ b/internal/controller/live.go @@ -8,8 +8,8 @@ import ( "net" "sync" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) type liveBuffer struct { diff --git a/pkg/controller/output.go b/internal/controller/output.go similarity index 95% rename from pkg/controller/output.go rename to internal/controller/output.go index 9a6cc5b6..a1b73bd4 100644 --- a/pkg/controller/output.go +++ b/internal/controller/output.go @@ -5,7 +5,7 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/server" ) func (c *Controller) cmdOutput(msg *server.Message) (res resp.Value, err error) { diff --git a/pkg/controller/pubsub.go b/internal/controller/pubsub.go similarity index 98% rename from pkg/controller/pubsub.go rename to internal/controller/pubsub.go index 2199b650..6badd3fd 100644 --- a/pkg/controller/pubsub.go +++ b/internal/controller/pubsub.go @@ -11,8 +11,8 @@ import ( "github.com/tidwall/match" "github.com/tidwall/redcon" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) const ( diff --git a/pkg/controller/readonly.go b/internal/controller/readonly.go similarity index 90% rename from pkg/controller/readonly.go rename to internal/controller/readonly.go index fee36f20..6a9a758f 100644 --- a/pkg/controller/readonly.go +++ b/internal/controller/readonly.go @@ -5,8 +5,8 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/log" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/log" + "github.com/tidwall/tile38/internal/server" ) func (c *Controller) cmdReadOnly(msg *server.Message) (res resp.Value, err error) { diff --git a/pkg/controller/scan.go b/internal/controller/scan.go similarity index 94% rename from pkg/controller/scan.go rename to internal/controller/scan.go index 6293e03e..df59f432 100644 --- a/pkg/controller/scan.go +++ b/internal/controller/scan.go @@ -6,9 +6,9 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/geojson" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/server" ) func (c *Controller) cmdScanArgs(vs []resp.Value) ( diff --git a/pkg/controller/scanner.go b/internal/controller/scanner.go similarity index 89% rename from pkg/controller/scanner.go rename to internal/controller/scanner.go index 444cac24..d4e0f99c 100644 --- a/pkg/controller/scanner.go +++ b/internal/controller/scanner.go @@ -7,11 +7,13 @@ import ( "strconv" "sync" + "github.com/mmcloughlin/geohash" + "github.com/tidwall/geojson" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/collection" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/clip" + "github.com/tidwall/tile38/internal/collection" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/server" ) const limitItems = 100 @@ -58,6 +60,7 @@ type scanWriter struct { respOut resp.Value } +// ScanWriterParams ... type ScanWriterParams struct { id string o geojson.Object @@ -65,8 +68,7 @@ type ScanWriterParams struct { distance float64 noLock bool ignoreGlobMatch bool - clip bool - clipbox geojson.BBox + clip geojson.Object } func (c *Controller) newScanWriter( @@ -199,7 +201,9 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl for _, where := range sw.wheres { if where.field == "z" { if !gotz { - z = o.CalculatedPoint().Z + if point, ok := o.(*geojson.Point); ok { + z = point.Z() + } } if !where.match(z) { return @@ -253,7 +257,9 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl for _, where := range sw.wheres { if where.field == "z" { if !gotz { - z = o.CalculatedPoint().Z + if point, ok := o.(*geojson.Point); ok { + z = point.Z() + } } if !where.match(z) { return @@ -344,8 +350,8 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool { if sw.output == outputCount { return sw.count < sw.limit } - if opts.clip { - opts.o = opts.o.Clipped(opts.clipbox) + if opts.clip != nil { + opts.o = clip.Clip(opts.o, opts.clip) } switch sw.msg.OutputType { case server.JSON: @@ -392,23 +398,21 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool { wr.WriteString(`{"id":` + jsonString(opts.id)) switch sw.output { case outputObjects: - wr.WriteString(`,"object":` + opts.o.JSON()) + wr.WriteString(`,"object":` + string(opts.o.AppendJSON(nil))) case outputPoints: - wr.WriteString(`,"point":` + opts.o.CalculatedPoint().ExternalJSON()) + wr.WriteString(`,"point":` + string(appendJSONSimplePoint(nil, opts.o))) case outputHashes: - p, err := opts.o.Geohash(int(sw.precision)) - if err != nil { - p = "" - } + center := opts.o.Center() + p := geohash.EncodeWithPrecision(center.Y, center.X, uint(sw.precision)) wr.WriteString(`,"hash":"` + p + `"`) case outputBounds: - wr.WriteString(`,"bounds":` + opts.o.CalculatedBBox().ExternalJSON()) + wr.WriteString(`,"bounds":` + string(appendJSONSimpleBounds(nil, opts.o))) } wr.WriteString(jsfields) if opts.distance > 0 { - wr.WriteString(`,"distance":` + strconv.FormatFloat(opts.distance, 'f', 2, 64)) + wr.WriteString(`,"distance":` + strconv.FormatFloat(opts.distance, 'f', -1, 64)) } wr.WriteString(`}`) @@ -424,12 +428,16 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool { case outputObjects: vals = append(vals, resp.StringValue(opts.o.String())) case outputPoints: - point := opts.o.CalculatedPoint() - if point.Z != 0 { + point := opts.o.Center() + var z float64 + if point, ok := opts.o.(*geojson.Point); ok { + z = point.Z() + } + if z != 0 { vals = append(vals, resp.ArrayValue([]resp.Value{ resp.FloatValue(point.Y), resp.FloatValue(point.X), - resp.FloatValue(point.Z), + resp.FloatValue(z), })) } else { vals = append(vals, resp.ArrayValue([]resp.Value{ @@ -438,13 +446,11 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool { })) } case outputHashes: - p, err := opts.o.Geohash(int(sw.precision)) - if err != nil { - p = "" - } + center := opts.o.Center() + p := geohash.EncodeWithPrecision(center.Y, center.X, uint(sw.precision)) vals = append(vals, resp.StringValue(p)) case outputBounds: - bbox := opts.o.CalculatedBBox() + bbox := opts.o.Rect() vals = append(vals, resp.ArrayValue([]resp.Value{ resp.ArrayValue([]resp.Value{ resp.FloatValue(bbox.Min.Y), diff --git a/pkg/controller/scripts.go b/internal/controller/scripts.go similarity index 99% rename from pkg/controller/scripts.go rename to internal/controller/scripts.go index 53426736..e975ca77 100644 --- a/pkg/controller/scripts.go +++ b/internal/controller/scripts.go @@ -14,7 +14,7 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/server" "github.com/yuin/gopher-lua" luajson "layeh.com/gopher-json" ) @@ -39,8 +39,8 @@ type lStatePool struct { total int } -// NewPool returns a new pool of lua states -func (c *Controller) NewPool() *lStatePool { +// newPool returns a new pool of lua states +func (c *Controller) newPool() *lStatePool { pl := &lStatePool{ saved: make([]*lua.LState, iniLuaPoolSize), c: c, @@ -206,7 +206,7 @@ func (sm *lScriptMap) Flush() { } // NewScriptMap returns a new map with lua scripts -func (c *Controller) NewScriptMap() *lScriptMap { +func (c *Controller) newScriptMap() *lScriptMap { return &lScriptMap{ scripts: make(map[string]*lua.FunctionProto), } diff --git a/pkg/controller/search.go b/internal/controller/search.go similarity index 74% rename from pkg/controller/search.go rename to internal/controller/search.go index 63c2aca7..09016136 100644 --- a/pkg/controller/search.go +++ b/internal/controller/search.go @@ -8,24 +8,24 @@ import ( "strings" "time" + "github.com/mmcloughlin/geohash" + "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geometry" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/bing" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/geojson/geohash" - "github.com/tidwall/tile38/pkg/glob" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/internal/bing" + "github.com/tidwall/tile38/internal/glob" + "github.com/tidwall/tile38/internal/server" ) +const defaultCircleSteps = 64 + type liveFenceSwitches struct { searchScanBaseTokens - lat, lon, meters float64 - o geojson.Object - minLat, minLon float64 - maxLat, maxLon float64 - cmd string - roam roamSwitches - knn bool - groups map[string]string + obj geojson.Object + cmd string + roam roamSwitches + knn bool + groups map[string]string } type roamSwitches struct { @@ -102,9 +102,14 @@ func (c *Controller) cmdSearchArgs( err = errInvalidArgument(typ) return } - s.meters = -1 // this will become non-negative if search is within a circle switch ltyp { case "point": + fallthrough + case "circle": + if s.clip { + err = errInvalidArgument("cannnot clip with " + ltyp) + return + } var slat, slon, smeters string if vs, slat, ok = tokenval(vs); !ok || slat == "" { err = errInvalidNumberOfArguments @@ -114,11 +119,6 @@ func (c *Controller) cmdSearchArgs( err = errInvalidNumberOfArguments return } - - if s.clip { - err = errInvalidArgument("cannnot clip with point") - } - umeters := true if vs, smeters, ok = tokenval(vs); !ok || smeters == "" { umeters = false @@ -132,72 +132,42 @@ func (c *Controller) cmdSearchArgs( return } } - - if s.lat, err = strconv.ParseFloat(slat, 64); err != nil { + var lat, lon, meters float64 + if lat, err = strconv.ParseFloat(slat, 64); err != nil { err = errInvalidArgument(slat) return } - if s.lon, err = strconv.ParseFloat(slon, 64); err != nil { + if lon, err = strconv.ParseFloat(slon, 64); err != nil { err = errInvalidArgument(slon) return } - if umeters { - if s.meters, err = strconv.ParseFloat(smeters, 64); err != nil { + if meters, err = strconv.ParseFloat(smeters, 64); err != nil { err = errInvalidArgument(smeters) return } - if s.meters < 0 { + if meters < 0 { err = errInvalidArgument(smeters) return } } - case "circle": - if s.clip { - err = errInvalidArgument("cannnot clip with circle") - } - - var slat, slon, smeters string - if vs, slat, ok = tokenval(vs); !ok || slat == "" { - err = errInvalidNumberOfArguments - return - } - if vs, slon, ok = tokenval(vs); !ok || slon == "" { - err = errInvalidNumberOfArguments - return - } - if vs, smeters, ok = tokenval(vs); !ok || smeters == "" { - err = errInvalidArgument(slat) - return - } - - if s.lat, err = strconv.ParseFloat(slat, 64); err != nil { - err = errInvalidArgument(slat) - return - } - if s.lon, err = strconv.ParseFloat(slon, 64); err != nil { - err = errInvalidArgument(slon) - return - } - if s.meters, err = strconv.ParseFloat(smeters, 64); err != nil { - err = errInvalidArgument(smeters) - return - } - if s.meters < 0 { - err = errInvalidArgument(smeters) - return + if s.knn { + s.obj = geojson.NewPoint(geometry.Point{X: lon, Y: lat}) + } else { + s.obj = geojson.NewCircle(geometry.Point{X: lon, Y: lat}, + meters, defaultCircleSteps) } case "object": if s.clip { err = errInvalidArgument("cannnot clip with object") + return } - var obj string if vs, obj, ok = tokenval(vs); !ok || obj == "" { err = errInvalidNumberOfArguments return } - s.o, err = geojson.ObjectJSON(obj) + s.obj, err = geojson.Parse(obj, nil) if err != nil { return } @@ -219,42 +189,54 @@ func (c *Controller) cmdSearchArgs( err = errInvalidNumberOfArguments return } - if s.minLat, err = strconv.ParseFloat(sminLat, 64); err != nil { + var minLat, minLon, maxLat, maxLon float64 + if minLat, err = strconv.ParseFloat(sminLat, 64); err != nil { err = errInvalidArgument(sminLat) return } - if s.minLon, err = strconv.ParseFloat(sminLon, 64); err != nil { + if minLon, err = strconv.ParseFloat(sminLon, 64); err != nil { err = errInvalidArgument(sminLon) return } - if s.maxLat, err = strconv.ParseFloat(smaxlat, 64); err != nil { + if maxLat, err = strconv.ParseFloat(smaxlat, 64); err != nil { err = errInvalidArgument(smaxlat) return } - if s.maxLon, err = strconv.ParseFloat(smaxlon, 64); err != nil { + if maxLon, err = strconv.ParseFloat(smaxlon, 64); err != nil { err = errInvalidArgument(smaxlon) return } + s.obj = geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: minLon, Y: minLat}, + Max: geometry.Point{X: maxLon, Y: maxLat}, + }) case "hash": var hash string if vs, hash, ok = tokenval(vs); !ok || hash == "" { err = errInvalidNumberOfArguments return } - if s.minLat, s.minLon, s.maxLat, s.maxLon, err = geohash.Bounds(hash); err != nil { - err = errInvalidArgument(hash) - return - } + box := geohash.BoundingBox(hash) + s.obj = geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: box.MinLng, Y: box.MinLat}, + Max: geometry.Point{X: box.MaxLng, Y: box.MaxLat}, + }) case "quadkey": var key string if vs, key, ok = tokenval(vs); !ok || key == "" { err = errInvalidNumberOfArguments return } - if s.minLat, s.minLon, s.maxLat, s.maxLon, err = bing.QuadKeyToBounds(key); err != nil { + var minLat, minLon, maxLat, maxLon float64 + minLat, minLon, maxLat, maxLon, err = bing.QuadKeyToBounds(key) + if err != nil { err = errInvalidArgument(key) return } + s.obj = geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: minLon, Y: minLat}, + Max: geometry.Point{X: maxLon, Y: maxLat}, + }) case "tile": var sx, sy, sz string if vs, sx, ok = tokenval(vs); !ok || sx == "" { @@ -283,7 +265,12 @@ func (c *Controller) cmdSearchArgs( err = errInvalidArgument(sz) return } - s.minLat, s.minLon, s.maxLat, s.maxLon = bing.TileXYToBounds(x, y, z) + var minLat, minLon, maxLat, maxLon float64 + minLat, minLon, maxLat, maxLon = bing.TileXYToBounds(x, y, z) + s.obj = geojson.NewRect(geometry.Rect{ + Min: geometry.Point{X: minLon, Y: minLat}, + Max: geometry.Point{X: maxLon, Y: maxLat}, + }) case "get": if s.clip { err = errInvalidArgument("cannnot clip with get") @@ -302,20 +289,11 @@ func (c *Controller) cmdSearchArgs( err = errKeyNotFound return } - o, _, ok := col.Get(id) + s.obj, _, ok = col.Get(id) if !ok { err = errIDNotFound return } - if o.IsBBoxDefined() { - bbox := o.CalculatedBBox() - s.minLat = bbox.Min.Y - s.minLon = bbox.Min.X - s.maxLat = bbox.Max.Y - s.maxLon = bbox.Max.X - } else { - s.o = o - } case "roam": s.roam.on = true if vs, s.roam.key, ok = tokenval(vs); !ok || s.roam.key == "" { @@ -336,7 +314,6 @@ func (c *Controller) cmdSearchArgs( err = errInvalidArgument(smeters) return } - var scan string if vs, scan, ok = tokenval(vs); ok { if strings.ToLower(scan) != "scan" { @@ -383,7 +360,6 @@ func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) if s.fence { return server.NOMessage, s } - minZ, maxZ := zMinMaxFromWheres(s.wheres) sw, err := c.newScanWriter( wr, msg, s.key, s.output, s.precision, s.glob, false, s.cursor, s.limit, s.wheres, s.whereins, s.whereevals, s.nofields) @@ -403,7 +379,7 @@ func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) if dist != nil { distance = *dist } else { - distance = o.CalculatedPoint().DistanceTo(geojson.Position{X: s.lon, Y: s.lat, Z: 0}) + distance = o.Distance(s.obj) } } return sw.writeObject(ScanWriterParams{ @@ -416,16 +392,16 @@ func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) }) } if s.knn { - c.nearestNeighbors(&s, sw, s.lat, s.lon, &matched, iter) + c.nearestNeighbors(&s, sw, s.obj, &matched, iter) } else { - sw.col.Nearby(s.sparse, s.lat, s.lon, s.meters, minZ, maxZ, - func(id string, o geojson.Object, fields []float64) bool { - if c.hasExpired(s.key, id) { - return true - } - return iter(id, o, fields, nil) - }, - ) + sw.col.Intersects(s.obj, s.sparse, func( + id string, o geojson.Object, fields []float64, + ) bool { + if c.hasExpired(s.key, id) { + return true + } + return iter(id, o, fields, nil) + }) } } sw.writeFoot() @@ -443,11 +419,13 @@ type iterItem struct { dist float64 } -func (c *Controller) nearestNeighbors(s *liveFenceSwitches, sw *scanWriter, lat, lon float64, matched *uint32, - iter func(id string, o geojson.Object, fields []float64, dist *float64) bool) { +func (c *Controller) nearestNeighbors( + s *liveFenceSwitches, sw *scanWriter, target geojson.Object, matched *uint32, + iter func(id string, o geojson.Object, fields []float64, dist *float64, + ) bool) { limit := int(sw.cursor + sw.limit) var items []iterItem - sw.col.NearestNeighbors(lat, lon, func(id string, o geojson.Object, fields []float64) bool { + sw.col.Nearby(target, func(id string, o geojson.Object, fields []float64) bool { if c.hasExpired(s.key, id) { return true } @@ -458,7 +436,7 @@ func (c *Controller) nearestNeighbors(s *liveFenceSwitches, sw *scanWriter, lat, if !match { return true } - dist := o.CalculatedPoint().DistanceTo(geojson.Position{X: lon, Y: lat, Z: 0}) + dist := o.Distance(target) items = append(items, iterItem{id: id, o: o, fields: fields, dist: dist}) if !keepGoing { return false @@ -517,46 +495,40 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res } sw.writeHead() if sw.col != nil { - minZ, maxZ := zMinMaxFromWheres(s.wheres) if cmd == "within" { - sw.col.Within(s.sparse, - s.o, - s.minLat, s.minLon, s.maxLat, s.maxLon, - s.lat, s.lon, s.meters, - minZ, maxZ, - func(id string, o geojson.Object, fields []float64) bool { - if c.hasExpired(s.key, id) { - return true - } - return sw.writeObject(ScanWriterParams{ - id: id, - o: o, - fields: fields, - noLock: true, - }) - }, - ) + sw.col.Within(s.obj, s.sparse, func( + id string, o geojson.Object, fields []float64, + ) bool { + if c.hasExpired(s.key, id) { + return true + } + return sw.writeObject(ScanWriterParams{ + id: id, + o: o, + fields: fields, + noLock: true, + }) + }) } else if cmd == "intersects" { - sw.col.Intersects(s.sparse, - s.o, - s.minLat, s.minLon, s.maxLat, s.maxLon, - s.lat, s.lon, s.meters, - minZ, maxZ, - s.clip, - func(id string, o geojson.Object, fields []float64, clipbox geojson.BBox) bool { - if c.hasExpired(s.key, id) { - return true - } - return sw.writeObject(ScanWriterParams{ - id: id, - o: o, - fields: fields, - noLock: true, - clip: s.clip, - clipbox: clipbox, - }) - }, - ) + sw.col.Intersects(s.obj, s.sparse, func( + id string, + o geojson.Object, + fields []float64, + ) bool { + if c.hasExpired(s.key, id) { + return true + } + params := ScanWriterParams{ + id: id, + o: o, + fields: fields, + noLock: true, + } + if s.clip { + params.clip = s.obj + } + return sw.writeObject(params) + }) } } sw.writeFoot() diff --git a/pkg/controller/stats.go b/internal/controller/stats.go similarity index 99% rename from pkg/controller/stats.go rename to internal/controller/stats.go index ee5a01f0..b6fc7240 100644 --- a/pkg/controller/stats.go +++ b/internal/controller/stats.go @@ -12,8 +12,8 @@ import ( "github.com/tidwall/btree" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/core" - "github.com/tidwall/tile38/pkg/server" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/server" ) func (c *Controller) cmdStats(msg *server.Message) (res resp.Value, err error) { diff --git a/pkg/controller/stats_cpu.go b/internal/controller/stats_cpu.go similarity index 100% rename from pkg/controller/stats_cpu.go rename to internal/controller/stats_cpu.go diff --git a/pkg/controller/stats_cpu_darlin.go b/internal/controller/stats_cpu_darlin.go similarity index 100% rename from pkg/controller/stats_cpu_darlin.go rename to internal/controller/stats_cpu_darlin.go diff --git a/pkg/controller/token.go b/internal/controller/token.go similarity index 100% rename from pkg/controller/token.go rename to internal/controller/token.go diff --git a/pkg/controller/token_test.go b/internal/controller/token_test.go similarity index 100% rename from pkg/controller/token_test.go rename to internal/controller/token_test.go diff --git a/pkg/ds/btree.go b/internal/ds/btree.go similarity index 100% rename from pkg/ds/btree.go rename to internal/ds/btree.go diff --git a/pkg/ds/btree_test.go b/internal/ds/btree_test.go similarity index 100% rename from pkg/ds/btree_test.go rename to internal/ds/btree_test.go diff --git a/pkg/endpoint/amqp.go b/internal/endpoint/amqp.go similarity index 100% rename from pkg/endpoint/amqp.go rename to internal/endpoint/amqp.go diff --git a/pkg/endpoint/disque.go b/internal/endpoint/disque.go similarity index 97% rename from pkg/endpoint/disque.go rename to internal/endpoint/disque.go index 131254b3..94b841f2 100644 --- a/pkg/endpoint/disque.go +++ b/internal/endpoint/disque.go @@ -6,7 +6,7 @@ import ( "time" "github.com/garyburd/redigo/redis" - "github.com/tidwall/tile38/pkg/log" + "github.com/tidwall/tile38/internal/log" ) const ( diff --git a/pkg/endpoint/endpoint.go b/internal/endpoint/endpoint.go similarity index 100% rename from pkg/endpoint/endpoint.go rename to internal/endpoint/endpoint.go diff --git a/pkg/endpoint/grpc.go b/internal/endpoint/grpc.go similarity index 96% rename from pkg/endpoint/grpc.go rename to internal/endpoint/grpc.go index 08497031..48b9616e 100644 --- a/pkg/endpoint/grpc.go +++ b/internal/endpoint/grpc.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/tidwall/tile38/pkg/hservice" + "github.com/tidwall/tile38/internal/hservice" "golang.org/x/net/context" "google.golang.org/grpc" ) diff --git a/pkg/endpoint/http.go b/internal/endpoint/http.go similarity index 100% rename from pkg/endpoint/http.go rename to internal/endpoint/http.go diff --git a/pkg/endpoint/kafka.go b/internal/endpoint/kafka.go similarity index 100% rename from pkg/endpoint/kafka.go rename to internal/endpoint/kafka.go diff --git a/pkg/endpoint/local.go b/internal/endpoint/local.go similarity index 100% rename from pkg/endpoint/local.go rename to internal/endpoint/local.go diff --git a/pkg/endpoint/mqtt.go b/internal/endpoint/mqtt.go similarity index 100% rename from pkg/endpoint/mqtt.go rename to internal/endpoint/mqtt.go diff --git a/pkg/endpoint/nats.go b/internal/endpoint/nats.go similarity index 100% rename from pkg/endpoint/nats.go rename to internal/endpoint/nats.go diff --git a/pkg/endpoint/redis.go b/internal/endpoint/redis.go similarity index 100% rename from pkg/endpoint/redis.go rename to internal/endpoint/redis.go diff --git a/pkg/endpoint/sqs.go b/internal/endpoint/sqs.go similarity index 100% rename from pkg/endpoint/sqs.go rename to internal/endpoint/sqs.go diff --git a/pkg/expire/expire.go b/internal/expire/expire.go similarity index 100% rename from pkg/expire/expire.go rename to internal/expire/expire.go diff --git a/pkg/expire/expire_test.go b/internal/expire/expire_test.go similarity index 100% rename from pkg/expire/expire_test.go rename to internal/expire/expire_test.go diff --git a/pkg/glob/glob.go b/internal/glob/glob.go similarity index 100% rename from pkg/glob/glob.go rename to internal/glob/glob.go diff --git a/pkg/glob/glob_test.go b/internal/glob/glob_test.go similarity index 100% rename from pkg/glob/glob_test.go rename to internal/glob/glob_test.go diff --git a/pkg/glob/match.go b/internal/glob/match.go similarity index 100% rename from pkg/glob/match.go rename to internal/glob/match.go diff --git a/pkg/hservice/gen.sh b/internal/hservice/gen.sh similarity index 100% rename from pkg/hservice/gen.sh rename to internal/hservice/gen.sh diff --git a/pkg/hservice/hservice.pb.go b/internal/hservice/hservice.pb.go similarity index 100% rename from pkg/hservice/hservice.pb.go rename to internal/hservice/hservice.pb.go diff --git a/pkg/hservice/hservice.proto b/internal/hservice/hservice.proto similarity index 100% rename from pkg/hservice/hservice.proto rename to internal/hservice/hservice.proto diff --git a/pkg/log/log.go b/internal/log/log.go similarity index 100% rename from pkg/log/log.go rename to internal/log/log.go diff --git a/pkg/log/log_test.go b/internal/log/log_test.go similarity index 100% rename from pkg/log/log_test.go rename to internal/log/log_test.go diff --git a/pkg/server/reader.go b/internal/server/reader.go similarity index 99% rename from pkg/server/reader.go rename to internal/server/reader.go index c9ba2a3a..b0e4a656 100644 --- a/pkg/server/reader.go +++ b/internal/server/reader.go @@ -18,6 +18,7 @@ var errInvalidHTTP = errors.New("invalid HTTP request") // Type is resp type type Type int +// Message types const ( Null Type = iota RESP diff --git a/pkg/server/server.go b/internal/server/server.go similarity index 98% rename from pkg/server/server.go rename to internal/server/server.go index d9c5ba40..1c8819d0 100644 --- a/pkg/server/server.go +++ b/internal/server/server.go @@ -11,8 +11,8 @@ import ( "time" "github.com/tidwall/resp" - "github.com/tidwall/tile38/pkg/core" - "github.com/tidwall/tile38/pkg/log" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/log" ) // This phrase is copied nearly verbatim from Redis. @@ -202,4 +202,5 @@ func OKMessage(msg *Message, start time.Time) resp.Value { return resp.SimpleStringValue("") } +// NOMessage is no message var NOMessage = resp.SimpleStringValue("") diff --git a/pkg/collection/collection.go b/pkg/collection/collection.go deleted file mode 100644 index bc04027b..00000000 --- a/pkg/collection/collection.go +++ /dev/null @@ -1,690 +0,0 @@ -package collection - -import ( - "math" - - "github.com/tidwall/btree" - "github.com/tidwall/tile38/pkg/ds" - "github.com/tidwall/tile38/pkg/geojson" - "github.com/tidwall/tile38/pkg/index" -) - -const ( - idOrdered = 0 - valueOrdered = 1 -) - -type itemT struct { - id string - object geojson.Object -} - -func (i *itemT) Less(item btree.Item, ctx interface{}) bool { - switch ctx { - default: - return false - case idOrdered: - return i.id < item.(*itemT).id - case valueOrdered: - i1, i2 := i.object.String(), item.(*itemT).object.String() - if i1 < i2 { - return true - } - if i1 > i2 { - return false - } - // the values match so we will compare the ids, which are always unique. - return i.id < item.(*itemT).id - } -} - -func (i *itemT) Rect() (minX, minY, maxX, maxY float64) { - bbox := i.object.CalculatedBBox() - return bbox.Min.X, bbox.Min.Y, bbox.Max.X, bbox.Max.Y -} - -func (i *itemT) Point() (x, y float64) { - x, y, _, _ = i.Rect() - return -} - -// Collection represents a collection of geojson objects. -type Collection struct { - items ds.BTree // items sorted by keys - values *btree.BTree // items sorted by value+key - index *index.Index // items geospatially indexed - fieldMap map[string]int - fieldValues map[string][]float64 - weight int - points int - objects int // geometry count - nobjects int // non-geometry count -} - -var counter uint64 - -// New creates an empty collection -func New() *Collection { - col := &Collection{ - index: index.New(), - values: btree.New(16, valueOrdered), - fieldMap: make(map[string]int), - } - return col -} - -func (c *Collection) setFieldValues(id string, values []float64) { - if c.fieldValues == nil { - c.fieldValues = make(map[string][]float64) - } - c.fieldValues[id] = values -} - -func (c *Collection) getFieldValues(id string) (values []float64) { - if c.fieldValues == nil { - return nil - } - return c.fieldValues[id] -} -func (c *Collection) deleteFieldValues(id string) { - if c.fieldValues != nil { - delete(c.fieldValues, id) - } -} - -// Count returns the number of objects in collection. -func (c *Collection) Count() int { - return c.objects + c.nobjects -} - -// StringCount returns the number of string values. -func (c *Collection) StringCount() int { - return c.nobjects -} - -// PointCount returns the number of points (lat/lon coordinates) in collection. -func (c *Collection) PointCount() int { - return c.points -} - -// TotalWeight calculates the in-memory cost of the collection in bytes. -func (c *Collection) TotalWeight() int { - return c.weight -} - -// Bounds returns the bounds of all the items in the collection. -func (c *Collection) Bounds() (minX, minY, maxX, maxY float64) { - return c.index.Bounds() -} - -// Set adds or replaces an object in the collection and returns the fields -// array. If an item with the same id is already in the collection then the -// new item will adopt the old item's fields. -// The fields argument is optional. -// The return values are the old object, the old fields, and the new fields -func (c *Collection) Set( - id string, obj geojson.Object, fields []string, values []float64, -) ( - oldObject geojson.Object, oldFields []float64, newFields []float64, -) { - var oldItem *itemT - newItem := &itemT{id: id, object: obj} - // add the new item to main btree and remove the old one if needed - oldItemPtr, _ := c.items.Set(id, newItem) - if oldItemPtr != nil { - // the old item was removed, now let's remove from the rtree - // or strings tree. - oldItem = oldItemPtr.(*itemT) - if obj.IsGeometry() { - // geometry - c.index.Remove(oldItem) - c.objects-- - } else { - // string - c.values.Delete(oldItem) - c.nobjects-- - } - // decrement the point count - c.points -= oldItem.object.PositionCount() - - // decrement the weights - c.weight -= len(c.getFieldValues(id)) * 8 - c.weight -= oldItem.object.Weight() + len(oldItem.id) - - // references - oldObject = oldItem.object - oldFields = c.getFieldValues(id) - newFields = oldFields - } - // insert the new item into the rtree or strings tree. - if obj.IsGeometry() { - c.index.Insert(newItem) - c.objects++ - } else { - c.values.ReplaceOrInsert(newItem) - c.nobjects++ - } - // increment the point count - c.points += obj.PositionCount() - - // add the new weights - c.weight += len(newFields) * 8 - c.weight += obj.Weight() + len(id) - if fields == nil { - if len(values) > 0 { - // directly set the field values, update weight - c.weight -= len(newFields) * 8 - newFields = values - c.setFieldValues(id, newFields) - c.weight += len(newFields) * 8 - } - } else { - //if len(fields) == 0 { - // panic("if fields is empty, make it nil") - //} - // map field name to value - for i, field := range fields { - c.setField(newItem, field, values[i]) - } - newFields = c.getFieldValues(id) - } - return oldObject, oldFields, newFields -} - -// Delete removes an object and returns it. -// If the object does not exist then the 'ok' return value will be false. -func (c *Collection) Delete(id string) ( - obj geojson.Object, fields []float64, ok bool, -) { - old, _ := c.items.Delete(id) - if old == nil { - return nil, nil, false - } - item := old.(*itemT) - if item.object.IsGeometry() { - c.index.Remove(item) - c.objects-- - } else { - c.values.Delete(item) - c.nobjects-- - } - fields = c.getFieldValues(id) - c.deleteFieldValues(id) - c.weight -= len(fields) * 8 - c.weight -= item.object.Weight() + len(item.id) - c.points -= item.object.PositionCount() - return item.object, fields, true -} - -// Get returns an object. -// If the object does not exist then the 'ok' return value will be false. -func (c *Collection) Get(id string) ( - obj geojson.Object, fields []float64, ok bool, -) { - val, _ := c.items.Get(id) - if val == nil { - return nil, nil, false - } - item := val.(*itemT) - return item.object, c.getFieldValues(id), true -} - -// SetField set a field value for an object and returns that object. -// If the object does not exist then the 'ok' return value will be false. -func (c *Collection) SetField(id, field string, value float64) ( - obj geojson.Object, fields []float64, updated bool, ok bool, -) { - val, _ := c.items.Get(id) - if val == nil { - ok = false - return - } - item := val.(*itemT) - updated = c.setField(item, field, value) - return item.object, c.getFieldValues(id), updated, true -} - -// SetFields is similar to SetField, just setting multiple fields at once -func (c *Collection) SetFields( - id string, inFields []string, inValues []float64, -) ( - obj geojson.Object, fields []float64, updatedCount int, ok bool, -) { - val, _ := c.items.Get(id) - if val == nil { - ok = false - return - } - item := val.(*itemT) - for idx, field := range inFields { - if c.setField(item, field, inValues[idx]) { - updatedCount++ - } - } - return item.object, c.getFieldValues(id), updatedCount, true -} - -func (c *Collection) setField(item *itemT, field string, value float64) ( - updated bool, -) { - idx, ok := c.fieldMap[field] - if !ok { - idx = len(c.fieldMap) - c.fieldMap[field] = idx - } - fields := c.getFieldValues(item.id) - c.weight -= len(fields) * 8 - for idx >= len(fields) { - fields = append(fields, 0) - } - c.weight += len(fields) * 8 - ovalue := fields[idx] - fields[idx] = value - c.setFieldValues(item.id, fields) - return ovalue != value -} - -// FieldMap return a maps of the field names. -func (c *Collection) FieldMap() map[string]int { - return c.fieldMap -} - -// FieldArr return an array representation of the field names. -func (c *Collection) FieldArr() []string { - arr := make([]string, len(c.fieldMap)) - for field, i := range c.fieldMap { - arr[i] = field - } - return arr -} - -// Scan iterates though the collection ids. -func (c *Collection) Scan(desc bool, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - var keepon = true - iter := func(key string, value interface{}) bool { - iitm := value.(*itemT) - keepon = iterator(iitm.id, iitm.object, c.getFieldValues(iitm.id)) - return keepon - } - if desc { - c.items.Reverse(iter) - } else { - c.items.Scan(iter) - } - return keepon -} - -// ScanRange iterates though the collection starting with specified id. -func (c *Collection) ScanRange(start, end string, desc bool, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - var keepon = true - iter := func(key string, value interface{}) bool { - if !desc { - if key >= end { - return false - } - } else { - if key <= end { - return false - } - } - iitm := value.(*itemT) - keepon = iterator(iitm.id, iitm.object, c.getFieldValues(iitm.id)) - return keepon - } - - if desc { - c.items.Descend(start, iter) - } else { - c.items.Ascend(start, iter) - } - return keepon -} - -// SearchValues iterates though the collection values. -func (c *Collection) SearchValues(desc bool, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - var keepon = true - iter := func(item btree.Item) bool { - iitm := item.(*itemT) - keepon = iterator(iitm.id, iitm.object, c.getFieldValues(iitm.id)) - return keepon - } - if desc { - c.values.Descend(iter) - } else { - c.values.Ascend(iter) - } - return keepon -} - -// SearchValuesRange iterates though the collection values. -func (c *Collection) SearchValuesRange(start, end string, desc bool, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - var keepon = true - iter := func(item btree.Item) bool { - iitm := item.(*itemT) - keepon = iterator(iitm.id, iitm.object, c.getFieldValues(iitm.id)) - return keepon - } - if desc { - c.values.DescendRange(&itemT{object: geojson.String(start)}, - &itemT{object: geojson.String(end)}, iter) - } else { - c.values.AscendRange(&itemT{object: geojson.String(start)}, - &itemT{object: geojson.String(end)}, iter) - } - return keepon -} - -// ScanGreaterOrEqual iterates though the collection starting with specified id. -func (c *Collection) ScanGreaterOrEqual(id string, desc bool, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - var keepon = true - iter := func(key string, value interface{}) bool { - iitm := value.(*itemT) - keepon = iterator(iitm.id, iitm.object, c.getFieldValues(iitm.id)) - return keepon - } - if desc { - c.items.Descend(id, iter) - } else { - c.items.Ascend(id, iter) - } - return keepon -} - -func (c *Collection) geoSearch( - bbox geojson.BBox, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - return c.index.Search(bbox.Min.X, bbox.Min.Y, bbox.Max.X, bbox.Max.Y, - func(item interface{}) bool { - iitm := item.(*itemT) - if !iterator(iitm.id, iitm.object, c.getFieldValues(iitm.id)) { - return false - } - return true - }, - ) -} - -// Nearby returns all object that are nearby a point. -func (c *Collection) Nearby( - sparse uint8, lat, lon, meters, minZ, maxZ float64, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - var keepon = true - center := geojson.Position{X: lon, Y: lat, Z: 0} - bbox := geojson.BBoxesFromCenter(lat, lon, meters) - bboxes := bbox.Sparse(sparse) - if sparse > 0 { - for _, bbox := range bboxes { - bbox.Min.Z, bbox.Max.Z = minZ, maxZ - keepon = c.geoSearch(bbox, - func(id string, obj geojson.Object, fields []float64) bool { - if obj.Nearby(center, meters) { - if iterator(id, obj, fields) { - return false - } - } - return true - }, - ) - if !keepon { - break - } - } - return keepon - } - bbox.Min.Z, bbox.Max.Z = minZ, maxZ - return c.geoSearch(bbox, - func(id string, obj geojson.Object, fields []float64) bool { - if obj.Nearby(center, meters) { - return iterator(id, obj, fields) - } - return true - }, - ) -} - -// Within returns all object that are fully contained within an object or -// bounding box. Set obj to nil in order to use the bounding box. -func (c *Collection) Within( - sparse uint8, obj geojson.Object, - minLat, minLon, maxLat, maxLon, lat, lon, meters, minZ, maxZ float64, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - var keepon = true - var bbox geojson.BBox - center := geojson.Position{X: lon, Y: lat, Z: 0} - - if obj != nil { - bbox = obj.CalculatedBBox() - if minZ == math.Inf(-1) && maxZ == math.Inf(+1) { - if bbox.Min.Z == 0 && bbox.Max.Z == 0 { - bbox.Min.Z = minZ - bbox.Max.Z = maxZ - } - } - } else if meters != -1 { - bbox = geojson.BBoxesFromCenter(lat, lon, meters) - } else { - bbox = geojson.BBox{ - Min: geojson.Position{X: minLon, Y: minLat, Z: minZ}, - Max: geojson.Position{X: maxLon, Y: maxLat, Z: maxZ}, - } - } - bboxes := bbox.Sparse(sparse) - if sparse > 0 { - for _, bbox := range bboxes { - if obj != nil { - keepon = c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.Within(obj) { - if iterator(id, o, fields) { - return false - } - } - return true - }, - ) - } else if meters != -1 { - keepon = c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.WithinCircle(center, meters) { - if iterator(id, o, fields) { - return false - } - } - return true - }, - ) - } - if keepon { - keepon = c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.WithinBBox(bbox) { - if iterator(id, o, fields) { - return false - } - } - return true - }, - ) - } - if !keepon { - break - } - } - return keepon - } - if obj != nil { - return c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.Within(obj) { - return iterator(id, o, fields) - } - return true - }, - ) - } else if meters != -1 { - return c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.WithinCircle(center, meters) { - return iterator(id, o, fields) - } - return true - }, - ) - } - return c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.WithinBBox(bbox) { - return iterator(id, o, fields) - } - return true - }, - ) -} - -// Intersects returns all object that are intersect an object or bounding box. -// Set obj to nil in order to use the bounding box. -func (c *Collection) Intersects( - sparse uint8, obj geojson.Object, - minLat, minLon, maxLat, maxLon, lat, lon, meters, minZ, maxZ float64, - doClip bool, - iterator func( - id string, obj geojson.Object, fields []float64, clipBox geojson.BBox, - ) bool, -) bool { - - var keepon = true - var clipbox, bbox geojson.BBox - center := geojson.Position{X: lon, Y: lat, Z: 0} - if obj != nil { - bbox = obj.CalculatedBBox() - if minZ == math.Inf(-1) && maxZ == math.Inf(+1) { - if bbox.Min.Z == 0 && bbox.Max.Z == 0 { - bbox.Min.Z = minZ - bbox.Max.Z = maxZ - } - } - } else if meters != -1 { - bbox = geojson.BBoxesFromCenter(lat, lon, meters) - } else { - bbox = geojson.BBox{ - Min: geojson.Position{X: minLon, Y: minLat, Z: minZ}, - Max: geojson.Position{X: maxLon, Y: maxLat, Z: maxZ}, - } - if doClip { - clipbox = bbox - } - } - var bboxes []geojson.BBox - if sparse > 0 { - split := 1 << sparse - xpart := (bbox.Max.X - bbox.Min.X) / float64(split) - ypart := (bbox.Max.Y - bbox.Min.Y) / float64(split) - for y := bbox.Min.Y; y < bbox.Max.Y; y += ypart { - for x := bbox.Min.X; x < bbox.Max.X; x += xpart { - bboxes = append(bboxes, geojson.BBox{ - Min: geojson.Position{X: x, Y: y, Z: minZ}, - Max: geojson.Position{X: x + xpart, Y: y + ypart, Z: maxZ}, - }) - } - } - for _, bbox := range bboxes { - if obj != nil { - keepon = c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.Intersects(obj) { - if iterator(id, o, fields, clipbox) { - return false - } - } - return true - }, - ) - } else if meters != -1 { - keepon = c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.IntersectsCircle(center, meters) { - if iterator(id, o, fields, clipbox) { - return false - } - } - return true - }, - ) - } - if keepon { - keepon = c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.IntersectsBBox(bbox) { - if iterator(id, o, fields, clipbox) { - return false - } - } - return true - }, - ) - } - if !keepon { - break - } - } - return keepon - } - if obj != nil { - return c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.Intersects(obj) { - return iterator(id, o, fields, clipbox) - } - return true - }, - ) - } else if meters != -1 { - return c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.IntersectsCircle(center, meters) { - return iterator(id, o, fields, clipbox) - } - return true - }, - ) - } - return c.geoSearch(bbox, - func(id string, o geojson.Object, fields []float64) bool { - if o.IntersectsBBox(bbox) { - return iterator(id, o, fields, clipbox) - } - return true - }, - ) -} - -// NearestNeighbors returns the nearest neighbors -func (c *Collection) NearestNeighbors( - lat, lon float64, - iterator func(id string, obj geojson.Object, fields []float64) bool, -) bool { - return c.index.KNN(lon, lat, func(item interface{}) bool { - var iitm *itemT - iitm, ok := item.(*itemT) - if !ok { - return true // just ignore - } - if !iterator(iitm.id, iitm.object, c.getFieldValues(iitm.id)) { - return false - } - return true - }) -} diff --git a/pkg/collection/collection_test.go b/pkg/collection/collection_test.go deleted file mode 100644 index 1ee48077..00000000 --- a/pkg/collection/collection_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package collection - -import ( - "fmt" - "math/rand" - "strconv" - "testing" - "time" - - "github.com/tidwall/tile38/pkg/geojson" -) - -func TestCollection(t *testing.T) { - const numItems = 10000 - objs := make(map[string]geojson.Object) - c := New() - for i := 0; i < numItems; i++ { - id := strconv.FormatInt(int64(i), 10) - var obj geojson.Object - p := geojson.Position{X: rand.Float64()*360 - 180, Y: rand.Float64()*180 - 90, Z: 0} - if rand.Int()%2 == 0 { - obj = geojson.Point{Coordinates: p} - } else { - minX, minY := rand.Float64()*360-180, rand.Float64()*180-90 - obj = geojson.Point{Coordinates: p, BBox: &geojson.BBox{ - Min: geojson.Position{X: minX, Y: minY, Z: 0}, - Max: geojson.Position{X: minX + 100, Y: minY + 100, Z: 0}, - }} - } - objs[id] = obj - c.Set(id, obj, nil, nil) - } - count := 0 - bbox := geojson.BBox{Min: geojson.Position{X: -180, Y: -90, Z: 0}, Max: geojson.Position{X: 180, Y: 90, Z: 0}} - c.geoSearch(bbox, func(id string, obj geojson.Object, field []float64) bool { - count++ - return true - }) - if count != len(objs) { - t.Fatalf("count = %d, expect %d", count, len(objs)) - } - count = c.Count() - if count != len(objs) { - t.Fatalf("c.Count() = %d, expect %d", count, len(objs)) - } - testCollectionVerifyContents(t, c, objs) -} - -func testCollectionVerifyContents(t *testing.T, c *Collection, objs map[string]geojson.Object) { - for id, o2 := range objs { - o1, _, ok := c.Get(id) - if !ok { - t.Fatalf("ok[%s] = false, expect true", id) - } - j1 := o1.JSON() - j2 := o2.JSON() - if j1 != j2 { - t.Fatalf("j1 == %s, expect %s", j1, j2) - } - } -} - -func TestManyCollections(t *testing.T) { - colsM := make(map[string]*Collection) - cols := 100 - objs := 1000 - k := 0 - for i := 0; i < cols; i++ { - key := strconv.FormatInt(int64(i), 10) - for j := 0; j < objs; j++ { - id := strconv.FormatInt(int64(j), 10) - p := geojson.Position{X: rand.Float64()*360 - 180, Y: rand.Float64()*180 - 90, Z: 0} - obj := geojson.Object(geojson.Point{Coordinates: p}) - col, ok := colsM[key] - if !ok { - col = New() - colsM[key] = col - } - col.Set(id, obj, nil, nil) - k++ - } - } - - col := colsM["13"] - //println(col.Count()) - bbox := geojson.BBox{Min: geojson.Position{X: -180, Y: 30, Z: 0}, Max: geojson.Position{X: 34, Y: 100, Z: 0}} - col.geoSearch(bbox, func(id string, obj geojson.Object, fields []float64) bool { - //println(id) - return true - }) -} - -type testPointItem struct { - id string - object geojson.Object -} - -func BenchmarkInsert(t *testing.B) { - rand.Seed(time.Now().UnixNano()) - items := make([]testPointItem, t.N) - for i := 0; i < t.N; i++ { - items[i] = testPointItem{ - fmt.Sprintf("%d", i), - geojson.SimplePoint{ - Y: rand.Float64()*180 - 90, - X: rand.Float64()*360 - 180, - }, - } - } - col := New() - t.ResetTimer() - for i := 0; i < t.N; i++ { - col.Set(items[i].id, items[i].object, nil, nil) - } -} - -func BenchmarkReplace(t *testing.B) { - rand.Seed(time.Now().UnixNano()) - items := make([]testPointItem, t.N) - for i := 0; i < t.N; i++ { - items[i] = testPointItem{ - fmt.Sprintf("%d", i), - geojson.SimplePoint{ - Y: rand.Float64()*180 - 90, - X: rand.Float64()*360 - 180, - }, - } - } - col := New() - for i := 0; i < t.N; i++ { - col.Set(items[i].id, items[i].object, nil, nil) - } - t.ResetTimer() - for _, i := range rand.Perm(t.N) { - o, _, _ := col.Set(items[i].id, items[i].object, nil, nil) - if o != items[i].object { - t.Fatal("shoot!") - } - } -} - -func BenchmarkGet(t *testing.B) { - rand.Seed(time.Now().UnixNano()) - items := make([]testPointItem, t.N) - for i := 0; i < t.N; i++ { - items[i] = testPointItem{ - fmt.Sprintf("%d", i), - geojson.SimplePoint{ - Y: rand.Float64()*180 - 90, - X: rand.Float64()*360 - 180, - }, - } - } - col := New() - for i := 0; i < t.N; i++ { - col.Set(items[i].id, items[i].object, nil, nil) - } - t.ResetTimer() - for _, i := range rand.Perm(t.N) { - o, _, _ := col.Get(items[i].id) - if o != items[i].object { - t.Fatal("shoot!") - } - } -} - -func BenchmarkRemove(t *testing.B) { - rand.Seed(time.Now().UnixNano()) - items := make([]testPointItem, t.N) - for i := 0; i < t.N; i++ { - items[i] = testPointItem{ - fmt.Sprintf("%d", i), - geojson.SimplePoint{ - Y: rand.Float64()*180 - 90, - X: rand.Float64()*360 - 180, - }, - } - } - col := New() - for i := 0; i < t.N; i++ { - col.Set(items[i].id, items[i].object, nil, nil) - } - t.ResetTimer() - for _, i := range rand.Perm(t.N) { - o, _, _ := col.Delete(items[i].id) - if o != items[i].object { - t.Fatal("shoot!") - } - } -} diff --git a/pkg/geojson/bbox.go b/pkg/geojson/bbox.go deleted file mode 100644 index e65fa763..00000000 --- a/pkg/geojson/bbox.go +++ /dev/null @@ -1,260 +0,0 @@ -package geojson - -import ( - "math" - "strconv" - - "github.com/tidwall/gjson" - "github.com/tidwall/tile38/pkg/geojson/poly" -) - -// BBox is a bounding box -type BBox struct { - Min Position - Max Position -} - -// New2DBBox creates a new bounding box -func New2DBBox(minX, minY, maxX, maxY float64) BBox { - return BBox{Min: Position{X: minX, Y: minY, Z: 0}, Max: Position{X: maxX, Y: maxY, Z: 0}} -} - -func fillBBox(json string) (*BBox, error) { - var bbox *BBox - res := gjson.Get(json, "bbox") - switch res.Type { - default: - return nil, errBBoxInvalidType - case gjson.Null: - case gjson.JSON: - v := res.Array() - if !(len(v) == 4 || len(v) == 6) { - return nil, errBBoxInvalidNumberOfValues - } - bbox = &BBox{} - for i := 0; i < len(v); i++ { - if v[i].Type != gjson.Number { - return nil, errBBoxInvalidValue - } - } - bbox.Min.X = v[0].Float() - bbox.Min.Y = v[1].Float() - i := 2 - if len(v) == 6 { - bbox.Min.Z = v[2].Float() - i = 3 - } else { - bbox.Min.Z = nilz - } - bbox.Max.X = v[i+0].Float() - bbox.Max.Y = v[i+1].Float() - if len(v) == 6 { - bbox.Max.Z = v[i+2].Float() - i = 3 - } else { - bbox.Max.Z = nilz - } - } - return bbox, nil -} - -func (b *BBox) isCordZDefined() bool { - return b != nil && (b.Min.Z != nilz || b.Max.Z != nilz) -} - -func appendBBoxJSON(json []byte, b *BBox) []byte { - if b == nil { - return json - } - hasZ := b.Min.Z != nilz && b.Max.Z != nilz - json = append(json, `,"bbox":[`...) - json = strconv.AppendFloat(json, b.Min.X, 'f', -1, 64) - json = append(json, ',') - json = strconv.AppendFloat(json, b.Min.Y, 'f', -1, 64) - if hasZ { - json = append(json, ',') - json = strconv.AppendFloat(json, b.Min.Z, 'f', -1, 64) - } - json = append(json, ',') - json = strconv.AppendFloat(json, b.Max.X, 'f', -1, 64) - json = append(json, ',') - json = strconv.AppendFloat(json, b.Max.Y, 'f', -1, 64) - if hasZ { - json = append(json, ',') - json = strconv.AppendFloat(json, b.Max.Z, 'f', -1, 64) - } - json = append(json, ']') - return json -} - -func (b BBox) center() Position { - return Position{ - (b.Max.X-b.Min.X)/2 + b.Min.X, - (b.Max.Y-b.Min.Y)/2 + b.Min.Y, - 0, - } -} - -func (b BBox) union(bbox BBox) BBox { - if bbox.Min.X < b.Min.X { - b.Min.X = bbox.Min.X - } - if bbox.Min.Y < b.Min.Y { - b.Min.Y = bbox.Min.Y - } - if bbox.Max.X > b.Max.X { - b.Max.X = bbox.Max.X - } - if bbox.Max.Y > b.Max.Y { - b.Max.Y = bbox.Max.Y - } - return b -} - -func (b BBox) exterior() []Position { - return []Position{ - {b.Min.X, b.Min.Y, 0}, - {b.Min.X, b.Max.Y, 0}, - {b.Max.X, b.Max.Y, 0}, - {b.Max.X, b.Min.Y, 0}, - {b.Min.X, b.Min.Y, 0}, - } -} - -func rectBBox(bbox BBox) poly.Rect { - return poly.Rect{ - Min: poly.Point{X: bbox.Min.X, Y: bbox.Min.Y, Z: 0}, - Max: poly.Point{X: bbox.Max.X, Y: bbox.Max.Y, Z: 0}, - } -} - -// ExternalJSON is the simple json representation of the bounding box used for external applications. -func (b BBox) ExternalJSON() string { - sw, ne := b.Min, b.Max - sw.Z, ne.Z = 0, 0 - return `{"sw":` + sw.ExternalJSON() + `,"ne":` + ne.ExternalJSON() + `}` -} - -// Sparse returns back an evenly distributed number of sub bboxs. -func (b BBox) Sparse(amount byte) []BBox { - if amount == 0 { - return []BBox{b} - } - var bboxes []BBox - split := 1 << amount - var xsize, ysize float64 - if b.Max.X < b.Min.X { - // crosses the prime meridian - xsize = (b.Min.X - b.Max.X) / float64(split) - } else { - xsize = (b.Max.X - b.Min.X) / float64(split) - } - if b.Max.Y < b.Min.Y { - // crosses the equator - ysize = (b.Min.Y - b.Max.Y) / float64(split) - } else { - ysize = (b.Max.Y - b.Min.Y) / float64(split) - } - - for y := b.Min.Y; y < b.Max.Y; y += ysize { - for x := b.Min.X; x < b.Max.X; x += xsize { - bboxes = append(bboxes, BBox{ - Min: Position{X: x, Y: y, Z: b.Min.Z}, - Max: Position{X: x + xsize, Y: y + ysize, Z: b.Max.Z}, - }) - } - } - return bboxes -} - -// BBoxesFromCenter calculates the bounding box surrounding a circle. -func BBoxesFromCenter(lat, lon, meters float64) (outer BBox) { - - outer.Min.Y, outer.Min.X, outer.Max.Y, outer.Max.X = BoundsFromCenter(lat, lon, meters) - if outer.Min.X == outer.Max.X { - switch outer.Min.X { - case -180: - outer.Max.X = 180 - case 180: - outer.Min.X = -180 - } - } - - return outer -} - -// BoundsFromCenter calculates the bounding box surrounding a circle. -func BoundsFromCenter(lat, lon, meters float64) (latMin, lonMin, latMax, lonMax float64) { - - // see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Latitude - lat = toRadians(lat) - lon = toRadians(lon) - - r := meters / earthRadius // angular radius - - latMin = lat - r - latMax = lat + r - - latT := math.Asin(math.Sin(lat) / math.Cos(r)) - lonΔ := math.Acos((math.Cos(r) - math.Sin(latT)*math.Sin(lat)) / (math.Cos(latT) * math.Cos(lat))) - - lonMin = lon - lonΔ - lonMax = lon + lonΔ - - // Adjust for north poll - if latMax > math.Pi/2 { - lonMin = -math.Pi - latMax = math.Pi / 2 - lonMax = math.Pi - } - - // Adjust for south poll - if latMin < -math.Pi/2 { - latMin = -math.Pi / 2 - lonMin = -math.Pi - lonMax = math.Pi - } - - // Adjust for wraparound. Remove this if the commented-out condition below this block is added. - if lonMin < -math.Pi || lonMax > math.Pi { - lonMin = -math.Pi - lonMax = math.Pi - } - - /* - // Consider splitting area into two bboxes, using the below checks, and erasing above block for performance. See http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#PolesAnd180thMeridian - - // Adjust for wraparound if minimum longitude is less than -180 degrees. - if lonMin < -math.Pi { - // box 1: - latMin = latMin - latMax = latMax - lonMin += 2*math.Pi - lonMax = math.Pi - // box 2: - latMin = latMin - latMax = latMax - lonMin = -math.Pi - lonMax = lonMax - } - - // Adjust for wraparound if maximum longitude is greater than 180 degrees. - if lonMax > math.Pi { - // box 1: - latMin = latMin - latMax = latMax - lonMin = lonMin - lonMax = -math.Pi - // box 2: - latMin = latMin - latMax = latMax - lonMin = -math.Pi - lonMax -= 2*math.Pi - } - */ - - lonMin = math.Mod(lonMin+3*math.Pi, 2*math.Pi) - math.Pi // normalise to -180..+180° - lonMax = math.Mod(lonMax+3*math.Pi, 2*math.Pi) - math.Pi - - return toDegrees(latMin), toDegrees(lonMin), toDegrees(latMax), toDegrees(lonMax) -} diff --git a/pkg/geojson/circle.go b/pkg/geojson/circle.go deleted file mode 100644 index 6ebdc44e..00000000 --- a/pkg/geojson/circle.go +++ /dev/null @@ -1,34 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/tile38/pkg/geojson/geo" -) - -func SegmentIntersectsCircle(start, end, center Position, meters float64) bool { - // These are faster checks. If they succeed there's no need do complicate things. - if center.DistanceTo(start) <= meters { - return true - } - if center.DistanceTo(end) <= meters { - return true - } - - // Distance between start and end - l := geo.DistanceTo(start.Y, start.X, end.Y, end.X) - - // Unit direction vector - dx := (end.X - start.X) / l - dy := (end.Y - start.Y) / l - - // Point of the line closest to the center - t := dx * (center.X - start.X) + dy * (center.Y - start.Y) - px := t * dx + start.X - py := t * dy + start.Y - if px < start.X || px > end.X || py < start.Y || py > end.Y { - // closest point is outside the segment - return false - } - - // Distance from the closest point to the center - return geo.DistanceTo(center.Y, center.X, py, px) <= meters -} diff --git a/pkg/geojson/circle_test.go b/pkg/geojson/circle_test.go deleted file mode 100644 index 8d693c25..00000000 --- a/pkg/geojson/circle_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package geojson - -import ( - "testing" -) - -func TestSegmentIntersectsCirclePointsInside(t *testing.T) { - // either start or end is within the circle - start := Position{X: -122.4408378, Y: 37.7341129, Z: 0} - end := Position{X: -122.4408378, Y: 37.733, Z: 0} - center := Position{X: -122.4409, Y: 37.734, Z: 0} - meters := 30.0 - if !SegmentIntersectsCircle(start, end, center, meters) { - t.Fatal("!") - } - center = Position{X: -122.4409, Y: 37.733, Z: 0} - if !SegmentIntersectsCircle(start, end, center, meters) { - t.Fatal("!") - } -} - -func TestSegmentIntersectsCirclePointsOutside(t *testing.T) { - // neither start nor end are within the circle, but the segment intersects it - start := Position{X: -122.4408378, Y: 37.7341129, Z: 0} - end := Position{X: -122.4408378, Y: 37.733, Z: 0} - center := Position{X: -122.4412, Y: 37.7335, Z: 0} - meters := 70.0 - if !SegmentIntersectsCircle(start, end, center, meters) { - t.Fatal("!") - } -} - -func TestSegmentIntersectsCircleLineButNotSegment(t *testing.T) { - // the line of the segment intersects the circle, but the segment does not - start := Position{X: -122.4408378, Y: 37.7341129, Z: 0} - end := Position{X: -122.4408378, Y: 37.733, Z: 0} - center := Position{X: -122.4412, Y: 37.737, Z: 0} - meters := 70.0 - if SegmentIntersectsCircle(start, end, center, meters) { - t.Fatal("!") - } -} diff --git a/pkg/geojson/clip.go b/pkg/geojson/clip.go deleted file mode 100644 index 11ebc365..00000000 --- a/pkg/geojson/clip.go +++ /dev/null @@ -1,121 +0,0 @@ -package geojson - -// Cohen-Sutherland Line Clipping -// https://www.cs.helsinki.fi/group/goa/viewing/leikkaus/lineClip.html -func ClipSegment(start, end Position, bbox BBox) (resStart, resEnd Position, rejected bool) { - startCode := getCode(bbox, start) - endCode := getCode(bbox, end) - - if (startCode | endCode) == 0 { - // trivially accept - resStart, resEnd = start, end - } else if (startCode & endCode) != 0 { - // trivially reject - rejected = true - } else if startCode != 0 { - // start is outside. get new start. - newStart := intersect(bbox, startCode, start, end) - resStart, resEnd, rejected = ClipSegment(newStart, end, bbox) - } else { - // end is outside. get new end. - newEnd := intersect(bbox, endCode, start, end) - resStart, resEnd, rejected = ClipSegment(start, newEnd, bbox) - } - - return -} - -// Sutherland-Hodgman Polygon Clipping -// https://www.cs.helsinki.fi/group/goa/viewing/leikkaus/intro2.html -func ClipRing(ring[] Position, bbox BBox) (resRing []Position) { - - if len(ring) < 4 { - // under 4 elements this is not a polygon ring! - return - } - - var edge uint8 - var inside, prevInside bool - var prev Position - - for edge = 1; edge <= 8; edge *=2 { - prev = ring[len(ring) - 2] - prevInside = (getCode(bbox, prev) & edge) == 0 - - for _, p := range ring { - - inside = (getCode(bbox, p) & edge) == 0 - - if prevInside && inside { - // Staying inside - resRing = append(resRing, p) - } else if prevInside && !inside { - // Leaving - resRing = append(resRing, intersect(bbox, edge, prev, p)) - } else if !prevInside && inside { - // Entering - resRing = append(resRing, intersect(bbox, edge, prev, p)) - resRing = append(resRing, p) - } else { - // Staying outside - } - - prev, prevInside = p, inside - } - - if resRing[0] != resRing[len(resRing)-1] { - resRing = append(resRing, resRing[0]) - } - ring, resRing = resRing, []Position{} - } - - resRing = ring - return -} - - -func getCode(bbox BBox, point Position) (code uint8) { - code = 0 - - if point.X < bbox.Min.X { - code |= 1 // left - } else if point.X > bbox.Max.X { - code |= 2 // right - } - - if point.Y < bbox.Min.Y { - code |= 4 // bottom - } else if point.Y > bbox.Max.Y { - code |= 8 // top - } - - return -} - - -func intersect(bbox BBox, code uint8, start, end Position) (new Position) { - if (code & 8) != 0 { // top - new = Position{ - X: start.X + (end.X - start.X) * (bbox.Max.Y - start.Y) / (end.Y - start.Y), - Y: bbox.Max.Y, - } - } else if (code & 4) != 0 { // bottom - new = Position{ - X: start.X + (end.X - start.X) * (bbox.Min.Y - start.Y) / (end.Y - start.Y), - Y: bbox.Min.Y, - } - } else if (code & 2) != 0 { //right - new = Position{ - X: bbox.Max.X, - Y: start.Y + (end.Y - start.Y) * (bbox.Max.X - start.X) / (end.X - start.X), - } - } else if (code & 1) != 0 { // left - new = Position{ - X: bbox.Min.X, - Y: start.Y + (end.Y - start.Y) * (bbox.Min.X - start.X) / (end.X - start.X), - } - } else { // should not call intersect with the zero code - } - - return -} diff --git a/pkg/geojson/clip_test.go b/pkg/geojson/clip_test.go deleted file mode 100644 index 1e1825fd..00000000 --- a/pkg/geojson/clip_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package geojson - -import "testing" - -func TestClipLineString(t *testing.T) { - ls, _ := fillLineString( - []Position{ - {X: 1, Y: 1}, - {X: 2, Y: 2}, - {X: 3, Y: 1}, - }, nil, nil) - bbox := BBox{ - Min: Position{X: 1.5, Y: 0.5}, - Max: Position{X: 2.5, Y: 1.8}, - } - clipped := ls.Clipped(bbox) - cl, ok := clipped.(MultiLineString) - if !ok { - t.Fatal("wrong type") - } - if len(cl.Coordinates) != 2 { - t.Fatal("result must have two parts in MultiString") - } -} - - -func TestClipPolygon(t *testing.T) { - outer := []Position{ - {X: 2, Y: 2}, - {X: 1, Y: 2}, - {X: 1.5, Y: 1.5}, - {X: 1, Y: 1}, - {X: 2, Y: 1}, - {X: 2, Y: 2}, - } - inner := []Position{ - {X: 1.9, Y: 1.9}, - {X: 1.2, Y: 1.9}, - {X: 1.45, Y: 1.65}, - {X: 1.9, Y: 1.5}, - {X: 1.9, Y: 1.9}, - - } - polygon, _ := fillPolygon([][]Position{outer, inner}, nil, nil) - bbox := BBox{ - Min: Position{X: 1.3, Y: 1.3}, - Max: Position{X: 1.4, Y: 2.15}, - } - clipped := polygon.Clipped(bbox) - cp, ok := clipped.(Polygon) - if !ok { - t.Fatal("wrong type") - } - if len(cp.Coordinates) != 2 { - t.Fatal("result must have two parts in Polygon") - } -} diff --git a/pkg/geojson/detect_test.go b/pkg/geojson/detect_test.go deleted file mode 100644 index ffc55efa..00000000 --- a/pkg/geojson/detect_test.go +++ /dev/null @@ -1,362 +0,0 @@ -package geojson - -import ( - "testing" - - "github.com/tidwall/gjson" -) - -// https://gist.github.com/tidwall/5524c468fa4212b89e9c3532a5b1f355 -var detectJSON = `{"type":"FeatureCollection","features":[ - {"type":"Feature","properties":{"id":"point1"},"geometry":{"type":"Point","coordinates":[-73.98549556732178,40.72198994979771]}}, - {"type":"Feature","properties":{"id":"polygon1"},"geometry":{"type":"Polygon","coordinates":[[[-74.0035629272461,40.71994085251552],[-73.98914337158203,40.71994085251552],[-73.98914337158203,40.72755146730012],[-74.0035629272461,40.72755146730012],[-74.0035629272461,40.71994085251552]]]}}, - {"type":"Feature","properties":{"id":"linestring1"},"geometry":{"type":"LineString","coordinates":[[-73.98382186889648,40.73652697126574],[-73.98821868896482,40.73652697126574],[-73.97420883178711,40.72943772441242]]}}, - {"type":"Feature","properties":{"id":"linestring2"},"geometry":{"type":"LineString","coordinates":[[-73.98146152496338,40.72716120053256],[-73.99098873138428,40.724754504892424]]}}, - {"type":"Feature","properties":{"id":"linestring3"},"geometry":{"type":"LineString","coordinates":[[-73.98386478424072,40.72696606629052],[-73.98090362548828,40.72501469240076],[-73.97837162017821,40.72621804639551]]}}, - {"type":"Feature","properties":{"id":"polygon2","fill":"#0433ff"},"geometry":{"type":"Polygon","coordinates":[[[-73.98661136627197,40.72540497175607],[-73.99064540863037,40.71938791069558],[-73.98807048797607,40.71779411151556],[-73.97571086883545,40.72338850378556],[-73.98017406463623,40.72960033028089],[-73.98661136627197,40.72540497175607]]]}}, - {"type":"Feature","properties":{"id":"polygon3"},"geometry":{"type":"Polygon","coordinates":[[[-73.98352146148682,40.72550254123727],[-73.98579597473145,40.72088409560772],[-73.97914409637451,40.72251034541217],[-73.98017406463623,40.72599038649773],[-73.98352146148682,40.72550254123727]],[[-73.98300647735596,40.72439674540761],[-73.98111820220947,40.72446179272971],[-73.98141860961913,40.7221525738643],[-73.98300647735596,40.72439674540761]]]}}, - {"type":"Feature","properties":{"id":"multipoint1","marker-color":"#941751"},"geometry":{"type":"MultiPoint","coordinates":[[-73.98957252502441,40.72049378974239],[-73.9897871017456,40.720233584560724],[-73.9897871017456,40.721664700472566],[-73.99085998535155,40.720916620993194],[-73.9912462234497,40.720331161623065]]}}, - {"type":"Feature","properties":{"id":"multilinestring1","stroke":"#941751"},"geometry":{"type":"MultiLineString","coordinates":[[[-73.98442268371582,40.72459188718318],[-73.98463726043701,40.72384384060296],[-73.98382186889648,40.72355112443509]],[[-73.9850664138794,40.72358364851732],[-73.98476600646973,40.72485207532725],[-73.9854097366333,40.72491712220435]]]}}, - {"type":"Feature","properties":{"id":"multipolygon1","fill":"#941751"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-73.98021697998047,40.72429917430525],[-73.97892951965332,40.7250472157678],[-73.98000240325926,40.72524235563617],[-73.98021697998047,40.72429917430525]]],[[[-73.97901535034178,40.72452683998823],[-73.9788007736206,40.72345355209305],[-73.97764205932617,40.72410403167144],[-73.97901535034178,40.72452683998823]]]]}}, - {"type":"Feature","properties":{"id":"point2"},"geometry":{"type":"Point","coordinates":[-73.98326396942139,40.723681220668624]}}, - {"type":"Feature","properties":{"id":"point3"},"geometry":{"type":"Point","coordinates":[-73.98196396942139,40.723681220668624]}}, - {"type":"Feature","properties":{"id":"point4"},"geometry":{"type":"Point","coordinates":[-73.9785396942139,40.7238220668624]}} -]}` - -func getByID(id string) Object { - var r gjson.Result - gjson.Get(detectJSON, "features").ForEach(func(_, v gjson.Result) bool { - if v.Get("properties.id").String() == id { - r = v.Get("geometry") - return false - } - return true - }) - if !r.Exists() { - panic("not found '" + id + "'") - } - o, err := ObjectJSON(r.String()) - if err != nil { - panic(err) - } - if p, ok := o.(SimplePoint); ok { - o = Point{Coordinates: Position{X: p.X, Y: p.Y}} - } - return o -} - -func toSimplePoint(p Object) Object { - return SimplePoint{X: p.(Point).Coordinates.X, Y: p.(Point).Coordinates.Y} -} - -// Basic geometry detections -// Point -> Point -// Point -> MultiPoint -// Point -> LineString -// Point -> MultiLineString -// Point -> Polygon -// Point -> MultiPolygon -// MultiPoint -> Point -// MultiPoint -> MultiPoint -// MultiPoint -> LineString -// MultiPoint -> MultiLineString -// MultiPoint -> Polygon -// MultiPoint -> MultiPolygon -// LineString -> Point -// LineString -> MultiPoint -// LineString -> LineString -// LineString -> MultiLineString -// LineString -> Polygon -// LineString -> MultiPolygon -// MultiLineString -> Point -// MultiLineString -> MultiPoint -// MultiLineString -> LineString -// MultiLineString -> MultiLineString -// MultiLineString -> Polygon -// MultiLineString -> MultiPolygon -// Polygon -> Point -// Polygon -> MultiPoint -// Polygon -> LineString -// Polygon -> MultiLineString -// Polygon -> Polygon -// Polygon -> MultiPolygon -// MultiPolygon -> Point -// MultiPolygon -> MultiPoint -// MultiPolygon -> LineString -// MultiPolygon -> MultiLineString -// MultiPolygon -> Polygon -// MultiPolygon -> MultiPolygon - -func TestDetectSimplePointSimplePoint(t *testing.T) { - p1 := toSimplePoint(getByID("point1")) - p2 := toSimplePoint(getByID("point2")) - if p1.Intersects(p2) { - t.Fatal("expected false") - } - if !p1.Intersects(p1) { - t.Fatal("expected true") - } - if p1.Within(p2) { - t.Fatal("expected false") - } - if !p1.Within(p1) { - t.Fatal("expected true") - } -} -func TestDetectPointPoint(t *testing.T) { - p1 := getByID("point1") - p2 := getByID("point2") - if p1.Intersects(p2) { - t.Fatal("expected false") - } - if !p1.Intersects(p1) { - t.Fatal("expected true") - } - if p1.Within(p2) { - t.Fatal("expected false") - } - if !p1.Within(p1) { - t.Fatal("expected true") - } -} -func TestDetectPointMultPoint(t *testing.T) { - p1 := getByID("point1") - mp1 := getByID("multipoint1") - if p1.Intersects(mp1) { - t.Fatal("expected false") - } - if p1.Within(mp1) { - t.Fatal("expected false") - } - pp := Point{Coordinates: mp1.(MultiPoint).Coordinates[0]} - if !pp.Intersects(mp1) { - t.Fatal("expected true") - } - if !pp.Within(mp1) { - t.Fatal("expected true") - } -} -func TestDetectPointLineString(t *testing.T) { - p1 := getByID("point1") - ls1 := getByID("linestring1") - if p1.Intersects(ls1) { - t.Fatal("expected false") - } - if p1.Within(ls1) { - t.Fatal("expected false") - } - pp := Point{Coordinates: ls1.(LineString).Coordinates[0]} - if !pp.Intersects(ls1) { - t.Fatal("expected true") - } - if !pp.Within(ls1) { - t.Fatal("expected true") - } -} -func TestDetectPointMultiLineString(t *testing.T) { - p1 := getByID("point1") - mls1 := getByID("multilinestring1") - if p1.Intersects(mls1) { - t.Fatal("expected false") - } - if p1.Within(mls1) { - t.Fatal("expected false") - } - pp := Point{Coordinates: mls1.(MultiLineString).Coordinates[0][1]} - if !pp.Intersects(mls1) { - t.Fatal("expected true") - } - if !pp.Within(mls1) { - t.Fatal("expected true") - } -} -func TestDetectPointPolygon(t *testing.T) { - p1 := getByID("point1") - pl3 := getByID("polygon3") - if p1.Intersects(pl3) { - t.Fatal("expected false") - } - if p1.Within(pl3) { - t.Fatal("expected false") - } - p2 := getByID("point2") - if !p2.Intersects(pl3) { - t.Fatal("expected true") - } - if !p2.Within(pl3) { - t.Fatal("expected true") - } - p3 := getByID("point3") - if p3.Intersects(pl3) { - t.Fatal("expected false") - } - if p3.Within(pl3) { - t.Fatal("expected false") - } -} - -func TestDetectPointMultiPolygon(t *testing.T) { - p3 := getByID("point3") - p4 := getByID("point4") - mp1 := getByID("multipolygon1") - if p3.Intersects(mp1) { - t.Fatal("expected false") - } - if p3.Within(mp1) { - t.Fatal("expected false") - } - if !p4.Intersects(mp1) { - t.Fatal("expected true") - } - if !p4.Within(mp1) { - t.Fatal("expected true") - } -} - -func TestDetectMultiPointPoint(t *testing.T) { - p1 := getByID("point1") - mp1 := getByID("multipoint1") - if mp1.Intersects(p1) { - t.Fatal("expected false") - } - if mp1.Within(p1) { - t.Fatal("expected false") - } - pp := Point{Coordinates: mp1.(MultiPoint).Coordinates[0]} - if !mp1.Intersects(pp) { - t.Fatal("expected true") - } - if mp1.Within(pp) { - t.Fatal("expected false") - } -} - -func TestDetectMultiPointPolygon(t *testing.T) { - mp1 := getByID("multipoint1") - pl1 := getByID("polygon1") - pl2 := getByID("polygon2") - pl3 := getByID("polygon3") - if !mp1.Intersects(pl1) { - t.Fatal("expected true") - } - if !mp1.Within(pl1) { - t.Fatal("expected true") - } - if !mp1.Intersects(pl2) { - t.Fatal("expected true") - } - if mp1.Within(pl2) { - t.Fatal("expected false") - } - if mp1.Intersects(pl3) { - t.Fatal("expected false") - } - if mp1.Within(pl3) { - t.Fatal("expected false") - } -} - -func TestDetectLineStringLineString(t *testing.T) { - ls1 := getByID("linestring1") - ls2 := getByID("linestring2") - ls3 := getByID("linestring3") - if ls1.Intersects(ls2) { - t.Fatal("expected false") - } - if ls2.Intersects(ls1) { - t.Fatal("expected false") - } - if !ls2.Intersects(ls3) { - t.Fatal("expected true") - } - if !ls2.Intersects(ls3) { - t.Fatal("expected true") - } -} -func TestDetectLineStringPolygon(t *testing.T) { - ls1 := getByID("linestring1") - ls2 := getByID("linestring2") - ls3 := getByID("linestring3") - pl1 := getByID("polygon1") - pl2 := getByID("polygon2") - pl3 := getByID("polygon3") - - if ls1.Intersects(pl1) { - t.Fatal("expected false") - } - if pl1.Intersects(ls1) { - t.Fatal("expected false") - } - if ls1.Intersects(pl3) { - t.Fatal("expected false") - } - if pl3.Intersects(ls1) { - t.Fatal("expected false") - } - if !ls2.Intersects(pl1) { - t.Fatal("expected true") - } - if !pl2.Intersects(ls2) { - t.Fatal("expected true") - } - if ls2.Within(pl1) { - t.Fatal("expected false") - } - if ls2.Intersects(pl3) { - t.Fatal("expected false") - } - if pl3.Intersects(ls2) { - t.Fatal("expected false") - } - if !ls3.Intersects(pl2) { - t.Fatal("expected true") - } - if !pl2.Intersects(ls3) { - t.Fatal("expected true") - } - if !ls3.Within(pl2) { - t.Fatal("expected true") - } - if ls2.Intersects(pl3) { - t.Fatal("expected false") - } - if pl3.Intersects(ls2) { - t.Fatal("expected false") - } - if ls2.Intersects(pl3) { - t.Fatal("expected false") - } - if pl3.Intersects(ls2) { - t.Fatal("expected false") - } - if !ls3.Intersects(pl3) { - t.Fatal("expected true") - } - if !pl3.Intersects(ls3) { - t.Fatal("expected true") - } -} -func TestDetectMultiLineStringPolygon(t *testing.T) { - mls1 := getByID("multilinestring1") - pl1 := getByID("polygon1") - pl2 := getByID("polygon2") - pl3 := getByID("polygon3") - if mls1.Intersects(pl1) { - t.Fatal("expected false") - } - if mls1.Within(pl1) { - t.Fatal("expected false") - } - if !mls1.Intersects(pl2) { - t.Fatal("expected true") - } - if !mls1.Within(pl2) { - t.Fatal("expected true") - } - if !mls1.Intersects(pl3) { - t.Fatal("expected true") - } - if mls1.Within(pl3) { - t.Fatal("expected false") - } -} diff --git a/pkg/geojson/feature.go b/pkg/geojson/feature.go deleted file mode 100644 index e365e127..00000000 --- a/pkg/geojson/feature.go +++ /dev/null @@ -1,245 +0,0 @@ -package geojson - -import ( - "encoding/binary" - - "github.com/tidwall/gjson" - "github.com/tidwall/tile38/pkg/geojson/geohash" -) - -// Feature is a geojson object with the type "Feature" -type Feature struct { - Geometry Object - BBox *BBox - bboxDefined bool - idprops string // raw id and properties separated by a '\0' -} - -func fillFeatureMap(json string) (Feature, error) { - var g Feature - v := gjson.Get(json, "geometry") - switch v.Type { - default: - return g, errInvalidGeometryMember - case gjson.Null: - return g, errGeometryMemberRequired - case gjson.JSON: - var err error - g.Geometry, err = objectMap(v.Raw, feat) - if err != nil { - return g, err - } - } - var err error - g.BBox, err = fillBBox(json) - if err != nil { - return g, err - } - g.bboxDefined = g.BBox != nil - if !g.bboxDefined { - cbbox := g.CalculatedBBox() - g.BBox = &cbbox - } - var propsExists bool - props := gjson.Get(json, "properties") - switch props.Type { - default: - return g, errInvalidPropertiesMember - case gjson.Null: - case gjson.JSON: - propsExists = true - } - id := gjson.Get(json, "id") - if id.Exists() || propsExists { - g.idprops = makeCompositeRaw(id.Raw, props.Raw) - } - return g, err -} - -// Geohash converts the object to a geohash value. -func (g Feature) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// CalculatedPoint is a point representation of the object. -func (g Feature) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// CalculatedBBox is exterior bbox containing the object. -func (g Feature) CalculatedBBox() BBox { - if g.BBox != nil { - return *g.BBox - } - return g.Geometry.CalculatedBBox() -} - -// PositionCount return the number of coordinates. -func (g Feature) PositionCount() int { - res := g.Geometry.PositionCount() - if g.BBox != nil { - return 2 + res - } - return res -} - -// Weight returns the in-memory size of the object. -func (g Feature) Weight() int { - res := g.PositionCount() * sizeofPosition - res += len(g.idprops) - return res -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g Feature) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g Feature) getRaw() (id, props string) { - if len(g.idprops) == 0 { - return "", "" - } - switch g.idprops[0] { - default: - lnp := int(g.idprops[0]) + 1 - return g.idprops[1:lnp], g.idprops[lnp:] - case 255: - lnp := int(binary.LittleEndian.Uint64([]byte(g.idprops[1:9]))) + 9 - return g.idprops[9:lnp], g.idprops[lnp:] - } -} - -func makeCompositeRaw(idRaw, propsRaw string) string { - idRaw = stripWhitespace(idRaw) - propsRaw = stripWhitespace(propsRaw) - if len(idRaw) == 0 && len(propsRaw) == 0 { - return "" - } - var raw []byte - if len(idRaw) > 0xFF-1 { - raw = make([]byte, len(idRaw)+len(propsRaw)+9) - raw[0] = 0xFF - binary.LittleEndian.PutUint64(raw[1:9], uint64(len(idRaw))) - copy(raw[9:], idRaw) - copy(raw[len(idRaw)+9:], propsRaw) - } else { - raw = make([]byte, len(idRaw)+len(propsRaw)+1) - raw[0] = byte(len(idRaw)) - copy(raw[1:], idRaw) - copy(raw[len(idRaw)+1:], propsRaw) - } - return string(raw) -} - -func (g Feature) appendJSON(json []byte) []byte { - json = append(json, `{"type":"Feature","geometry":`...) - json = append(json, g.Geometry.JSON()...) - if g.bboxDefined { - json = appendBBoxJSON(json, g.BBox) - } - idRaw, propsRaw := g.getRaw() - if propsRaw != "" { - json = append(json, `,"properties":`...) - json = append(json, propsRaw...) - } - if idRaw != "" { - json = append(json, `,"id":`...) - json = append(json, idRaw...) - } - return append(json, '}') -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g Feature) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g Feature) String() string { - return g.JSON() -} - -// Bytes is the bytes representation of the object. -func (g Feature) Bytes() []byte { - return []byte(g.JSON()) -} -func (g Feature) bboxPtr() *BBox { - return g.BBox -} -func (g Feature) hasPositions() bool { - if g.bboxDefined { - return true - } - return g.Geometry.hasPositions() -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g Feature) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - return g.Geometry.WithinBBox(bbox) -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g Feature) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - return g.Geometry.IntersectsBBox(bbox) -} - -// Within detects if the object is fully contained inside another object. -func (g Feature) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - return g.Geometry.Within(o) - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g Feature) WithinCircle(center Position, meters float64) bool { - return g.Geometry.WithinCircle(center, meters) -} - -// Intersects detects if the object intersects another object. -func (g Feature) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - return g.Geometry.Intersects(o) - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g Feature) IntersectsCircle(center Position, meters float64) bool { - return g.Geometry.IntersectsCircle(center, meters) -} - -// Nearby detects if the object is nearby a position. -func (g Feature) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g Feature) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g Feature) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g Feature) Clipped(bbox BBox) Object { - clippedGeometry := g.Geometry.Clipped(bbox) - - res := Feature{Geometry: clippedGeometry, idprops: g.idprops} - cbbox := clippedGeometry.CalculatedBBox() - res.BBox = &cbbox - - return res -} diff --git a/pkg/geojson/feature_test.go b/pkg/geojson/feature_test.go deleted file mode 100644 index 1e68bcb2..00000000 --- a/pkg/geojson/feature_test.go +++ /dev/null @@ -1,769 +0,0 @@ -package geojson - -import ( - "testing" -) - -func TestFeature(t *testing.T) { - testJSON(t, `{ - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [-80.72487831115721,35.26545403190955], - [-80.72135925292969,35.26727607954368], - [-80.71517944335938,35.26769654625573], - [-80.7125186920166,35.27035945142482], - [-80.70857048034668,35.268257165144064], - [-80.70479393005371,35.268397319259996], - [-80.70324897766113,35.26503355355979], - [-80.71088790893555,35.2553619492954], - [-80.71681022644043,35.2553619492954], - [-80.7150936126709,35.26054831539319], - [-80.71869850158691,35.26026797976481], - [-80.72032928466797,35.26061839914875], - [-80.72264671325684,35.26033806376283], - [-80.72487831115721,35.26545403190955] - ] - ] - }, - "id": "102374", - "properties": { - "name": "Plaza Road Park" - } -}`) -} - -var complexFeature = `{ - "id": 202418985, - "type": "Feature", - "properties": { - "addr:full":"5607 McKinley Ave Los Angeles CA 90011", - "addr:housenumber":"5607", - "addr:postcode":"90011", - "addr:street":"Mckinley Ave", - "edtf:cessation":"uuuu", - "edtf:inception":"uuuu", - "geom:area":0.0, - "geom:bbox":"-118.26089,33.99073,-118.26089,33.99073", - "geom:latitude":33.99073, - "geom:longitude":-118.26089, - "iso:country":"US", - "mz:hierarchy_label":1, - "sg:address":"5607 McKinley Ave", - "sg:city":"Los Angeles", - "sg:classifiers":[ - { - "category":"Wholesale", - "subcategory":"Toys & Hobbies", - "type":"Manufacturing & Wholesale Goods" - } - ], - "sg:owner":"simplegeo", - "sg:phone":"+1 323 231 0540", - "sg:postcode":"90011", - "sg:province":"CA", - "sg:tags":[ - "wholesaler" - ], - "src:geom":"simplegeo", - "wof:belongsto":[ - 85633793, - 85688637 - ], - "wof:breaches":[], - "wof:concordances":{ - "sg:id":"SG_0i3ZtGVxBmvnGGcg7wZrlY_33.990730_-118.260890@1294081369" - }, - "wof:country":"US", - "wof:geomhash":"fa3426d7a9b6c92b5e2857b4daef560f", - "wof:hierarchy":[ - { - "continent_id":-1, - "country_id":85633793, - "locality_id":-1, - "neighbourhood_id":-1, - "region_id":85688637, - "venue_id":202418985 - } - ], - "wof:id":202418985, - "wof:lastmodified":1472331065, - "wof:name":"Hotfix Wholesale Inc", - "wof:parent_id":-1, - "wof:placetype":"venue", - "wof:repo":"whosonfirst-data-venue-us-ca", - "wof:superseded_by":[], - "wof:supersedes":[], - "wof:tags":[ - "wholesaler" - ], - "added:by:tidwall": "\n\"\\\\15\u00f8C 3\u0111" -}, - "bbox": [ - -118.26089, - 33.99073, - -118.26089, - 33.99073 -], - "geometry": {"coordinates":[-118.26089,33.99073],"type":"Point"} -}` - -func TestComplexFeature(t *testing.T) { - testJSON(t, complexFeature) - o, err := ObjectJSON(complexFeature) - if err != nil { - t.Fatal(err) - } - _ = o -} - -func TestIssue245(t *testing.T) { - json := `{ - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [113.840391, 23.36073], - [113.840837, 23.360618], - [113.841198, 23.360528], - [113.84295, 23.360252], - [113.844386, 23.359914], - [113.844674, 23.359847], - [113.845843, 23.359466], - [113.846787, 23.358753], - [113.84679, 23.358751], - [113.847128, 23.358254], - [113.847571, 23.357602], - [113.847573, 23.357598], - [113.848175, 23.356521], - [113.848177, 23.356517], - [113.848187, 23.3565], - [113.848189, 23.356497], - [113.84847, 23.355449], - [113.848471, 23.355445], - [113.848475, 23.355126], - [113.848486, 23.354203], - [113.848373, 23.353516], - [113.848122, 23.352467], - [113.847432, 23.351258], - [113.846102, 23.349542], - [113.845853, 23.349288], - [113.84494, 23.348358], - [113.844435, 23.347908], - [113.843638, 23.347198], - [113.840895, 23.345392], - [113.836433, 23.342373], - [113.835963, 23.342033], - [113.83469, 23.34111], - [113.833801, 23.340465], - [113.831238, 23.338917], - [113.830698, 23.33835], - [113.830644, 23.337639], - [113.830642, 23.337639], - [113.825764, 23.337862], - [113.82575, 23.337897], - [113.82575, 23.337899], - [113.825749, 23.337901], - [113.825748, 23.337905], - [113.825747, 23.337907], - [113.825746, 23.337909], - [113.825746, 23.337912], - [113.825745, 23.337914], - [113.825744, 23.337916], - [113.825743, 23.337919], - [113.825742, 23.337921], - [113.825741, 23.337923], - [113.82574, 23.337926], - [113.825739, 23.337928], - [113.825738, 23.33793], - [113.825738, 23.337933], - [113.825737, 23.337935], - [113.825736, 23.337937], - [113.825735, 23.33794], - [113.825734, 23.337942], - [113.825733, 23.337944], - [113.825732, 23.337947], - [113.825731, 23.337949], - [113.82573, 23.337951], - [113.825729, 23.337954], - [113.825728, 23.337956], - [113.825727, 23.337959], - [113.825726, 23.337962], - [113.825725, 23.337964], - [113.825725, 23.337966], - [113.825724, 23.337969], - [113.825723, 23.337971], - [113.825722, 23.337973], - [113.825721, 23.337975], - [113.82572, 23.337978], - [113.825719, 23.33798], - [113.825718, 23.337982], - [113.825717, 23.337985], - [113.825717, 23.337987], - [113.825716, 23.337989], - [113.825715, 23.337992], - [113.825715, 23.337994], - [113.825714, 23.337996], - [113.825713, 23.337999], - [113.825712, 23.338001], - [113.825711, 23.338003], - [113.82571, 23.338006], - [113.825709, 23.338008], - [113.825708, 23.33801], - [113.825707, 23.338013], - [113.825706, 23.338015], - [113.825705, 23.338018], - [113.825705, 23.338021], - [113.825704, 23.338023], - [113.825703, 23.338025], - [113.825702, 23.338028], - [113.825701, 23.33803], - [113.8257, 23.338032], - [113.825699, 23.338035], - [113.825698, 23.338037], - [113.825697, 23.338039], - [113.825696, 23.338042], - [113.825696, 23.338044], - [113.825695, 23.338046], - [113.825694, 23.338049], - [113.825693, 23.338051], - [113.825692, 23.338053], - [113.825691, 23.338056], - [113.82569, 23.338058], - [113.82569, 23.338061], - [113.825689, 23.338063], - [113.825689, 23.338065], - [113.825688, 23.338068], - [113.825687, 23.33807], - [113.825686, 23.338072], - [113.825685, 23.338075], - [113.825684, 23.338078], - [113.825683, 23.33808], - [113.825682, 23.338083], - [113.825682, 23.338085], - [113.825681, 23.338087], - [113.82568, 23.33809], - [113.825679, 23.338092], - [113.825678, 23.338094], - [113.825677, 23.338097], - [113.825676, 23.338099], - [113.825675, 23.338101], - [113.825675, 23.338104], - [113.825674, 23.338106], - [113.825673, 23.338108], - [113.825672, 23.338111], - [113.825671, 23.338113], - [113.82567, 23.338115], - [113.825669, 23.338118], - [113.825669, 23.33812], - [113.825668, 23.338123], - [113.825668, 23.338125], - [113.825667, 23.338127], - [113.825666, 23.33813], - [113.825665, 23.338132], - [113.825664, 23.338134], - [113.825664, 23.338138], - [113.825663, 23.33814], - [113.825662, 23.338142], - [113.825661, 23.338145], - [113.82566, 23.338147], - [113.825659, 23.338149], - [113.825659, 23.338152], - [113.825658, 23.338154], - [113.825657, 23.338156], - [113.825656, 23.338159], - [113.825655, 23.338161], - [113.825654, 23.338164], - [113.825654, 23.338166], - [113.825653, 23.338168], - [113.825652, 23.338171], - [113.825651, 23.338173], - [113.82565, 23.338175], - [113.82565, 23.338178], - [113.825649, 23.33818], - [113.825648, 23.338182], - [113.825647, 23.338185], - [113.825646, 23.338187], - [113.825646, 23.33819], - [113.825646, 23.338192], - [113.825645, 23.338194], - [113.825644, 23.338197], - [113.825643, 23.3382], - [113.825642, 23.338202], - [113.825642, 23.338205], - [113.825641, 23.338207], - [113.82564, 23.33821], - [113.825639, 23.338212], - [113.825639, 23.338214], - [113.825638, 23.338217], - [113.825637, 23.338219], - [113.825636, 23.338221], - [113.825635, 23.338224], - [113.825635, 23.338226], - [113.825634, 23.338229], - [113.825633, 23.338231], - [113.825632, 23.338233], - [113.825632, 23.338236], - [113.825631, 23.338238], - [113.82563, 23.33824], - [113.825629, 23.338243], - [113.825628, 23.338245], - [113.825629, 23.338248], - [113.825628, 23.33825], - [113.825627, 23.338252], - [113.825626, 23.338255], - [113.825626, 23.338257], - [113.825625, 23.338259], - [113.825624, 23.338262], - [113.825623, 23.338265], - [113.825623, 23.338268], - [113.825622, 23.33827], - [113.825621, 23.338272], - [113.82562, 23.338275], - [113.82562, 23.338277], - [113.825619, 23.33828], - [113.825618, 23.338282], - [113.825618, 23.338284], - [113.825617, 23.338287], - [113.825616, 23.338289], - [113.825615, 23.338292], - [113.825532, 23.338833], - [113.825532, 23.338835], - [113.825532, 23.338838], - [113.825532, 23.33884], - [113.825532, 23.338843], - [113.825532, 23.338845], - [113.825531, 23.338848], - [113.825532, 23.33885], - [113.825532, 23.338853], - [113.825532, 23.338855], - [113.825532, 23.338858], - [113.825532, 23.33886], - [113.825532, 23.338863], - [113.825531, 23.338865], - [113.825531, 23.338868], - [113.825531, 23.33887], - [113.825531, 23.338873], - [113.825531, 23.338875], - [113.825531, 23.338878], - [113.825532, 23.33888], - [113.825532, 23.338883], - [113.825531, 23.338885], - [113.825531, 23.338888], - [113.825531, 23.33889], - [113.825531, 23.338893], - [113.825531, 23.338895], - [113.825531, 23.338898], - [113.825531, 23.3389], - [113.825531, 23.338903], - [113.825531, 23.338905], - [113.82553, 23.338908], - [113.825531, 23.33891], - [113.825531, 23.338913], - [113.825531, 23.338915], - [113.825531, 23.338918], - [113.825531, 23.33892], - [113.825531, 23.338923], - [113.825531, 23.338925], - [113.825531, 23.338928], - [113.825531, 23.33893], - [113.825531, 23.338933], - [113.82553, 23.338935], - [113.82553, 23.338938], - [113.825531, 23.33894], - [113.825531, 23.338942], - [113.825531, 23.338945], - [113.825531, 23.338947], - [113.825531, 23.33895], - [113.825531, 23.338952], - [113.825531, 23.338956], - [113.825531, 23.338958], - [113.825531, 23.338961], - [113.825531, 23.338963], - [113.825531, 23.338966], - [113.825532, 23.338968], - [113.825532, 23.338971], - [113.825532, 23.338973], - [113.825531, 23.338976], - [113.825531, 23.338978], - [113.825531, 23.338981], - [113.825531, 23.338983], - [113.825531, 23.338986], - [113.825531, 23.338988], - [113.825531, 23.338991], - [113.825531, 23.338993], - [113.825531, 23.338996], - [113.825532, 23.338998], - [113.825532, 23.339001], - [113.825532, 23.339003], - [113.825532, 23.339006], - [113.825532, 23.339008], - [113.825532, 23.339011], - [113.825532, 23.339013], - [113.825532, 23.339016], - [113.825532, 23.339018], - [113.825532, 23.339021], - [113.825532, 23.339023], - [113.825533, 23.339026], - [113.825533, 23.339028], - [113.825533, 23.339031], - [113.825533, 23.339033], - [113.825533, 23.339036], - [113.825533, 23.339038], - [113.825533, 23.339041], - [113.825533, 23.339043], - [113.825533, 23.339046], - [113.825533, 23.339048], - [113.825533, 23.339051], - [113.825533, 23.339053], - [113.825534, 23.339056], - [113.825534, 23.339058], - [113.825534, 23.339061], - [113.825534, 23.339063], - [113.825534, 23.339066], - [113.825534, 23.339068], - [113.825534, 23.339071], - [113.825534, 23.339073], - [113.825534, 23.339076], - [113.825534, 23.339078], - [113.825534, 23.339081], - [113.825535, 23.339083], - [113.825535, 23.339086], - [113.825535, 23.339088], - [113.825535, 23.339091], - [113.825535, 23.339093], - [113.825535, 23.339096], - [113.825535, 23.339098], - [113.825535, 23.339101], - [113.825535, 23.339103], - [113.825535, 23.339106], - [113.825535, 23.339108], - [113.825536, 23.339111], - [113.825536, 23.339113], - [113.825536, 23.339116], - [113.825536, 23.339118], - [113.825536, 23.339121], - [113.825536, 23.339123], - [113.825536, 23.339125], - [113.825536, 23.339128], - [113.825536, 23.33913], - [113.825536, 23.339133], - [113.825536, 23.339135], - [113.825537, 23.339138], - [113.825537, 23.33914], - [113.825538, 23.339143], - [113.825538, 23.339145], - [113.825538, 23.339148], - [113.825538, 23.33915], - [113.825538, 23.339153], - [113.825538, 23.339156], - [113.825538, 23.339159], - [113.825538, 23.339161], - [113.825538, 23.339164], - [113.825539, 23.339166], - [113.825539, 23.339169], - [113.825539, 23.339171], - [113.825539, 23.339174], - [113.825539, 23.339176], - [113.825539, 23.339179], - [113.825539, 23.339181], - [113.825539, 23.339184], - [113.825539, 23.339186], - [113.825539, 23.339189], - [113.825539, 23.339191], - [113.82554, 23.339194], - [113.82554, 23.339196], - [113.825541, 23.339199], - [113.825541, 23.339201], - [113.825541, 23.339204], - [113.825541, 23.339206], - [113.825541, 23.339209], - [113.825541, 23.339211], - [113.825541, 23.339214], - [113.825541, 23.339216], - [113.825542, 23.339219], - [113.825542, 23.339221], - [113.825542, 23.339224], - [113.825542, 23.339226], - [113.825542, 23.339229], - [113.825542, 23.339231], - [113.825542, 23.339234], - [113.825542, 23.339236], - [113.825542, 23.339239], - [113.825542, 23.339241], - [113.825543, 23.339244], - [113.825544, 23.339246], - [113.825544, 23.339249], - [113.825544, 23.339251], - [113.825544, 23.339254], - [113.825544, 23.339256], - [113.825544, 23.339259], - [113.825544, 23.339261], - [113.825544, 23.339264], - [113.825544, 23.339266], - [113.825544, 23.339269], - [113.825544, 23.339271], - [113.825545, 23.339274], - [113.825545, 23.339276], - [113.825545, 23.339279], - [113.825545, 23.339281], - [113.825545, 23.339284], - [113.825675, 23.341405], - [113.825676, 23.341408], - [113.825676, 23.34141], - [113.825676, 23.341413], - [113.825676, 23.341415], - [113.825676, 23.341418], - [113.825676, 23.34142], - [113.825676, 23.341423], - [113.825676, 23.341425], - [113.825676, 23.341428], - [113.825676, 23.34143], - [113.825676, 23.341433], - [113.825676, 23.341435], - [113.825677, 23.341438], - [113.825677, 23.34144], - [113.825677, 23.341443], - [113.825678, 23.341445], - [113.825678, 23.341448], - [113.825678, 23.34145], - [113.825678, 23.341453], - [113.825678, 23.341455], - [113.825678, 23.341458], - [113.825678, 23.34146], - [113.825679, 23.341463], - [113.825679, 23.341465], - [113.825679, 23.341468], - [113.825679, 23.34147], - [113.825679, 23.341473], - [113.825679, 23.341475], - [113.825679, 23.341478], - [113.825679, 23.34148], - [113.825679, 23.341483], - [113.825679, 23.341485], - [113.82568, 23.341488], - [113.825681, 23.34149], - [113.825681, 23.341493], - [113.825681, 23.341495], - [113.825681, 23.341498], - [113.825681, 23.3415], - [113.825681, 23.341503], - [113.825681, 23.341505], - [113.825681, 23.341508], - [113.825681, 23.34151], - [113.825681, 23.341513], - [113.825681, 23.341515], - [113.825682, 23.341518], - [113.825682, 23.34152], - [113.825682, 23.341524], - [113.825682, 23.341526], - [113.825682, 23.341529], - [113.825682, 23.341531], - [113.825682, 23.341534], - [113.825683, 23.341536], - [113.825683, 23.341539], - [113.825683, 23.341541], - [113.825684, 23.341544], - [113.825684, 23.341546], - [113.825684, 23.341549], - [113.825684, 23.341551], - [113.825684, 23.341554], - [113.825684, 23.341556], - [113.825684, 23.341558], - [113.825684, 23.341561], - [113.825684, 23.341563], - [113.825684, 23.341566], - [113.825684, 23.341568], - [113.825684, 23.341571], - [113.825685, 23.341573], - [113.825685, 23.341576], - [113.825685, 23.341578], - [113.825685, 23.341581], - [113.825685, 23.341583], - [113.825686, 23.341586], - [113.825686, 23.341588], - [113.825686, 23.341591], - [113.825686, 23.341593], - [113.825686, 23.341596], - [113.825687, 23.341598], - [113.825687, 23.341601], - [113.825687, 23.341603], - [113.825687, 23.341606], - [113.825687, 23.341608], - [113.825687, 23.341611], - [113.825687, 23.341613], - [113.825687, 23.341616], - [113.825687, 23.341618], - [113.825687, 23.341621], - [113.825687, 23.341623], - [113.825688, 23.341626], - [113.825688, 23.341628], - [113.825688, 23.341631], - [113.825688, 23.341633], - [113.825688, 23.341636], - [113.825688, 23.341638], - [113.825688, 23.341641], - [113.825688, 23.341643], - [113.825688, 23.341646], - [113.825688, 23.341648], - [113.825689, 23.341651], - [113.82569, 23.341653], - [113.82569, 23.341656], - [113.82569, 23.341658], - [113.82569, 23.341661], - [113.82569, 23.341663], - [113.82569, 23.341666], - [113.82569, 23.341668], - [113.82569, 23.341671], - [113.82569, 23.341673], - [113.82569, 23.341676], - [113.82569, 23.341678], - [113.825691, 23.341681], - [113.825691, 23.341683], - [113.825691, 23.341686], - [113.825691, 23.341688], - [113.825691, 23.341691], - [113.825691, 23.341693], - [113.825691, 23.341696], - [113.825691, 23.341698], - [113.825691, 23.341701], - [113.825691, 23.341703], - [113.825691, 23.341706], - [113.825691, 23.341708], - [113.825692, 23.341711], - [113.825692, 23.341713], - [113.825692, 23.341716], - [113.825692, 23.341718], - [113.825692, 23.341721], - [113.825692, 23.341723], - [113.825692, 23.341726], - [113.825692, 23.341728], - [113.825692, 23.341731], - [113.825692, 23.341733], - [113.825693, 23.341736], - [113.825693, 23.341738], - [113.825693, 23.34174], - [113.825693, 23.341744], - [113.825693, 23.341746], - [113.825693, 23.341749], - [113.825693, 23.341751], - [113.825693, 23.341754], - [113.825693, 23.341756], - [113.825693, 23.341759], - [113.825693, 23.341761], - [113.825693, 23.341764], - [113.825694, 23.341766], - [113.825694, 23.341769], - [113.825694, 23.341771], - [113.825694, 23.341774], - [113.825694, 23.341776], - [113.825694, 23.341779], - [113.825694, 23.341781], - [113.825694, 23.341784], - [113.825694, 23.341786], - [113.825694, 23.341789], - [113.825694, 23.341791], - [113.825695, 23.341794], - [113.825695, 23.341796], - [113.825695, 23.341799], - [113.825695, 23.341801], - [113.825695, 23.341804], - [113.825695, 23.341806], - [113.825695, 23.341809], - [113.825695, 23.341811], - [113.825695, 23.341814], - [113.825695, 23.341816], - [113.825695, 23.341819], - [113.825696, 23.341821], - [113.825696, 23.341824], - [113.825696, 23.341826], - [113.825696, 23.341829], - [113.825696, 23.341831], - [113.825696, 23.341834], - [113.825696, 23.341836], - [113.825696, 23.341839], - [113.825696, 23.341841], - [113.825696, 23.341844], - [113.825696, 23.341846], - [113.825697, 23.341849], - [113.825697, 23.341851], - [113.825697, 23.341854], - [113.825696, 23.341856], - [113.825639, 23.342752], - [113.825909, 23.343038], - [113.826124, 23.34317], - [113.826246, 23.343207], - [113.826544, 23.343462], - [113.826663, 23.343725], - [113.826921, 23.344028], - [113.826946, 23.344045], - [113.827411, 23.344775], - [113.827452, 23.345071], - [113.827433, 23.345444], - [113.827373, 23.345688], - [113.827847, 23.346486], - [113.827867, 23.346492], - [113.828217, 23.346555], - [113.828812, 23.346578], - [113.829478, 23.346804], - [113.829926, 23.347127], - [113.830393, 23.34749], - [113.830756, 23.347827], - [113.83112, 23.348231], - [113.832415, 23.349689], - [113.833006, 23.350048], - [113.834533, 23.350448], - [113.835235, 23.350919], - [113.835526, 23.35132], - [113.836805, 23.353126], - [113.836931, 23.353474], - [113.837019, 23.35471], - [113.837041, 23.354934], - [113.837129, 23.355633], - [113.837135, 23.355731], - [113.837148, 23.35729], - [113.837124, 23.357391], - [113.837093, 23.357543], - [113.837116, 23.357842], - [113.837231, 23.358547], - [113.837488, 23.359569], - [113.837398, 23.359954], - [113.838019, 23.360873], - [113.838056, 23.360928], - [113.840391, 23.36073] - ] - ] - }, - "properties": { - "center": { - "lat": 0, - "lon": 0 - }, - "smuserid": 960, - "area": 2807456.367185, - "refname": "10301", - "name": "", - "refid": 293, - "districtid": 440183, - "ver": "v1.20170705", - "class": "–°«¯", - "id": 960, - "verdate": "20170705", - "oriid": 1724 - } -} -` - poly := testJSON(t, json).(Feature) - g, _ := ObjectJSON(`{"type":"Point","coordinates":[113.837019, 23.35471]}`) - if !g.Within(poly) { - t.Fatal("!") - } - g, _ = ObjectJSON(`{"type":"Point","coordinates":[114.837019, 23.35471]}`) - if g.Within(poly) { - t.Fatal("!") - } - -} diff --git a/pkg/geojson/featurecollection.go b/pkg/geojson/featurecollection.go deleted file mode 100644 index 4e92221e..00000000 --- a/pkg/geojson/featurecollection.go +++ /dev/null @@ -1,263 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/gjson" - "github.com/tidwall/tile38/pkg/geojson/geohash" -) - -// FeatureCollection is a geojson object with the type "FeatureCollection" -type FeatureCollection struct { - Features []Object - BBox *BBox - bboxDefined bool -} - -func fillFeatureCollectionMap(json string) (FeatureCollection, error) { - var g FeatureCollection - res := gjson.Get(json, "features") - switch res.Type { - default: - return g, errInvalidFeaturesMember - case gjson.Null: - return g, errFeaturesMemberRequired - case gjson.JSON: - if !resIsArray(res) { - return g, errInvalidFeaturesMember - } - v := res.Array() - g.Features = make([]Object, len(v)) - for i, res := range v { - if res.Type != gjson.JSON { - return g, errInvalidFeature - } - o, err := objectMap(res.Raw, fcoll) - if err != nil { - return g, err - } - g.Features[i] = o - } - } - var err error - g.BBox, err = fillBBox(json) - if err != nil { - return g, err - } - g.bboxDefined = g.BBox != nil - if !g.bboxDefined { - cbbox := g.CalculatedBBox() - g.BBox = &cbbox - } - return g, err -} - -// Geohash converts the object to a geohash value. -func (g FeatureCollection) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// CalculatedPoint is a point representation of the object. -func (g FeatureCollection) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// CalculatedBBox is exterior bbox containing the object. -func (g FeatureCollection) CalculatedBBox() BBox { - if g.BBox != nil { - return *g.BBox - } - var bbox BBox - for i, g := range g.Features { - if i == 0 { - bbox = g.CalculatedBBox() - } else { - bbox = bbox.union(g.CalculatedBBox()) - } - } - return bbox -} - -// PositionCount return the number of coordinates. -func (g FeatureCollection) PositionCount() int { - var res int - for _, g := range g.Features { - res += g.PositionCount() - } - if g.BBox != nil { - return 2 + res - } - return res -} - -// Weight returns the in-memory size of the object. -func (g FeatureCollection) Weight() int { - var res int - for _, g := range g.Features { - res += g.Weight() - } - return res -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g FeatureCollection) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g FeatureCollection) appendJSON(json []byte) []byte { - json = append(json, `{"type":"FeatureCollection","features":[`...) - for i, g := range g.Features { - if i != 0 { - json = append(json, ',') - } - json = append(json, g.JSON()...) - } - json = append(json, ']') - if g.bboxDefined { - json = appendBBoxJSON(json, g.BBox) - } - return append(json, '}') -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g FeatureCollection) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g FeatureCollection) String() string { - return g.JSON() -} - -// Bytes is the bytes representation of the object. -func (g FeatureCollection) Bytes() []byte { - return []byte(g.JSON()) -} -func (g FeatureCollection) bboxPtr() *BBox { - return g.BBox -} -func (g FeatureCollection) hasPositions() bool { - if g.BBox != nil { - return true - } - for _, g := range g.Features { - if g.hasPositions() { - return true - } - } - return false -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g FeatureCollection) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - if len(g.Features) == 0 { - return false - } - for _, g := range g.Features { - if !g.WithinBBox(bbox) { - return false - } - } - return true -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g FeatureCollection) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - for _, g := range g.Features { - if g.IntersectsBBox(bbox) { - return true - } - } - return false -} - -// Within detects if the object is fully contained inside another object. -func (g FeatureCollection) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - if len(g.Features) == 0 { - return false - } - for _, f := range g.Features { - if !f.Within(o) { - return false - } - } - return true - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g FeatureCollection) WithinCircle(center Position, meters float64) bool { - if len(g.Features) == 0 { - return false - } - for _, feature := range g.Features { - if !feature.WithinCircle(center, meters) { - return false - } - } - return true -} - -// Intersects detects if the object intersects another object. -func (g FeatureCollection) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - if len(g.Features) == 0 { - return false - } - for _, f := range g.Features { - - if f.Intersects(o) { - return true - } - } - return false - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g FeatureCollection) IntersectsCircle(center Position, meters float64) bool { - for _, feature := range g.Features { - if feature.IntersectsCircle(center, meters) { - return true - } - } - return false -} - -// Nearby detects if the object is nearby a position. -func (g FeatureCollection) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g FeatureCollection) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g FeatureCollection) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g FeatureCollection) Clipped(bbox BBox) Object { - var new_features []Object - for _, feature := range g.Features { - new_features = append(new_features, feature.Clipped(bbox)) - } - - fc := FeatureCollection{Features: new_features} - cbbox := fc.CalculatedBBox() - fc.BBox = &cbbox - return fc -} diff --git a/pkg/geojson/featurecollection_test.go b/pkg/geojson/featurecollection_test.go deleted file mode 100644 index 47064443..00000000 --- a/pkg/geojson/featurecollection_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package geojson - -import ( - "testing" -) - -func TestFeatureCollection(t *testing.T) { - testJSON(t, `{ - - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": { - "type": "Point","coordinates": [-80.87088507656375,35.21515162500578] - }, - "properties": { - "name": "ABBOTT NEIGHBORHOOD PARK","address": "1300 SPRUCE ST" - } - }, - { - "type": "Feature", - "geometry": { - "type": "Point","coordinates": [-80.83775386582222,35.24980190252168] - }, - "properties": { - "name": "DOUBLE OAKS CENTER","address": "1326 WOODWARD AV" - } - }, - { - "type": "Feature", - "geometry": { - "type": "Point","coordinates": [-80.83827000459532,35.25674709224663] - }, - "properties": { - "name": "DOUBLE OAKS NEIGHBORHOOD PARK","address": "2605 DOUBLE OAKS RD" - } - }, - { - "type": "Feature", - "geometry": { - "type": "Point","coordinates": [-80.83697759172735,35.25751734669229] - }, - "properties": { - "name": "DOUBLE OAKS POOL","address": "1200 NEWLAND RD" - } - }, - { - "type": "Feature", - "geometry": { - "type": "Point","coordinates": [-80.81647652154736,35.40148708491418] - }, - "properties": { - "name": "DAVID B. WAYMER FLYING REGIONAL PARK","address": "15401 HOLBROOKS RD" - } - }, - { - "type": "Feature", - "geometry": { - "type": "Point","coordinates": [-80.83556459443902,35.39917224760999] - }, - "properties": { - "name": "DAVID B. WAYMER COMMUNITY PARK","address": "302 HOLBROOKS RD" - } - }, - { - "type": "Feature", - "geometry": { - "type": "Polygon","coordinates": [ - [ - [-80.72487831115721,35.26545403190955], - [-80.72135925292969,35.26727607954368], - [-80.71517944335938,35.26769654625573], - [-80.7125186920166,35.27035945142482], - [-80.70857048034668,35.268257165144064], - [-80.70479393005371,35.268397319259996], - [-80.70324897766113,35.26503355355979], - [-80.71088790893555,35.2553619492954], - [-80.71681022644043,35.2553619492954], - [-80.7150936126709,35.26054831539319], - [-80.71869850158691,35.26026797976481], - [-80.72032928466797,35.26061839914875], - [-80.72264671325684,35.26033806376283], - [-80.72487831115721,35.26545403190955] - ] - ] - }, - "properties": { - "name": "Plaza Road Park" - } - } - ] -}`) -} - -func TestPointBounding(t *testing.T) { - featureCollectionJSON := `{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.1,56.283333]}}]}` - polyFeatureJSON := `{"type": "FeatureCollection","features": [{"type": "Feature","properties": {},"geometry": {"type": "Polygon","coordinates": [[ - [-112.8515625,-29.535229562948444], - [85.4296875,-2.535229562948444], - [85.4296875,65.36683689226321], - [-112.8515625,65.36683689226321], - [-112.8515625,-29.535229562948444] - ]]}}]}` - - featureCollection := testJSON(t, featureCollectionJSON).(FeatureCollection) - poly := testJSON(t, polyFeatureJSON).(FeatureCollection) - - r1 := featureCollection.Within(poly) - r2 := featureCollection.Intersects(poly) - if r1 != r2 || !r1 { - t.Fatalf("expected %v/%v, got %v/%v", true, true, r1, r2) - } -} diff --git a/pkg/geojson/geo/geo.go b/pkg/geojson/geo/geo.go deleted file mode 100644 index 626f051e..00000000 --- a/pkg/geojson/geo/geo.go +++ /dev/null @@ -1,34 +0,0 @@ -package geo - -import "math" - -const earthRadius = 6371e3 - -func toRadians(deg float64) float64 { return deg * math.Pi / 180 } -func toDegrees(rad float64) float64 { return rad * 180 / math.Pi } - -// DistanceTo return the distance in meteres between two point. -func DistanceTo(latA, lonA, latB, lonB float64) (meters float64) { - φ1 := toRadians(latA) - λ1 := toRadians(lonA) - φ2 := toRadians(latB) - λ2 := toRadians(lonB) - Δφ := φ2 - φ1 - Δλ := λ2 - λ1 - a := math.Sin(Δφ/2)*math.Sin(Δφ/2) + math.Cos(φ1)*math.Cos(φ2)*math.Sin(Δλ/2)*math.Sin(Δλ/2) - c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) - return earthRadius * c -} - -// DestinationPoint return the destination from a point based on a distance and bearing. -func DestinationPoint(lat, lon, meters, bearingDegrees float64) (destLat, destLon float64) { - // see http://williams.best.vwh.net/avform.htm#LL - δ := meters / earthRadius // angular distance in radians - θ := toRadians(bearingDegrees) - φ1 := toRadians(lat) - λ1 := toRadians(lon) - φ2 := math.Asin(math.Sin(φ1)*math.Cos(δ) + math.Cos(φ1)*math.Sin(δ)*math.Cos(θ)) - λ2 := λ1 + math.Atan2(math.Sin(θ)*math.Sin(δ)*math.Cos(φ1), math.Cos(δ)-math.Sin(φ1)*math.Sin(φ2)) - λ2 = math.Mod(λ2+3*math.Pi, 2*math.Pi) - math.Pi // normalise to -180..+180° - return toDegrees(φ2), toDegrees(λ2) -} diff --git a/pkg/geojson/geohash/geohash.go b/pkg/geojson/geohash/geohash.go deleted file mode 100644 index f2ec4d01..00000000 --- a/pkg/geojson/geohash/geohash.go +++ /dev/null @@ -1,257 +0,0 @@ -// Derived from javascript at http://www.movable-type.co.uk/scripts/geohash.html -// -// Original copyright states... -// "Geohash encoding/decoding and associated functions (c) Chris Veness 2014 / MIT Licence" - -package geohash - -import ( - "bytes" - "errors" - "strings" -) - -// Encode latitude/longitude to geohash, either to specified precision or to automatically evaluated precision. -func Encode(lat, lon float64, precision int) (string, error) { - var idx = 0 // index into base32 map - var bit = 0 // each char holds 5 bits - var evenBit = true - var latMin = -90.0 - var latMax = 90.0 - var lonMin = -180.0 - var lonMax = 180.0 - if precision < 1 { - return "", errors.New("invalid precision") - } - var geohash bytes.Buffer - for geohash.Len() < precision { - if evenBit { - // bisect E-W longitude - var lonMid = (lonMin + lonMax) / 2 - if lon > lonMid { - idx = idx*2 + 1 - lonMin = lonMid - } else { - idx = idx * 2 - lonMax = lonMid - } - } else { - // bisect N-S latitude - var latMid = (latMin + latMax) / 2 - if lat > latMid { - idx = idx*2 + 1 - latMin = latMid - } else { - idx = idx * 2 - latMax = latMid - } - } - evenBit = !evenBit - - bit = bit + 1 - if bit == 5 { - // 5 bits gives us a character: append it and start over - b := base32F(idx) - if b == '?' { - return "", errors.New("encoding error") - } - geohash.WriteByte(b) - bit = 0 - idx = 0 - } - } - return geohash.String(), nil -} - -// Decode geohash to latitude/longitude (location is approximate centre of geohash cell, to reasonable precision). -func Decode(geohash string) (lat, lon float64, err error) { - swLat, swLon, neLat, neLon, err1 := Bounds(geohash) // <-- the hard work - if err1 != nil { - return 0, 0, err1 - } - return (neLat-swLat)/2 + swLat, (neLon-swLon)/2 + swLon, nil -} - -// Bounds returns SW/NE latitude/longitude bounds of specified geohash. -func Bounds(geohash string) (swLat, swLon, neLat, neLon float64, err error) { - geohash = strings.ToLower(geohash) - var evenBit = true - var latMin = -90.0 - var latMax = 90.0 - var lonMin = -180.0 - var lonMax = 180.0 - for i := 0; i < len(geohash); i++ { - var chr = geohash[i] - var idx = base32R(chr) - if idx == -1 { - return 0, 0, 0, 0, errors.New("invalid geohash") - } - for n := uint(4); ; n-- { - var bitN = idx >> n & 1 - if evenBit { - // longitude - var lonMid = (lonMin + lonMax) / 2 - if bitN == 1 { - lonMin = lonMid - } else { - lonMax = lonMid - } - } else { - // latitude - var latMid = (latMin + latMax) / 2 - if bitN == 1 { - latMin = latMid - } else { - latMax = latMid - } - } - evenBit = !evenBit - if n == 0 { - break - } - } - } - return latMin, lonMin, latMax, lonMax, nil -} - -func base32R(b byte) int { - switch b { - default: - return -1 - case '0': - return 0 - case '1': - return 1 - case '2': - return 2 - case '3': - return 3 - case '4': - return 4 - case '5': - return 5 - case '6': - return 6 - case '7': - return 7 - case '8': - return 8 - case '9': - return 9 - case 'b': - return 10 - case 'c': - return 11 - case 'd': - return 12 - case 'e': - return 13 - case 'f': - return 14 - case 'g': - return 15 - case 'h': - return 16 - case 'j': - return 17 - case 'k': - return 18 - case 'm': - return 19 - case 'n': - return 20 - case 'p': - return 21 - case 'q': - return 22 - case 'r': - return 23 - case 's': - return 24 - case 't': - return 25 - case 'u': - return 26 - case 'v': - return 27 - case 'w': - return 28 - case 'x': - return 29 - case 'y': - return 30 - case 'z': - return 31 - } -} - -func base32F(i int) byte { - switch i { - default: - return '?' - case 0: - return '0' - case 1: - return '1' - case 2: - return '2' - case 3: - return '3' - case 4: - return '4' - case 5: - return '5' - case 6: - return '6' - case 7: - return '7' - case 8: - return '8' - case 9: - return '9' - case 10: - return 'b' - case 11: - return 'c' - case 12: - return 'd' - case 13: - return 'e' - case 14: - return 'f' - case 15: - return 'g' - case 16: - return 'h' - case 17: - return 'j' - case 18: - return 'k' - case 19: - return 'm' - case 20: - return 'n' - case 21: - return 'p' - case 22: - return 'q' - case 23: - return 'r' - case 24: - return 's' - case 25: - return 't' - case 26: - return 'u' - case 27: - return 'v' - case 28: - return 'w' - case 29: - return 'x' - case 30: - return 'y' - case 31: - return 'z' - } -} diff --git a/pkg/geojson/geohash/geohash_test.go b/pkg/geojson/geohash/geohash_test.go deleted file mode 100644 index 214b2268..00000000 --- a/pkg/geojson/geohash/geohash_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package geohash - -import ( - "fmt" - "testing" -) - -func fixed(f float64, d int) string { - return fmt.Sprintf(fmt.Sprintf("%%0.%df", d), f) -} - -func TestABC(t *testing.T) { - lat, lon := 33.52345123, -115.512345123 - hash, err := Encode(lat, lon, 32) - if err != nil { - t.Fatal(err) - } - lat2, lon2, err := Decode(hash) - if err != nil { - t.Fatal(err) - } - if fixed(lat, 10) != fixed(lat2, 10) || fixed(lon, 10) != fixed(lon2, 10) { - t.Fatalf("bad geohash %v,%v %v,%v", lat, lon, lat2, lon2) - } -} - -// TestEqualsWebserviceHash checks whether an encoded geohash is equal to a -// geohash encoded by geohash.org for identical lat/lon values. -func TestEqualsWebserviceHash(t *testing.T) { - lat, lon := 27.173117, 78.042122 - hash, err := Encode(lat, lon, 12) - if err != nil { - t.Fatal(err) - } - - hash2 := "tsz6xfswchpu" - if hash != hash2 { - t.Errorf("geohash should be equal %v, %v", hash, hash2) - } -} - -func TestNearbyHasCommonPrefix(t *testing.T) { - lat, lon := 27.174583139355413, 78.04258346557617 - hash, err := Encode(lat, lon, 32) - if err != nil { - t.Fatal(err) - } - - lat2, lon2 := 27.174559277910305, 78.04163932800293 - hash2, err := Encode(lat2, lon2, 32) - if err != nil { - t.Fatal(err) - } - - // common prefix should be at least of length 7 - pref := hash[:7] - pref2 := hash2[:7] - if pref != pref2 { - t.Errorf("prefix should be equal %v, %v", pref, pref2) - } -} diff --git a/pkg/geojson/geojson_test.go b/pkg/geojson/geojson_test.go deleted file mode 100644 index fb1b9a6f..00000000 --- a/pkg/geojson/geojson_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package geojson - -import ( - "encoding/json" - "fmt" - "log" - "testing" -) - -func P(x, y float64) Position { - return Position{x, y, 0} -} - -func P3(x, y, z float64) Position { - return Position{x, y, z} -} - -const testPolyHoles = ` -{"type":"Polygon","coordinates":[ -[[0,0],[0,6],[12,-6],[12,0],[0,0]], -[[1,1],[1,2],[2,2],[2,1],[1,1]], -[[11,-1],[11,-3],[9,-1],[11,-1]] -]}` - -func tPoint(x, y float64) Point { - o, err := ObjectJSON(fmt.Sprintf(`{"type":"Point","coordinates":[%f,%f]}`, x, y)) - if err != nil { - log.Fatal(err) - } - return testConvertToPoint(o) -} - -func doesJSONMatch(js1, js2 string) bool { - var m1, m2 map[string]interface{} - json.Unmarshal([]byte(js1), &m1) - json.Unmarshal([]byte(js2), &m2) - b1, _ := json.Marshal(m1) - b2, _ := json.Marshal(m2) - return string(b1) == string(b2) -} - -func testJSON(t *testing.T, jstr string) Object { - t.Helper() - o, err := ObjectJSON(jstr) - if err != nil { - t.Fatal(err) - } - if !doesJSONMatch(o.JSON(), jstr) { - t.Fatalf("expected '%v', got '%v'", o.JSON(), jstr) - } - return o -} - -func testInvalidJSON(t *testing.T, js string, expecting error) { - t.Helper() - _, err := ObjectJSON(js) - if err == nil { - t.Fatalf("expecting an error for json '%s'", js) - } - if err.Error() != expecting.Error() { - t.Fatalf("\nInvalid error for json:\n'%s'\ngot '%s'\nexpected '%s'", js, err.Error(), expecting.Error()) - } -} - -func TestInvalidJSON(t *testing.T) { - testInvalidJSON(t, `{}`, errInvalidTypeMember) - testInvalidJSON(t, `{"type":"Poin"}`, fmt.Errorf(fmtErrTypeIsUnknown, "Poin")) - testInvalidJSON(t, `{"type":"Point"}`, errCoordinatesRequired) - testInvalidJSON(t, `{"type":"Point","coordinates":[]}`, errInvalidNumberOfPositionValues) - testInvalidJSON(t, `{"type":"Point","coordinates":[1]}`, errInvalidNumberOfPositionValues) - testInvalidJSON(t, `{"type":"Point","coordinates":[1,2,"asd"]}`, errInvalidPositionValue) - testInvalidJSON(t, `{"type":"Point","coordinates":[[1,2]]}`, errInvalidPositionValue) - testInvalidJSON(t, `{"type":"Point","coordinates":[[1,2],[1,3]]}`, errInvalidPositionValue) - testInvalidJSON(t, `{"type":"MultiPoint","coordinates":[1,2]}`, errInvalidCoordinates) - testInvalidJSON(t, `{"type":"MultiPoint","coordinates":[[]]}`, errInvalidNumberOfPositionValues) - testInvalidJSON(t, `{"type":"MultiPoint","coordinates":[[1]]}`, errInvalidNumberOfPositionValues) - testInvalidJSON(t, `{"type":"MultiPoint","coordinates":[[1,2,"asd"]]}`, errInvalidPositionValue) - testInvalidJSON(t, `{"type":"LineString","coordinates":[]}`, errLineStringInvalidCoordinates) - testInvalidJSON(t, `{"type":"MultiLineString","coordinates":[[]]}`, errLineStringInvalidCoordinates) - testInvalidJSON(t, `{"type":"MultiLineString","coordinates":[[[]]]}`, errInvalidNumberOfPositionValues) - testInvalidJSON(t, `{"type":"MultiLineString","coordinates":[[[]]]}`, errInvalidNumberOfPositionValues) - testInvalidJSON(t, `{"type":"Polygon","coordinates":[[1,1],[2,2],[3,3],[4,4]]}`, errInvalidCoordinates) - testInvalidJSON(t, `{"type":"Polygon","coordinates":[[[1,1],[2,2],[3,3],[4,4]]]}`, errMustBeALinearRing) - testInvalidJSON(t, `{"type":"Polygon","coordinates":[[[1,1],[2,2],[3,3],[1,1]],[[1,1],[2,2],[3,3],[4,4]]]}`, errMustBeALinearRing) - testInvalidJSON(t, `{"type":"Point","coordinates":[1,2,3],"bbox":123}`, errBBoxInvalidType) - testInvalidJSON(t, `{"type":"Point","coordinates":[1,2,3],"bbox":[]}`, errBBoxInvalidNumberOfValues) - testInvalidJSON(t, `{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3]}`, errBBoxInvalidNumberOfValues) - testInvalidJSON(t, `{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,"a"]}`, errBBoxInvalidValue) -} diff --git a/pkg/geojson/geometrycollection.go b/pkg/geojson/geometrycollection.go deleted file mode 100644 index 0a3b6977..00000000 --- a/pkg/geojson/geometrycollection.go +++ /dev/null @@ -1,261 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/gjson" - "github.com/tidwall/tile38/pkg/geojson/geohash" -) - -// GeometryCollection is a geojson object with the type "GeometryCollection" -type GeometryCollection struct { - Geometries []Object - BBox *BBox - bboxDefined bool -} - -func fillGeometryCollectionMap(json string) (GeometryCollection, error) { - var g GeometryCollection - res := gjson.Get(json, "geometries") - switch res.Type { - default: - return g, errInvalidGeometries - case gjson.Null: - return g, errGeometriesRequired - case gjson.JSON: - if !resIsArray(res) { - return g, errInvalidGeometries - } - v := res.Array() - g.Geometries = make([]Object, len(v)) - for i, res := range v { - if res.Type != gjson.JSON { - return g, errInvalidGeometry - } - o, err := objectMap(res.Raw, gcoll) - if err != nil { - return g, err - } - g.Geometries[i] = o - } - } - var err error - g.BBox, err = fillBBox(json) - if err != nil { - return g, err - } - g.bboxDefined = g.BBox != nil - if !g.bboxDefined { - cbbox := g.CalculatedBBox() - g.BBox = &cbbox - } - return g, err -} - -// CalculatedBBox is exterior bbox containing the object. -func (g GeometryCollection) CalculatedBBox() BBox { - if g.BBox != nil { - return *g.BBox - } - var bbox BBox - for i, g := range g.Geometries { - if i == 0 { - bbox = g.CalculatedBBox() - } else { - bbox = bbox.union(g.CalculatedBBox()) - } - } - return bbox -} - -// CalculatedPoint is a point representation of the object. -func (g GeometryCollection) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// Geohash converts the object to a geohash value. -func (g GeometryCollection) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// PositionCount return the number of coordinates. -func (g GeometryCollection) PositionCount() int { - var res int - for _, g := range g.Geometries { - res += g.PositionCount() - } - if g.BBox != nil { - return 2 + res - } - return res -} - -// Weight returns the in-memory size of the object. -func (g GeometryCollection) Weight() int { - var res int - for _, g := range g.Geometries { - res += g.Weight() - } - return res -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g GeometryCollection) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} -func (g GeometryCollection) appendJSON(json []byte) []byte { - json = append(json, `{"type":"GeometryCollection","geometries":[`...) - for i, g := range g.Geometries { - if i != 0 { - json = append(json, ',') - } - json = append(json, g.JSON()...) - } - json = append(json, ']') - if g.bboxDefined { - json = appendBBoxJSON(json, g.BBox) - } - return append(json, '}') -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g GeometryCollection) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g GeometryCollection) String() string { - return g.JSON() -} - -// Bytes is the bytes representation of the object. -func (g GeometryCollection) Bytes() []byte { - return []byte(g.JSON()) -} -func (g GeometryCollection) bboxPtr() *BBox { - return g.BBox -} -func (g GeometryCollection) hasPositions() bool { - if g.BBox != nil { - return true - } - for _, g := range g.Geometries { - if g.hasPositions() { - return true - } - } - return false -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g GeometryCollection) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - if len(g.Geometries) == 0 { - return false - } - for _, g := range g.Geometries { - if !g.WithinBBox(bbox) { - return false - } - } - return true -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g GeometryCollection) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - for _, g := range g.Geometries { - if g.IntersectsBBox(bbox) { - return true - } - } - return false -} - -// Within detects if the object is fully contained inside another object. -func (g GeometryCollection) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - if len(g.Geometries) == 0 { - return false - } - for _, g := range g.Geometries { - if !g.Within(o) { - return false - } - } - return true - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g GeometryCollection) WithinCircle(center Position, meters float64) bool { - if len(g.Geometries) == 0 { - return false - } - for _, geometry := range g.Geometries { - if !geometry.WithinCircle(center, meters) { - return false - } - } - return true -} - -// Intersects detects if the object intersects another object. -func (g GeometryCollection) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - if len(g.Geometries) == 0 { - return false - } - for _, g := range g.Geometries { - if g.Intersects(o) { - return true - } - } - return false - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g GeometryCollection) IntersectsCircle(center Position, meters float64) bool { - for _, geometry := range g.Geometries { - if geometry.IntersectsCircle(center, meters) { - return true - } - } - return false -} - -// Nearby detects if the object is nearby a position. -func (g GeometryCollection) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g GeometryCollection) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g GeometryCollection) IsGeometry() bool { - return true -} - -// Clip returns the object of the same type as this object, clipped by a bbox. -func (g GeometryCollection) Clipped(bbox BBox) Object { - var new_geometries []Object - for _, geometry := range g.Geometries { - new_geometries = append(new_geometries, geometry.Clipped(bbox)) - } - - gc := GeometryCollection{Geometries: new_geometries} - cbbox := gc.CalculatedBBox() - gc.BBox = &cbbox - return gc -} diff --git a/pkg/geojson/geometrycollection_test.go b/pkg/geojson/geometrycollection_test.go deleted file mode 100644 index 71fe1f2e..00000000 --- a/pkg/geojson/geometrycollection_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package geojson - -import "testing" - -func TestGeometryCollection(t *testing.T) { - testJSON(t, `{ - "type": "GeometryCollection", - "geometries": [ - { - "type": "Point", - "coordinates": [-80.66080570220947,35.04939206472683] - }, - { - "type": "Polygon", - "coordinates": [ - [ - [-80.66458225250244,35.04496519190309], - [-80.66344499588013,35.04603679820616], - [-80.66258668899536,35.045580049697556], - [-80.66387414932251,35.044280059194946], - [-80.66458225250244,35.04496519190309] - ] - ] - }, - { - "type": "LineString", - "coordinates": [ - [-80.66237211227417,35.05950973022538], - [-80.66269397735596,35.0592638296087], - [-80.66284418106079,35.05893010615862], - [-80.66308021545409,35.05833291342246], - [-80.66359519958496,35.057753281001425], - [-80.66387414932251,35.05740198662245], - [-80.66441059112549,35.05703312589789], - [-80.66486120223999,35.056787217822475], - [-80.66541910171509,35.05650617911516], - [-80.66563367843628,35.05631296444281], - [-80.66601991653441,35.055891403570705], - [-80.66619157791138,35.05545227534804], - [-80.66619157791138,35.05517123204622], - [-80.66625595092773,35.05489018777713], - [-80.6662130355835,35.054222703761525], - [-80.6662130355835,35.05392409072499], - [-80.66595554351807,35.05290528508858], - [-80.66569805145262,35.052044560077285], - [-80.66550493240356,35.0514824490509], - [-80.665762424469,35.05048117920187], - [-80.66617012023926,35.04972582715769], - [-80.66651344299316,35.049286665781096], - [-80.66692113876343,35.0485313026898], - [-80.66700696945189,35.048215102112344], - [-80.66707134246826,35.04777593261294], - [-80.66704988479614,35.04738946150025], - [-80.66696405410767,35.04698542156371], - [-80.66681385040283,35.046353007216055], - [-80.66659927368164,35.04596652937105], - [-80.66640615463257,35.04561518428889], - [-80.6659984588623,35.045193568195565], - [-80.66552639007568,35.044877354697526], - [-80.6649899482727,35.04454357245502], - [-80.66449642181396,35.04417465365292], - [-80.66385269165039,35.04387600387859], - [-80.66303730010986,35.043717894732545] - ] - } - ] -}`) -} -func TestPointBoundingGeomColl(t *testing.T) { - geometryCollectionJSON := `{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[-75.1,56.283333]}]}` - polyFeatureJSON := `{"type": "GeometryCollection","geometries": [{"type": "Polygon","coordinates": [[ - [-112.8515625,-29.535229562948444], - [85.4296875,-2.535229562948444], - [85.4296875,65.36683689226321], - [-112.8515625,65.36683689226321], - [-112.8515625,-29.535229562948444] - ]]}]}` - geometryCollection := testJSON(t, geometryCollectionJSON).(GeometryCollection) - poly := testJSON(t, polyFeatureJSON).(GeometryCollection) - r1 := geometryCollection.Within(poly) - r2 := geometryCollection.Intersects(poly) - if r1 != r2 || !r1 { - t.Fatalf("expected %v/%v, got %v/%v", true, true, r1, r2) - } -} diff --git a/pkg/geojson/levels.go b/pkg/geojson/levels.go deleted file mode 100644 index e31a587b..00000000 --- a/pkg/geojson/levels.go +++ /dev/null @@ -1,434 +0,0 @@ -package geojson - -import "github.com/tidwall/gjson" - -func resIsArray(res gjson.Result) bool { - if res.Type == gjson.JSON { - for i := 0; i < len(res.Raw); i++ { - if res.Raw[i] == '[' { - return true - } - if res.Raw[i] <= ' ' { - continue - } - break - } - } - return false -} - -//////////////////////////////// -// level 1 -//////////////////////////////// - -func fillLevel1Map(json string) ( - coordinates Position, bbox *BBox, err error, -) { - coords := gjson.Get(json, "coordinates") - switch coords.Type { - default: - err = errInvalidCoordinates - return - case gjson.Null: - err = errCoordinatesRequired - return - case gjson.JSON: - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - coordinates, err = fillPosition(coords) - if err != nil { - return - } - } - bbox, err = fillBBox(json) - return -} - -func level1CalculatedBBox(coordinates Position, bbox *BBox) BBox { - if bbox != nil { - return *bbox - } - return BBox{ - Min: coordinates, - Max: coordinates, - } -} - -func level1PositionCount(coordinates Position, bbox *BBox) int { - if bbox != nil { - return 3 - } - return 1 -} - -func level1Weight(coordinates Position, bbox *BBox) int { - return level1PositionCount(coordinates, bbox) * sizeofPosition -} - -func appendLevel1JSON(json []byte, name string, coordinates Position, bbox *BBox, bboxDefined bool) []byte { - if bbox != nil && !bboxDefined { - bbox = nil - } - isCordZ := level1IsCoordZDefined(coordinates, bbox) - json = append(json, `{"type":"`...) - json = append(json, name...) - json = append(json, `","coordinates":[`...) - json = appendPositionJSON(json, coordinates, isCordZ) - json = append(json, ']') - if bboxDefined { - json = appendBBoxJSON(json, bbox) - } - return append(json, '}') -} - -func level1IsCoordZDefined(coordinates Position, bbox *BBox) bool { - if bbox.isCordZDefined() { - return true - } - return coordinates.Z != nilz -} - -//////////////////////////////// -// level 2 -//////////////////////////////// - -func fillLevel2Map(json string) ( - coordinates []Position, bbox *BBox, err error, -) { - coords := gjson.Get(json, "coordinates") - switch coords.Type { - default: - err = errInvalidCoordinates - return - case gjson.Null: - err = errCoordinatesRequired - return - case gjson.JSON: - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - v := coords.Array() - coordinates = make([]Position, len(v)) - for i, coords := range v { - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - var p Position - p, err = fillPosition(coords) - if err != nil { - return - } - coordinates[i] = p - } - } - bbox, err = fillBBox(json) - return -} - -func level2CalculatedBBox(coordinates []Position, bbox *BBox) BBox { - if bbox != nil { - return *bbox - } - _, bbox2 := positionBBox(0, BBox{}, coordinates) - return bbox2 -} - -func level2PositionCount(coordinates []Position, bbox *BBox) int { - if bbox != nil { - return 2 + len(coordinates) - } - return len(coordinates) -} - -func level2Weight(coordinates []Position, bbox *BBox) int { - return level2PositionCount(coordinates, bbox) * sizeofPosition -} - -func appendLevel2JSON(json []byte, name string, coordinates []Position, bbox *BBox, bboxDefined bool) []byte { - if bbox != nil && !bboxDefined { - bbox = nil - } - isCordZ := level2IsCoordZDefined(coordinates, bbox) - json = append(json, `{"type":"`...) - json = append(json, name...) - json = append(json, `","coordinates":[`...) - for i, p := range coordinates { - if i > 0 { - json = append(json, ',') - } - json = append(json, '[') - json = appendPositionJSON(json, p, isCordZ) - json = append(json, ']') - } - json = append(json, ']') - if bboxDefined { - json = appendBBoxJSON(json, bbox) - } - json = append(json, '}') - return json -} - -func level2IsCoordZDefined(coordinates []Position, bbox *BBox) bool { - if bbox.isCordZDefined() { - return true - } - for _, p := range coordinates { - if p.Z != nilz { - return true - } - } - return false -} - -//////////////////////////////// -// level 3 -//////////////////////////////// - -func fillLevel3Map(json string) ( - coordinates [][]Position, bbox *BBox, err error, -) { - coords := gjson.Get(json, "coordinates") - switch coords.Type { - default: - err = errInvalidCoordinates - return - case gjson.Null: - err = errCoordinatesRequired - return - case gjson.JSON: - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - v := coords.Array() - coordinates = make([][]Position, len(v)) - for i, coords := range v { - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - v := coords.Array() - ps := make([]Position, len(v)) - for i, coords := range v { - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - var p Position - p, err = fillPosition(coords) - if err != nil { - return - } - ps[i] = p - } - coordinates[i] = ps - } - } - bbox, err = fillBBox(json) - return -} - -func level3CalculatedBBox(coordinates [][]Position, bbox *BBox, isPolygon bool) BBox { - if bbox != nil { - return *bbox - } - var bbox2 BBox - var i = 0 - for _, ps := range coordinates { - i, bbox2 = positionBBox(i, bbox2, ps) - if isPolygon { - break // only the exterior ring should be calculated for a polygon - } - } - return bbox2 -} - -func level3Weight(coordinates [][]Position, bbox *BBox) int { - return level3PositionCount(coordinates, bbox) * sizeofPosition -} - -func level3PositionCount(coordinates [][]Position, bbox *BBox) int { - var res int - for _, p := range coordinates { - res += len(p) - } - if bbox != nil { - return 2 + res - } - return res -} - -func appendLevel3JSON(json []byte, name string, coordinates [][]Position, bbox *BBox, bboxDefined bool) []byte { - if bbox != nil && !bboxDefined { - bbox = nil - } - isCordZ := level3IsCoordZDefined(coordinates, bbox) - json = append(json, `{"type":"`...) - json = append(json, name...) - json = append(json, `","coordinates":[`...) - for i, p := range coordinates { - if i > 0 { - json = append(json, ',') - } - json = append(json, '[') - for i, p := range p { - if i > 0 { - json = append(json, ',') - } - json = append(json, '[') - json = appendPositionJSON(json, p, isCordZ) - json = append(json, ']') - } - json = append(json, ']') - } - json = append(json, ']') - if bboxDefined { - json = appendBBoxJSON(json, bbox) - } - return append(json, '}') -} - -func level3IsCoordZDefined(coordinates [][]Position, bbox *BBox) bool { - if bbox.isCordZDefined() { - return true - } - for _, p := range coordinates { - for _, p := range p { - if p.Z != nilz { - return true - } - } - } - return false -} - -//////////////////////////////// -// level 4 -//////////////////////////////// - -func fillLevel4Map(json string) ( - coordinates [][][]Position, bbox *BBox, err error, -) { - coords := gjson.Get(json, "coordinates") - switch coords.Type { - default: - err = errInvalidCoordinates - return - case gjson.Null: - err = errCoordinatesRequired - return - case gjson.JSON: - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - v := coords.Array() - coordinates = make([][][]Position, len(v)) - for i, coords := range v { - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - v := coords.Array() - ps := make([][]Position, len(v)) - for i, coords := range v { - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - v := coords.Array() - pss := make([]Position, len(v)) - for i, coords := range v { - if !resIsArray(coords) { - err = errInvalidCoordinates - return - } - var p Position - p, err = fillPosition(coords) - if err != nil { - return - } - pss[i] = p - } - ps[i] = pss - } - coordinates[i] = ps - } - } - bbox, err = fillBBox(json) - return -} - -func level4Weight(coordinates [][][]Position, bbox *BBox) int { - return level4PositionCount(coordinates, bbox) * sizeofPosition -} - -func level4PositionCount(coordinates [][][]Position, bbox *BBox) int { - var res int - for _, p := range coordinates { - for _, p := range p { - res += len(p) - } - } - if bbox != nil { - return 2 + res - } - return res -} - -func appendLevel4JSON(json []byte, name string, coordinates [][][]Position, bbox *BBox, bboxDefined bool) []byte { - if bbox != nil && !bboxDefined { - bbox = nil - } - isCordZ := level4IsCoordZDefined(coordinates, bbox) - json = append(json, `{"type":"`...) - json = append(json, name...) - json = append(json, `","coordinates":[`...) - for i, p := range coordinates { - if i > 0 { - json = append(json, ',') - } - json = append(json, '[') - for i, p := range p { - if i > 0 { - json = append(json, ',') - } - json = append(json, '[') - for i, p := range p { - if i > 0 { - json = append(json, ',') - } - json = append(json, '[') - json = appendPositionJSON(json, p, isCordZ) - json = append(json, ']') - } - json = append(json, ']') - } - json = append(json, ']') - } - json = append(json, ']') - if bboxDefined { - json = appendBBoxJSON(json, bbox) - } - return append(json, '}') -} - -func level4IsCoordZDefined(coordinates [][][]Position, bbox *BBox) bool { - if bbox.isCordZDefined() { - return true - } - for _, p := range coordinates { - for _, p := range p { - for _, p := range p { - if p.Z != nilz { - return true - } - } - } - } - return false -} diff --git a/pkg/geojson/linestring.go b/pkg/geojson/linestring.go deleted file mode 100644 index 66620b05..00000000 --- a/pkg/geojson/linestring.go +++ /dev/null @@ -1,181 +0,0 @@ -package geojson - -import "github.com/tidwall/tile38/pkg/geojson/geohash" - -// LineString is a geojson object with the type "LineString" -type LineString struct { - Coordinates []Position - BBox *BBox - bboxDefined bool -} - -func fillLineString(coordinates []Position, bbox *BBox, err error) (LineString, error) { - if err == nil { - if len(coordinates) < 2 { - err = errLineStringInvalidCoordinates - } - } - bboxDefined := bbox != nil - if !bboxDefined { - cbbox := level2CalculatedBBox(coordinates, nil) - bbox = &cbbox - } - return LineString{ - Coordinates: coordinates, - BBox: bbox, - bboxDefined: bboxDefined, - }, err -} - -// CalculatedBBox is exterior bbox containing the object. -func (g LineString) CalculatedBBox() BBox { - return level2CalculatedBBox(g.Coordinates, g.BBox) -} - -// CalculatedPoint is a point representation of the object. -func (g LineString) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// Geohash converts the object to a geohash value. -func (g LineString) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// PositionCount return the number of coordinates. -func (g LineString) PositionCount() int { - return level2PositionCount(g.Coordinates, g.BBox) -} - -// Weight returns the in-memory size of the object. -func (g LineString) Weight() int { - return level2Weight(g.Coordinates, g.BBox) -} - -func (g LineString) appendJSON(json []byte) []byte { - return appendLevel2JSON(json, "LineString", g.Coordinates, g.BBox, g.bboxDefined) -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g LineString) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g LineString) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g LineString) String() string { - return g.JSON() -} - -func (g LineString) bboxPtr() *BBox { - return g.BBox -} - -func (g LineString) hasPositions() bool { - return g.bboxDefined || len(g.Coordinates) > 0 -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g LineString) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - return polyPositions(g.Coordinates).InsideRect(rectBBox(bbox)) -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g LineString) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - return polyPositions(g.Coordinates).IntersectsRect(rectBBox(bbox)) -} - -// Within detects if the object is fully contained inside another object. -func (g LineString) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - return polyPositions(g.Coordinates).Inside(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g LineString) WithinCircle(center Position, meters float64) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, position := range g.Coordinates { - if center.DistanceTo(position) >= meters { - return false - } - } - return true -} - -// Intersects detects if the object intersects another object. -func (g LineString) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - return polyPositions(g.Coordinates).LineStringIntersects(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g LineString) IntersectsCircle(center Position, meters float64) bool { - for i := 0; i < len(g.Coordinates) - 1 ; i++ { - if SegmentIntersectsCircle(g.Coordinates[i], g.Coordinates[i + 1], center, meters) { - return true - } - } - return false -} - -// Nearby detects if the object is nearby a position. -func (g LineString) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g LineString) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g LineString) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g LineString) Clipped(bbox BBox) Object { - var new_coordinates [][]Position - var clipedStart, clippedEnd Position - var rejected bool - var line []Position - - for i := 0; i < len(g.Coordinates) - 1 ; i++ { - clipedStart, clippedEnd, rejected = ClipSegment(g.Coordinates[i], g.Coordinates[i + 1], bbox) - if rejected { - continue - } - if len(line) > 0 && line[len(line) - 1] != clipedStart { - new_coordinates = append(new_coordinates, line) - line = []Position{clipedStart} - } else if len(line) == 0 { - line = append(line, clipedStart) - } - line = append(line, clippedEnd) - } - if len(line) > 0 { - new_coordinates = append(new_coordinates, line) - } - - res, _ := fillMultiLineString(new_coordinates, nil, nil) - return res -} diff --git a/pkg/geojson/linestring_test.go b/pkg/geojson/linestring_test.go deleted file mode 100644 index 03c213bb..00000000 --- a/pkg/geojson/linestring_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package geojson - -import "testing" - -func TestLineString(t *testing.T) { - testJSON(t, `{"type":"LineString","coordinates":[[100.1,5.1],[101.1,51.1]]}`) - testJSON(t, `{"type":"LineString","coordinates":[[100.1,5.1],[101.1,51.1]],"bbox":[10,20,30,40]}`) - testJSON(t, `{"type":"LineString","coordinates":[[100.1,5.1,15.5],[101.1,51.1,20],[10001.1,71.1,10]],"bbox":[10,20,12,30,40,15]}`) - testJSON(t, `{ - "type": "LineString", - "coordinates": [ - [-101.744384765625,39.32155002466662], - [-101.5521240234375,39.330048552942415], - [-101.40380859375,39.330048552942415], - [-101.33239746093749,39.364032338047984], - [-101.041259765625,39.36827914916011], - [-100.975341796875,39.30454987014581], - [-100.9149169921875,39.24501680713314], - [-100.843505859375,39.16414104768742], - [-100.8050537109375,39.104488809440475], - [-100.491943359375,39.10022600175347], - [-100.43701171875,39.095962936305476], - [-100.338134765625,39.095962936305476], - [-100.1953125,39.027718840211605], - [-100.008544921875,39.01064750994083], - [-99.86572265625,39.00211029922512], - [-99.6844482421875,38.97222194853654], - [-99.51416015625,38.929502416386605], - [-99.38232421875,38.92095542046727], - [-99.3218994140625,38.89530825492018], - [-99.1131591796875,38.86965182408357], - [-99.0802001953125,38.85682013474361], - [-98.82202148437499,38.85682013474361], - [-98.44848632812499,38.84826438869913], - [-98.20678710937499,38.84826438869913], - [-98.02001953125,38.8782049970615], - [-97.635498046875,38.87392853923629] - ] -}`) -} - -func TestLineStringWithinBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"LineString","coordinates":[[10,10],[20,20]],"bbox":[0,0,100,100]}`).(LineString) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"LineString","coordinates":[[10,10],[20,20]]}`).(LineString) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"LineString","coordinates":[[10,10],[20,20]],"bbox":[-10,-10,100,100]}`).(LineString) - if p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"LineString","coordinates":[[-10,-10],[-20,-20]]}`).(LineString) - if p.WithinBBox(bbox) { - t.Fatal("!") - } -} - -func TestLineStringIntersectsBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"LineString","coordinates":[[10,10],[20,20]],"bbox":[0,0,100,100]}`).(LineString) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"LineString","coordinates":[[-1,3],[3,-1]]}`).(LineString) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"LineString","coordinates":[[-1,1],[1,-1]]}`).(LineString) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"LineString","coordinates":[[-2,1],[1,-1]]}`).(LineString) - if p.IntersectsBBox(bbox) { - t.Fatal("!") - } -} diff --git a/pkg/geojson/multilinestring.go b/pkg/geojson/multilinestring.go deleted file mode 100644 index 71f5837c..00000000 --- a/pkg/geojson/multilinestring.go +++ /dev/null @@ -1,226 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/tile38/pkg/geojson/geohash" - "github.com/tidwall/tile38/pkg/geojson/poly" -) - -// MultiLineString is a geojson object with the type "MultiLineString" -type MultiLineString struct { - Coordinates [][]Position - BBox *BBox - bboxDefined bool -} - -func fillMultiLineString(coordinates [][]Position, bbox *BBox, err error) (MultiLineString, error) { - if err == nil { - for _, coordinates := range coordinates { - if len(coordinates) < 2 { - err = errLineStringInvalidCoordinates - break - } - } - } - bboxDefined := bbox != nil - if !bboxDefined { - cbbox := level3CalculatedBBox(coordinates, nil, false) - bbox = &cbbox - } - return MultiLineString{ - Coordinates: coordinates, - BBox: bbox, - bboxDefined: bboxDefined, - }, err -} - -func (g MultiLineString) getLineString(index int) LineString { - return LineString{Coordinates: g.Coordinates[index]} -} - -// CalculatedBBox is exterior bbox containing the object. -func (g MultiLineString) CalculatedBBox() BBox { - return level3CalculatedBBox(g.Coordinates, g.BBox, false) -} - -// CalculatedPoint is a point representation of the object. -func (g MultiLineString) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// Geohash converts the object to a geohash value. -func (g MultiLineString) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// PositionCount return the number of coordinates. -func (g MultiLineString) PositionCount() int { - return level3PositionCount(g.Coordinates, g.BBox) -} - -// Weight returns the in-memory size of the object. -func (g MultiLineString) Weight() int { - return level3Weight(g.Coordinates, g.BBox) -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g MultiLineString) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g MultiLineString) appendJSON(json []byte) []byte { - return appendLevel3JSON(json, "MultiLineString", g.Coordinates, g.BBox, g.bboxDefined) -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g MultiLineString) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g MultiLineString) String() string { - return g.JSON() -} - -func (g MultiLineString) bboxPtr() *BBox { - return g.BBox -} -func (g MultiLineString) hasPositions() bool { - if g.bboxDefined { - return true - } - for _, c := range g.Coordinates { - if len(c) > 0 { - return true - } - } - return false -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g MultiLineString) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - if len(g.Coordinates) == 0 { - return false - } - for _, ls := range g.Coordinates { - if len(ls) == 0 { - return false - } - for _, p := range ls { - if !poly.Point(p).InsideRect(rectBBox(bbox)) { - return false - } - } - } - return true -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g MultiLineString) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - for _, ls := range g.Coordinates { - if polyPositions(ls).IntersectsRect(rectBBox(bbox)) { - return true - } - } - return false -} - -// Within detects if the object is fully contained inside another object. -func (g MultiLineString) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, ls := range g.Coordinates { - if !polyPositions(ls).Inside(polyExteriorHoles(v.Coordinates)) { - return false - } - } - return true - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g MultiLineString) WithinCircle(center Position, meters float64) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, ls := range g.Coordinates { - if len(ls) == 0 { - return false - } - for _, position := range ls { - if center.DistanceTo(position) >= meters { - return false - } - } - } - return true -} - -// Intersects detects if the object intersects another object. -func (g MultiLineString) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, ls := range g.Coordinates { - if polyPositions(ls).Intersects(polyExteriorHoles(v.Coordinates)) { - return true - } - } - return false - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g MultiLineString) IntersectsCircle(center Position, meters float64) bool { - for _, ls := range g.Coordinates { - for i := 0; i < len(ls) - 1 ; i++ { - if SegmentIntersectsCircle(ls[i], ls[i + 1], center, meters) { - return true - } - } - } - return false -} - -// Nearby detects if the object is nearby a position. -func (g MultiLineString) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g MultiLineString) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g MultiLineString) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g MultiLineString) Clipped(bbox BBox) Object { - var new_coordinates [][]Position - - for ix := range g.Coordinates { - clippedMultiLineString, _ := g.getLineString(ix).Clipped(bbox).(MultiLineString) - for _, ls := range clippedMultiLineString.Coordinates { - new_coordinates = append(new_coordinates, ls) - } - } - - res, _ := fillMultiLineString(new_coordinates, nil, nil) - return res -} diff --git a/pkg/geojson/multilinestring_test.go b/pkg/geojson/multilinestring_test.go deleted file mode 100644 index 2f66dee2..00000000 --- a/pkg/geojson/multilinestring_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package geojson - -import ( - "testing" -) - -func TestMultiLineString(t *testing.T) { - testJSON(t, `{"type":"MultiLineString","coordinates":[[[100.1,5.1],[101.1,6.1]],[[102.1,7.1],[103.1,8.1]]]}`) - testJSON(t, `{ - "type": "MultiLineString", - "coordinates": [ - [ - [-105.0214433670044,39.57805759162015], - [-105.02150774002075,39.57780951131517], - [-105.02157211303711,39.57749527498758], - [-105.02157211303711,39.57716449836683], - [-105.02157211303711,39.57703218727656], - [-105.02152919769287,39.57678410330158] - ], - [ - [-105.01989841461182,39.574997872470774], - [-105.01959800720215,39.57489863607502], - [-105.01906156539916,39.57478286010041] - ], - [ - [-105.01717329025269,39.5744024519653], - [-105.01698017120361,39.574385912433804], - [-105.0166368484497,39.574385912433804], - [-105.01650810241699,39.5744024519653], - [-105.0159502029419,39.574270135602866] - ], - [ - [-105.0142765045166,39.57397242286402], - [-105.01412630081175,39.57403858136094], - [-105.0138258934021,39.57417089816531], - [-105.01331090927124,39.57445207053608] - ] - ] -}`) -} - -func TestMultiLineStringWithinBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"MultiLineString","coordinates":[[[10,10],[20,20]]],"bbox":[0,0,100,100]}`).(MultiLineString) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiLineString","coordinates":[[[10,10],[20,20]],[[30,30],[40,40]]]}`).(MultiLineString) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiLineString","coordinates":[[[10,10],[20,20]]],"bbox":[-10,-10,100,100]}`).(MultiLineString) - if p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiLineString","coordinates":[[[-10,-10],[-20,-20]]]}`).(MultiLineString) - if p.WithinBBox(bbox) { - t.Fatal("!") - } -} - -func TestMultiLineStringIntersectsBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"MultiLineString","coordinates":[[[10,10],[20,20]]],"bbox":[0,0,100,100]}`).(MultiLineString) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiLineString","coordinates":[[[-1,3],[3,-1]],[[-1000,-1000],[-1020,-1020]]]}`).(MultiLineString) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiLineString","coordinates":[[[-1,1],[1,-1]]]}`).(MultiLineString) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiLineString","coordinates":[[[-2,1],[1,-1]]]}`).(MultiLineString) - if p.IntersectsBBox(bbox) { - t.Fatal("!") - } -} diff --git a/pkg/geojson/multipoint.go b/pkg/geojson/multipoint.go deleted file mode 100644 index 9f8efa11..00000000 --- a/pkg/geojson/multipoint.go +++ /dev/null @@ -1,194 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/tile38/pkg/geojson/geohash" - "github.com/tidwall/tile38/pkg/geojson/poly" -) - -// MultiPoint is a geojson object with the type "MultiPoint" -type MultiPoint struct { - Coordinates []Position - BBox *BBox - bboxDefined bool -} - -func fillMultiPoint(coordinates []Position, bbox *BBox, err error) (MultiPoint, error) { - bboxDefined := bbox != nil - if !bboxDefined { - cbbox := level2CalculatedBBox(coordinates, nil) - bbox = &cbbox - } - return MultiPoint{ - Coordinates: coordinates, - BBox: bbox, - bboxDefined: bboxDefined, - }, err -} - -// CalculatedBBox is exterior bbox containing the object. -func (g MultiPoint) CalculatedBBox() BBox { - return level2CalculatedBBox(g.Coordinates, g.BBox) -} - -// CalculatedPoint is a point representation of the object. -func (g MultiPoint) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// Geohash converts the object to a geohash value. -func (g MultiPoint) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// PositionCount return the number of coordinates. -func (g MultiPoint) PositionCount() int { - return level2PositionCount(g.Coordinates, g.BBox) -} - -// Weight returns the in-memory size of the object. -func (g MultiPoint) Weight() int { - return level2Weight(g.Coordinates, g.BBox) -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g MultiPoint) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g MultiPoint) appendJSON(json []byte) []byte { - return appendLevel2JSON(json, "MultiPoint", g.Coordinates, g.BBox, g.bboxDefined) -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g MultiPoint) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g MultiPoint) String() string { - return g.JSON() -} - -func (g MultiPoint) bboxPtr() *BBox { - return g.BBox -} -func (g MultiPoint) hasPositions() bool { - return g.bboxDefined || len(g.Coordinates) > 0 -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g MultiPoint) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - if len(g.Coordinates) == 0 { - return false - } - for _, p := range g.Coordinates { - if !poly.Point(p).InsideRect(rectBBox(bbox)) { - return false - } - } - return true -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g MultiPoint) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - for _, p := range g.Coordinates { - if poly.Point(p).InsideRect(rectBBox(bbox)) { - return true - } - } - return false -} - -// Within detects if the object is fully contained inside another object. -func (g MultiPoint) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, p := range g.Coordinates { - if !poly.Point(p).Inside(polyExteriorHoles(v.Coordinates)) { - return false - } - } - return true - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g MultiPoint) WithinCircle(center Position, meters float64) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, position := range g.Coordinates { - if center.DistanceTo(position) >= meters { - return false - } - } - return true -} - -// Intersects detects if the object intersects another object. -func (g MultiPoint) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, p := range g.Coordinates { - if poly.Point(p).Intersects(polyExteriorHoles(v.Coordinates)) { - return true - } - } - return true - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g MultiPoint) IntersectsCircle(center Position, meters float64) bool { - for _, position := range g.Coordinates { - if center.DistanceTo(position) <= meters { - return true - } - } - return false -} - -// Nearby detects if the object is nearby a position. -func (g MultiPoint) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g MultiPoint) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g MultiPoint) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g MultiPoint) Clipped(bbox BBox) Object { - var new_coordinates []Position - - for _, position := range g.Coordinates { - if poly.Point(position).InsideRect(rectBBox(bbox)) { - new_coordinates = append(new_coordinates, position) - } - } - - res, _ := fillMultiPoint(new_coordinates, nil, nil) - - return res -} diff --git a/pkg/geojson/multipoint_test.go b/pkg/geojson/multipoint_test.go deleted file mode 100644 index e6d9e9e5..00000000 --- a/pkg/geojson/multipoint_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package geojson - -import "testing" - -func TestMultiPointJSON(t *testing.T) { - testJSON(t, `{"type":"MultiPoint","coordinates":[[100.1,5.1,10],[101.1,51.1,10]],"bbox":[0.1,0.1,15.1,100.1,100.1,19.1]}`) - testJSON(t, `{"type":"MultiPoint","coordinates":[[100.1,5.1],[101.1,51.1]]}`) - testJSON(t, `{"type":"MultiPoint","coordinates":[[100.1,5.1],[101.1,51.1]],"bbox":[0.1,0.1,100.1,100.1]}`) - testJSON(t, `{"type":"MultiPoint","coordinates":[[100.1,5.1,20],[101.1,51.1,50]]}`) -} -func TestMultiPointWithinBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]],"bbox":[0,0,100,100]}`).(MultiPoint) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]]}`).(MultiPoint) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]],"bbox":[-10,-10,100,100]}`).(MultiPoint) - if p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[-10,-10],[-20,-20]]}`).(MultiPoint) - if p.WithinBBox(bbox) { - t.Fatal("!") - } -} -func TestMultiPointIntersectsBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]],"bbox":[0,0,100,100]}`).(MultiPoint) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]]}`).(MultiPoint) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]],"bbox":[-10,-10,100,100]}`).(MultiPoint) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]],"bbox":[-10,-10,0,0]}`).(MultiPoint) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[20,20]],"bbox":[-10,-10,-1,-1]}`).(MultiPoint) - if p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[-10,-10],[-20,-20]]}`).(MultiPoint) - if p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"MultiPoint","coordinates":[[10,10],[-20,-20]]}`).(MultiPoint) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - -} diff --git a/pkg/geojson/multipolygon.go b/pkg/geojson/multipolygon.go deleted file mode 100644 index 2b4f7ddc..00000000 --- a/pkg/geojson/multipolygon.go +++ /dev/null @@ -1,230 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/tile38/pkg/geojson/geohash" -) - -// MultiPolygon is a geojson object with the type "MultiPolygon" -type MultiPolygon struct { - Coordinates [][][]Position - BBox *BBox - bboxDefined bool - polygons []Polygon -} - -func fillMultiPolygon(coordinates [][][]Position, bbox *BBox, err error) (MultiPolygon, error) { - polygons := make([]Polygon, len(coordinates)) - if err == nil { - for i, ps := range coordinates { - polygons[i], err = fillPolygon(ps, nil, nil) - if err != nil { - break - } - } - } - bboxDefined := bbox != nil - if !bboxDefined { - cbbox := calculatedBBox(polygons, nil) - bbox = &cbbox - } - return MultiPolygon{ - Coordinates: coordinates, - BBox: bbox, - bboxDefined: bboxDefined, - polygons: polygons, - }, err -} - -func calculatedBBox(polygons []Polygon, bbox *BBox) BBox { - if bbox != nil { - return *bbox - } - var cbbox BBox - for i, p := range polygons { - if i == 0 { - cbbox = p.CalculatedBBox() - } else { - cbbox = cbbox.union(p.CalculatedBBox()) - } - } - return cbbox -} - -// CalculatedBBox is exterior bbox containing the object. -func (g MultiPolygon) CalculatedBBox() BBox { - return calculatedBBox(g.polygons, g.BBox) -} - -// CalculatedPoint is a point representation of the object. -func (g MultiPolygon) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// Geohash converts the object to a geohash value. -func (g MultiPolygon) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// PositionCount return the number of coordinates. -func (g MultiPolygon) PositionCount() int { - return level4PositionCount(g.Coordinates, g.BBox) -} - -// Weight returns the in-memory size of the object. -func (g MultiPolygon) Weight() int { - return level4Weight(g.Coordinates, g.BBox) -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g MultiPolygon) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g MultiPolygon) appendJSON(json []byte) []byte { - return appendLevel4JSON(json, "MultiPolygon", g.Coordinates, g.BBox, g.bboxDefined) -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g MultiPolygon) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g MultiPolygon) String() string { - return g.JSON() -} - -func (g MultiPolygon) bboxPtr() *BBox { - return g.BBox -} -func (g MultiPolygon) hasPositions() bool { - if g.bboxDefined { - return true - } - for _, c := range g.Coordinates { - for _, c := range c { - if len(c) > 0 { - return true - } - } - } - return false -} - -func (g MultiPolygon) getPolygon(index int) Polygon { - if index < len(g.polygons) { - return g.polygons[index] - } - return Polygon{Coordinates: g.Coordinates[index]} -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g MultiPolygon) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - if len(g.Coordinates) == 0 { - return false - } - for i := range g.Coordinates { - if !g.getPolygon(i).WithinBBox(bbox) { - return false - } - } - return true -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g MultiPolygon) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - for i := range g.Coordinates { - if g.getPolygon(i).IntersectsBBox(bbox) { - return true - } - } - return false -} - -// Within detects if the object is fully contained inside another object. -func (g MultiPolygon) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - if !v.Within(g) { - return false - } - return true - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g MultiPolygon) WithinCircle(center Position, meters float64) bool { - if len(g.polygons) == 0 { - return false - } - for _, polygon := range g.polygons { - if !polygon.WithinCircle(center, meters) { - return false - } - } - return true -} - -// Intersects detects if the object intersects another object. -func (g MultiPolygon) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - if v.Intersects(g) { - return true - } - return false - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g MultiPolygon) IntersectsCircle(center Position, meters float64) bool { - for _, polygon := range g.polygons { - if polygon.IntersectsCircle(center, meters) { - return true - } - } - return false -} - -// Nearby detects if the object is nearby a position. -func (g MultiPolygon) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g MultiPolygon) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g MultiPolygon) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g MultiPolygon) Clipped(bbox BBox) Object { - var new_coordinates [][][]Position - - for _, polygon := range g.polygons { - clippedPolygon, _ := polygon.Clipped(bbox).(Polygon) - new_coordinates = append(new_coordinates, clippedPolygon.Coordinates) - } - - res, _ := fillMultiPolygon(new_coordinates, nil, nil) - return res -} diff --git a/pkg/geojson/multipolygon_test.go b/pkg/geojson/multipolygon_test.go deleted file mode 100644 index f82fdaeb..00000000 --- a/pkg/geojson/multipolygon_test.go +++ /dev/null @@ -1,274 +0,0 @@ -package geojson - -import ( - "testing" -) - -func TestMultiPolygon(t *testing.T) { - testJSON(t, ` - { - "type": "MultiPolygon", - "coordinates": [ - [ - [ - [-84.32281494140625,34.9895035675793], - [-84.29122924804688,35.21981940793435], - [-84.24041748046875,35.25459097465022], - [-84.22531127929688,35.266925688950074], - [-84.20745849609375,35.26580442886754], - [-84.19921875,35.24674063355999], - [-84.16213989257812,35.24113278166642], - [-84.12368774414062,35.24898366572645], - [-84.09072875976562,35.24898366572645], - [-84.08798217773438,35.264683153268116], - [-84.04266357421875,35.27701633139884], - [-84.03030395507812,35.291589484566124], - [-84.0234375,35.306160014550784], - [-84.03305053710936,35.32745068492882], - [-84.03579711914062,35.34313496028189], - [-84.03579711914062,35.348735749472546], - [-84.01657104492188,35.35545618392078], - [-84.01107788085938,35.37337460834958], - [-84.00970458984374,35.39128905521763], - [-84.01931762695312,35.41479572901859], - [-84.00283813476562,35.429344044107154], - [-83.93692016601562,35.47409160773029], - [-83.91220092773438,35.47632833265728], - [-83.88885498046875,35.504282143299655], - [-83.88473510742186,35.516578738902936], - [-83.8751220703125,35.52104976129943], - [-83.85314941406249,35.52104976129943], - [-83.82843017578125,35.52104976129943], - [-83.8092041015625,35.53446133418443], - [-83.80233764648438,35.54116627999813], - [-83.76800537109374,35.56239491058853], - [-83.7432861328125,35.56239491058853], - [-83.71994018554688,35.56239491058853], - [-83.67050170898438,35.569097520776054], - [-83.6334228515625,35.570214567965984], - [-83.61007690429688,35.576916524038616], - [-83.59634399414061,35.574682600980914], - [-83.5894775390625,35.55904339525896], - [-83.55239868164062,35.56574628576276], - [-83.49746704101562,35.563512051219696], - [-83.47000122070312,35.586968406786475], - [-83.4466552734375,35.60818490437746], - [-83.37936401367188,35.63609277863135], - [-83.35739135742188,35.65618041632016], - [-83.32305908203124,35.66622234103479], - [-83.3148193359375,35.65394870599763], - [-83.29971313476561,35.660643649881614], - [-83.28598022460938,35.67180064238771], - [-83.26126098632811,35.6907639509368], - [-83.25714111328125,35.69968630125201], - [-83.25576782226562,35.715298012125295], - [-83.23516845703125,35.72310272092263], - [-83.19808959960936,35.72756221127198], - [-83.16238403320312,35.753199435570316], - [-83.15826416015625,35.76322914549896], - [-83.10333251953125,35.76991491635478], - [-83.08685302734375,35.7843988251953], - [-83.0511474609375,35.787740890986576], - [-83.01681518554688,35.78328477203738], - [-83.001708984375,35.77882840327371], - [-82.96737670898438,35.793310688351724], - [-82.94540405273438,35.820040281161], - [-82.9193115234375,35.85121343450061], - [-82.9083251953125,35.86902116501695], - [-82.90557861328125,35.87792352995116], - [-82.91244506835938,35.92353244718235], - [-82.88360595703125,35.94688293218141], - [-82.85614013671875,35.951329861522666], - [-82.8424072265625,35.94243575255426], - [-82.825927734375,35.92464453144099], - [-82.80670166015625,35.927980690382704], - [-82.80532836914062,35.94243575255426], - [-82.77923583984375,35.97356075349624], - [-82.78060913085938,35.99245209055831], - [-82.76138305664062,36.00356252895066], - [-82.69546508789062,36.04465753921525], - [-82.64465332031249,36.060201412392914], - [-82.61306762695312,36.060201412392914], - [-82.60620117187499,36.033552893400376], - [-82.60620117187499,35.991340960635405], - [-82.60620117187499,35.97911749857497], - [-82.5787353515625,35.96133453736691], - [-82.5677490234375,35.951329861522666], - [-82.53067016601562,35.97244935753683], - [-82.46475219726562,36.006895355244666], - [-82.41668701171875,36.070192281208456], - [-82.37960815429686,36.10126686921446], - [-82.35488891601562,36.117908916563685], - [-82.34115600585936,36.113471382052175], - [-82.29583740234375,36.13343831245866], - [-82.26287841796874,36.13565654678543], - [-82.23403930664062,36.13565654678543], - [-82.2216796875,36.154509006695], - [-82.20382690429688,36.15561783381855], - [-82.19009399414062,36.144528857027744], - [-82.15438842773438,36.15007354140755], - [-82.14065551757812,36.134547437460064], - [-82.1337890625,36.116799556445024], - [-82.12142944335938,36.10570509327921], - [-82.08984375,36.10792411128649], - [-82.05276489257811,36.12678323326429], - [-82.03628540039062,36.12900165569652], - [-81.91268920898438,36.29409768373033], - [-81.89071655273438,36.30959215409138], - [-81.86325073242188,36.33504067209607], - [-81.83029174804688,36.34499652561904], - [-81.80145263671875,36.35605709240176], - [-81.77947998046874,36.34610265300638], - [-81.76162719726562,36.33835943134047], - [-81.73690795898438,36.33835943134047], - [-81.71905517578125,36.33835943134047], - [-81.70669555664062,36.33504067209607], - [-81.70669555664062,36.342784223707234], - [-81.72317504882812,36.357163062654365], - [-81.73278808593749,36.379279167407965], - [-81.73690795898438,36.40028364332352], - [-81.73690795898438,36.41354670392876], - [-81.72454833984374,36.423492513472326], - [-81.71768188476562,36.445589751779174], - [-81.69845581054688,36.47541104282962], - [-81.69845581054688,36.51073994146672], - [-81.705322265625,36.53060536411363], - [-81.69158935546875,36.55929085774001], - [-81.68060302734375,36.56480607840351], - [-81.68197631835938,36.58686302344181], - [-81.04202270507812,36.56370306576917], - [-80.74264526367186,36.561496993252575], - [-79.89120483398438,36.54053616262899], - [-78.68408203124999,36.53943280355122], - [-77.88345336914062,36.54053616262899], - [-76.91665649414062,36.54163950596125], - [-76.91665649414062,36.55046568575947], - [-76.31103515625,36.551568887374], - [-75.79605102539062,36.54936246839778], - [-75.6298828125,36.07574221562703], - [-75.4925537109375,35.82226734114509], - [-75.3936767578125,35.639441068973916], - [-75.41015624999999,35.43829554739668], - [-75.43212890625,35.263561862152095], - [-75.487060546875,35.18727767598896], - [-75.5914306640625,35.17380831799959], - [-75.9210205078125,35.04798673426734], - [-76.17919921875,34.867904962568744], - [-76.41540527343749,34.62868797377061], - [-76.4593505859375,34.57442951865274], - [-76.53076171875,34.53371242139567], - [-76.5911865234375,34.551811369170494], - [-76.651611328125,34.615126683462194], - [-76.761474609375,34.63320791137959], - [-77.069091796875,34.59704151614417], - [-77.376708984375,34.45674800347809], - [-77.5909423828125,34.3207552752374], - [-77.8326416015625,33.97980872872457], - [-77.9150390625,33.80197351806589], - [-77.9754638671875,33.73804486328907], - [-78.11279296875,33.8521697014074], - [-78.2830810546875,33.8521697014074], - [-78.4808349609375,33.815666308702774], - [-79.6728515625,34.8047829195724], - [-80.782470703125,34.836349990763864], - [-80.782470703125,34.91746688928252], - [-80.9307861328125,35.092945313732635], - [-81.0516357421875,35.02999636902566], - [-81.0516357421875,35.05248370662468], - [-81.0516357421875,35.137879119634185], - [-82.3150634765625,35.19625600786368], - [-82.3590087890625,35.19625600786368], - [-82.40295410156249,35.22318504970181], - [-82.4688720703125,35.16931803601131], - [-82.6885986328125,35.1154153142536], - [-82.781982421875,35.06147690849717], - [-83.1060791015625,35.003003395276714], - [-83.616943359375,34.99850370014629], - [-84.05639648437499,34.985003130171066], - [-84.22119140625,34.985003130171066], - [-84.32281494140625,34.9895035675793] - ], - [ - [-75.69030761718749,35.74205383068037], - [-75.5914306640625,35.74205383068037], - [-75.5419921875,35.585851593232356], - [-75.56396484375,35.32633026307483], - [-75.69030761718749,35.285984736065735], - [-75.970458984375,35.16482750605027], - [-76.2066650390625,34.994003757575776], - [-76.300048828125,35.02999636902566], - [-76.409912109375,35.07946034047981], - [-76.5252685546875,35.10642805736423], - [-76.4208984375,35.25907654252574], - [-76.3385009765625,35.294952147406576], - [-76.0858154296875,35.29943548054543], - [-75.948486328125,35.44277092585766], - [-75.8660888671875,35.53669637839501], - [-75.772705078125,35.567980458012094], - [-75.706787109375,35.634976650677295], - [-75.706787109375,35.74205383068037], - [-75.69030761718749,35.74205383068037] - ] - ], - [ - [ - [-109.0283203125,36.98500309285596], - [-109.0283203125,40.97989806962013], - [-102.06298828125,40.97989806962013], - [-102.06298828125,37.00255267215955], - [-109.0283203125,36.98500309285596] - ] - ] - ] - } -`) -} -func TestIssue244(t *testing.T) { - json := `{"type":"MultiPolygon","coordinates":[[[[180.0,40.0],[180.0,50.0],[170.0,50.0],[170.0,40.0],[180.0,40.0]]],[[[-170.0,40.0],[-170.0,50.0],[-180.0,50.0],[-180.0,40.0],[-170.0,40.0]]]]}` - g := testJSON(t, json).(MultiPolygon) - if !(SimplePoint{180.0, 40.0}).Within(g) { - t.Fatal("!") - } - if !(SimplePoint{179.0, 45.0}).Within(g) { - t.Fatal("!") - } - if !(SimplePoint{179.0, 45.0}).Within(Polygon{Coordinates: g.Coordinates[0]}) { - t.Fatal("!") - } - if (SimplePoint{179.0, 45.0}).Within(Polygon{Coordinates: g.Coordinates[1]}) { - t.Fatal("!") - } -} - -func TestIssue268(t *testing.T) { - // Simplified geojson of Jersey City from https://github.com/tidwall/tile38/issues/268. - var json = `{"type":"MultiPolygon","coordinates":[[[[-74.116904,40.710477],[-74.103844,40.718665],[-74.103774,40.718694],[-74.094018,40.735138],[-74.093956,40.735199],[-74.080262,40.741407],[-74.077047,40.743248],[-74.075202,40.747395],[-74.078686,40.750601],[-74.075259,40.752693],[-74.075229,40.752748],[-74.076518,40.754725],[-74.076522,40.754807],[-74.074893,40.755611],[-74.074865,40.755663],[-74.074799,40.758149],[-74.074768,40.758186],[-74.065019,40.763723],[-74.06493,40.763755],[-74.060077,40.762508],[-74.06002,40.762542],[-74.060007,40.765648],[-74.05998,40.765723],[-74.057029,40.768911],[-74.056969,40.768987],[-74.052873,40.761349],[-74.05262,40.761053],[-74.044837,40.757022],[-74.04365,40.756586],[-74.042444,40.752501],[-74.042373,40.752416],[-74.038769,40.750888],[-74.037784,40.750317],[-74.043674,40.740008],[-74.043781,40.739849],[-74.042816,40.736036],[-74.042821,40.735952],[-74.038596,40.736322],[-74.038572,40.736322],[-74.022888,40.731061],[-74.020405,40.730321],[-74.02597,40.701624],[-74.026284,40.699902],[-74.048937,40.662702],[-74.049594,40.661622],[-74.065826,40.666881],[-74.097399,40.680376],[-74.098129,40.680685],[-74.102849,40.69536],[-74.102851,40.695424],[-74.114231,40.700944],[-74.114559,40.701072],[-74.113338,40.702477],[-74.112787,40.703102],[-74.116904,40.710477]],[[-74.047285,40.690503],[-74.043556,40.689147],[-74.043513,40.689274],[-74.04586,40.691102],[-74.04607,40.691185],[-74.047285,40.690503]],[[-74.04074,40.700131],[-74.038372,40.698669],[-74.038368,40.698687],[-74.04074,40.700131]]]]}` - g := testJSON(t, json).(MultiPolygon) - p := testJSON(t, `{"type":"Point","coordinates":[-74.03466,40.767623]}`).(SimplePoint) - if p.Intersects(g) { - t.Fatal("expected false") - } - if g.Intersects(p) { - t.Fatal("expected false") - } -} - -func TestIssue313(t *testing.T) { - seattle := `{"type":"MultiPolygon","coordinates":[[[[-122.459774,47.672418],[-122.459709,47.675379],[-122.4597,47.675788],[-122.459683,47.675916],[-122.458971,47.680926],[-122.458558,47.682498],[-122.458111,47.683765],[-122.458,47.684079],[-122.457614,47.685171],[-122.457488,47.68543],[-122.456968,47.686504],[-122.456092,47.688312],[-122.454756,47.690489],[-122.454429,47.690985],[-122.453318,47.692671],[-122.453026,47.693266],[-122.452737,47.693789],[-122.451562,47.695856],[-122.450629,47.697496],[-122.449862,47.698864],[-122.449309,47.700139],[-122.448927,47.700862],[-122.448835,47.701036],[-122.448531,47.70152],[-122.448055,47.702279],[-122.446925,47.704478],[-122.446429,47.705188],[-122.445657,47.706293],[-122.444914,47.707091],[-122.444314,47.707646],[-122.441601,47.709459],[-122.440025,47.710588],[-122.439289,47.711232],[-122.43746,47.713048],[-122.437052,47.713926],[-122.436881,47.714291],[-122.436612,47.715198],[-122.435789,47.717976],[-122.435323,47.719821],[-122.434396,47.724353],[-122.434061,47.726357],[-122.434002,47.726715],[-122.433464,47.728908],[-122.433291,47.729347],[-122.433205,47.729578],[-122.432673,47.730958],[-122.432216,47.731932],[-122.431994,47.732405],[-122.42992,47.73528],[-122.381268,47.734302],[-122.375049,47.734164],[-122.374886,47.734164],[-122.374681,47.734163],[-122.374618,47.734163],[-122.374476,47.734165],[-122.37432,47.734163],[-122.374302,47.734163],[-122.374241,47.734163],[-122.374196,47.734163],[-122.373467,47.734161],[-122.373354,47.734161],[-122.372448,47.73416],[-122.37216,47.734159],[-122.372085,47.734159],[-122.371754,47.734158],[-122.371669,47.734158],[-122.370363,47.734156],[-122.370249,47.734156],[-122.36903,47.734154],[-122.367154,47.734144],[-122.367055,47.734144],[-122.3663,47.73414],[-122.363917,47.734136],[-122.363853,47.734136],[-122.361959,47.734145],[-122.360931,47.734139],[-122.359169,47.734136],[-122.355572,47.734129],[-122.35426,47.734128],[-122.35304,47.734128],[-122.351774,47.734128],[-122.350507,47.734128],[-122.347803,47.734127],[-122.3463,47.734127],[-122.345211,47.734127],[-122.345098,47.734127], -[-122.344994,47.734127],[-122.344005,47.734128],[-122.343425,47.734128],[-122.342393,47.734129],[-122.341337,47.73413],[-122.341034,47.73413],[-122.340744,47.73413],[-122.339688,47.734131],[-122.338631,47.734132],[-122.338076,47.734132],[-122.336983,47.734133],[-122.335926,47.734134],[-122.335546,47.734134],[-122.335333,47.734134],[-122.334277,47.734135],[-122.333749,47.73413],[-122.33303,47.734125],[-122.331579,47.734114],[-122.330226,47.734103],[-122.328881,47.734093],[-122.326382,47.734074],[-122.326216,47.734073],[-122.325655,47.734069],[-122.32532,47.734062],[-122.325246,47.734065],[-122.324982,47.734063],[-122.324734,47.734061],[-122.324523,47.73406],[-122.324457,47.734059],[-122.323589,47.734052],[-122.322219,47.734039],[-122.320801,47.734026],[-122.318117,47.734001],[-122.31676,47.733988],[-122.315443,47.733975],[-122.31275,47.73395],[-122.310048,47.733919],[-122.308696,47.733904],[-122.308045,47.733896],[-122.307326,47.733889],[-122.306171,47.733876],[-122.305994,47.733874],[-122.304844,47.73386],[-122.304574,47.733856],[-122.303599,47.733846],[-122.301955,47.733828],[-122.300586,47.733817],[-122.29923,47.733806],[-122.297877,47.733795],[-122.29652,47.733784],[-122.295306,47.733774],[-122.295163,47.733773],[-122.294692,47.733769],[-122.29381,47.733762],[-122.292435,47.733751],[-122.291098,47.73374],[-122.289753,47.733732],[-122.288415,47.733729],[-122.28705,47.733721],[-122.286711,47.733724],[-122.286323,47.733729],[-122.285957,47.733732],[-122.285869,47.73372],[-122.284606,47.73365],[-122.284471,47.733648],[-122.283695,47.733605],[-122.28194,47.733583],[-122.276442,47.733515],[-122.274368,47.73349],[-122.273588,47.726928],[-122.273307,47.725913],[-122.271923,47.720434],[-122.271223,47.718857],[-122.269138,47.714124],[-122.267094,47.71093],[-122.265263,47.708068],[-122.262323,47.704308],[-122.260725,47.702264],[-122.255062,47.696866],[-122.25105,47.693615],[-122.246956,47.691202],[-122.243423,47.689524],[-122.242453,47.689064],[-122.241812,47.688797],[-122.236308,47.686224],[-122.233657,47.684762],[-122.232807,47.684085],[-122.232066,47.683353],[-122.231443,47.682572],[-122.230944,47.681752],[-122.230575,47.680901],[-122.23034,47.680029],[-122.23027,47.679561],[-122.229983,47.677172],[-122.229681,47.668681],[-122.229725,47.667777],[-122.229908,47.666881],[-122.230229,47.666003],[-122.230684,47.665152],[-122.230814,47.66495],[-122.231924,47.663757],[-122.23226,47.663395],[-122.233939,47.661951],[-122.235832,47.660633],[-122.237078,47.65993],[-122.237918,47.659456],[-122.240175,47.658433],[-122.24199,47.657767],[-122.245926,47.656132],[-122.249553,47.654244],[-122.25103,47.653346],[-122.251167,47.653243],[-122.253351,47.651413],[-122.255292,47.649384],[-122.256908,47.647229],[-122.258182,47.644971],[-122.2591,47.642636],[-122.25937,47.641524],[-122.259604,47.640454],[-122.259622,47.640374],[-122.260051,47.63699],[-122.260675,47.632007],[-122.261091,47.628694],[-122.262212,47.624885],[-122.262234,47.624812],[-122.262238,47.624776],[-122.262722,47.621146],[-122.263651,47.613115],[-122.265147,47.601321],[-122.265672,47.597061],[-122.266327,47.591805],[-122.266544,47.59005],[-122.266556,47.589953],[-122.266573,47.589794],[-122.26661,47.589473],[-122.266719,47.588843],[-122.266832,47.587369],[-122.266821,47.586371],[-122.266493,47.583781],[-122.266241,47.582742],[-122.265413,47.580486],[-122.264919,47.57948],[-122.264851,47.579372],[-122.264156,47.578503],[-122.26328,47.577637],[-122.262695,47.577172],[-122.262276,47.576838],[-122.261153,47.576115],[-122.259925,47.575475],[-122.258604,47.574925],[-122.257206,47.574472],[-122.255868,47.574148],[-122.255706,47.574107],[-122.253772,47.573614],[-122.251847,47.57296],[-122.25116,47.572682],[-122.251032,47.572612],[-122.249739,47.571901],[-122.247649,47.570537],[-122.24578,47.569033],[-122.244154,47.567405],[-122.242788,47.565671],[-122.241698,47.563849],[-122.240895,47.561961],[-122.240388,47.560026],[-122.240183,47.558065],[-122.240282,47.556101],[-122.240575,47.554038],[-122.241197,47.551954],[-122.242137,47.549925],[-122.243386,47.547974],[-122.244928,47.546122],[-122.245003,47.546051],[-122.246748,47.544389],[-122.248826,47.542795],[-122.251073,47.541396],[-122.251215,47.541298],[-122.252158,47.540468],[-122.253022,47.539516],[-122.253734,47.538509],[-122.254287,47.537457],[-122.254675,47.536371],[-122.254892,47.535264],[-122.254938,47.534147],[-122.254914,47.53377],[-122.254699,47.532185],[-122.254241,47.530623],[-122.253544,47.529103],[-122.252617,47.52764],[-122.251468,47.526251],[-122.251192,47.525962],[-122.251058,47.52586],[-122.248731,47.524144],[-122.246099,47.522571],[-122.243239,47.521192],[-122.243131,47.521146],[-122.240322,47.519865],[-122.235699,47.517769],[-122.227476,47.513624],[-122.219822,47.509801],[-122.224613,47.509758],[-122.235294,47.509513],[-122.236233,47.509521],[-122.237053,47.509524],[-122.238194,47.509519],[-122.23822,47.50953], -[-122.238326,47.509542],[-122.238425,47.509544],[-122.238806,47.509528],[-122.238919,47.509557],[-122.241152,47.509591],[-122.241569,47.509597],[-122.243034,47.509597],[-122.243218,47.509604],[-122.243302,47.509644],[-122.24336,47.509682],[-122.243726,47.509687],[-122.244089,47.509692],[-122.244131,47.509692],[-122.245001,47.509703],[-122.245387,47.509708],[-122.245652,47.509721],[-122.245787,47.509728],[-122.247099,47.509734],[-122.247146,47.509734],[-122.247118,47.509658],[-122.246942,47.509233],[-122.246945,47.50915],[-122.246897,47.509076],[-122.246833,47.509015],[-122.246742,47.508968],[-122.246972,47.508211],[-122.24611,47.507269],[-122.246079,47.507236],[-122.246079,47.507227],[-122.246078,47.507211],[-122.246077,47.507187],[-122.246075,47.507147],[-122.246067,47.50697],[-122.246059,47.506791],[-122.24605,47.506612],[-122.246048,47.506557],[-122.246045,47.506502],[-122.246039,47.506374],[-122.246037,47.50632],[-122.246032,47.506205],[-122.24603,47.506169],[-122.246047,47.506106],[-122.246063,47.506043],[-122.246069,47.506021],[-122.246108,47.505877],[-122.24615,47.505717],[-122.246203,47.505517],[-122.246259,47.505306],[-122.246266,47.505278],[-122.246278,47.505232],[-122.246238,47.505163],[-122.246203,47.505104],[-122.246188,47.505079],[-122.246106,47.50494],[-122.245991,47.504745],[-122.245896,47.504583],[-122.245802,47.504425],[-122.245714,47.504276],[-122.245609,47.504097],[-122.2455,47.503913],[-122.245485,47.503886],[-122.245496,47.503814],[-122.245548,47.503531],[-122.245635,47.503056],[-122.247206,47.503073],[-122.24802,47.503081],[-122.248016,47.503515],[-122.248015,47.503653],[-122.248014,47.503711],[-122.248012,47.50385],[-122.248009,47.504048],[-122.248003,47.50446],[-122.248003,47.504505],[-122.247994,47.505148],[-122.247993,47.505182],[-122.247992,47.505288],[-122.247988,47.505591],[-122.247985,47.505789],[-122.247982,47.505986],[-122.24798,47.506128],[-122.24798,47.506164],[-122.247979,47.506225],[-122.247979,47.506253],[-122.247978,47.506316],[-122.247977,47.506349],[-122.248001,47.506349],[-122.248081,47.50635],[-122.248102,47.506351],[-122.248327,47.506354],[-122.248544,47.506357],[-122.248651,47.506358],[-122.248687,47.506359],[-122.248757,47.50636],[-122.248784,47.506361],[-122.248787,47.506278],[-122.248787,47.506259],[-122.24879,47.506116],[-122.248811,47.504792],[-122.248822,47.504134],[-122.248824,47.504021],[-122.248827,47.503838],[-122.248839,47.503076],[-122.248843,47.502995],[-122.245644,47.502953],[-122.244959,47.502944],[-122.243131,47.502915],[-122.24238,47.502904],[-122.24125,47.502891],[-122.240819,47.502883],[-122.240828,47.502227],[-122.240836,47.501466],[-122.240862,47.499633],[-122.240861,47.499568],[-122.24087,47.499245],[-122.241583,47.49925],[-122.248161,47.49935],[-122.24841,47.499353],[-122.24846,47.499354],[-122.248674,47.499357],[-122.248794,47.499358],[-122.248816,47.499359],[-122.248835,47.499359],[-122.248902,47.49936],[-122.249023,47.499357],[-122.24951,47.499344],[-122.249635,47.499343],[-122.249754,47.499711],[-122.249954,47.500388],[-122.250166,47.501099],[-122.250258,47.5014],[-122.250349,47.501702],[-122.250506,47.502219],[-122.250625,47.502519],[-122.250734,47.502716],[-122.250814,47.502831],[-122.251413,47.502801],[-122.251642,47.502825],[-122.25362,47.502729],[-122.259515,47.502537],[-122.259529,47.501306],[-122.259552,47.500154],[-122.259608,47.497346],[-122.259638,47.495987],[-122.259643,47.495601],[-122.264081,47.495551],[-122.26427,47.495571],[-122.264853,47.495565],[-122.265348,47.495564],[-122.266417,47.495563],[-122.266858,47.495546],[-122.268426,47.495523],[-122.268575,47.495524],[-122.269276,47.495531],[-122.269685,47.495535],[-122.270261,47.495541],[-122.270302,47.495543],[-122.270298,47.496033],[-122.270294,47.496354],[-122.270292,47.496569],[-122.270283,47.497002],[-122.270282,47.497059],[-122.270278,47.497327],[-122.270273,47.49758],[-122.270227,47.500282],[-122.270327,47.500303],[-122.270449,47.500275],[-122.270591,47.500284],[-122.270671,47.500303],[-122.270755,47.500808],[-122.270704,47.500827],[-122.270855,47.501456],[-122.270875,47.501548],[-122.270887,47.501613],[-122.270787,47.501592],[-122.270264,47.501468],[-122.270202,47.502629],[-122.270175,47.503177],[-122.270166,47.504475],[-122.270157,47.504896],[-122.270157,47.505101],[-122.270145,47.50609],[-122.27014,47.506581],[-122.270125,47.507025],[-122.270087,47.508783],[-122.27007,47.509537],[-122.270069,47.509663],[-122.271549,47.509696],[-122.274076,47.509753],[-122.275411,47.509784],[-122.275412,47.509633],[-122.275418,47.50893],[-122.27543,47.507142],[-122.275432,47.506927],[-122.275774,47.507007],[-122.276072,47.507126],[-122.276241,47.507231],[-122.277078,47.507773],[-122.277225,47.507839],[-122.277387,47.507892],[-122.277542,47.507936],[-122.277703,47.507967],[-122.277889,47.507992],[-122.278229,47.507991],[-122.278341,47.507987],[-122.27838,47.508871], -[-122.278534,47.509577],[-122.278544,47.509813],[-122.278547,47.509855],[-122.278645,47.509856],[-122.278721,47.509858],[-122.27985,47.509883],[-122.280761,47.509904],[-122.281278,47.509916],[-122.282464,47.509943],[-122.282557,47.509945],[-122.282967,47.509953],[-122.283365,47.509962],[-122.283686,47.509974],[-122.28387,47.509975],[-122.284424,47.509988],[-122.284673,47.509993],[-122.284798,47.509996],[-122.284885,47.509998],[-122.285645,47.510015],[-122.285757,47.510018],[-122.285928,47.510022],[-122.286147,47.510028],[-122.286235,47.51003],[-122.286518,47.510037],[-122.287504,47.510059],[-122.287646,47.510063],[-122.287825,47.510067],[-122.289847,47.510112],[-122.290052,47.510117],[-122.290168,47.510119],[-122.290257,47.510121],[-122.290795,47.510133],[-122.29134,47.510146],[-122.291339,47.510247],[-122.291338,47.510399],[-122.291336,47.510629],[-122.291334,47.510695],[-122.291334,47.510766],[-122.291334,47.51083],[-122.291334,47.510883],[-122.291325,47.511627],[-122.291312,47.513415],[-122.291306,47.513592],[-122.291305,47.513673],[-122.291305,47.513744],[-122.291303,47.513885],[-122.291287,47.51489],[-122.291282,47.516414],[-122.29128,47.51698],[-122.291281,47.517682],[-122.291281,47.517825],[-122.291282,47.518567],[-122.291283,47.518838],[-122.291283,47.519301],[-122.291283,47.519524],[-122.291284,47.51969],[-122.291284,47.519737],[-122.291284,47.519998],[-122.291285,47.52049],[-122.291285,47.520553],[-122.291285,47.520669],[-122.291286,47.521258],[-122.291286,47.521315],[-122.291287,47.522313],[-122.291287,47.522362],[-122.291288,47.522843],[-122.291288,47.522948],[-122.291289,47.523339],[-122.291289,47.523379],[-122.29129,47.524199],[-122.29129,47.52428],[-122.291293,47.524381],[-122.291296,47.524463],[-122.291372,47.524462],[-122.291801,47.524462],[-122.293357,47.524461],[-122.29365,47.52446],[-122.293887,47.52446],[-122.294029,47.52446],[-122.295486,47.524459],[-122.29986,47.524454],[-122.299966,47.524454],[-122.300481,47.524454],[-122.300501,47.524453],[-122.300374,47.524581],[-122.300302,47.524663],[-122.300235,47.52471],[-122.300212,47.524749], -[-122.300208,47.52478],[-122.300139,47.524907],[-122.300088,47.525001],[-122.299756,47.526019],[-122.299746,47.526257],[-122.299776,47.526614],[-122.299862,47.526967],[-122.29989,47.52709],[-122.300272,47.52798],[-122.300916,47.529034],[-122.301367,47.529248],[-122.301578,47.529347],[-122.303344,47.530249],[-122.305076,47.531051],[-122.305114,47.531303],[-122.305172,47.531678],[-122.307104,47.531709],[-122.307306,47.531712],[-122.30843,47.531733],[-122.308924,47.531741],[-122.309177,47.531745],[-122.313285,47.531774],[-122.31681,47.53177],[-122.317958,47.531767],[-122.31803,47.531765],[-122.318264,47.531766],[-122.318263,47.531643],[-122.318258,47.530963],[-122.318256,47.530464],[-122.318249,47.529776],[-122.318245,47.529455],[-122.318244,47.529438],[-122.31822,47.529438],[-122.318118,47.529438],[-122.318016,47.529437],[-122.317914,47.529437],[-122.317812,47.529437],[-122.31771,47.529436],[-122.317576,47.529436],[-122.317575,47.529413],[-122.317568,47.529169],[-122.317575,47.529026],[-122.31759,47.528825],[-122.317599,47.528549],[-122.317554,47.52821],[-122.317483,47.528171],[-122.314898,47.527369],[-122.314718,47.527316],[-122.312833,47.526819],[-122.312645,47.526777],[-122.311887,47.526583],[-122.311836,47.526557],[-122.311724,47.526485],[-122.311609,47.526363],[-122.311428,47.526182],[-122.311084,47.525758],[-122.310315,47.525051],[-122.310148,47.524907],[-122.309972,47.524754],[-122.309856,47.524584],[-122.309703,47.524299],[-122.309637,47.52412],[-122.309595,47.524004],[-122.309533,47.523704],[-122.309521,47.523466],[-122.309518,47.5234],[-122.309549,47.523097],[-122.309619,47.522756],[-122.309667,47.522187],[-122.309671,47.522145],[-122.309758,47.522145],[-122.310126,47.522145],[-122.310995,47.522146],[-122.313361,47.522133],[-122.313516,47.522154],[-122.313666,47.522183],[-122.314266,47.522183],[-122.314889,47.522191],[-122.314984,47.522197],[-122.315112,47.52221],[-122.31586,47.522206],[-122.316023,47.522218],[-122.317656,47.522222],[-122.317641,47.521828],[-122.317611,47.521011],[-122.317593,47.520549],[-122.317588,47.520393],[-122.317578,47.520107],[-122.317571,47.51995],[-122.317567,47.519832],[-122.317544,47.519205],[-122.31753,47.518832],[-122.317526,47.518725],[-122.318131,47.518727],[-122.318413,47.518724],[-122.318601,47.518722],[-122.320136,47.518714],[-122.320343,47.518714],[-122.321292,47.518716],[-122.321607,47.518714],[-122.323154,47.518686],[-122.32323,47.518684],[-122.323438,47.518685],[-122.323858,47.518686],[-122.324073,47.518686],[-122.324135,47.518686],[-122.32451,47.518687],[-122.3251,47.518674],[-122.325879,47.518691],[-122.325881,47.520232],[-122.325876,47.52092],[-122.327899,47.520921],[-122.329592,47.520913],[-122.330073,47.520906],[-122.330599,47.520902],[-122.330779,47.520901],[-122.330906,47.520902],[-122.33063,47.520513],[-122.329917,47.519564],[-122.329914,47.519549],[-122.329852,47.519245],[-122.329836,47.519168],[-122.32969,47.518457],[-122.329611,47.518302],[-122.329556,47.518195],[-122.329518,47.518122],[-122.329369,47.517833],[-122.329357,47.517809],[-122.329308,47.51753],[-122.329256,47.517381],[-122.329238,47.517329],[-122.329227,47.517281],[-122.329215,47.517225],[-122.329207,47.517184],[-122.3292,47.51717],[-122.329177,47.517126],[-122.328849,47.516755],[-122.328713,47.516593],[-122.328639,47.516505],[-122.327326,47.513997],[-122.328184,47.513994],[-122.328565,47.513999],[-122.330079,47.513997],[-122.330273,47.513999],[-122.330289,47.514029],[-122.330586,47.514539],[-122.33097,47.514944],[-122.331153,47.515121],[-122.331182,47.515149],[-122.331385,47.515132],[-122.331405,47.515098],[-122.332616,47.514882],[-122.333497,47.514725],[-122.333555,47.514742],[-122.333512,47.514531],[-122.334036,47.514522],[-122.334357,47.514523],[-122.334353,47.514499],[-122.334252,47.513897],[-122.335707,47.513923],[-122.336507,47.513914],[-122.336645,47.513907],[-122.33704,47.513893],[-122.337124,47.515023],[-122.337275,47.515858],[-122.33739,47.516502],[-122.337408,47.516602],[-122.337469,47.516944],[-122.33748,47.517005],[-122.337492,47.517073],[-122.337521,47.517257],[-122.337537,47.517256],[-122.33793,47.517258],[-122.339915,47.517269],[-122.340134,47.517277],[-122.340448,47.51724],[-122.34075,47.51722],[-122.341437,47.517276],[-122.34161,47.517281],[-122.342878,47.517287],[-122.343873,47.517293],[-122.344151,47.517294],[-122.344639,47.517299],[-122.345426,47.517302],[-122.346699,47.517309],[-122.347972,47.517317],[-122.348017,47.517317],[-122.349246,47.517324],[-122.350655,47.517332],[-122.351735,47.517339],[-122.351778,47.517339],[-122.352909,47.517361],[-122.353404,47.51737],[-122.354037,47.517374],[-122.355167,47.51738],[-122.356112,47.517386],[-122.356513,47.517382],[-122.357859,47.51736],[-122.358228,47.517353],[-122.358557,47.517347],[-122.358564,47.515528],[-122.359181,47.515528],[-122.360495,47.515528],[-122.360536,47.51682],[-122.360542,47.51736], -[-122.361127,47.517362],[-122.361851,47.517361],[-122.36262,47.517361],[-122.363226,47.517362],[-122.36457,47.517362],[-122.365915,47.517361],[-122.36728,47.517377],[-122.367695,47.517381],[-122.368662,47.517393],[-122.3714,47.517424],[-122.371316,47.516653],[-122.371272,47.516076],[-122.371274,47.516024],[-122.371263,47.515745],[-122.371152,47.514459],[-122.371104,47.513705],[-122.37108,47.513335],[-122.37102,47.512548],[-122.371,47.51229],[-122.370984,47.512047],[-122.370888,47.510038],[-122.37086,47.509458],[-122.370877,47.508199],[-122.370862,47.508017],[-122.370714,47.507259],[-122.37053,47.506664],[-122.370465,47.506409],[-122.370464,47.506398],[-122.370394,47.505821],[-122.370404,47.505686],[-122.370449,47.505503],[-122.370474,47.505402],[-122.37054,47.505135],[-122.37055,47.50452],[-122.370561,47.503866],[-122.370596,47.503683],[-122.370694,47.503511],[-122.370919,47.503241],[-122.371413,47.502778],[-122.372051,47.502182],[-122.372257,47.502014],[-122.373121,47.501378],[-122.3738,47.500834],[-122.373923,47.500697],[-122.373972,47.500581],[-122.374059,47.499917],[-122.374133,47.499617],[-122.374278,47.499221],[-122.37485,47.497659],[-122.37492,47.497497],[-122.375024,47.49732],[-122.375068,47.497248],[-122.375208,47.497032],[-122.375312,47.496869],[-122.375652,47.496434],[-122.380128,47.493203],[-122.38181,47.491912],[-122.399237,47.48522],[-122.407658,47.48215],[-122.409315,47.485945],[-122.409341,47.485999],[-122.409371,47.486053],[-122.415446,47.497105],[-122.416946,47.499846],[-122.418072,47.501568],[-122.423326,47.508282],[-122.42629,47.512069],[-122.430797,47.516605],[-122.436093,47.521938],[-122.436484,47.522337],[-122.439282,47.525625],[-122.441558,47.529092],[-122.442968,47.532037],[-122.443244,47.53264],[-122.443323,47.532812],[-122.444449,47.536414],[-122.445032,47.540187],[-122.445031,47.541851],[-122.445031,47.54253],[-122.444867,47.542661],[-122.444636,47.543295],[-122.444778,47.544416],[-122.444372,47.54783],[-122.443864,47.551798],[-122.443888,47.553717],[-122.444013,47.554485],[-122.444297,47.555565],[-122.444622,47.55646],[-122.444772,47.556877], -[-122.445159,47.557759],[-122.445723,47.558828],[-122.447001,47.560745],[-122.44849,47.562978],[-122.450247,47.565615],[-122.450834,47.566373],[-122.451683,47.567468],[-122.452447,47.568371],[-122.453062,47.569097],[-122.453249,47.569399],[-122.453374,47.569604],[-122.453994,47.57091],[-122.454134,47.571318],[-122.45448,47.572328],[-122.454766,47.573685],[-122.454787,47.57397],[-122.454847,47.574757],[-122.454781,47.576186],[-122.454746,47.576391],[-122.454726,47.576591],[-122.454627,47.577209],[-122.4541,47.579662],[-122.453582,47.581237],[-122.453158,47.582208],[-122.451696,47.585085],[-122.451383,47.585702],[-122.450513,47.587416],[-122.449692,47.588963],[-122.448332,47.591253],[-122.447883,47.592297],[-122.447569,47.593313],[-122.447305,47.594455],[-122.447157,47.595574],[-122.446984,47.597612],[-122.446947,47.5981],[-122.446859,47.599162],[-122.446558,47.602772],[-122.446203,47.607035],[-122.446063,47.607998],[-122.446503,47.611494],[-122.446853,47.61242],[-122.447298,47.613069],[-122.447711,47.614209],[-122.447908,47.614741],[-122.448321,47.615696],[-122.448615,47.616382],[-122.448642,47.616446],[-122.44867,47.61651],[-122.449203,47.617523],[-122.449543,47.618171],[-122.45005,47.619022],[-122.451403,47.620957],[-122.453006,47.622876],[-122.453763,47.623782],[-122.454252,47.624636],[-122.454908,47.626561],[-122.454985,47.627192],[-122.45491,47.627647],[-122.454567,47.629007],[-122.454372,47.629669],[-122.454199,47.630258],[-122.454069,47.630696],[-122.453871,47.631371],[-122.453682,47.632013],[-122.453522,47.632556],[-122.453067,47.634524],[-122.45284,47.636087],[-122.452694,47.638749],[-122.452675,47.63911],[-122.452686,47.642348],[-122.452812,47.643798],[-122.453181,47.646248],[-122.453251,47.646713],[-122.453391,47.647654],[-122.454105,47.651119],[-122.454282,47.652018],[-122.454346,47.652662],[-122.454405,47.653253],[-122.455216,47.655316],[-122.456059,47.657707],[-122.456757,47.659683],[-122.457251,47.661335],[-122.458099,47.663906],[-122.458569,47.665605],[-122.459375,47.669161],[-122.45962,47.671118],[-122.459737,47.67212],[-122.459774,47.672418]]]]}` - bounds := `{"type":"MultiPolygon","coordinates":[[[[-114.2399485999843,42.681876499955926],[-114.23997050001262,42.67824680037998],[-114.23627140565759,42.678233981038666],[-114.23504369971833,42.67822970021946],[-114.23503067863831,42.67578671168899],[-114.23543512533203,42.67583999946153],[-114.23594982495554,42.676124423371455],[-114.23672596666609,42.67625624813994],[-114.23685805764039,42.676776002285635],[-114.23663532665391,42.677055424446124],[-114.23671287282251,42.67747314961749],[-114.23681394047848,42.677805126285634],[-114.23706193209075,42.67813127497741],[-114.23784992168157,42.678186635769315],[-114.23838959806322,42.67813118119811],[-114.23875417123647,42.67791953618522],[-114.23912485834349,42.67784524816636],[-114.23952718950476,42.67769791345572],[-114.23999116575574,42.677386626685646],[-114.24048944148089,42.67724527831817],[-114.241314164082,42.67699656963459],[-114.24195248087176,42.676729402854455],[-114.24240805338327,42.67645818447628],[-114.24311814376759,42.67608562786248],[-114.24355505557568,42.67596026910592],[-114.24477528843009,42.67578037408335],[-114.24521195320149,42.675790315723084],[-114.24576310286596,42.67606342521258],[-114.2467516503271,42.676300425443266],[-114.2474970507903,42.676777404382534],[-114.24910805618318,42.67760249691951],[-114.24971203666782,42.67767705336001],[-114.25014930420876,42.67756278302376],[-114.25107740672941,42.677654319638556],[-114.25187256038463,42.677646259180754],[-114.25216885710465,42.677753657678224],[-114.25223329954821,42.678306700105715],[-114.25279040055142,42.678712300300425],[-114.25345425195313,42.678693062516],[-114.25372209992645,42.67868529937817],[-114.25422830058905,42.67839869989619],[-114.25469089972272,42.67851959990614],[-114.25525630026085,42.67839799985663],[-114.2563918004244,42.678740799683624],[-114.25714179936351,42.67950249986307],[-114.25712530000669,42.6819502000424],[-114.25711160069862,42.68397369991884],[-114.25465699999884,42.68442620019711],[-114.25229229999773,42.684861999674204],[-114.24974329893801,42.68533019969429],[-114.2486267002264,42.685511500004914],[-114.24862660051338,42.68554050010786],[-114.24483440027073,42.68552580030558],[-114.24131170000318,42.68551210025312],[-114.24115469964434,42.685511400293755],[-114.2399323000535,42.68459820015348],[-114.2399485999843,42.681876499955926]]],[[[-114.24970880003785,42.69640819992281],[-114.24970389972798,42.692787600018605],[-114.25460269953487,42.69281529997132],[-114.25950169966605,42.69284299991165],[-114.25951680034598,42.69646220021525],[-114.2546128001919,42.69643530009867],[-114.24970880003785,42.69640819992281]]],[[[-122.50865178071805,47.61633533468668],[-122.50746427348945,47.6148250023897],[-122.50762759349631,47.61480254865959],[-122.50778991457659,47.61477700108817],[-122.50795111006781,47.61474837844346],[-122.50811105151087,47.61471670312629],[-122.51010371333871,47.61430140449804],[-122.51016786562644,47.6142883120613],[-122.51041138093365,47.61424362213619],[-122.51040948099681,47.61458176413451],[-122.51040824760994,47.614801121949036],[-122.5104030903819,47.61571858749849],[-122.51039374700463,47.61572085227092],[-122.51038431649077,47.61572294324917],[-122.51037480423024,47.61572485861667],[-122.51036521830783,47.61572659716236],[-122.51026191115182,47.615750784801655],[-122.51015949332798,47.615776646785115],[-122.51007599582063,47.61579915156894],[-122.50999317294806,47.6158227693494],[-122.5099449262308,47.615836900530496],[-122.50989743499666,47.61585215743006],[-122.50985075673788,47.6158685200638],[-122.50980494714995,47.61588596844757],[-122.50976006103016,47.61590448380809],[-122.50971615497232,47.61592404192218],[-122.50967327838381,47.61594461977769],[-122.50963148606186,47.61596619133478],[-122.50959082651548,47.61598873297563],[-122.50955135094863,47.6160122150271],[-122.50951310517539,47.61603661144916],[-122.50947613500989,47.61606189135753],[-122.50944048716445,47.61608802386794],[-122.50940620296163,47.616114978095965],[-122.50939632239182,47.61612695102005],[-122.50938583635751,47.616138685960564],[-122.50937475743511,47.61615016959558],[-122.50936310089598,47.6161613886031],[-122.50935087841822,47.61617232905581],[-122.50933810527319,47.61618297763166],[-122.50932479673226,47.61619332161441],[-122.50931096896512,47.616203350104364],[-122.50929663724307,47.61621305038511],[-122.50928181953245,47.61622241034595],[-122.50926653379958,47.6162314184816],[-122.50925079801074,47.61624006570906],[-122.50923462923394,47.6162483411286],[-122.50921804992707,47.61625623444614],[-122.50920107715808,47.61626373657857],[-122.50918373158828,47.61627083844286],[-122.50916603387886,47.61627753156154],[-122.50914800648772,47.616283808668186],[-122.50912966917784,47.61628966128524],[-122.50911104350872,47.61629508275189],[-122.50909215373491,47.616300066407184],[-122.50907301961938,47.61630460740689], - [-122.50905366631491,47.61630869909014],[-122.50903411627938,47.61631233782372],[-122.50901439107238,47.61631551815777],[-122.50899451584671,47.61631823645919],[-122.5089745139586,47.61632049030575],[-122.50895440966254,47.61632227606429],[-122.50893422541641,47.61632359312931],[-122.50891398637307,47.61632443726198],[-122.50889371678699,47.616324809673436],[-122.50887343911609,47.61632470915263],[-122.50885317941147,47.61632413630507],[-122.50883296102938,47.616323091130724],[-122.508812807326,47.6163215748407],[-122.50879274345411,47.61631958985712],[-122.50877279276996,47.61631713860209],[-122.50875901261351,47.61631791309698],[-122.50874528006777,47.616319003081905],[-122.50873160950579,47.616320408556895],[-122.5087180179955,47.61632212588859],[-122.50870451991005,47.616324155682584],[-122.50869113231737,47.61632649430547],[-122.50867786959051,47.616329139335114],[-122.50866474700086,47.61633208713811],[-122.50865178071805,47.61633533468668]]]]}` - seattleObj := testJSON(t, seattle).(MultiPolygon) - boundsObj := testJSON(t, bounds).(MultiPolygon) - if seattleObj.Intersects(boundsObj) { - t.Fatalf("should not intersect") - } - if boundsObj.Intersects(seattleObj) { - t.Fatalf("should not intersect") - } -} diff --git a/pkg/geojson/object.go b/pkg/geojson/object.go deleted file mode 100644 index 3d40d1e4..00000000 --- a/pkg/geojson/object.go +++ /dev/null @@ -1,443 +0,0 @@ -package geojson - -import ( - "encoding/json" - "errors" - "fmt" - - "github.com/tidwall/gjson" - "github.com/tidwall/tile38/pkg/geojson/poly" -) - -const ( - point = 0 - multiPoint = 1 - lineString = 2 - multiLineString = 3 - polygon = 4 - multiPolygon = 5 - geometryCollection = 6 - feature = 7 - featureCollection = 8 -) - -var ( - errNotEnoughData = errors.New("not enough data") - errTooMuchData = errors.New("too much data") - errInvalidData = errors.New("invalid data") -) - -var ( // json errors - fmtErrTypeIsUnknown = "The type '%s' is unknown" - errInvalidTypeMember = errors.New("Type member is invalid. Expecting a string") - errInvalidCoordinates = errors.New("Coordinates member is invalid. Expecting an array") - errCoordinatesRequired = errors.New("Coordinates member is required") - errInvalidGeometries = errors.New("Geometries member is invalid. Expecting an array") - errGeometriesRequired = errors.New("Geometries member is required") - errInvalidGeometryMember = errors.New("Geometry member is invalid. Expecting an object") - errGeometryMemberRequired = errors.New("Geometry member is required") - errInvalidFeaturesMember = errors.New("Features member is invalid. Expecting an array") - errFeaturesMemberRequired = errors.New("Features member is required") - errInvalidFeature = errors.New("Invalid feature in collection") - errInvalidPropertiesMember = errors.New("Properties member in invalid. Expecting an array") - errInvalidCoordinatesValue = errors.New("Coordinates member has an invalid value") - errLineStringInvalidCoordinates = errors.New("Coordinates must be an array of two or more positions") - errInvalidNumberOfPositionValues = errors.New("Position must have two or more numbers") - errInvalidPositionValue = errors.New("Position has an invalid value") - errCoordinatesMustBeArray = errors.New("Coordinates member must be an array of positions") - errMustBeALinearRing = errors.New("Polygon must have at least 4 positions and the first and last position must be the same") - errBBoxInvalidType = errors.New("BBox member is an invalid. Expecting an array") - errBBoxInvalidNumberOfValues = errors.New("BBox member requires exactly 4 or 6 values") - errBBoxInvalidValue = errors.New("BBox has an invalid value") - errInvalidGeometry = errors.New("Invalid geometry in collection") -) - -const nilz = 0 - -// Object is a geojson object -type Object interface { - bboxPtr() *BBox - hasPositions() bool - appendJSON(dst []byte) []byte - - // WithinBBox detects if the object is fully contained inside a bbox. - WithinBBox(bbox BBox) bool - // IntersectsBBox detects if the object intersects a bbox. - IntersectsBBox(bbox BBox) bool - // Within detects if the object is fully contained inside another object. - Within(o Object) bool - // Intersects detects if the object intersects another object. - Intersects(o Object) bool - // WithinCircle detects if the object is fully contained inside a circle. - WithinCircle(center Position, meters float64) bool - // IntersectsCircle detects if the object intersects a circle. - IntersectsCircle(center Position, meters float64) bool - // Nearby detects if the object is nearby a position. - Nearby(center Position, meters float64) bool - // CalculatedBBox is exterior bbox containing the object. - CalculatedBBox() BBox - // CalculatedPoint is a point representation of the object. - CalculatedPoint() Position - // JSON is the json representation of the object. This might not be exactly the same as the original. - JSON() string - // String returns a string representation of the object. This may be JSON or something else. - String() string - // PositionCount return the number of coordinates. - PositionCount() int - // Weight returns the in-memory size of the object. - Weight() int - // MarshalJSON allows the object to be encoded in json.Marshal calls. - MarshalJSON() ([]byte, error) - // Geohash converts the object to a geohash value. - Geohash(precision int) (string, error) - // IsBBoxDefined returns true if the object has a defined bbox. - IsBBoxDefined() bool - // IsGeometry return true if the object is a geojson geometry object. false if it something else. - IsGeometry() bool - // Clip returns the object obtained by clipping this object by a bbox. - Clipped(bbox BBox) Object -} - -func positionBBox(i int, bbox BBox, ps []Position) (int, BBox) { - for _, p := range ps { - if i == 0 { - bbox.Min = p - bbox.Max = p - } else { - if p.X < bbox.Min.X { - bbox.Min.X = p.X - } - if p.Y < bbox.Min.Y { - bbox.Min.Y = p.Y - } - if p.X > bbox.Max.X { - bbox.Max.X = p.X - } - if p.Y > bbox.Max.Y { - bbox.Max.Y = p.Y - } - } - i++ - } - return i, bbox -} - -func isLinearRing(ps []Position) bool { - return len(ps) >= 4 && ps[0] == ps[len(ps)-1] -} - -// ObjectJSON parses geojson and returns an Object -func ObjectJSON(json string) (Object, error) { - return objectMap(json, root) -} - -var ( - root = 0 // accept all types - gcoll = 1 // accept only geometries - feat = 2 // accept only geometries - fcoll = 3 // accept only features -) - -func objectMap(json string, from int) (Object, error) { - var err error - res := gjson.Get(json, "type") - if res.Type != gjson.String { - return nil, errInvalidTypeMember - } - typ := res.String() - if from != root { - switch from { - case gcoll, feat: - switch typ { - default: - return nil, fmt.Errorf(fmtErrTypeIsUnknown, typ) - case "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "GeometryCollection": - } - case fcoll: - switch typ { - default: - return nil, fmt.Errorf(fmtErrTypeIsUnknown, typ) - case "Feature": - } - } - } - - var o Object - switch typ { - default: - return nil, fmt.Errorf(fmtErrTypeIsUnknown, typ) - case "Point": - o, err = fillSimplePointOrPoint(fillLevel1Map(json)) - case "MultiPoint": - o, err = fillMultiPoint(fillLevel2Map(json)) - case "LineString": - o, err = fillLineString(fillLevel2Map(json)) - case "MultiLineString": - o, err = fillMultiLineString(fillLevel3Map(json)) - case "Polygon": - o, err = fillPolygon(fillLevel3Map(json)) - case "MultiPolygon": - o, err = fillMultiPolygon(fillLevel4Map(json)) - case "GeometryCollection": - o, err = fillGeometryCollectionMap(json) - case "Feature": - o, err = fillFeatureMap(json) - case "FeatureCollection": - o, err = fillFeatureCollectionMap(json) - } - return o, err -} - -func withinObjectShared(g Object, o Object, pin func(v Polygon) bool) bool { - bbp := o.bboxPtr() - if bbp != nil { - if !g.WithinBBox(*bbp) { - return false - } - if o.IsBBoxDefined() { - return true - } - } - switch v := o.(type) { - default: - return false - case Point: - return g.WithinBBox(v.CalculatedBBox()) - case SimplePoint: - return g.WithinBBox(v.CalculatedBBox()) - case MultiPoint: - for i := range v.Coordinates { - if g.Within(Point{Coordinates: v.Coordinates[i]}) { - return true - } - } - return false - case LineString: - if len(v.Coordinates) == 0 { - return false - } - switch g := g.(type) { - default: - return false - case SimplePoint: - return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).IntersectsLineString(polyPositions(v.Coordinates)) - case Point: - return poly.Point(g.Coordinates).IntersectsLineString(polyPositions(v.Coordinates)) - case MultiPoint: - if len(v.Coordinates) == 0 { - return false - } - for _, p := range v.Coordinates { - if !poly.Point(p).IntersectsLineString(polyPositions(v.Coordinates)) { - return false - } - } - return true - } - case MultiLineString: - for i := range v.Coordinates { - if g.Within(v.getLineString(i)) { - return true - } - } - return false - case Polygon: - if len(v.Coordinates) == 0 { - return false - } - return pin(v) - case MultiPolygon: - for i := range v.Coordinates { - if pin(v.getPolygon(i)) { - return true - } - } - return false - case Feature: - return g.Within(v.Geometry) - case FeatureCollection: - if len(v.Features) == 0 { - return false - } - for _, f := range v.Features { - if !g.Within(f) { - return false - } - } - return true - case GeometryCollection: - if len(v.Geometries) == 0 { - return false - } - for _, f := range v.Geometries { - if !g.Within(f) { - return false - } - } - return true - } -} - -func intersectsObjectShared(g Object, o Object, pin func(v Polygon) bool) bool { - bbp := o.bboxPtr() - if bbp != nil { - if !g.IntersectsBBox(*bbp) { - return false - } - if o.IsBBoxDefined() { - return true - } - } - switch v := o.(type) { - default: - return false - case Point: - return g.IntersectsBBox(v.CalculatedBBox()) - case SimplePoint: - return g.IntersectsBBox(v.CalculatedBBox()) - case MultiPoint: - for i := range v.Coordinates { - if (Point{Coordinates: v.Coordinates[i]}).Intersects(g) { - return true - } - } - return false - case LineString: - if g, ok := g.(LineString); ok { - a := polyPositions(g.Coordinates) - b := polyPositions(v.Coordinates) - return a.LineStringIntersectsLineString(b) - } - return o.Intersects(g) - case MultiLineString: - for i := range v.Coordinates { - if g.Intersects(v.getLineString(i)) { - return true - } - } - return false - case Polygon: - if len(v.Coordinates) == 0 { - return false - } - return pin(v) - case MultiPolygon: - for _, coords := range v.Coordinates { - if pin(Polygon{Coordinates: coords}) { - return true - } - } - return false - case Feature: - return g.Intersects(v.Geometry) - case FeatureCollection: - if len(v.Features) == 0 { - return false - } - for _, f := range v.Features { - if g.Intersects(f) { - return true - } - } - return false - case GeometryCollection: - if len(v.Geometries) == 0 { - return false - } - for _, f := range v.Geometries { - if g.Intersects(f) { - return true - } - } - return false - } -} - -// CirclePolygon returns a Polygon around the radius. -func CirclePolygon(x, y, meters float64, steps int) Polygon { - if steps < 3 { - steps = 3 - } - p := Polygon{ - Coordinates: [][]Position{make([]Position, steps+1)}, - } - center := Position{X: x, Y: y, Z: 0} - step := 360.0 / float64(steps) - i := 0 - for deg := 360.0; deg > 0; deg -= step { - c := Position(poly.Point(center.Destination(meters, deg))) - p.Coordinates[0][i] = c - i++ - } - p.Coordinates[0][i] = p.Coordinates[0][0] - return p -} - -// The object's calculated bounding box must intersect the radius of the circle to pass. -func nearbyObjectShared(g Object, x, y float64, meters float64) bool { - if !g.hasPositions() { - return false - } - center := Position{X: x, Y: y, Z: 0} - bbox := g.CalculatedBBox() - if bbox.Min.X == bbox.Max.X && bbox.Min.Y == bbox.Max.Y { - // just a point, return is point is inside of the circle - return center.DistanceTo(bbox.Min) <= meters - } - circlePoly := CirclePolygon(x, y, meters, 12) - return g.Intersects(circlePoly) -} - -func jsonMarshalString(s string) []byte { - for i := 0; i < len(s); i++ { - if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 { - b, _ := json.Marshal(s) - return b - } - } - b := make([]byte, len(s)+2) - b[0] = '"' - copy(b[1:], s) - b[len(b)-1] = '"' - return b -} - -func stripWhitespace(s string) string { - var p []byte - var str bool - var escs int - for i := 0; i < len(s); i++ { - c := s[i] - if str { - // We're inside of a string. Look out for '"' and '\' characters. - if c == '\\' { - // Increment the escape character counter. - escs++ - } else { - if c == '"' && escs%2 == 0 { - // We reached the end of string - str = false - } - // Reset the escape counter - escs = 0 - } - } else if c == '"' { - // We encountared a double quote character. - str = true - } else if c <= ' ' { - // Ignore the whitespace - if p == nil { - p = []byte(s[:i]) - } - continue - } - // Append the character - if p != nil { - p = append(p, c) - } - } - if p == nil { - return s - } - return string(p) -} diff --git a/pkg/geojson/object_test.go b/pkg/geojson/object_test.go deleted file mode 100644 index f6e9ef66..00000000 --- a/pkg/geojson/object_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package geojson - -import "testing" - -func TestCirclePolygon(t *testing.T) { - circle := CirclePolygon(-115, 33, 10000, 20) - point := Point{Coordinates: Position{-115, 33, 0}} - if !point.Intersects(circle) { - t.Fatal("should intersect") - } - circle2 := CirclePolygon(-115, 33, 20000, 20) - if !circle2.Intersects(circle) { - t.Fatal("should intersect") - } - if !circle.Intersects(circle2) { - t.Fatal("should intersect") - } - rect := Polygon{ - Coordinates: [][]Position{ - { - {X: -120, Y: 20, Z: 0}, - {X: -120, Y: 40, Z: 0}, - {X: -100, Y: 40, Z: 0}, - {X: -100, Y: 40, Z: 0}, - {X: -120, Y: 20, Z: 0}, - }, - }, - } - if !circle.Intersects(rect) { - t.Fatal("should intersect") - } - if !rect.Intersects(circle) { - t.Fatal("should intersect") - } - line := LineString{ - Coordinates: []Position{ - {X: -116, Y: 23, Z: 0}, - {X: -114, Y: 43, Z: 0}, - }, - } - if !line.Intersects(circle) { - t.Fatal("should intersect") - } -} diff --git a/pkg/geojson/point.go b/pkg/geojson/point.go deleted file mode 100644 index ccd6ca1b..00000000 --- a/pkg/geojson/point.go +++ /dev/null @@ -1,157 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/tile38/pkg/geojson/geo" - "github.com/tidwall/tile38/pkg/geojson/geohash" - "github.com/tidwall/tile38/pkg/geojson/poly" -) - -// Point is a geojson object with the type "Point" -type Point struct { - Coordinates Position - BBox *BBox - bboxDefined bool -} - -func fillSimplePointOrPoint(coordinates Position, bbox *BBox, err error) (Object, error) { - if coordinates.Z == 0 && bbox == nil { - return fillSimplePoint(coordinates, bbox, err) - } - return fillPoint(coordinates, bbox, err) -} - -func fillPoint(coordinates Position, bbox *BBox, err error) (Point, error) { - bboxDefined := bbox != nil - if !bboxDefined { - cbbox := level1CalculatedBBox(coordinates, nil) - bbox = &cbbox - } - return Point{ - Coordinates: coordinates, - BBox: bbox, - bboxDefined: bboxDefined, - }, err -} - -// CalculatedBBox is exterior bbox containing the object. -func (g Point) CalculatedBBox() BBox { - return level1CalculatedBBox(g.Coordinates, g.BBox) -} - -// CalculatedPoint is a point representation of the object. -func (g Point) CalculatedPoint() Position { - if g.BBox == nil { - return g.Coordinates - } - return g.CalculatedBBox().center() -} - -// Geohash converts the object to a geohash value. -func (g Point) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g Point) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g Point) appendJSON(json []byte) []byte { - return appendLevel1JSON(json, "Point", g.Coordinates, g.BBox, g.bboxDefined) -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g Point) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g Point) String() string { - return g.JSON() -} - -// PositionCount return the number of coordinates. -func (g Point) PositionCount() int { - return level1PositionCount(g.Coordinates, g.BBox) -} - -// Weight returns the in-memory size of the object. -func (g Point) Weight() int { - return level1Weight(g.Coordinates, g.BBox) -} -func (g Point) bboxPtr() *BBox { - return g.BBox -} -func (g Point) hasPositions() bool { - return true -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g Point) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - return poly.Point(g.Coordinates).InsideRect(rectBBox(bbox)) -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g Point) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - return poly.Point(g.Coordinates).InsideRect(rectBBox(bbox)) -} - -// Within detects if the object is fully contained inside another object. -func (g Point) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - return poly.Point(g.Coordinates).Inside(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g Point) WithinCircle(center Position, meters float64) bool { - return geo.DistanceTo(g.Coordinates.Y, g.Coordinates.X, center.Y, center.X) < meters -} - -// Intersects detects if the object intersects another object. -func (g Point) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - return poly.Point(g.Coordinates).Intersects(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g Point) IntersectsCircle(center Position, meters float64) bool { - return geo.DistanceTo(g.Coordinates.Y, g.Coordinates.X, center.Y, center.X) <= meters -} - -// Nearby detects if the object is nearby a position. -func (g Point) Nearby(center Position, meters float64) bool { - return geo.DistanceTo(g.Coordinates.Y, g.Coordinates.X, center.Y, center.X) <= meters -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g Point) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g Point) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g Point) Clipped(bbox BBox) Object { - if g.IntersectsBBox(bbox) { - return g - } - - res, _ := fillMultiPoint([]Position{}, nil, nil) - return res -} diff --git a/pkg/geojson/point_test.go b/pkg/geojson/point_test.go deleted file mode 100644 index 654cb74d..00000000 --- a/pkg/geojson/point_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package geojson - -import "testing" - -func testJSONPoint(t *testing.T, js string) Point { - g := testJSON(t, js) - switch v := g.(type) { - case Point: - return v - case SimplePoint: - return Point{Coordinates: Position{X: v.X, Y: v.Y, Z: 0}} - } - t.Fatalf("not a point: %v", g) - return Point{} -} -func testConvertToPoint(g Object) Point { - switch v := g.(type) { - default: - panic("not a point") - case Point: - return v - case SimplePoint: - return Point{Coordinates: Position{X: v.X, Y: v.Y, Z: 0}} - } -} -func TestPointJSON(t *testing.T) { - testJSON(t, `{"type":"Point","coordinates":[100.1,5.1],"bbox":[0.1,0.1,100.1,100.1]}`) - testJSON(t, `{"type":"Point","coordinates":[100.1,5.1]}`) - testJSON(t, `{"type":"Point","coordinates":[100.1,5.1,10.5],"bbox":[0.1,0.1,20,100.1,100.1,30]}`) - testJSON(t, `{"type":"Point","coordinates":[100.1,5.1,10.5]}`) -} -func TestPointCreation2D(t *testing.T) { - p := P(100.5, 200.1) - g1 := Point{Coordinates: p} - jstr := g1.JSON() - g2, err := ObjectJSON(jstr) - if err != nil { - t.Fatal(err) - } - jstr2 := g2.JSON() - if jstr2 != jstr { - t.Fatalf("%v != %v", jstr2, jstr) - } - if testConvertToPoint(g2).Coordinates != p { - t.Fatalf("%v != %v", testConvertToPoint(g2).Coordinates, p) - } -} -func TestPointCreation3D(t *testing.T) { - p := P3(100.5, 200.1, 1029.3) - g1 := Point{Coordinates: p} - jstr := g1.JSON() - g2, err := ObjectJSON(jstr) - if err != nil { - t.Fatal(err) - } - jstr2 := g2.JSON() - if jstr2 != jstr { - t.Fatalf("%v != %v", jstr2, jstr) - } - if testConvertToPoint(g2).Coordinates != p { - t.Fatalf("%v != %v", testConvertToPoint(g2).Coordinates, p) - } -} -func TestPointWithinBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[0,0,100,100]}`) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[10,10]}`) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[-10,-10,100,100]}`) - if p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[-10,-10]}`) - if p.WithinBBox(bbox) { - t.Fatal("!") - } -} -func TestPointIntersectsBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[0,0,100,100]}`) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[10,10]}`) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[-10,-10,100,100]}`) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[-10,-10,0,0]}`) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[-10,-10,-1,-1]}`) - if p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSONPoint(t, `{"type":"Point","coordinates":[-10,-10]}`) - if p.IntersectsBBox(bbox) { - t.Fatal("!") - } - -} - -func TestPointWithinObject(t *testing.T) { - p := testJSONPoint(t, `{"type":"Point","coordinates":[10,10]}`) - if p.Within(testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[1,1,2,2]}`)) { - t.Fatal("!") - } - if !p.Within(testJSONPoint(t, `{"type":"Point","coordinates":[10,10],"bbox":[0,0,100,100]}`)) { - t.Fatal("!") - } - poly := testJSON(t, testPolyHoles) - ps := []Position{P(.5, 3), P(3.5, .5), P(6, 0), P(11, -1), P(11.5, -4.5)} - expect := true - for _, p := range ps { - got := tPoint(p.X, p.Y).Within(poly) - if got != expect { - t.Fatalf("%v within = %t, expect %t", p, got, expect) - } - } - ps = []Position{P(-2, 0), P(0, -2), P(1.5, 1.5), P(8, 1), P(10.5, -1.5), P(14, -1), P(8, -3)} - expect = false - for _, p := range ps { - got := tPoint(p.X, p.Y).Within(poly) - if got != expect { - t.Fatalf("%v within = %t, expect %t", p, got, expect) - } - } - -} diff --git a/pkg/geojson/poly/README.md b/pkg/geojson/poly/README.md deleted file mode 100644 index 4a7b11e7..00000000 --- a/pkg/geojson/poly/README.md +++ /dev/null @@ -1,14 +0,0 @@ -Poly -==== -[![Build Status](https://travis-ci.org/tidwall/poly.svg?branch=master)](https://travis-ci.org/tidwall/poly) -[![GoDoc](https://godoc.org/github.com/tidwall/poly?status.svg)](https://godoc.org/github.com/tidwall/poly) - -Polygon detection methods for Go. - -Contact -------- -Josh Baker [@tidwall](http://twitter.com/tidwall) - -License -------- -Poly source code is available under the MIT [License](/LICENSE). diff --git a/pkg/geojson/poly/inside.go b/pkg/geojson/poly/inside.go deleted file mode 100644 index ee6bcaab..00000000 --- a/pkg/geojson/poly/inside.go +++ /dev/null @@ -1,58 +0,0 @@ -package poly - -// Inside returns true if point is inside of exterior and not in a hole. -// The validity of the exterior and holes must be done elsewhere and are assumed valid. -// A valid exterior is a near-linear ring. -// A valid hole is one that is full contained inside the exterior. -// A valid hole may not share the same segment line as the exterior. -func (p Point) Inside(exterior Polygon, holes []Polygon) bool { - if !insideshpext(p, exterior, true) { - return false - } - for i := 0; i < len(holes); i++ { - if insideshpext(p, holes[i], false) { - return false - } - } - return true -} - -// Inside detects if a rect intersects another polygon -func (r Rect) Inside(exterior Polygon, holes []Polygon) bool { - return r.Polygon().Inside(exterior, holes) -} - -// Inside returns true if shape is inside of exterior and not in a hole. -func (shape Polygon) Inside(exterior Polygon, holes []Polygon) bool { - var ok bool - for _, p := range shape { - ok = p.Inside(exterior, holes) - if !ok { - return false - } - } - ok = true - for _, hole := range holes { - if hole.Inside(shape, nil) { - return false - } - } - return ok -} - -func insideshpext(p Point, shape Polygon, exterior bool) bool { - // if len(shape) < 3 { - // return false - // } - in := false - for i := 0; i < len(shape); i++ { - res := raycast(p, shape[i], shape[(i+1)%len(shape)]) - if res.on { - return exterior - } - if res.in { - in = !in - } - } - return in -} diff --git a/pkg/geojson/poly/inside_test.go b/pkg/geojson/poly/inside_test.go deleted file mode 100644 index a79296f8..00000000 --- a/pkg/geojson/poly/inside_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package poly - -import "testing" - -func testRayInside(t *testing.T, p Point, ps []Point, expect bool) { - res := insideshpext(p, ps, true) - if res != expect { - t.Fatalf("{%v,%v} = %t, expect %t", p.X, p.Y, res, expect) - } -} - -func TestRayInside(t *testing.T) { - strange := []Point{P(0, 0), P(0, 3), P(4, -3), P(4, 0), P(0, 0)} - - // on the edge - testRayInside(t, P(0, 0), strange, true) - testRayInside(t, P(0, 3), strange, true) - - testRayInside(t, P(4, -3), strange, true) - testRayInside(t, P(4, -2), strange, true) - testRayInside(t, P(3, 0), strange, true) - testRayInside(t, P(1, 0), strange, true) - - // ouside by just a tad - testRayInside(t, P(-0.1, 0), strange, false) - testRayInside(t, P(-0.1, -0.1), strange, false) - testRayInside(t, P(0, 3.1), strange, false) - testRayInside(t, P(0.1, 3.1), strange, false) - testRayInside(t, P(-0.1, 3), strange, false) - testRayInside(t, P(4, -3.1), strange, false) - testRayInside(t, P(3.9, -3), strange, false) - testRayInside(t, P(4.1, -2), strange, false) - testRayInside(t, P(3, 0.1), strange, false) - testRayInside(t, P(1, -0.1), strange, false) -} - -func TestRayInside2(t *testing.T) { - normal := []Point{P(0, 0), P(4, 3), P(5, 2), P(0, 0)} - testRayInside(t, P(1, 2), normal, false) - testRayInside(t, P(1, 3), normal, false) - testRayInside(t, P(4, 2), normal, true) - testRayInside(t, P(2, 1), normal, true) -} - -var texterior = Polygon{ - P(0, 0), - P(0, 6), - P(12, -6), - P(12, 0), - P(0, 0), -} -var tholeA = Polygon{ - P(1, 1), - P(1, 2), - P(2, 2), - P(2, 1), -} -var tholeB = Polygon{ - P(11, -1), - P(11, -3), - P(9, -1), -} -var tholes = []Polygon{tholeA, tholeB} - -func TestRayExteriorHoles(t *testing.T) { - - type point struct { - p Point - ok bool - } - - points := []point{ - {P(.5, 3), true}, - {P(11.5, -4.5), true}, - {P(6, 0), true}, - - {P(3.5, .5), true}, - {P(1.5, 1.5), false}, - {P(10.5, -1.5), false}, - {P(-2, 0), false}, - {P(0, -2), false}, - {P(8, -3), false}, - {P(8, 1), false}, - {P(14, -1), false}, - - {P(8, -0.5), true}, - {P(8, -1.5), true}, - {P(8, -1), true}, - } - // add the edges, all should be inside - for i := 0; i < len(texterior); i++ { - points = append(points, point{texterior[i], true}) - } - for i := 0; i < len(tholeA); i++ { - points = append(points, point{tholeA[i], true}) - } - for i := 0; i < len(tholeB); i++ { - points = append(points, point{tholeB[i], true}) - } - - for i := 0; i < len(points); i++ { - ok := points[i].p.Inside(texterior, tholes) - if ok != points[i].ok { - t.Fatalf("{%v,%v} = %t, expect %t", points[i].p.X, points[i].p.Y, ok, points[i].ok) - } - } -} - -func TestInsideShapes(t *testing.T) { - if texterior.Inside(texterior, nil) == false { - t.Fatalf("expect true, got false") - } - if texterior.Inside(texterior, tholes) == true { - t.Fatalf("expect false, got true") - } - if tholeA.Inside(texterior, nil) == false { - t.Fatalf("expect true, got false") - } - if tholeB.Inside(texterior, nil) == false { - t.Fatalf("expect true, got false") - } - if tholeA.Inside(tholeB, nil) == true { - t.Fatalf("expect false, got true") - } -} diff --git a/pkg/geojson/poly/intersects.go b/pkg/geojson/poly/intersects.go deleted file mode 100644 index e8238f96..00000000 --- a/pkg/geojson/poly/intersects.go +++ /dev/null @@ -1,172 +0,0 @@ -package poly - -// IntersectsLineString detect if a point intersects a linestring -func (p Point) IntersectsLineString(exterior Polygon) bool { - for j := 0; j < len(exterior); j++ { - if raycast(p, exterior[j], exterior[(j+1)%len(exterior)]).on { - return true - } - } - return false -} - -// Intersects detects if a point intersects another polygon -func (p Point) Intersects(exterior Polygon, holes []Polygon) bool { - return p.Inside(exterior, holes) -} - -// Intersects detects if a rect intersects another polygon -func (r Rect) Intersects(exterior Polygon, holes []Polygon) bool { - return r.Polygon().Intersects(exterior, holes) -} - -// Intersects detects if a polygon intersects another polygon -func (shape Polygon) Intersects(exterior Polygon, holes []Polygon) bool { - return shape.doesIntersects(false, exterior, holes) -} - -// LineStringIntersectsLineString detects if a linestring intersects a linestring -// assume shape and exterior are actually linestrings -func (shape Polygon) LineStringIntersectsLineString(exterior Polygon) bool { - for i := 0; i < len(shape); i++ { - for j := 0; j < len(exterior); j++ { - if lineintersects( - shape[i], shape[(i+1)%len(shape)], - exterior[j], exterior[(j+1)%len(exterior)], - ) { - return true - } - } - } - return false -} - -// LineStringIntersects detects if a polygon intersects a linestring -// assume shape is a linestring -func (shape Polygon) LineStringIntersects(exterior Polygon, holes []Polygon) bool { - return shape.doesIntersects(true, exterior, holes) -} -func (shape Polygon) doesIntersects(isLineString bool, exterior Polygon, holes []Polygon) bool { - switch len(shape) { - case 0: - return false - case 1: - switch len(exterior) { - case 0: - return false - case 1: - return shape[0].X == exterior[0].X && shape[0].Y == shape[0].Y - default: - return shape[0].Inside(exterior, holes) - } - default: - switch len(exterior) { - case 0: - return false - case 1: - return exterior[0].Inside(shape, holes) - } - } - if !shape.Rect().IntersectsRect(exterior.Rect()) { - return false - } - for i := 0; i < len(shape); i++ { - for j := 0; j < len(exterior); j++ { - if lineintersects( - shape[i], shape[(i+1)%len(shape)], - exterior[j], exterior[(j+1)%len(exterior)], - ) { - return true - } - } - } - for _, hole := range holes { - if shape.Inside(hole, nil) { - return false - } - } - if shape.Inside(exterior, nil) { - return true - } - if !isLineString { - if exterior.Inside(shape, nil) { - return true - } - } - return false -} - -func lineintersects( - a, b Point, // segment 1 - c, d Point, // segment 2 -) bool { - // do the bounding boxes intersect? - // the following checks without swapping values. - if a.Y > b.Y { - if c.Y > d.Y { - if b.Y > c.Y || a.Y < d.Y { - return false - } - } else { - if b.Y > d.Y || a.Y < c.Y { - return false - } - } - } else { - if c.Y > d.Y { - if a.Y > c.Y || b.Y < d.Y { - return false - } - } else { - if a.Y > d.Y || b.Y < c.Y { - return false - } - } - } - if a.X > b.X { - if c.X > d.X { - if b.X > c.X || a.X < d.X { - return false - } - } else { - if b.X > d.X || a.X < c.X { - return false - } - } - } else { - if c.X > d.X { - if a.X > c.X || b.X < d.X { - return false - } - } else { - if a.X > d.X || b.X < c.X { - return false - } - } - } - - // the following code is from http://ideone.com/PnPJgb - cmpx, cmpy := c.X-a.X, c.Y-a.Y - rx, ry := b.X-a.X, b.Y-a.Y - cmpxr := cmpx*ry - cmpy*rx - if cmpxr == 0 { - // Lines are collinear, and so intersect if they have any overlap - if !(((c.X-a.X <= 0) != (c.X-b.X <= 0)) || ((c.Y-a.Y <= 0) != (c.Y-b.Y <= 0))) { - return false - } - return true - } - sx, sy := d.X-c.X, d.Y-c.Y - cmpxs := cmpx*sy - cmpy*sx - rxs := rx*sy - ry*sx - if rxs == 0 { - return false // Lines are parallel. - } - rxsr := 1 / rxs - t := cmpxs * rxsr - u := cmpxr * rxsr - if !((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) { - return false - } - return true -} diff --git a/pkg/geojson/poly/intersects_test.go b/pkg/geojson/poly/intersects_test.go deleted file mode 100644 index 9528f26d..00000000 --- a/pkg/geojson/poly/intersects_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package poly - -import "testing" - -func testIntersectsLinesA(t *testing.T, a, b, c, d Point, expect bool) { - res := lineintersects(a, b, c, d) - if res != expect { - t.Fatalf("{%v,%v}, {%v,%v} = %t, expect %t", a, b, c, d, res, expect) - } - res = lineintersects(b, a, c, d) - if res != expect { - t.Fatalf("{%v,%v}, {%v,%v} = %t, expect %t", b, a, c, d, res, expect) - } - res = lineintersects(a, b, d, c) - if res != expect { - t.Fatalf("{%v,%v}, {%v,%v} = %t, expect %t", a, b, d, c, res, expect) - } - res = lineintersects(b, a, d, c) - if res != expect { - t.Fatalf("{%v,%v}, {%v,%v} = %t, expect %t", b, a, d, c, res, expect) - } -} - -func testIntersectsLines(t *testing.T, a, b, c, d Point, expect bool) { - testIntersectsLinesA(t, a, b, c, d, expect) - testIntersectsLinesA(t, c, d, a, b, expect) -} - -func TestIntersectsLines(t *testing.T) { - testIntersectsLines(t, P(0, 6), P(12, -6), P(0, 0), P(12, 0), true) - testIntersectsLines(t, P(0, 0), P(5, 5), P(5, 5), P(0, 10), true) - testIntersectsLines(t, P(0, 0), P(5, 5), P(5, 6), P(0, 10), false) - testIntersectsLines(t, P(0, 0), P(5, 5), P(5, 4), P(0, 10), true) - testIntersectsLines(t, P(0, 0), P(2, 2), P(0, 2), P(2, 0), true) - testIntersectsLines(t, P(0, 0), P(2, 2), P(0, 2), P(1, 1), true) - testIntersectsLines(t, P(0, 0), P(2, 2), P(2, 0), P(1, 1), true) - testIntersectsLines(t, P(0, 0), P(0, 4), P(1, 4), P(4, 1), false) - testIntersectsLines(t, P(0, 0), P(0, 4), P(1, 4), P(4, 4), false) - testIntersectsLines(t, P(0, 0), P(0, 4), P(4, 1), P(4, 4), false) - testIntersectsLines(t, P(0, 0), P(4, 0), P(1, 4), P(4, 1), false) - testIntersectsLines(t, P(0, 0), P(4, 0), P(1, 4), P(4, 4), false) - testIntersectsLines(t, P(0, 0), P(4, 0), P(4, 1), P(4, 4), false) - testIntersectsLines(t, P(0, 4), P(4, 0), P(1, 4), P(4, 1), false) - testIntersectsLines(t, P(0, 4), P(4, 0), P(1, 4), P(4, 4), false) - testIntersectsLines(t, P(0, 4), P(4, 0), P(4, 1), P(4, 4), false) -} - -func testIntersectsShapes(t *testing.T, exterior Polygon, holes []Polygon, shape Polygon, expect bool) { - got := shape.Intersects(exterior, holes) - if got != expect { - t.Fatalf("%v intersects %v = %v, expect %v", shape, exterior, got, expect) - } - got = exterior.Intersects(shape, nil) - if got != expect { - t.Fatalf("%v intersects %v = %v, expect %v", exterior, shape, got, expect) - } -} - -func TestIntersectsShapes(t *testing.T) { - - testIntersectsShapes(t, - Polygon{P(6, 0), P(12, 0), P(12, -6), P(6, 0)}, - nil, - Polygon{P(0, 0), P(0, 6), P(6, 0), P(0, 0)}, - true) - - testIntersectsShapes(t, - Polygon{P(7, 0), P(12, 0), P(12, -6), P(7, 0)}, - nil, - Polygon{P(0, 0), P(0, 6), P(6, 0), P(0, 0)}, - false) - - testIntersectsShapes(t, - Polygon{P(0.5, 0.5), P(0.5, 4.5), P(4.5, 0.5), P(0.5, 0.5)}, - nil, - Polygon{P(0, 0), P(0, 6), P(6, 0), P(0, 0)}, - true) - - testIntersectsShapes(t, - Polygon{P(0, 0), P(0, 6), P(6, 0), P(0, 0)}, - []Polygon{{P(1, 1), P(1, 2), P(2, 2), P(2, 1), P(1, 1)}}, - Polygon{P(0.5, 0.5), P(0.5, 4.5), P(4.5, 0.5), P(0.5, 0.5)}, - true) - - testIntersectsShapes(t, - Polygon{P(0, 0), P(0, 10), P(10, 10), P(10, 0), P(0, 0)}, - []Polygon{{P(2, 2), P(2, 6), P(6, 6), P(6, 2), P(2, 2)}}, - Polygon{P(1, 1), P(1, 9), P(9, 9), P(9, 1), P(1, 1)}, - true) -} diff --git a/pkg/geojson/poly/poly.go b/pkg/geojson/poly/poly.go deleted file mode 100644 index fae25be5..00000000 --- a/pkg/geojson/poly/poly.go +++ /dev/null @@ -1,120 +0,0 @@ -// Package poly provides polygon detection methods. -package poly - -import "fmt" - -// Point is simple 2D point -// For geo locations: X is lat, Y is lon, and Z is elev or time measure. -type Point struct { - X, Y, Z float64 -} - -// InsideRect detects point is inside of another rect -func (p Point) InsideRect(rect Rect) bool { - if p.X < rect.Min.X || p.X > rect.Max.X { - return false - } - if p.Y < rect.Min.Y || p.Y > rect.Max.Y { - return false - } - return true -} - -// Polygon is series of points that make up a polygon -type Polygon []Point - -// InsideRect detects polygon is inside of another rect -func (p Polygon) InsideRect(rect Rect) bool { - if len(p) == 0 { - return false - } - for _, p := range p { - if !p.InsideRect(rect) { - return false - } - } - return true -} - -// IntersectsRect detects polygon is inside of another rect -func (p Polygon) IntersectsRect(rect Rect) bool { - if len(p) == 0 { - return false - } - rectPoly := Polygon{rect.Min, {rect.Min.X, rect.Max.Y, 0}, rect.Max, {rect.Max.X, rect.Min.Y, 0}, rect.Min} - return p.Intersects(rectPoly, nil) -} - -// String returns a string representation of the polygon. -func (p Polygon) String() string { - s := "{" - for i, p := range p { - if i > 0 { - s += ", " - } - s += fmt.Sprintf("{%v, %v}", p.X, p.Y) - } - s += "}" - return s -} - -// Rect is rectangle -type Rect struct { - Min, Max Point -} - -// Polygon returns a polygon for the rect -func (r Rect) Polygon() Polygon { - p := Polygon(make([]Point, 5)) - p[0] = Point{X: r.Min.X, Y: r.Max.Y} - p[1] = Point{X: r.Max.X, Y: r.Max.Y} - p[2] = Point{X: r.Max.X, Y: r.Min.Y} - p[3] = Point{X: r.Min.X, Y: r.Min.Y} - p[4] = Point{X: r.Min.X, Y: r.Max.Y} - return p -} - -// Rect returns the bounding box rectangle for the polygon -func (p Polygon) Rect() Rect { - var bbox Rect - for i, p := range p { - if i == 0 { - bbox.Min = p - bbox.Max = p - } else { - if p.X < bbox.Min.X { - bbox.Min.X = p.X - } else if p.X > bbox.Max.X { - bbox.Max.X = p.X - } - if p.Y < bbox.Min.Y { - bbox.Min.Y = p.Y - } else if p.Y > bbox.Max.Y { - bbox.Max.Y = p.Y - } - } - } - return bbox -} - -// IntersectsRect detects if two bboxes intersect. -func (r Rect) IntersectsRect(rect Rect) bool { - if r.Min.Y > rect.Max.Y || r.Max.Y < rect.Min.Y { - return false - } - if r.Min.X > rect.Max.X || r.Max.X < rect.Min.X { - return false - } - return true -} - -// InsideRect detects rect is inside of another rect -func (r Rect) InsideRect(rect Rect) bool { - if r.Min.X < rect.Min.X || r.Max.X > rect.Max.X { - return false - } - if r.Min.Y < rect.Min.Y || r.Max.Y > rect.Max.Y { - return false - } - return true -} diff --git a/pkg/geojson/poly/poly_test.go b/pkg/geojson/poly/poly_test.go deleted file mode 100644 index 6b1afbaf..00000000 --- a/pkg/geojson/poly/poly_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package poly - -import "testing" - -func P(x, y float64) Point { - return Point{x, y, 0} -} - -func TestRectIntersects(t *testing.T) { - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(-1, -1), P(1, 1)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(9, 9), P(11, 11)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(9, -1), P(11, 1)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(-1, 9), P(1, 11)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(-1, -1), P(0, 0)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(10, 10), P(11, 11)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(10, -1), P(11, 0)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(-1, 10), P(0, 11)}) { - t.Fatal("!") - } - if !(Rect{P(0, 0), P(10, 10)}).IntersectsRect(Rect{P(1, 1), P(2, 2)}) { - t.Fatal("!") - } -} - -func TestRectInside(t *testing.T) { - if !(Rect{P(1, 1), P(9, 9)}).InsideRect(Rect{P(0, 0), P(10, 10)}) { - t.Fatal("!") - } - if (Rect{P(-1, -1), P(9, 9)}).InsideRect(Rect{P(0, 0), P(10, 10)}) { - t.Fatal("!") - } -} diff --git a/pkg/geojson/poly/raycast.go b/pkg/geojson/poly/raycast.go deleted file mode 100644 index e841127e..00000000 --- a/pkg/geojson/poly/raycast.go +++ /dev/null @@ -1,95 +0,0 @@ -package poly - -import "math" - -type rayres struct { - in, on bool -} - -func raycast(p, a, b Point) rayres { - // make sure that the point is inside the segment bounds - if a.Y < b.Y && (p.Y < a.Y || p.Y > b.Y) { - return rayres{false, false} - } else if a.Y > b.Y && (p.Y < b.Y || p.Y > a.Y) { - return rayres{false, false} - } - - // test if point is in on the segment - if a.Y == b.Y { - if a.X == b.X { - if p == a { - return rayres{false, true} - } else { - return rayres{false, false} - } - } - if p.Y == b.Y { - // horizontal segment - // check if the point in on the line - if a.X < b.X { - if p.X >= a.X && p.X <= b.X { - return rayres{false, true} - } - } else { - if p.X >= b.X && p.X <= a.X { - return rayres{false, true} - } - } - } - } - if a.X == b.X && p.X == b.X { - // vertical segment - // check if the point in on the line - if a.Y < b.Y { - if p.Y >= a.Y && p.Y <= b.Y { - return rayres{false, true} - } - } else { - if p.Y >= b.Y && p.Y <= a.Y { - return rayres{false, true} - } - } - } - if (p.X-a.X)/(b.X-a.X) == (p.Y-a.Y)/(b.Y-a.Y) { - return rayres{false, true} - } - - // do the actual raycast here. - for p.Y == a.Y || p.Y == b.Y { - p.Y = math.Nextafter(p.Y, math.Inf(1)) - } - if a.Y < b.Y { - if p.Y < a.Y || p.Y > b.Y { - return rayres{false, false} - } - } else { - if p.Y < b.Y || p.Y > a.Y { - return rayres{false, false} - } - } - if a.X > b.X { - if p.X > a.X { - return rayres{false, false} - } - if p.X < b.X { - return rayres{true, false} - } - } else { - if p.X > b.X { - return rayres{false, false} - } - if p.X < a.X { - return rayres{true, false} - } - } - if a.Y < b.Y { - if (p.Y-a.Y)/(p.X-a.X) >= (b.Y-a.Y)/(b.X-a.X) { - return rayres{true, false} - } - } else { - if (p.Y-b.Y)/(p.X-b.X) >= (a.Y-b.Y)/(a.X-b.X) { - return rayres{true, false} - } - } - return rayres{false, false} -} diff --git a/pkg/geojson/polygon.go b/pkg/geojson/polygon.go deleted file mode 100644 index db28fb03..00000000 --- a/pkg/geojson/polygon.go +++ /dev/null @@ -1,223 +0,0 @@ -package geojson - -import "github.com/tidwall/tile38/pkg/geojson/geohash" - -// Polygon is a geojson object with the type "Polygon" -type Polygon struct { - Coordinates [][]Position - BBox *BBox - bboxDefined bool -} - -func fillPolygon(coordinates [][]Position, bbox *BBox, err error) (Polygon, error) { - if err == nil { - if len(coordinates) == 0 { - err = errMustBeALinearRing - } - } - if err == nil { - for _, ps := range coordinates { - if !isLinearRing(ps) { - err = errMustBeALinearRing - break - } - } - } - bboxDefined := bbox != nil - if !bboxDefined { - cbbox := level3CalculatedBBox(coordinates, nil, true) - bbox = &cbbox - } - return Polygon{ - Coordinates: coordinates, - BBox: bbox, - bboxDefined: bboxDefined, - }, err -} - -// CalculatedBBox is exterior bbox containing the object. -func (g Polygon) CalculatedBBox() BBox { - return level3CalculatedBBox(g.Coordinates, g.BBox, true) -} - -// CalculatedPoint is a point representation of the object. -func (g Polygon) CalculatedPoint() Position { - return g.CalculatedBBox().center() -} - -// Geohash converts the object to a geohash value. -func (g Polygon) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// PositionCount return the number of coordinates. -func (g Polygon) PositionCount() int { - return level3PositionCount(g.Coordinates, g.BBox) -} - -// Weight returns the in-memory size of the object. -func (g Polygon) Weight() int { - return level3Weight(g.Coordinates, g.BBox) -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g Polygon) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g Polygon) appendJSON(json []byte) []byte { - return appendLevel3JSON(json, "Polygon", g.Coordinates, g.BBox, g.bboxDefined) -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g Polygon) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g Polygon) String() string { - return g.JSON() -} - -func (g Polygon) bboxPtr() *BBox { - return g.BBox -} -func (g Polygon) hasPositions() bool { - if g.bboxDefined { - return true - } - for _, c := range g.Coordinates { - if len(c) > 0 { - return true - } - } - return false -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g Polygon) WithinBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).InsideRect(rectBBox(bbox)) - } - if len(g.Coordinates) == 0 { - return false - } - if g.BBox != nil { - if !rectBBox(*g.BBox).IntersectsRect(rectBBox(bbox)) { - return false - } - } - rbbox := rectBBox(bbox) - ext, holes := polyExteriorHoles(g.Coordinates) - if len(holes) > 0 { - if rbbox.Max == rbbox.Min { - return rbbox.Min.Inside(ext, holes) - } - return rbbox.Inside(ext, holes) - } - return ext.InsideRect(rectBBox(bbox)) -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g Polygon) IntersectsBBox(bbox BBox) bool { - if g.bboxDefined { - return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) - } - if len(g.Coordinates) == 0 { - return false - } - if g.BBox != nil { - if !rectBBox(*g.BBox).IntersectsRect(rectBBox(bbox)) { - return false - } - } - rbbox := rectBBox(bbox) - ext, holes := polyExteriorHoles(g.Coordinates) - if len(holes) > 0 { - if rbbox.Max == rbbox.Min { - return rbbox.Min.Intersects(ext, holes) - } - return rbbox.Intersects(ext, holes) - } - return ext.IntersectsRect(rectBBox(bbox)) -} - -// Within detects if the object is fully contained inside another object. -func (g Polygon) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - return polyPositions(g.Coordinates[0]).Inside(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g Polygon) WithinCircle(center Position, meters float64) bool { - if len(g.Coordinates) == 0 { - return false - } - for _, position := range g.Coordinates[0] { - if center.DistanceTo(position) >= meters { - return false - } - } - return true -} - -// Intersects detects if the object intersects another object. -func (g Polygon) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - if len(g.Coordinates) == 0 { - return false - } - return polyPositions(g.Coordinates[0]).Intersects(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g Polygon) IntersectsCircle(center Position, meters float64) bool { - if g.Intersects(New2DPoint(center.X, center.Y)) { - return true - } - for _, polygon := range g.Coordinates { - for i := 0; i < len(polygon) - 1 ; i++ { - if SegmentIntersectsCircle(polygon[i], polygon[i + 1], center, meters) { - return true - } - } - } - return false -} - -// Nearby detects if the object is nearby a position. -func (g Polygon) Nearby(center Position, meters float64) bool { - return nearbyObjectShared(g, center.X, center.Y, meters) -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g Polygon) IsBBoxDefined() bool { - return g.bboxDefined -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g Polygon) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g Polygon) Clipped(bbox BBox) Object { - var new_coordinates [][]Position - - for _, ring := range g.Coordinates { - new_coordinates = append(new_coordinates, ClipRing(ring, bbox)) - } - - res, _ := fillPolygon(new_coordinates, nil, nil) - return res -} diff --git a/pkg/geojson/polygon_test.go b/pkg/geojson/polygon_test.go deleted file mode 100644 index e4cf50d3..00000000 --- a/pkg/geojson/polygon_test.go +++ /dev/null @@ -1,300 +0,0 @@ -package geojson - -import "testing" - -func TestPolygon(t *testing.T) { - testJSON(t, ` -{ - "type": "Polygon", - "coordinates": [ - [ - [-84.32281494140625,34.9895035675793], - [-84.29122924804688,35.21981940793435], - [-84.24041748046875,35.25459097465022], - [-84.22531127929688,35.266925688950074], - [-84.20745849609375,35.26580442886754], - [-84.19921875,35.24674063355999], - [-84.16213989257812,35.24113278166642], - [-84.12368774414062,35.24898366572645], - [-84.09072875976562,35.24898366572645], - [-84.08798217773438,35.264683153268116], - [-84.04266357421875,35.27701633139884], - [-84.03030395507812,35.291589484566124], - [-84.0234375,35.306160014550784], - [-84.03305053710936,35.32745068492882], - [-84.03579711914062,35.34313496028189], - [-84.03579711914062,35.348735749472546], - [-84.01657104492188,35.35545618392078], - [-84.01107788085938,35.37337460834958], - [-84.00970458984374,35.39128905521763], - [-84.01931762695312,35.41479572901859], - [-84.00283813476562,35.429344044107154], - [-83.93692016601562,35.47409160773029], - [-83.91220092773438,35.47632833265728], - [-83.88885498046875,35.504282143299655], - [-83.88473510742186,35.516578738902936], - [-83.8751220703125,35.52104976129943], - [-83.85314941406249,35.52104976129943], - [-83.82843017578125,35.52104976129943], - [-83.8092041015625,35.53446133418443], - [-83.80233764648438,35.54116627999813], - [-83.76800537109374,35.56239491058853], - [-83.7432861328125,35.56239491058853], - [-83.71994018554688,35.56239491058853], - [-83.67050170898438,35.569097520776054], - [-83.6334228515625,35.570214567965984], - [-83.61007690429688,35.576916524038616], - [-83.59634399414061,35.574682600980914], - [-83.5894775390625,35.55904339525896], - [-83.55239868164062,35.56574628576276], - [-83.49746704101562,35.563512051219696], - [-83.47000122070312,35.586968406786475], - [-83.4466552734375,35.60818490437746], - [-83.37936401367188,35.63609277863135], - [-83.35739135742188,35.65618041632016], - [-83.32305908203124,35.66622234103479], - [-83.3148193359375,35.65394870599763], - [-83.29971313476561,35.660643649881614], - [-83.28598022460938,35.67180064238771], - [-83.26126098632811,35.6907639509368], - [-83.25714111328125,35.69968630125201], - [-83.25576782226562,35.715298012125295], - [-83.23516845703125,35.72310272092263], - [-83.19808959960936,35.72756221127198], - [-83.16238403320312,35.753199435570316], - [-83.15826416015625,35.76322914549896], - [-83.10333251953125,35.76991491635478], - [-83.08685302734375,35.7843988251953], - [-83.0511474609375,35.787740890986576], - [-83.01681518554688,35.78328477203738], - [-83.001708984375,35.77882840327371], - [-82.96737670898438,35.793310688351724], - [-82.94540405273438,35.820040281161], - [-82.9193115234375,35.85121343450061], - [-82.9083251953125,35.86902116501695], - [-82.90557861328125,35.87792352995116], - [-82.91244506835938,35.92353244718235], - [-82.88360595703125,35.94688293218141], - [-82.85614013671875,35.951329861522666], - [-82.8424072265625,35.94243575255426], - [-82.825927734375,35.92464453144099], - [-82.80670166015625,35.927980690382704], - [-82.80532836914062,35.94243575255426], - [-82.77923583984375,35.97356075349624], - [-82.78060913085938,35.99245209055831], - [-82.76138305664062,36.00356252895066], - [-82.69546508789062,36.04465753921525], - [-82.64465332031249,36.060201412392914], - [-82.61306762695312,36.060201412392914], - [-82.60620117187499,36.033552893400376], - [-82.60620117187499,35.991340960635405], - [-82.60620117187499,35.97911749857497], - [-82.5787353515625,35.96133453736691], - [-82.5677490234375,35.951329861522666], - [-82.53067016601562,35.97244935753683], - [-82.46475219726562,36.006895355244666], - [-82.41668701171875,36.070192281208456], - [-82.37960815429686,36.10126686921446], - [-82.35488891601562,36.117908916563685], - [-82.34115600585936,36.113471382052175], - [-82.29583740234375,36.13343831245866], - [-82.26287841796874,36.13565654678543], - [-82.23403930664062,36.13565654678543], - [-82.2216796875,36.154509006695], - [-82.20382690429688,36.15561783381855], - [-82.19009399414062,36.144528857027744], - [-82.15438842773438,36.15007354140755], - [-82.14065551757812,36.134547437460064], - [-82.1337890625,36.116799556445024], - [-82.12142944335938,36.10570509327921], - [-82.08984375,36.10792411128649], - [-82.05276489257811,36.12678323326429], - [-82.03628540039062,36.12900165569652], - [-81.91268920898438,36.29409768373033], - [-81.89071655273438,36.30959215409138], - [-81.86325073242188,36.33504067209607], - [-81.83029174804688,36.34499652561904], - [-81.80145263671875,36.35605709240176], - [-81.77947998046874,36.34610265300638], - [-81.76162719726562,36.33835943134047], - [-81.73690795898438,36.33835943134047], - [-81.71905517578125,36.33835943134047], - [-81.70669555664062,36.33504067209607], - [-81.70669555664062,36.342784223707234], - [-81.72317504882812,36.357163062654365], - [-81.73278808593749,36.379279167407965], - [-81.73690795898438,36.40028364332352], - [-81.73690795898438,36.41354670392876], - [-81.72454833984374,36.423492513472326], - [-81.71768188476562,36.445589751779174], - [-81.69845581054688,36.47541104282962], - [-81.69845581054688,36.51073994146672], - [-81.705322265625,36.53060536411363], - [-81.69158935546875,36.55929085774001], - [-81.68060302734375,36.56480607840351], - [-81.68197631835938,36.58686302344181], - [-81.04202270507812,36.56370306576917], - [-80.74264526367186,36.561496993252575], - [-79.89120483398438,36.54053616262899], - [-78.68408203124999,36.53943280355122], - [-77.88345336914062,36.54053616262899], - [-76.91665649414062,36.54163950596125], - [-76.91665649414062,36.55046568575947], - [-76.31103515625,36.551568887374], - [-75.79605102539062,36.54936246839778], - [-75.6298828125,36.07574221562703], - [-75.4925537109375,35.82226734114509], - [-75.3936767578125,35.639441068973916], - [-75.41015624999999,35.43829554739668], - [-75.43212890625,35.263561862152095], - [-75.487060546875,35.18727767598896], - [-75.5914306640625,35.17380831799959], - [-75.9210205078125,35.04798673426734], - [-76.17919921875,34.867904962568744], - [-76.41540527343749,34.62868797377061], - [-76.4593505859375,34.57442951865274], - [-76.53076171875,34.53371242139567], - [-76.5911865234375,34.551811369170494], - [-76.651611328125,34.615126683462194], - [-76.761474609375,34.63320791137959], - [-77.069091796875,34.59704151614417], - [-77.376708984375,34.45674800347809], - [-77.5909423828125,34.3207552752374], - [-77.8326416015625,33.97980872872457], - [-77.9150390625,33.80197351806589], - [-77.9754638671875,33.73804486328907], - [-78.11279296875,33.8521697014074], - [-78.2830810546875,33.8521697014074], - [-78.4808349609375,33.815666308702774], - [-79.6728515625,34.8047829195724], - [-80.782470703125,34.836349990763864], - [-80.782470703125,34.91746688928252], - [-80.9307861328125,35.092945313732635], - [-81.0516357421875,35.02999636902566], - [-81.0516357421875,35.05248370662468], - [-81.0516357421875,35.137879119634185], - [-82.3150634765625,35.19625600786368], - [-82.3590087890625,35.19625600786368], - [-82.40295410156249,35.22318504970181], - [-82.4688720703125,35.16931803601131], - [-82.6885986328125,35.1154153142536], - [-82.781982421875,35.06147690849717], - [-83.1060791015625,35.003003395276714], - [-83.616943359375,34.99850370014629], - [-84.05639648437499,34.985003130171066], - [-84.22119140625,34.985003130171066], - [-84.32281494140625,34.9895035675793] - ], - [ - [-75.69030761718749,35.74205383068037], - [-75.5914306640625,35.74205383068037], - [-75.5419921875,35.585851593232356], - [-75.56396484375,35.32633026307483], - [-75.69030761718749,35.285984736065735], - [-75.970458984375,35.16482750605027], - [-76.2066650390625,34.994003757575776], - [-76.300048828125,35.02999636902566], - [-76.409912109375,35.07946034047981], - [-76.5252685546875,35.10642805736423], - [-76.4208984375,35.25907654252574], - [-76.3385009765625,35.294952147406576], - [-76.0858154296875,35.29943548054543], - [-75.948486328125,35.44277092585766], - [-75.8660888671875,35.53669637839501], - [-75.772705078125,35.567980458012094], - [-75.706787109375,35.634976650677295], - [-75.706787109375,35.74205383068037], - [-75.69030761718749,35.74205383068037] - ] - ] -} - `) -} - -func TestPolygonWithinBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"Polygon","coordinates":[[[10,10],[10,20],[20,10],[10,10]]],"bbox":[0,0,100,100]}`).(Polygon) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"Polygon","coordinates":[[[10,10],[10,20],[20,10],[10,10]]]}`).(Polygon) - if !p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"Polygon","coordinates":[[[10,10],[10,20],[20,10],[10,10]]],"bbox":[-10,-10,100,100]}`).(Polygon) - if p.WithinBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"Polygon","coordinates":[[[-10,-10],[10,20],[20,10],[-10,-10]]]}`).(Polygon) - if p.WithinBBox(bbox) { - t.Fatal("!") - } -} - -func TestPolygonIntersectsBBox(t *testing.T) { - bbox := BBox{Min: Position{0, 0, 0}, Max: Position{100, 100, 0}} - p := testJSON(t, `{"type":"Polygon","coordinates":[[[-10,-10],[10,20],[20,10],[-10,-10]]],"bbox":[0,0,100,100]}`).(Polygon) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"Polygon","coordinates":[[[-10,-10],[10,20],[20,10],[-10,-10]]]}`).(Polygon) - if !p.IntersectsBBox(bbox) { - t.Fatal("!") - } - p = testJSON(t, `{"type":"Polygon","coordinates":[[[-10,-10],[-30,-40],[-30,-90],[-10,-10]]]}`).(Polygon) - if p.IntersectsBBox(bbox) { - t.Fatal("!") - } -} - -func TestIssue241(t *testing.T) { - g, _ := ObjectJSON(`{ - "type": "Polygon", - "coordinates": [ - [ - [2.2571754455566406, 48.84472294197522], - [2.252626419067383, 48.8473212003792], - [2.2455883026123047, 48.847660093710566], - [2.2386789321899414, 48.846022088028505], - [2.2353315353393555, 48.842152792889486], - [2.235288619995117, 48.83966724854628], - [2.236189842224121, 48.836870863722986], - [2.240910530090332, 48.83461104476972], - [2.2461462020874023, 48.8343568087582], - [2.2527122497558594, 48.83540199299912], - [2.2573471069335938, 48.837746516162916], - [2.259106636047363, 48.84003443899817], - [2.259106636047363, 48.84302835299516], - [2.2571754455566406, 48.84472294197522] - ], - [ - [2.2546112537384033, 48.84285183001222], - [2.254890203475952, 48.842774159702614], - [2.255094051361084, 48.84197626682054], - [2.2551369667053223, 48.84108656596295], - [2.254589796066284, 48.8398649668164], - [2.2528302669525146, 48.83974492367404], - [2.2515535354614253, 48.839942641637556], - [2.2512423992156982, 48.84047223948045], - [2.2509634494781494, 48.841891534086436], - [2.2513389587402344, 48.84261881872204], - [2.2546112537384033, 48.84285183001222] - ] - ] -}`) - p1, _ := ObjectJSON(`{"type":"Point","coordinates":[2.253119945526123,48.841404318083505]}`) - if g.Intersects(p1) { - t.Fatalf("expected %v, got %v", false, g.Intersects(p1)) - } - if g.Within(p1) { - t.Fatalf("expected %v, got %v", false, g.Within(p1)) - } - p2, _ := ObjectJSON(`{"type":"Point","coordinates":[2.2564244270324703,48.83788774899389]}`) - if !g.Intersects(p2) { - t.Fatalf("expected %v, got %v", true, g.Intersects(p2)) - } - if !g.Within(p2) { - t.Fatalf("expected %v, got %v", false, g.Within(p2)) - } - -} diff --git a/pkg/geojson/position.go b/pkg/geojson/position.go deleted file mode 100644 index a913337a..00000000 --- a/pkg/geojson/position.go +++ /dev/null @@ -1,124 +0,0 @@ -package geojson - -import ( - "encoding/binary" - "math" - "strconv" - "unsafe" - - "github.com/tidwall/gjson" - "github.com/tidwall/tile38/pkg/geojson/geo" - "github.com/tidwall/tile38/pkg/geojson/poly" -) - -const sizeofPosition = 24 // (X,Y,Z) * 8 - -// Position is a simple point -type Position poly.Point - -func pointPositions(positions []Position) []poly.Point { - return *(*[]poly.Point)(unsafe.Pointer(&positions)) -} -func polyPositions(positions []Position) poly.Polygon { - return *(*poly.Polygon)(unsafe.Pointer(&positions)) -} -func polyMultiPositions(positions [][]Position) []poly.Polygon { - return *(*[]poly.Polygon)(unsafe.Pointer(&positions)) -} -func polyExteriorHoles(positions [][]Position) (exterior poly.Polygon, holes []poly.Polygon) { - switch len(positions) { - case 0: - case 1: - exterior = polyPositions(positions[0]) - default: - exterior = polyPositions(positions[0]) - holes = polyMultiPositions(positions[1:]) - } - return -} - -func appendPositionJSON(json []byte, p Position, isCordZ bool) []byte { - json = strconv.AppendFloat(json, p.X, 'f', -1, 64) - json = append(json, ',') - json = strconv.AppendFloat(json, p.Y, 'f', -1, 64) - if isCordZ { - json = append(json, ',') - json = strconv.AppendFloat(json, p.Z, 'f', -1, 64) - } - return json -} - -const earthRadius = 6371e3 - -func toRadians(deg float64) float64 { return deg * math.Pi / 180 } -func toDegrees(rad float64) float64 { return rad * 180 / math.Pi } - -// DistanceTo calculates the distance to a position -func (p Position) DistanceTo(position Position) float64 { - return geo.DistanceTo(p.Y, p.X, position.Y, position.X) -} - -// Destination calculates a new position based on the distance and bearing. -func (p Position) Destination(meters, bearingDegrees float64) Position { - lat, lon := geo.DestinationPoint(p.Y, p.X, meters, bearingDegrees) - return Position{X: lon, Y: lat, Z: 0} -} - -func fillPosition(coords gjson.Result) (Position, error) { - var p Position - v := coords.Array() - switch len(v) { - case 0: - return p, errInvalidNumberOfPositionValues - case 1: - if v[0].Type != gjson.Number { - return p, errInvalidPositionValue - } - return p, errInvalidNumberOfPositionValues - } - for i := 0; i < len(v); i++ { - if v[i].Type != gjson.Number { - return p, errInvalidPositionValue - } - } - p.X = v[0].Float() - p.Y = v[1].Float() - if len(v) > 2 { - p.Z = v[2].Float() - } else { - p.Z = nilz - } - return p, nil -} - -func fillPositionBytes(b []byte, isCordZ bool) (Position, []byte, error) { - var p Position - if len(b) < 8 { - return p, b, errNotEnoughData - } - p.X = math.Float64frombits(binary.LittleEndian.Uint64(b)) - b = b[8:] - if len(b) < 8 { - return p, b, errNotEnoughData - } - p.Y = math.Float64frombits(binary.LittleEndian.Uint64(b)) - b = b[8:] - if isCordZ { - if len(b) < 8 { - return p, b, errNotEnoughData - } - p.Z = math.Float64frombits(binary.LittleEndian.Uint64(b)) - b = b[8:] - } else { - p.Z = nilz - } - return p, b, nil -} - -// ExternalJSON is the simple json representation of the position used for external applications. -func (p Position) ExternalJSON() string { - if p.Z != 0 { - return `{"lat":` + strconv.FormatFloat(p.Y, 'f', -1, 64) + `,"lon":` + strconv.FormatFloat(p.X, 'f', -1, 64) + `,"z":` + strconv.FormatFloat(p.Z, 'f', -1, 64) + `}` - } - return `{"lat":` + strconv.FormatFloat(p.Y, 'f', -1, 64) + `,"lon":` + strconv.FormatFloat(p.X, 'f', -1, 64) + `}` -} diff --git a/pkg/geojson/simplepoint.go b/pkg/geojson/simplepoint.go deleted file mode 100644 index 35349a5f..00000000 --- a/pkg/geojson/simplepoint.go +++ /dev/null @@ -1,138 +0,0 @@ -package geojson - -import ( - "github.com/tidwall/tile38/pkg/geojson/geo" - "github.com/tidwall/tile38/pkg/geojson/geohash" - "github.com/tidwall/tile38/pkg/geojson/poly" -) - -// SimplePoint is a geojson object with the type "Point" and where there coordinate is 2D and there is no bbox. -type SimplePoint struct { - X, Y float64 -} - -// New2DPoint creates a SimplePoint -func New2DPoint(x, y float64) SimplePoint { - return SimplePoint{x, y} -} - -func fillSimplePoint(coordinates Position, bbox *BBox, err error) (SimplePoint, error) { - return SimplePoint{X: coordinates.X, Y: coordinates.Y}, err -} - -// CalculatedBBox is exterior bbox containing the object. -func (g SimplePoint) CalculatedBBox() BBox { - return BBox{ - Min: Position{X: g.X, Y: g.Y, Z: 0}, - Max: Position{X: g.X, Y: g.Y, Z: 0}, - } -} - -// CalculatedPoint is a point representation of the object. -func (g SimplePoint) CalculatedPoint() Position { - return Position{X: g.X, Y: g.Y, Z: 0} -} - -// Geohash converts the object to a geohash value. -func (g SimplePoint) Geohash(precision int) (string, error) { - p := g.CalculatedPoint() - return geohash.Encode(p.Y, p.X, precision) -} - -// PositionCount return the number of coordinates. -func (g SimplePoint) PositionCount() int { - return 1 -} - -// Weight returns the in-memory size of the object. -func (g SimplePoint) Weight() int { - return 2 * 8 -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (g SimplePoint) MarshalJSON() ([]byte, error) { - return g.appendJSON(nil), nil -} - -func (g SimplePoint) appendJSON(json []byte) []byte { - return appendLevel1JSON(json, "Point", Position{X: g.X, Y: g.Y, Z: 0}, nil, false) -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (g SimplePoint) JSON() string { - return string(g.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (g SimplePoint) String() string { - return g.JSON() -} - -func (g SimplePoint) bboxPtr() *BBox { - return nil -} -func (g SimplePoint) hasPositions() bool { - return true -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (g SimplePoint) WithinBBox(bbox BBox) bool { - return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).InsideRect(rectBBox(bbox)) -} - -// IntersectsBBox detects if the object intersects a bbox. -func (g SimplePoint) IntersectsBBox(bbox BBox) bool { - return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).InsideRect(rectBBox(bbox)) -} - -// Within detects if the object is fully contained inside another object. -func (g SimplePoint) Within(o Object) bool { - return withinObjectShared(g, o, - func(v Polygon) bool { - return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).Inside(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (g SimplePoint) WithinCircle(center Position, meters float64) bool { - return geo.DistanceTo(center.Y, center.X, g.Y, g.X) < meters -} - -// Intersects detects if the object intersects another object. -func (g SimplePoint) Intersects(o Object) bool { - return intersectsObjectShared(g, o, - func(v Polygon) bool { - return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).Intersects(polyExteriorHoles(v.Coordinates)) - }, - ) -} - -// IntersectsCircle detects if the object intersects a circle. -func (g SimplePoint) IntersectsCircle(center Position, meters float64) bool { - return geo.DistanceTo(center.Y, center.X, g.Y, g.X) <= meters -} - -// Nearby detects if the object is nearby a position. -func (g SimplePoint) Nearby(center Position, meters float64) bool { - return geo.DistanceTo(center.Y, center.X, g.Y, g.X) <= meters -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (g SimplePoint) IsBBoxDefined() bool { - return false -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (g SimplePoint) IsGeometry() bool { - return true -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (g SimplePoint) Clipped(bbox BBox) Object { - if g.IntersectsBBox(bbox) { - return g - } - res, _ := fillMultiPoint([]Position{}, nil, nil) - return res -} diff --git a/pkg/geojson/string.go b/pkg/geojson/string.go deleted file mode 100644 index e498ff85..00000000 --- a/pkg/geojson/string.go +++ /dev/null @@ -1,109 +0,0 @@ -package geojson - -// String is a not a geojson object, but just a string -type String string - -func (s String) bboxPtr() *BBox { - return nil -} -func (s String) hasPositions() bool { - return false -} - -// WithinBBox detects if the object is fully contained inside a bbox. -func (s String) WithinBBox(bbox BBox) bool { - return false -} - -// IntersectsBBox detects if the object intersects a bbox. -func (s String) IntersectsBBox(bbox BBox) bool { - return false -} - -// Within detects if the object is fully contained inside another object. -func (s String) Within(o Object) bool { - return false -} - -// WithinCircle detects if the object is fully contained inside a circle. -func (s String) WithinCircle(center Position, meters float64) bool { - return false -} - -// Intersects detects if the object intersects another object. -func (s String) Intersects(o Object) bool { - return false -} -func (s String) IntersectsCircle(center Position, meters float64) bool { - return false -} - -// Nearby detects if the object is nearby a position. -func (s String) Nearby(center Position, meters float64) bool { - return false -} - -// CalculatedBBox is exterior bbox containing the object. -func (s String) CalculatedBBox() BBox { - return BBox{} -} - -// CalculatedPoint is a point representation of the object. -func (s String) CalculatedPoint() Position { - return Position{} -} - -func (s String) appendJSON(json []byte) []byte { - b, _ := s.MarshalJSON() - return append(json, b...) -} - -// JSON is the json representation of the object. This might not be exactly the same as the original. -func (s String) JSON() string { - return string(s.appendJSON(nil)) -} - -// String returns a string representation of the object. This might be JSON or something else. -func (s String) String() string { - return string(s) -} - -// IsGeometry return true if the object is a geojson geometry object. false if it something else. -func (s String) IsGeometry() bool { - return false -} - -// Clip returns the object obtained by clipping this object by a bbox. -func (s String) Clipped(bbox BBox) Object { - return s -} - -// Bytes is the bytes representation of the object. -func (s String) Bytes() []byte { - return []byte(s.String()) -} - -// PositionCount return the number of coordinates. -func (s String) PositionCount() int { - return 0 -} - -// Weight returns the in-memory size of the object. -func (s String) Weight() int { - return len(s) -} - -// MarshalJSON allows the object to be encoded in json.Marshal calls. -func (s String) MarshalJSON() ([]byte, error) { - return jsonMarshalString(string(s)), nil -} - -// Geohash converts the object to a geohash value. -func (s String) Geohash(precision int) (string, error) { - return "", nil -} - -// IsBBoxDefined returns true if the object has a defined bbox. -func (s String) IsBBoxDefined() bool { - return false -} diff --git a/pkg/index/index.go b/pkg/index/index.go deleted file mode 100644 index 020d0d89..00000000 --- a/pkg/index/index.go +++ /dev/null @@ -1,95 +0,0 @@ -package index - -import ( - "github.com/tidwall/boxtree/d2" -) - -// Index is a geospatial index -type Index struct { - r d2.BoxTree -} - -// New create a new index -func New() *Index { - return &Index{} -} - -// Item represents an index item. -type Item interface { - Point() (x, y float64) - Rect() (minX, minY, maxX, maxY float64) -} - -// FlexItem can represent a point or a rectangle -type FlexItem struct { - MinX, MinY, MaxX, MaxY float64 -} - -// Rect returns the rectangle -func (item *FlexItem) Rect() (minX, minY, maxX, maxY float64) { - return item.MinX, item.MinY, item.MaxX, item.MaxY -} - -// Point returns the point -func (item *FlexItem) Point() (x, y float64) { - return item.MinX, item.MinY -} - -// Insert inserts an item into the index -func (ix *Index) Insert(item Item) { - minX, minY, maxX, maxY := item.Rect() - ix.r.Insert([]float64{minX, minY}, []float64{maxX, maxY}, item) -} - -// Remove removed an item from the index -func (ix *Index) Remove(item Item) { - minX, minY, maxX, maxY := item.Rect() - ix.r.Delete([]float64{minX, minY}, []float64{maxX, maxY}, item) -} - -// Count counts all items in the index. -func (ix *Index) Count() int { - return ix.r.Count() -} - -// Bounds returns the minimum bounding rectangle of all items in the index. -func (ix *Index) Bounds() (MinX, MinY, MaxX, MaxY float64) { - min, max := ix.r.Bounds() - return min[0], min[1], max[0], max[1] - -} - -// RemoveAll removes all items from the index. -func (ix *Index) RemoveAll() { - ix.r = d2.BoxTree{} -} - -// KNN returns the nearsest neighbors -func (ix *Index) KNN(x, y float64, iterator func(item interface{}) bool) bool { - res := true - ix.r.Nearby([]float64{x, y}, []float64{x, y}, - func(_, _ []float64, item interface{}) bool { - if !iterator(item) { - res = false - return false - } - return true - }) - return res -} - -// Search returns all items that intersect the bounding box. -func (ix *Index) Search(minX, minY, maxX, maxY float64, - iterator func(item interface{}) bool, -) bool { - res := true - ix.r.Search([]float64{minX, minY}, []float64{maxX, maxY}, - func(_, _ []float64, item interface{}) bool { - if !iterator(item) { - res = false - return false - } - return true - }) - return res -} diff --git a/pkg/index/index_test.go b/pkg/index/index_test.go deleted file mode 100644 index fdd6c893..00000000 --- a/pkg/index/index_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package index - -import ( - "fmt" - "math/rand" - "runtime" - "testing" - "time" -) - -func init() { - seed := time.Now().UnixNano() - fmt.Printf("seed: %d\n", seed) - rand.Seed(seed) -} - -func randf(min, max float64) float64 { - return rand.Float64()*(max-min) + min -} - -func randRect() (minX, minY, maxX, maxY float64) { - minX, minY = rand.Float64()*360-180, rand.Float64()*180-90 - maxX, maxY = rand.Float64()*360-180, rand.Float64()*180-90 - if minX > maxX { - minX, maxX = maxX, minX - } - if minY > maxY { - minY, maxY = maxY, minY - } - return -} - -func wp(minX, minY, maxX, maxY float64) *FlexItem { - return &FlexItem{ - MinX: minX, - MinY: minY, - MaxX: maxX, - MaxY: maxY, - } -} - -func TestRandomInserts(t *testing.T) { - - l := 100000 - tr := New() - i := 0 - var gitems []*FlexItem - var nitems []*FlexItem - - start := time.Now() - for ; i < l; i++ { - item := wp(randRect()) - tr.Insert(item) - if item.MinX >= -180 && item.MinY >= -90 && item.MaxX <= 180 && item.MaxY <= 90 { - gitems = append(gitems, item) - } else { - nitems = append(nitems, item) - } - } - insrdur := time.Now().Sub(start) - count := 0 - - count = tr.Count() - if count != l { - t.Fatalf("count == %d, expect %d", count, l) - } - count = 0 - items := make([]Item, 0, l) - tr.Search(-180, -90, +180, +90, func(item interface{}) bool { - count++ - items = append(items, item.(Item)) - return true - }) - - if count != len(gitems) { - t.Fatalf("count == %d, expect %d", count, len(gitems)) - } - start = time.Now() - count1 := 0 - tr.Search(33, -115, 34, -114, func(item interface{}) bool { - count1++ - return true - }) - searchdur1 := time.Now().Sub(start) - - start = time.Now() - count2 := 0 - - tr.Search(33-180, -115-360, 34-180, -114-360, func(item interface{}) bool { - count2++ - return true - }) - searchdur2 := time.Now().Sub(start) - - start = time.Now() - count3 := 0 - tr.Search(-10, 170, 20, 200, func(item interface{}) bool { - count3++ - return true - }) - searchdur3 := time.Now().Sub(start) - - fmt.Printf("Randomly inserted %d rects in %s.\n", l, insrdur.String()) - fmt.Printf("Searched %d items in %s.\n", count1, searchdur1.String()) - fmt.Printf("Searched %d items in %s.\n", count2, searchdur2.String()) - fmt.Printf("Searched %d items in %s.\n", count3, searchdur3.String()) - - tr.Search(-10, 170, 20, 200, func(item interface{}) bool { - lat1, lon1, lat2, lon2 := item.(Item).Rect() - if lat1 == lat2 && lon1 == lon2 { - return false - } - return true - }) - - tr.Search(-10, 170, 20, 200, func(item interface{}) bool { - lat1, lon1, lat2, lon2 := item.(Item).Rect() - if lat1 != lat2 || lon1 != lon2 { - return false - } - return true - }) - - // Remove all of the elements - for _, item := range items { - tr.Remove(item) - } - - count = tr.Count() - if count != 0 { - t.Fatalf("count == %d, expect %d", count, 0) - } - - tr.RemoveAll() - /* if tr.getQTreeItem(nil) != nil { - t.Fatal("getQTreeItem(nil) should return nil") - } - */ - // if tr.getRTreeItem(nil) != nil { - // t.Fatal("getRTreeItem(nil) should return nil") - // } -} - -func TestMemory(t *testing.T) { - rand.Seed(0) - l := 100000 - tr := New() - for i := 0; i < l; i++ { - swLat, swLon, neLat, neLon := randRect() - if rand.Int()%2 == 0 { // one in three chance that the rect is actually a point. - neLat, neLon = swLat, swLon - } - tr.Insert(wp(swLat, swLon, neLat, neLon)) - } - runtime.GC() - var m runtime.MemStats - runtime.ReadMemStats(&m) - const PtrSize = 32 << uintptr(uint64(^uintptr(0))>>63) - fmt.Printf("Memory consumption is %d bytes/object. Pointers are %d bytes.\n", int(m.HeapAlloc)/tr.Count(), PtrSize/8) -} - -func TestInsertVarious(t *testing.T) { - var count int - tr := New() - item := wp(33, -115, 33, -115) - tr.Insert(item) - count = tr.Count() - if count != 1 { - t.Fatalf("count = %d, expect 1", count) - } - tr.Remove(item) - count = tr.Count() - if count != 0 { - t.Fatalf("count = %d, expect 0", count) - } - tr.Insert(item) - count = tr.Count() - if count != 1 { - t.Fatalf("count = %d, expect 1", count) - } - found := false - tr.Search(-90, -180, 90, 180, func(item2 interface{}) bool { - if item2.(Item) == item { - found = true - } - return true - }) - if !found { - t.Fatal("did not find item") - } -} - -func BenchmarkInsertRect(b *testing.B) { - rand.Seed(time.Now().UnixNano()) - tr := New() - for i := 0; i < b.N; i++ { - swLat, swLon, neLat, neLon := randRect() - tr.Insert(wp(swLat, swLon, neLat, neLon)) - } -} - -func BenchmarkInsertPoint(b *testing.B) { - rand.Seed(time.Now().UnixNano()) - tr := New() - for i := 0; i < b.N; i++ { - swLat, swLon, _, _ := randRect() - tr.Insert(wp(swLat, swLon, swLat, swLon)) - } -} - -func BenchmarkInsertEither(b *testing.B) { - rand.Seed(time.Now().UnixNano()) - tr := New() - for i := 0; i < b.N; i++ { - swLat, swLon, neLat, neLon := randRect() - if rand.Int()%3 == 0 { // one in three chance that the rect is actually a point. - neLat, neLon = swLat, swLon - } - tr.Insert(wp(swLat, swLon, neLat, neLon)) - } -} - -// func BenchmarkSearchRect(b *testing.B) { -// rand.Seed(time.Now().UnixNano()) -// tr := New() -// for i := 0; i < 100000; i++ { -// swLat, swLon, neLat, neLon := randRect() -// tr.Insert(swLat, swLon, neLat, neLon) -// } -// b.ResetTimer() -// count := 0 -// //for i := 0; i < b.N; i++ { -// tr.Search(0, -180, 90, 180, func(id int) bool { -// count++ -// return true -// }) -// //} -// println(count) -// } diff --git a/tests/fence_tests.go b/tests/fence_test.go similarity index 100% rename from tests/fence_tests.go rename to tests/fence_test.go diff --git a/tests/json_tests.go b/tests/json_test.go similarity index 95% rename from tests/json_tests.go rename to tests/json_test.go index d632f0ae..17ee8a68 100644 --- a/tests/json_tests.go +++ b/tests/json_test.go @@ -22,6 +22,7 @@ func json_JSET_basic_test(mc *mockServer) error { {"JDEL", "mykey2", "myid1", "user.name.last"}, {0}, }) } + func json_JSET_geojson_test(mc *mockServer) error { return mc.DoBatch([][]interface{}{ {"SET", "mykey", "myid1", "POINT", 33, -115}, {"OK"}, @@ -35,6 +36,6 @@ func json_JSET_geojson_test(mc *mockServer) error { {"JSET", "mykey", "myid1", "properties.tags.-1", "united states"}, {"OK"}, {"JSET", "mykey", "myid1", "properties.tags.-1", "hot"}, {"OK"}, {"JGET", "mykey", "myid1"}, {`{"type":"Feature","geometry":{"type":"Point","coordinates":[-115,44]},"properties":{"tags":["southwest","united states","hot"]}}`}, - {"JDEL", "mykey", "myid1", "type"}, {"ERR Type member is invalid. Expecting a string"}, + {"JDEL", "mykey", "myid1", "type"}, {"ERR missing type"}, }) } diff --git a/tests/keys_search.go b/tests/keys_search_test.go similarity index 92% rename from tests/keys_search.go rename to tests/keys_search_test.go index d7831b3d..bfa05c46 100644 --- a/tests/keys_search.go +++ b/tests/keys_search_test.go @@ -16,17 +16,18 @@ func keys_KNN_test(mc *mockServer) error { {"SET", "mykey", "4", "POINT", -5, 5}, {"OK"}, {"SET", "mykey", "5", "POINT", 33, 21}, {"OK"}, {"NEARBY", "mykey", "LIMIT", 10, "DISTANCE", "POINTS", "POINT", 20, 20}, { - "[0 [" + - "[2 [19 19] 152808.67164037024] " + - "[3 [12 19] 895945.1409106688] " + - "[5 [33 21] 1448929.5916252395] " + - "[1 [5 5] 2327116.1069888202] " + - "[4 [-5 5] 3227402.6159841116]" + + "" + + "[0 [" + + ("" + + "[2 [19 19] 152808.67164036975] " + + "[3 [12 19] 895945.1409106685] " + + "[5 [33 21] 1448929.5916252395] " + + "[1 [5 5] 2327116.1069888202] " + + "[4 [-5 5] 3227402.6159841116]") + "]]"}, }) } - func keys_WITHIN_CIRCLE_test(mc *mockServer) error { return mc.DoBatch([][]interface{}{ {"SET", "mykey", "1", "POINT", 37.7335, -122.4412}, {"OK"}, diff --git a/tests/keys_test.go b/tests/keys_test.go index f2373ee8..f5e5f1c2 100644 --- a/tests/keys_test.go +++ b/tests/keys_test.go @@ -355,7 +355,6 @@ func keys_WHEREIN_test(mc *mockServer) error { }) } - func keys_WHEREEVAL_test(mc *mockServer) error { return mc.DoBatch([][]interface{}{ {"SET", "mykey", "myid_a1", "FIELD", "a", 1, "POINT", 33, -115}, {"OK"}, diff --git a/tests/mock_test.go b/tests/mock_test.go index 0641d9c5..52d686bd 100644 --- a/tests/mock_test.go +++ b/tests/mock_test.go @@ -12,9 +12,9 @@ import ( "time" "github.com/garyburd/redigo/redis" - "github.com/tidwall/tile38/pkg/controller" - "github.com/tidwall/tile38/pkg/core" - tlog "github.com/tidwall/tile38/pkg/log" + "github.com/tidwall/tile38/core" + "github.com/tidwall/tile38/internal/controller" + tlog "github.com/tidwall/tile38/internal/log" ) var errTimeout = errors.New("timeout") diff --git a/vendor/github.com/mmcloughlin/geohash/.appveyor.yml b/vendor/github.com/mmcloughlin/geohash/.appveyor.yml new file mode 100644 index 00000000..aecdaf0d --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/.appveyor.yml @@ -0,0 +1,15 @@ +version: "{build}" + +clone_folder: c:\gopath\src\github.com\mmcloughlin\geohash + +environment: + GOPATH: c:\gopath + +install: + - echo %PATH% + - echo %GOPATH% + - go version + - go env + +build_script: + - go test -v diff --git a/vendor/github.com/mmcloughlin/geohash/.gitignore b/vendor/github.com/mmcloughlin/geohash/.gitignore new file mode 100644 index 00000000..13e34578 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/.gitignore @@ -0,0 +1,26 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +coverage.out diff --git a/vendor/github.com/mmcloughlin/geohash/.travis.yml b/vendor/github.com/mmcloughlin/geohash/.travis.yml new file mode 100644 index 00000000..0332114c --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/.travis.yml @@ -0,0 +1,22 @@ +language: go +go: +- 1.x +- 1.3.x +- 1.4.x +- 1.5.x +- 1.6.x +- 1.7.x +- 1.8.x +- 1.9.x +- 1.10.x +- 1.11.x +before_install: +- go get github.com/axw/gocov/gocov +- go get github.com/mattn/goveralls +- go get golang.org/x/tools/cmd/cover +- go get github.com/klauspost/asmfmt/cmd/asmfmt +script: +- test -z "$(asmfmt -l *.s)" +- go test -v -covermode=count -coverprofile=profile.cov +after_success: +- goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/pkg/geojson/poly/LICENSE b/vendor/github.com/mmcloughlin/geohash/LICENSE similarity index 85% rename from pkg/geojson/poly/LICENSE rename to vendor/github.com/mmcloughlin/geohash/LICENSE index f820c7bd..c0190c9a 100644 --- a/pkg/geojson/poly/LICENSE +++ b/vendor/github.com/mmcloughlin/geohash/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2016 Josh Baker +The MIT License (MIT) + +Copyright (c) 2015 Michael McLoughlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7,15 +9,14 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -Contact GitHub API Training Shop Blog About +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mmcloughlin/geohash/README.md b/vendor/github.com/mmcloughlin/geohash/README.md new file mode 100644 index 00000000..0a34e3eb --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/README.md @@ -0,0 +1,214 @@ +# geohash + +Go [geohash](https://en.wikipedia.org/wiki/Geohash) library offering encoding +and decoding for string and integer geohashes. + +[![GoDoc Reference](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/mmcloughlin/geohash) +[![Build status](https://img.shields.io/travis/mmcloughlin/geohash.svg?style=flat-square)](https://travis-ci.org/mmcloughlin/geohash) +[![Coverage](https://img.shields.io/coveralls/mmcloughlin/geohash.svg?style=flat-square)](https://coveralls.io/r/mmcloughlin/geohash) +[![Go Report Card](https://goreportcard.com/badge/github.com/mmcloughlin/geohash?style=flat-square)](https://goreportcard.com/report/github.com/mmcloughlin/geohash) + +## Install + +Fetch the package with + +``` +go get github.com/mmcloughlin/geohash +``` + +And import it into your programs with + +```go +import "github.com/mmcloughlin/geohash" +``` + +## Usage + +#### func Decode + +```go +func Decode(hash string) (lat, lng float64) +``` +Decode the string geohash to a (lat, lng) point. + +#### func DecodeCenter + +```go +func DecodeCenter(hash string) (lat, lng float64) +``` +DecodeCenter decodes the string geohash to the central point of the bounding +box. + +#### func DecodeInt + +```go +func DecodeInt(hash uint64) (lat, lng float64) +``` +DecodeInt decodes the provided 64-bit integer geohash to a (lat, lng) point. + +#### func DecodeIntWithPrecision + +```go +func DecodeIntWithPrecision(hash uint64, bits uint) (lat, lng float64) +``` +DecodeIntWithPrecision decodes the provided integer geohash with bits of +precision to a (lat, lng) point. + +#### func Encode + +```go +func Encode(lat, lng float64) string +``` +Encode the point (lat, lng) as a string geohash with the standard 12 characters +of precision. + +#### func EncodeInt + +```go +func EncodeInt(lat, lng float64) uint64 +``` +EncodeInt encodes the point (lat, lng) to a 64-bit integer geohash. + +#### func EncodeIntWithPrecision + +```go +func EncodeIntWithPrecision(lat, lng float64, bits uint) uint64 +``` +EncodeIntWithPrecision encodes the point (lat, lng) to an integer with the +specified number of bits. + +#### func EncodeWithPrecision + +```go +func EncodeWithPrecision(lat, lng float64, chars uint) string +``` +EncodeWithPrecision encodes the point (lat, lng) as a string geohash with the +specified number of characters of precision (max 12). + +#### func Neighbor + +```go +func Neighbor(hash string, direction Direction) string +``` +Neighbor returns a geohash string that corresponds to the provided geohash's +neighbor in the provided direction + +#### func NeighborInt + +```go +func NeighborInt(hash uint64, direction Direction) uint64 +``` +NeighborInt returns a uint64 that corresponds to the provided hash's neighbor in +the provided direction at 64-bit precision. + +#### func NeighborIntWithPrecision + +```go +func NeighborIntWithPrecision(hash uint64, bits uint, direction Direction) uint64 +``` +NeighborIntWithPrecision returns a uint64s that corresponds to the provided +hash's neighbor in the provided direction at the given precision. + +#### func Neighbors + +```go +func Neighbors(hash string) []string +``` +Neighbors returns a slice of geohash strings that correspond to the provided +geohash's neighbors. + +#### func NeighborsInt + +```go +func NeighborsInt(hash uint64) []uint64 +``` +NeighborsInt returns a slice of uint64s that correspond to the provided hash's +neighbors at 64-bit precision. + +#### func NeighborsIntWithPrecision + +```go +func NeighborsIntWithPrecision(hash uint64, bits uint) []uint64 +``` +NeighborsIntWithPrecision returns a slice of uint64s that correspond to the +provided hash's neighbors at the given precision. + +#### type Box + +```go +type Box struct { + MinLat float64 + MaxLat float64 + MinLng float64 + MaxLng float64 +} +``` + +Box represents a rectangle in latitude/longitude space. + +#### func BoundingBox + +```go +func BoundingBox(hash string) Box +``` +BoundingBox returns the region encoded by the given string geohash. + +#### func BoundingBoxInt + +```go +func BoundingBoxInt(hash uint64) Box +``` +BoundingBoxInt returns the region encoded by the given 64-bit integer geohash. + +#### func BoundingBoxIntWithPrecision + +```go +func BoundingBoxIntWithPrecision(hash uint64, bits uint) Box +``` +BoundingBoxIntWithPrecision returns the region encoded by the integer geohash +with the specified precision. + +#### func (Box) Center + +```go +func (b Box) Center() (lat, lng float64) +``` +Center returns the center of the box. + +#### func (Box) Contains + +```go +func (b Box) Contains(lat, lng float64) bool +``` +Contains decides whether (lat, lng) is contained in the box. The containment +test is inclusive of the edges and corners. + +#### func (Box) Round + +```go +func (b Box) Round() (lat, lng float64) +``` +Round returns a point inside the box, making an effort to round to minimal +precision. + +#### type Direction + +```go +type Direction int +``` + +Direction represents directions in the latitute/longitude space. + +```go +const ( + North Direction = iota + NorthEast + East + SouthEast + South + SouthWest + West + NorthWest +) +``` +Cardinal and intercardinal directions diff --git a/vendor/github.com/mmcloughlin/geohash/README.tmpl b/vendor/github.com/mmcloughlin/geohash/README.tmpl new file mode 100644 index 00000000..6f0fe4b8 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/README.tmpl @@ -0,0 +1,25 @@ +# geohash + +Go [geohash](https://en.wikipedia.org/wiki/Geohash) library offering encoding +and decoding for string and integer geohashes. + +[![GoDoc Reference](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/mmcloughlin/geohash) +[![Build status](https://img.shields.io/travis/mmcloughlin/geohash.svg?style=flat-square)](https://travis-ci.org/mmcloughlin/geohash) +[![Coverage](https://img.shields.io/coveralls/mmcloughlin/geohash.svg?style=flat-square)](https://coveralls.io/r/mmcloughlin/geohash) +[![Go Report Card](https://goreportcard.com/badge/github.com/mmcloughlin/geohash?style=flat-square)](https://goreportcard.com/report/github.com/mmcloughlin/geohash) + +## Install + +Fetch the package with + +``` +go get {{ .ImportPath }} +``` + +And import it into your programs with + +```go +import "{{ .ImportPath }}" +``` + +{{ .EmitUsage }} diff --git a/vendor/github.com/mmcloughlin/geohash/asm_x86.s b/vendor/github.com/mmcloughlin/geohash/asm_x86.s new file mode 100644 index 00000000..0d2d80c0 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/asm_x86.s @@ -0,0 +1,56 @@ +// +build amd64,go1.6 + +#include "textflag.h" + +// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid(SB), NOSPLIT, $0-24 + MOVL eaxArg+0(FP), AX + MOVL ecxArg+4(FP), CX + CPUID + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) + RET + +// func EncodeInt(lat, lng float64) uint64 +TEXT ·EncodeInt(SB), NOSPLIT, $0 + CMPB ·useAsm(SB), $1 + JNE fallback + +#define LATF X0 +#define LATI R8 +#define LNGF X1 +#define LNGI R9 +#define TEMP R10 +#define GHSH R11 +#define MASK BX + + MOVSD lat+0(FP), LATF + MOVSD lng+8(FP), LNGF + + MOVQ $0x5555555555555555, MASK + + MULSD $(0.005555555555555556), LATF + ADDSD $(1.5), LATF + + MULSD $(0.002777777777777778), LNGF + ADDSD $(1.5), LNGF + + MOVQ LNGF, LNGI + SHRQ $20, LNGI + + MOVQ LATF, LATI + SHRQ $20, LATI + PDEPQ MASK, LATI, GHSH + + PDEPQ MASK, LNGI, TEMP + + SHLQ $1, TEMP + XORQ TEMP, GHSH + + MOVQ GHSH, ret+16(FP) + RET + +fallback: + JMP ·encodeInt(SB) diff --git a/vendor/github.com/mmcloughlin/geohash/base32.go b/vendor/github.com/mmcloughlin/geohash/base32.go new file mode 100644 index 00000000..916b272b --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/base32.go @@ -0,0 +1,44 @@ +package geohash + +// encoding encapsulates an encoding defined by a given base32 alphabet. +type encoding struct { + encode string + decode [256]byte +} + +// newEncoding constructs a new encoding defined by the given alphabet, +// which must be a 32-byte string. +func newEncoding(encoder string) *encoding { + e := new(encoding) + e.encode = encoder + for i := 0; i < len(e.decode); i++ { + e.decode[i] = 0xff + } + for i := 0; i < len(encoder); i++ { + e.decode[encoder[i]] = byte(i) + } + return e +} + +// Decode string into bits of a 64-bit word. The string s may be at most 12 +// characters. +func (e *encoding) Decode(s string) uint64 { + x := uint64(0) + for i := 0; i < len(s); i++ { + x = (x << 5) | uint64(e.decode[s[i]]) + } + return x +} + +// Encode bits of 64-bit word into a string. +func (e *encoding) Encode(x uint64) string { + b := [12]byte{} + for i := 0; i < 12; i++ { + b[11-i] = e.encode[x&0x1f] + x >>= 5 + } + return string(b[:]) +} + +// Base32Encoding with the Geohash alphabet. +var base32encoding = newEncoding("0123456789bcdefghjkmnpqrstuvwxyz") diff --git a/vendor/github.com/mmcloughlin/geohash/benchmark_test.go b/vendor/github.com/mmcloughlin/geohash/benchmark_test.go new file mode 100644 index 00000000..38752a28 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/benchmark_test.go @@ -0,0 +1,57 @@ +// +build go1.7 + +package geohash + +import ( + "strconv" + "testing" +) + +func BenchmarkEncodeInt(b *testing.B) { + points := RandomPoints(1024) + b.ResetTimer() + for i := 0; i < b.N; i++ { + EncodeInt(points[i%1024][0], points[i%1024][1]) + } +} + +func BenchmarkEncode(b *testing.B) { + points := RandomPoints(1024) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Encode(points[i%1024][0], points[i%1024][1]) + } +} + +func BenchmarkEncodeWithPrecision(b *testing.B) { + points := RandomPoints(1024) + for chars := uint(1); chars <= 12; chars++ { + name := strconv.FormatUint(uint64(chars), 10) + b.Run(name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + EncodeWithPrecision(points[i%1024][0], points[i%1024][1], chars) + } + }) + } +} + +func BenchmarkDecodeInt(b *testing.B) { + geohashes := RandomIntGeohashes(1024) + b.ResetTimer() + for i := 0; i < b.N; i++ { + DecodeInt(geohashes[i%1024]) + } +} + +func BenchmarkDecode(b *testing.B) { + for chars := uint(1); chars <= 12; chars++ { + name := strconv.FormatUint(uint64(chars), 10) + b.Run(name, func(b *testing.B) { + geohashes := RandomStringGeohashesWithPrecision(1024, chars) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Decode(geohashes[i%1024]) + } + }) + } +} diff --git a/vendor/github.com/mmcloughlin/geohash/decodecases_test.go b/vendor/github.com/mmcloughlin/geohash/decodecases_test.go new file mode 100644 index 00000000..12657848 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/decodecases_test.go @@ -0,0 +1,4125 @@ +package geohash + +// # Test cases generated with https://github.com/hkwi/python-geohash +// import sys +// import random +// import geohash +// +// trials_bits = int(sys.argv[1]) +// trials = 1 << trials_bits +// +// base32_alphabet = '0123456789bcdefghjkmnpqrstuvwxyz' +// +// print 'package geohash' +// print +// print ''.join(['//\t' + line for line in open(__file__, 'r')]) +// print 'var decodecases = []DecodeTestCase{' +// +// for i in range(trials): +// n = random.randrange(1, 13) +// hash_str = ''.join([random.choice(base32_alphabet) for i in range(n)]) +// lat, lng, lat_err, lng_err = geohash.decode_exactly(hash_str) +// print '\t{"%s", Box{%s, %s, %s, %s}},' % (hash_str, +// lat - lat_err, lat + lat_err, +// lng - lng_err, lng + lng_err,) +// +// print '}' + +var decodecases = []DecodeTestCase{ + {"91rc", Box{7.20703125, 7.3828125, -124.1015625, -123.75}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"0fuz", Box{-73.30078125, -73.125, -139.5703125, -139.21875}}, + {"dwfcndf", Box{38.1596374512, 38.1610107422, -63.3444213867, -63.3430480957}}, + {"2z7", Box{-4.21875, -2.8125, -142.03125, -140.625}}, + {"7spw2w", Box{-21.3684082031, -21.3629150391, -11.9311523438, -11.9201660156}}, + {"eq", Box{33.75, 39.375, -33.75, -22.5}}, + {"mgff0", Box{-23.5546875, -23.5107421875, 82.6171875, 82.6611328125}}, + {"dp7k386jtk0", Box{41.5306591988, 41.5306605399, -85.3607976437, -85.3607963026}}, + {"pjb", Box{-57.65625, -56.25, 135.0, 136.40625}}, + {"jkc7uh9", Box{-62.5973510742, -62.5959777832, 58.184967041, 58.186340332}}, + {"1gdp9", Box{-68.994140625, -68.9501953125, -98.3935546875, -98.349609375}}, + {"z9yj14mmnxte", Box{55.7359149121, 55.7359150797, 165.988941416, 165.988941751}}, + {"2brk", Box{-42.890625, -42.71484375, -136.0546875, -135.703125}}, + {"dhv5t2qh59", Box{27.3360496759, 27.3360550404, -82.7296471596, -82.7296364307}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"3fgd9k15b5k", Box{-29.0691630542, -29.0691617131, -96.2718147039, -96.2718133628}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"b4", Box{56.25, 61.875, -180.0, -168.75}}, + {"sb38", Box{1.40625, 1.58203125, 35.859375, 36.2109375}}, + {"puqeug", Box{-65.4180908203, -65.4125976562, 178.099365234, 178.110351562}}, + {"45", Box{-73.125, -67.5, -90.0, -78.75}}, + {"34b", Box{-29.53125, -28.125, -135.0, -133.59375}}, + {"tqb8jzn9dfqn", Box{38.0074727163, 38.0074728839, 57.2148630023, 57.2148633376}}, + {"9x", Box{39.375, 45.0, -112.5, -101.25}}, + {"tybf7", Box{38.3642578125, 38.408203125, 79.9365234375, 79.98046875}}, + {"9nc", Box{37.96875, 39.375, -133.59375, -132.1875}}, + {"pp21", Box{-49.04296875, -48.8671875, 135.0, 135.3515625}}, + {"s6wjfu76v", Box{15.0970602036, 15.0971031189, 19.8130273819, 19.8130702972}}, + {"wxh8ped7", Box{39.3947410583, 39.3949127197, 119.160804749, 119.161148071}}, + {"8gr", Box{18.28125, 19.6875, -136.40625, -135.0}}, + {"ug6hf", Box{64.1162109375, 64.16015625, 36.650390625, 36.6943359375}}, + {"pb", Box{-90.0, -84.375, 168.75, 180.0}}, + {"nmhvpv", Box{-60.9686279297, -60.9631347656, 108.270263672, 108.28125}}, + {"rxgthm", Box{-0.499877929688, -0.494384765625, 162.608642578, 162.619628906}}, + {"mj8t", Box{-13.18359375, -13.0078125, 45.703125, 46.0546875}}, + {"rkvw", Box{-17.2265625, -17.05078125, 153.984375, 154.3359375}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"u4ryw22k", Box{58.8008880615, 58.8010597229, 11.1734390259, 11.1737823486}}, + {"96bf6jfr", Box{15.8970451355, 15.8972167969, -122.60433197, -122.603988647}}, + {"ubhnbn2n1jvj", Box{46.2219173647, 46.2219175324, 39.3750496209, 39.3750499561}}, + {"3gmczz", Box{-26.3726806641, -26.3671875, -92.8234863281, -92.8125}}, + {"yb4jh3u7px0", Box{45.8890718222, 45.8890731633, 126.75542593, 126.755427271}}, + {"9ex7rkbw1duf", Box{20.2859266475, 20.2859268151, -101.985326596, -101.98532626}}, + {"xrkyg3mj", Box{41.9754981995, 41.9756698608, 153.079376221, 153.079719543}}, + {"z4hn2yfzryjq", Box{57.3869894072, 57.3869895749, 140.662075169, 140.662075505}}, + {"x357", Box{6.15234375, 6.328125, 150.8203125, 151.171875}}, + {"v3pew", Box{51.240234375, 51.2841796875, 67.060546875, 67.1044921875}}, + {"j1", Box{-84.375, -78.75, 45.0, 56.25}}, + {"ec1bkph03", Box{5.70744037628, 5.70748329163, -8.60774517059, -8.60770225525}}, + {"q4t85", Box{-30.9375, -30.8935546875, 97.8662109375, 97.91015625}}, + {"k26z8hf", Box{-42.2492980957, -42.2479248047, 15.119934082, 15.121307373}}, + {"p6fyq2u63", Box{-73.4281110764, -73.428068161, 150.397725105, 150.397768021}}, + {"uqu5w2yu", Box{83.5887908936, 83.5889625549, 17.1589279175, 17.1592712402}}, + {"terbkv", Box{18.3526611328, 18.3581542969, 78.6071777344, 78.6181640625}}, + {"8v2nwkp", Box{30.6958007812, 30.6971740723, -145.96572876, -145.964355469}}, + {"rbktxk2enhr", Box{-42.6030693948, -42.6030680537, 175.397682041, 175.397683382}}, + {"r1pnfct8", Box{-38.1802368164, -38.180065155, 144.97215271, 144.972496033}}, + {"rcmxg", Box{-36.6064453125, -36.5625, 176.616210938, 176.66015625}}, + {"jqw7xjdt8", Box{-52.7911090851, -52.7910661697, 65.350112915, 65.3501558304}}, + {"7mt737xd", Box{-13.4716415405, -13.4714698792, -26.3019561768, -26.301612854}}, + {"2dprx5rg57es", Box{-32.4132534117, -32.413253244, -146.986283138, -146.986282803}}, + {"fpr5d98ws0", Box{86.40583992, 86.4058452845, -80.0455284119, -80.045517683}}, + {"z4cmm3", Box{61.3970947266, 61.4025878906, 136.988525391, 136.999511719}}, + {"gz3b8j", Box{85.8966064453, 85.9020996094, -8.7890625, -8.77807617188}}, + {"svfztv42f2k", Box{33.6897052824, 33.6897066236, 37.8730648756, 37.8730662167}}, + {"z6f3yb6rum", Box{60.7790976763, 60.7791030407, 149.713965654, 149.713976383}}, + {"wvqv397", Box{30.4609680176, 30.4623413086, 133.312225342, 133.313598633}}, + {"r0ce65wshm", Box{-40.1900213957, -40.1900160313, 137.206374407, 137.206385136}}, + {"crqvbtfbjb09", Box{86.8235780485, 86.8235782161, -114.23181586, -114.231815524}}, + {"ptz0vc5z87", Box{-57.5176173449, -57.5176119804, 167.601596117, 167.601606846}}, + {"x806byp", Box{0.516357421875, 0.517730712891, 157.894134521, 157.895507812}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"kb23vjn10", Box{-43.2584953308, -43.2584524155, 34.3295288086, 34.3295717239}}, + {"n82kv", Box{-87.7587890625, -87.71484375, 113.071289062, 113.115234375}}, + {"dkhzmmqfg", Box{23.8037252426, 23.803768158, -71.830201149, -71.8301582336}}, + {"y1v", Box{54.84375, 56.25, 97.03125, 98.4375}}, + {"q977h9", Box{-37.4359130859, -37.4304199219, 117.268066406, 117.279052734}}, + {"sdffzu5qzu", Box{15.9753012657, 15.9753066301, 26.7125594616, 26.7125701904}}, + {"n3q", Box{-82.96875, -81.5625, 109.6875, 111.09375}}, + {"fr99zgs7e", Box{87.5149440765, 87.5149869919, -76.2940835953, -76.2940406799}}, + {"4d9cv4cbh", Box{-75.6147766113, -75.614733696, -64.8167610168, -64.8167181015}}, + {"np896wq5", Box{-47.557926178, -47.5577545166, 90.8212280273, 90.8215713501}}, + {"45", Box{-73.125, -67.5, -90.0, -78.75}}, + {"c5srsy", Box{66.0388183594, 66.0443115234, -128.814697266, -128.803710938}}, + {"vcshr", Box{54.1845703125, 54.228515625, 84.6826171875, 84.7265625}}, + {"3zbbs1wnn5", Box{-1.30907356739, -1.30906820297, -100.011034012, -100.011023283}}, + {"jtdvxn", Box{-58.0627441406, -58.0572509766, 71.6748046875, 71.6857910156}}, + {"yz27wu0e", Box{86.4189720154, 86.4191436768, 124.398880005, 124.399223328}}, + {"utb0ck65h9", Box{77.4994522333, 77.4994575977, 22.5578713417, 22.5578820705}}, + {"1t1dvq5jj", Box{-61.3577842712, -61.3577413559, -110.15557766, -110.155534744}}, + {"yzwgy5hvwz", Box{87.8641408682, 87.8641462326, 133.512672186, 133.512682915}}, + {"8dx38y6vby", Box{14.3615233898, 14.3615287542, -147.267919779, -147.26790905}}, + {"9bh", Box{0.0, 1.40625, -95.625, -94.21875}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"x14ve", Box{6.591796875, 6.6357421875, 138.999023438, 139.04296875}}, + {"3603v", Box{-33.4423828125, -33.3984375, -123.178710938, -123.134765625}}, + {"bsuje4cn6", Box{72.7017259598, 72.7017688751, -151.741704941, -151.741662025}}, + {"9nnut36epqhn", Box{34.5484302565, 34.5484304242, -125.273349881, -125.273349546}}, + {"7q27jg", Box{-9.29992675781, -9.29443359375, -33.1457519531, -33.134765625}}, + {"933zzd6", Box{8.40591430664, 8.40728759766, -120.956726074, -120.955352783}}, + {"5743y", Box{-72.8173828125, -72.7734375, -30.322265625, -30.2783203125}}, + {"7m94uc9", Box{-13.5708618164, -13.5694885254, -32.1336364746, -32.1322631836}}, + {"780", Box{-45.0, -43.59375, -22.5, -21.09375}}, + {"sqpqx2w0hh", Box{34.8953461647, 34.8953515291, 21.7723274231, 21.7723381519}}, + {"1ep", Box{-73.125, -71.71875, -102.65625, -101.25}}, + {"k0j", Box{-45.0, -43.59375, 7.03125, 8.4375}}, + {"z1zgr0g440", Box{55.4195022583, 55.4195076227, 146.210260391, 146.21027112}}, + {"681bf", Box{-44.8681640625, -44.82421875, -64.951171875, -64.9072265625}}, + {"dn6j", Box{36.03515625, 36.2109375, -87.1875, -86.8359375}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"t1m", Box{7.03125, 8.4375, 52.03125, 53.4375}}, + {"9qkshh67xe3", Box{35.8833391964, 35.8833405375, -117.242680639, -117.242679298}}, + {"4507gyj", Box{-72.4328613281, -72.4314880371, -89.476776123, -89.475402832}}, + {"bb844h", Box{48.1860351562, 48.1915283203, -146.162109375, -146.151123047}}, + {"trn6yz", Box{39.8968505859, 39.90234375, 65.3356933594, 65.3466796875}}, + {"vb99", Box{47.98828125, 48.1640625, 80.859375, 81.2109375}}, + {"dxpbksmed", Box{39.4428920746, 39.4429349899, -56.3961696625, -56.3961267471}}, + {"zc", Box{50.625, 56.25, 168.75, 180.0}}, + {"3webp3s6hdeb", Box{-8.42890352011, -8.42890335247, -106.901924349, -106.901924014}}, + {"8qg", Box{37.96875, 39.375, -164.53125, -163.125}}, + {"fgwzk", Box{65.9619140625, 66.005859375, -46.58203125, -46.5380859375}}, + {"1q2mxvd", Box{-53.8467407227, -53.8453674316, -123.055114746, -123.053741455}}, + {"sd", Box{11.25, 16.875, 22.5, 33.75}}, + {"8hhwv", Box{23.6865234375, 23.73046875, -173.452148438, -173.408203125}}, + {"1bw", Box{-87.1875, -85.78125, -92.8125, -91.40625}}, + {"66zkh0ex724", Box{-28.824133873, -28.8241325319, -68.3739575744, -68.3739562333}}, + {"w6qpy8", Box{14.0185546875, 14.0240478516, 109.973144531, 109.984130859}}, + {"nux13fvfr", Box{-64.4522809982, -64.4522380829, 133.678851128, 133.678894043}}, + {"cj2", Box{74.53125, 75.9375, -135.0, -133.59375}}, + {"1qrumeqp", Box{-54.0776252747, -54.0774536133, -112.601623535, -112.601280212}}, + {"hhm0", Box{-66.09375, -65.91796875, 7.03125, 7.3828125}}, + {"50cp37u6w86", Box{-84.4858060777, -84.4858047366, -43.5327002406, -43.5326988995}}, + {"0vjjfe5w", Box{-60.8467483521, -60.8465766907, -139.1040802, -139.103736877}}, + {"xm67", Box{30.05859375, 30.234375, 149.4140625, 149.765625}}, + {"1jmnqz1", Box{-59.3316650391, -59.330291748, -127.67074585, -127.669372559}}, + {"jjfh69u78", Box{-56.8989658356, -56.8989229202, 47.9281997681, 47.9282426834}}, + {"9xvnjtj3ytr", Box{44.6762318909, 44.676233232, -105.219552666, -105.219551325}}, + {"ebwk44", Box{3.52661132812, 3.53210449219, -2.373046875, -2.36206054688}}, + {"xej0p", Box{16.875, 16.9189453125, 164.838867188, 164.8828125}}, + {"hm4m", Box{-60.99609375, -60.8203125, 14.4140625, 14.765625}}, + {"71d1uhzq", Box{-36.2277603149, -36.2275886536, -42.0017623901, -42.0014190674}}, + {"u7d8cgc9h4w", Box{64.8401203752, 64.8401217163, 14.8447689414, 14.8447702825}}, + {"zwud", Box{83.3203125, 83.49609375, 163.828125, 164.1796875}}, + {"p4nbh7wnwsb", Box{-78.7296326458, -78.7296313047, 144.687473774, 144.687475115}}, + {"ev", Box{28.125, 33.75, -11.25, 0.0}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"ytj8jhg3vmke", Box{73.1514216028, 73.1514217705, 120.458796099, 120.458796434}}, + {"xn", Box{33.75, 39.375, 135.0, 146.25}}, + {"1t7f", Box{-60.1171875, -59.94140625, -107.2265625, -106.875}}, + {"wjt", Box{30.9375, 32.34375, 97.03125, 98.4375}}, + {"f7j3", Box{62.05078125, 62.2265625, -71.3671875, -71.015625}}, + {"62bd4mvcg", Box{-40.3978013992, -40.3977584839, -77.9399728775, -77.9399299622}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"577epg1nh0be", Box{-71.1738922633, -71.1738920957, -28.4860032052, -28.4860028699}}, + {"pvyz2qtjw6kc", Box{-56.3451739959, -56.3451738283, 178.260314874, 178.26031521}}, + {"2hrf6fbj", Box{-20.6822776794, -20.6821060181, -168.980712891, -168.980369568}}, + {"z1yz7p6dc3h8", Box{56.1584669352, 56.1584671028, 144.627516344, 144.627516679}}, + {"24v5r460td50", Box{-28.9475047588, -28.9475045912, -172.658146173, -172.658145837}}, + {"rdtnyvudc4mu", Box{-29.7189060599, -29.7189058922, 164.834111296, 164.834111631}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"e7he", Box{17.40234375, 17.578125, -27.421875, -27.0703125}}, + {"9yrkg", Box{35.9912109375, 36.03515625, -90.9228515625, -90.87890625}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"yppvw0", Box{85.341796875, 85.3472900391, 101.162109375, 101.173095703}}, + {"n3876wh", Box{-80.9582519531, -80.9568786621, 101.716918945, 101.718292236}}, + {"m1vjqf4", Box{-34.2224121094, -34.2210388184, 52.3306274414, 52.3320007324}}, + {"kev", Box{-23.90625, -22.5, 29.53125, 30.9375}}, + {"4qc", Box{-52.03125, -50.625, -77.34375, -75.9375}}, + {"5f4", Box{-78.75, -77.34375, -8.4375, -7.03125}}, + {"ug", Box{61.875, 67.5, 33.75, 45.0}}, + {"g5", Box{61.875, 67.5, -45.0, -33.75}}, + {"7wvx", Box{-5.80078125, -5.625, -14.765625, -14.4140625}}, + {"rx", Box{-5.625, 0.0, 157.5, 168.75}}, + {"jdg0t21qzr", Box{-74.4421631098, -74.4421577454, 71.9514906406, 71.9515013695}}, + {"606yg", Box{-42.4072265625, -42.36328125, -86.0009765625, -85.95703125}}, + {"nxt6zwhgqd", Box{-47.2955739498, -47.2955685854, 120.219204426, 120.219215155}}, + {"e71g7w8dr", Box{17.482380867, 17.4824237823, -31.1342668533, -31.134223938}}, + {"n6u", Box{-74.53125, -73.125, 106.875, 108.28125}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"zfcehd0d3", Box{61.0074663162, 61.0075092316, 171.057858467, 171.057901382}}, + {"hgx5efc24r5e", Box{-69.68212137, -69.6821212023, 43.760362789, 43.7603631243}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"q", Box{-45.0, 0.0, 90.0, 135.0}}, + {"zbzg87qu07g", Box{49.8525439203, 49.8525452614, 179.668708295, 179.668709636}}, + {"02urcn", Box{-84.3859863281, -84.3804931641, -162.729492188, -162.718505859}}, + {"p4", Box{-78.75, -73.125, 135.0, 146.25}}, + {"j6xjzcpsk78", Box{-74.9205163121, -74.920514971, 66.4448082447, 66.4448095858}}, + {"svchwhvd08d", Box{33.1612041593, 33.1612055004, 35.4274991155, 35.4275004566}}, + {"yughcgqek2", Box{72.5721216202, 72.5721269846, 128.054763079, 128.054773808}}, + {"18tc1b", Box{-87.01171875, -87.0062255859, -104.337158203, -104.326171875}}, + {"es43c", Box{22.8076171875, 22.8515625, -19.2919921875, -19.248046875}}, + {"zdcephk", Box{61.0194396973, 61.0208129883, 159.922485352, 159.923858643}}, + {"2zqh", Box{-3.515625, -3.33984375, -137.8125, -137.4609375}}, + {"sh9bfh31", Box{25.4678535461, 25.4680252075, 2.55020141602, 2.55054473877}}, + {"62vfgv", Box{-40.2703857422, -40.2648925781, -70.4992675781, -70.48828125}}, + {"u2yn4", Box{50.2734375, 50.3173828125, 19.775390625, 19.8193359375}}, + {"qx5wuf", Box{-4.42749023438, -4.42199707031, 117.630615234, 117.641601562}}, + {"9y163", Box{34.1455078125, 34.189453125, -99.4482421875, -99.404296875}}, + {"7ygu094y", Box{-6.32160186768, -6.3214302063, -5.95081329346, -5.9504699707}}, + {"nkfj9vxh9e6", Box{-62.2834508121, -62.283449471, 104.149084389, 104.14908573}}, + {"n220y9m", Box{-88.4550476074, -88.4536743164, 101.542510986, 101.543884277}}, + {"472p7k4d", Box{-70.4220199585, -70.4218482971, -78.6037445068, -78.6034011841}}, + {"7p", Box{-5.625, 0.0, -45.0, -33.75}}, + {"b1r1n2zp", Box{52.2123527527, 52.2125244141, -169.87197876, -169.871635437}}, + {"neu8fxsk8k4", Box{-68.7324213982, -68.7324200571, 118.943838179, 118.94383952}}, + {"m33fn", Box{-37.6171875, -37.5732421875, 58.974609375, 59.0185546875}}, + {"u6z5je", Box{61.0125732422, 61.0180664062, 21.3354492188, 21.3464355469}}, + {"5e6csnf", Box{-71.4179992676, -71.4166259766, -18.454284668, -18.452911377}}, + {"7f7k4", Box{-31.640625, -31.5966796875, -6.591796875, -6.5478515625}}, + {"ykd07ytm4", Box{70.3930091858, 70.3930521011, 104.23459053, 104.234633446}}, + {"ubyx97", Box{50.5535888672, 50.5590820312, 42.9455566406, 42.9565429688}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"ungw16", Box{84.0344238281, 84.0399169922, 4.97680664062, 4.98779296875}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"bhfkepk0n", Box{72.5495910645, 72.5496339798, -176.698350906, -176.698307991}}, + {"d3", Box{5.625, 11.25, -78.75, -67.5}}, + {"wsenn", Box{26.3671875, 26.4111328125, 116.982421875, 117.026367188}}, + {"b4b6qpqk", Box{60.9047698975, 60.9049415588, -179.376182556, -179.375839233}}, + {"zwjx2wv3", Box{80.0616645813, 80.0618362427, 165.263557434, 165.263900757}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"3sgq6", Box{-17.1826171875, -17.138671875, -107.841796875, -107.797851562}}, + {"9chbq", Box{5.6689453125, 5.712890625, -94.306640625, -94.2626953125}}, + {"37nehnbt", Box{-27.5597190857, -27.5595474243, -114.432907104, -114.432563782}}, + {"uhr5w71b", Box{69.5379638672, 69.5381355286, 10.1208114624, 10.1211547852}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"81", Box{5.625, 11.25, -180.0, -168.75}}, + {"vyxn", Box{82.6171875, 82.79296875, 88.59375, 88.9453125}}, + {"73jvv", Box{-38.3642578125, -38.3203125, -25.4443359375, -25.400390625}}, + {"nmw1ysg2f23", Box{-58.7286601961, -58.728658855, 109.977705628, 109.977706969}}, + {"5sv6js2nphq", Box{-62.9052887857, -62.9052874446, -14.8751798272, -14.8751784861}}, + {"289", Box{-42.1875, -40.78125, -156.09375, -154.6875}}, + {"cq9", Box{81.5625, 82.96875, -122.34375, -120.9375}}, + {"mm", Box{-16.875, -11.25, 56.25, 67.5}}, + {"49378fm9v", Box{-82.3408555984, -82.3408126831, -65.7014608383, -65.701417923}}, + {"ee079be", Box{17.492980957, 17.494354248, -22.0674133301, -22.0660400391}}, + {"4m5cs3h", Box{-61.6058349609, -61.6044616699, -73.2843017578, -73.2829284668}}, + {"hpdre", Box{-46.494140625, -46.4501953125, 3.2958984375, 3.33984375}}, + {"06", Box{-78.75, -73.125, -168.75, -157.5}}, + {"3x3r4evkpp1q", Box{-2.9669566825, -2.96695651487, -110.624812357, -110.624812022}}, + {"95074fyh23t", Box{17.4181875587, 17.4181888998, -134.51933071, -134.519329369}}, + {"5tdj7sf", Box{-58.1135559082, -58.1121826172, -19.5309448242, -19.5295715332}}, + {"8r9z", Box{43.41796875, 43.59375, -166.2890625, -165.9375}}, + {"nc", Box{-84.375, -78.75, 123.75, 135.0}}, + {"t2mb", Box{1.40625, 1.58203125, 64.3359375, 64.6875}}, + {"hcs1g2vbkq0g", Box{-81.2506873347, -81.250687167, 39.525902085, 39.5259024203}}, + {"pduzpxt", Box{-73.2595825195, -73.2582092285, 164.516143799, 164.51751709}}, + {"mw9u700", Box{-7.6904296875, -7.68905639648, 70.0927734375, 70.0941467285}}, + {"9y0ttsfr", Box{34.7440910339, 34.7442626953, -100.302085876, -100.301742554}}, + {"ztmu17de4", Box{75.2541160583, 75.2541589737, 165.644388199, 165.644431114}}, + {"1dqmehx", Box{-76.3522338867, -76.3508605957, -103.569488525, -103.568115234}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"6ydvkt6cp", Box{-7.48563766479, -7.48559474945, -52.180981636, -52.1809387207}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"706tn", Box{-42.71484375, -42.6708984375, -41.220703125, -41.1767578125}}, + {"6z89j2x0gun", Box{-2.63382196426, -2.63382062316, -55.3063800931, -55.306378752}}, + {"mt1t", Box{-15.99609375, -15.8203125, 69.609375, 69.9609375}}, + {"95ypdkpcd", Box{22.4343395233, 22.4343824387, -126.452894211, -126.452851295}}, + {"nr1wc", Box{-49.4384765625, -49.39453125, 103.403320312, 103.447265625}}, + {"rb2uxf4", Box{-42.7917480469, -42.7903747559, 170.148010254, 170.149383545}}, + {"gzz5w20e1wk", Box{89.2095328867, 89.2095342278, -1.13083541393, -1.13083407283}}, + {"mtuddkh7my", Box{-12.1942341328, -12.1942287683, 73.9330852032, 73.933095932}}, + {"r15def4", Box{-38.9245605469, -38.9231872559, 140.089416504, 140.090789795}}, + {"9fwcy0d", Box{14.3728637695, 14.3742370605, -91.491394043, -91.490020752}}, + {"6vx9z80hkd", Box{-13.7541425228, -13.7541371584, -45.3733420372, -45.3733313084}}, + {"qqxt55re", Box{-7.54022598267, -7.54005432129, 111.93901062, 111.939353943}}, + {"4hreqy", Box{-65.4895019531, -65.4840087891, -79.1564941406, -79.1455078125}}, + {"9nv6", Box{38.3203125, 38.49609375, -127.6171875, -127.265625}}, + {"k980q", Box{-36.5185546875, -36.474609375, 22.763671875, 22.8076171875}}, + {"zd6qmdhsrtce", Box{58.7666300498, 58.7666302174, 160.912265405, 160.91226574}}, + {"ucz57k", Box{55.4370117188, 55.4425048828, 43.7365722656, 43.7475585938}}, + {"u5pq", Box{62.9296875, 63.10546875, 10.1953125, 10.546875}}, + {"fey0v9", Box{66.2310791016, 66.2365722656, -58.8208007812, -58.8098144531}}, + {"mdtggek9fmh5", Box{-30.2601397969, -30.2601396292, 75.7460278273, 75.7460281625}}, + {"y6nfy", Box{56.7333984375, 56.77734375, 111.005859375, 111.049804688}}, + {"f", Box{45.0, 90.0, -90.0, -45.0}}, + {"33hz5xbsw", Box{-38.1011867523, -38.101143837, -116.915559769, -116.915516853}}, + {"2wnb71890np2", Box{-11.1976110935, -11.1976109259, -147.875280194, -147.875279859}}, + {"7d", Box{-33.75, -28.125, -22.5, -11.25}}, + {"71vsdbh", Box{-34.365234375, -34.363861084, -37.1392822266, -37.1379089355}}, + {"nk", Box{-67.5, -61.875, 101.25, 112.5}}, + {"pn6uks", Box{-54.0747070312, -54.0692138672, 139.064941406, 139.075927734}}, + {"t0", Box{0.0, 5.625, 45.0, 56.25}}, + {"ze7c4z3e", Box{63.4973716736, 63.497543335, 162.896347046, 162.896690369}}, + {"ujq6txghfjdv", Box{75.0141208805, 75.0141210482, 9.03497111052, 9.0349714458}}, + {"40muh9sfm7", Box{-87.8819829226, -87.8819775581, -81.7095601559, -81.709549427}}, + {"0y4kk1", Box{-55.4974365234, -55.4919433594, -142.91015625, -142.899169922}}, + {"q8btfscp1g", Box{-39.7431975603, -39.7431921959, 113.314436674, 113.314447403}}, + {"yg2mxt", Box{64.2755126953, 64.2810058594, 124.431152344, 124.442138672}}, + {"7r5kh", Box{-4.921875, -4.8779296875, -29.00390625, -28.9599609375}}, + {"cvg6t", Box{77.783203125, 77.8271484375, -96.4599609375, -96.416015625}}, + {"msj4q7e3db", Box{-22.0850086212, -22.0850032568, 74.8104894161, 74.810500145}}, + {"1452n4", Box{-78.7390136719, -78.7335205078, -130.166015625, -130.155029297}}, + {"xj3kvwr2d", Box{30.4006290436, 30.4006719589, 137.009553909, 137.009596825}}, + {"2wtvb", Box{-7.4267578125, -7.3828125, -149.4140625, -149.370117188}}, + {"c5x5dhk", Box{65.3260803223, 65.3274536133, -125.062866211, -125.06149292}}, + {"eph56u6", Box{39.9696350098, 39.9710083008, -39.2514038086, -39.2500305176}}, + {"5dhyg0hckf", Box{-77.5632512569, -77.5632458925, -15.6817495823, -15.6817388535}}, + {"9vrb", Box{29.53125, 29.70703125, -90.3515625, -90.0}}, + {"b7nsys", Box{62.7319335938, 62.7374267578, -159.323730469, -159.312744141}}, + {"kmenbsk0", Box{-12.8526306152, -12.8524589539, 15.4962158203, 15.4965591431}}, + {"j35", Box{-84.375, -82.96875, 60.46875, 61.875}}, + {"e7sx", Box{20.91796875, 21.09375, -27.421875, -27.0703125}}, + {"h87mpg", Box{-87.6983642578, -87.6928710938, 27.4108886719, 27.421875}}, + {"qjbtp", Box{-11.77734375, -11.7333984375, 91.0107421875, 91.0546875}}, + {"4zqs2pndx", Box{-48.4327983856, -48.4327554703, -47.100148201, -47.1001052856}}, + {"1fsc5v3", Box{-75.7328796387, -75.7315063477, -94.4041442871, -94.4027709961}}, + {"kp6hptx", Box{-3.48541259766, -3.48403930664, 3.15170288086, 3.15307617188}}, + {"3n77", Box{-9.31640625, -9.140625, -130.4296875, -130.078125}}, + {"q347uc", Box{-38.7103271484, -38.7048339844, 104.622802734, 104.633789062}}, + {"n8gckvg3", Box{-85.5297660828, -85.5295944214, 117.98664093, 117.986984253}}, + {"p7szbr6ceq", Box{-68.9100801945, -68.9100748301, 152.944589853, 152.944600582}}, + {"8w7n", Box{36.2109375, 36.38671875, -153.28125, -152.9296875}}, + {"k4s3ndj8", Box{-30.7507324219, -30.7505607605, 6.26976013184, 6.27010345459}}, + {"fh38ev", Box{69.0216064453, 69.0270996094, -87.7258300781, -87.71484375}}, + {"rzebrsw", Box{-2.74383544922, -2.7424621582, 174.36126709, 174.362640381}}, + {"un", Box{78.75, 84.375, 0.0, 11.25}}, + {"27u3d4ybbkt", Box{-23.6273190379, -23.6273176968, -162.676259726, -162.676258385}}, + {"5hk2", Box{-66.09375, -65.91796875, -39.0234375, -38.671875}}, + {"62f6wqsbfc6n", Box{-40.3059548512, -40.3059546836, -75.3046354651, -75.3046351299}}, + {"r6jvqxczv", Box{-32.7832460403, -32.783203125, 154.624199867, 154.624242783}}, + {"wyg1es5yx", Box{38.2555103302, 38.2555532455, 128.128008842, 128.128051758}}, + {"smp9qbcpnt", Box{28.3500748873, 28.3500802517, 22.0951581001, 22.095168829}}, + {"4q46h", Box{-55.8984375, -55.8544921875, -75.41015625, -75.3662109375}}, + {"9u6v9g2ebcm5", Box{24.8915505968, 24.8915507644, -97.3051826656, -97.3051823303}}, + {"25mwbs3", Box{-25.5088806152, -25.5075073242, -172.242279053, -172.240905762}}, + {"kwf", Box{-7.03125, -5.625, 25.3125, 26.71875}}, + {"ekqgkknev", Box{24.5001554489, 24.5001983643, -24.0619039536, -24.0618610382}}, + {"y9974617cb12", Box{53.9764738083, 53.9764739759, 114.358482845, 114.35848318}}, + {"htuu1sxcpd", Box{-56.9282233715, -56.9282180071, 29.2565703392, 29.256581068}}, + {"150sv8q", Box{-72.2886657715, -72.2872924805, -134.046936035, -134.045562744}}, + {"j36p6wnmqm", Box{-81.6604489088, -81.6604435444, 59.181214571, 59.1812252998}}, + {"py", Box{-56.25, -50.625, 168.75, 180.0}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"y87", Box{46.40625, 47.8125, 116.71875, 118.125}}, + {"6v90bwn999", Box{-13.8974422216, -13.8974368572, -54.8127865791, -54.8127758503}}, + {"8kyq", Box{27.7734375, 27.94921875, -159.9609375, -159.609375}}, + {"8cht765b92zg", Box{6.55892824754, 6.55892841518, -139.773838855, -139.77383852}}, + {"nu5jwk23v7f", Box{-66.5095366538, -66.5095353127, 128.243979514, 128.243980855}}, + {"0m10969", Box{-61.7733764648, -61.7720031738, -167.287445068, -167.286071777}}, + {"s29h49frdp", Box{3.52656304836, 3.52656841278, 12.7692890167, 12.7692997456}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"brmmeg2z8r", Box{86.7672246695, 86.7672300339, -161.201351881, -161.201341152}}, + {"r3bngg619d", Box{-33.9516055584, -33.951600194, 146.417605877, 146.417616606}}, + {"rmz76wced8c", Box{-12.0472772419, -12.0472759008, 156.557344347, 156.557345688}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"2h", Box{-22.5, -16.875, -180.0, -168.75}}, + {"ty2764x", Box{35.7412719727, 35.7426452637, 79.1990661621, 79.2004394531}}, + {"5yh3330r", Box{-56.0235786438, -56.0234069824, -5.21816253662, -5.21781921387}}, + {"9szz", Box{27.94921875, 28.125, -101.6015625, -101.25}}, + {"x7d41b", Box{20.0390625, 20.0445556641, 149.139404297, 149.150390625}}, + {"dw", Box{33.75, 39.375, -67.5, -56.25}}, + {"gnd4cw", Box{82.0788574219, 82.0843505859, -42.1215820312, -42.1105957031}}, + {"k9bxc2n8", Box{-33.7939453125, -33.7937736511, 23.2669830322, 23.267326355}}, + {"hump4nk", Box{-64.8289489746, -64.8275756836, 40.8746337891, 40.8760070801}}, + {"gkz", Box{71.71875, 73.125, -23.90625, -22.5}}, + {"g9e08yt", Box{53.5610961914, 53.5624694824, -18.2414245605, -18.2400512695}}, + {"3eyuyzpm", Box{-23.0319786072, -23.0318069458, -102.701225281, -102.700881958}}, + {"utpuc59p4m", Box{73.9804154634, 73.9804208279, 33.443852663, 33.4438633919}}, + {"cnqt", Box{81.03515625, 81.2109375, -125.859375, -125.5078125}}, + {"z05xfy72gk0", Box{46.3967871666, 46.3967885077, 140.04732728, 140.047328621}}, + {"skr4zd", Box{24.4006347656, 24.4061279297, 21.4233398438, 21.4343261719}}, + {"h2fe8mdnq", Box{-85.1347303391, -85.1346874237, 14.7796154022, 14.7796583176}}, + {"z18b", Box{53.4375, 53.61328125, 136.0546875, 136.40625}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"vh", Box{67.5, 73.125, 45.0, 56.25}}, + {"v64zfspngxw", Box{57.6354762912, 57.6354776323, 60.2368220687, 60.2368234098}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"d800", Box{0.0, 0.17578125, -67.5, -67.1484375}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"bnugeje1", Box{83.6143684387, 83.6145401001, -173.184356689, -173.184013367}}, + {"46", Box{-78.75, -73.125, -78.75, -67.5}}, + {"8rsncsbz2mx4", Box{43.4013903514, 43.401390519, -163.058031946, -163.058031611}}, + {"t4w9wh7f98je", Box{14.3499474786, 14.3499476463, 54.4095184654, 54.4095188007}}, + {"nsr1tp2", Box{-65.7902526855, -65.7888793945, 122.563476562, 122.564849854}}, + {"9me", Box{30.9375, 32.34375, -119.53125, -118.125}}, + {"t8250bh2y", Box{1.93372249603, 1.93376541138, 67.5390529633, 67.5390958786}}, + {"1", Box{-90.0, -45.0, -135.0, -90.0}}, + {"uqty0nmvvj0c", Box{82.652533818, 82.6525339857, 19.3440495059, 19.3440498412}}, + {"7nkxt", Box{-8.525390625, -8.4814453125, -38.4521484375, -38.408203125}}, + {"jzev", Box{-46.93359375, -46.7578125, 84.0234375, 84.375}}, + {"dmtj1fktxe", Box{31.8297261, 31.8297314644, -71.6353440285, -71.6353332996}}, + {"0r", Box{-50.625, -45.0, -168.75, -157.5}}, + {"5hqv273y", Box{-65.152015686, -65.1518440247, -35.4944229126, -35.4940795898}}, + {"78k", Box{-43.59375, -42.1875, -16.875, -15.46875}}, + {"f2krt5", Box{47.7410888672, 47.7465820312, -72.5537109375, -72.5427246094}}, + {"ffw63hhzm2u", Box{59.481229037, 59.4812303782, -47.4102383852, -47.4102370441}}, + {"mv3z5k", Box{-14.2163085938, -14.2108154297, 81.3537597656, 81.3647460938}}, + {"f15", Box{50.625, 52.03125, -85.78125, -84.375}}, + {"j710re25dhzs", Box{-73.0625749379, -73.0625747703, 57.9859357327, 57.985936068}}, + {"rtt328k93", Box{-13.8411855698, -13.8411426544, 164.911007881, 164.911050797}}, + {"d2x003t048", Box{2.82073974609, 2.82074511051, -68.8882899284, -68.8882791996}}, + {"22uy0", Box{-39.7265625, -39.6826171875, -162.0703125, -162.026367188}}, + {"tuzxx", Box{28.037109375, 28.0810546875, 89.6044921875, 89.6484375}}, + {"su44fdqr2pv1", Box{22.9970443435, 22.9970445111, 36.6809530556, 36.6809533909}}, + {"yttq", Box{76.9921875, 77.16796875, 119.8828125, 120.234375}}, + {"9fu5vyr", Box{16.1622619629, 16.1636352539, -95.362701416, -95.361328125}}, + {"zwmzk37wsh97", Box{81.4386709593, 81.438671127, 165.777684934, 165.77768527}}, + {"hd777ygzewj", Box{-76.7340624332, -76.7340610921, 27.2404141724, 27.2404155135}}, + {"0b1", Box{-90.0, -88.59375, -144.84375, -143.4375}}, + {"ejmgd", Box{30.146484375, 30.1904296875, -36.826171875, -36.7822265625}}, + {"rzxh6", Box{-2.0654296875, -2.021484375, 178.681640625, 178.725585938}}, + {"0rf4f4u", Box{-45.9077453613, -45.9063720703, -165.844116211, -165.84274292}}, + {"1m23ggbv8", Box{-60.1395893097, -60.1395463943, -123.23261261, -123.232569695}}, + {"gdvt2y6wfv", Box{61.4271193743, 61.4271247387, -14.7291147709, -14.7291040421}}, + {"wxb3eqeug0y0", Box{43.8939468563, 43.8939470239, 112.9996714, 112.999671735}}, + {"516kngbm6", Box{-82.2441244125, -82.2440814972, -41.5388774872, -41.5388345718}}, + {"xtuwe2zu", Box{33.4911346436, 33.4913063049, 163.981590271, 163.981933594}}, + {"dhb1c2jrz6c1", Box{27.027712483, 27.0277126506, -89.9375461042, -89.9375457689}}, + {"23c397n4v8g", Box{-34.8756225407, -34.8756211996, -166.928776056, -166.928774714}}, + {"1w81bnmc3", Box{-53.0953359604, -53.095293045, -112.492060661, -112.492017746}}, + {"03hu77r", Box{-83.6100769043, -83.6087036133, -161.917877197, -161.916503906}}, + {"z2vrm", Box{50.4931640625, 50.537109375, 153.852539062, 153.896484375}}, + {"q630e", Box{-32.255859375, -32.2119140625, 102.788085938, 102.83203125}}, + {"h9uzt", Box{-78.837890625, -78.7939453125, 29.3994140625, 29.443359375}}, + {"x09c9jz", Box{3.10775756836, 3.10913085938, 137.51449585, 137.515869141}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"xv9ee3gq2j", Box{31.5634471178, 31.5634524822, 171.006660461, 171.00667119}}, + {"6e8xjp4", Box{-24.0435791016, -24.0422058105, -66.5744018555, -66.5730285645}}, + {"0m8sj439bys", Box{-58.3466801047, -58.3466787636, -167.82505095, -167.825049609}}, + {"khf7yq8js6", Box{-17.5854098797, -17.5854045153, 3.43890309334, 3.43891382217}}, + {"hh", Box{-67.5, -61.875, 0.0, 11.25}}, + {"kcyx9b0rd65e", Box{-33.8365919329, -33.8365917653, 42.967973873, 42.9679742083}}, + {"qcy4pees", Box{-34.7847747803, -34.7846031189, 132.521896362, 132.522239685}}, + {"tc6", Box{7.03125, 8.4375, 81.5625, 82.96875}}, + {"mxhnk2fkh", Box{-4.52156066895, -4.5215177536, 73.3150291443, 73.3150720596}}, + {"1mggmg5x0k", Box{-57.067258358, -57.0672529936, -118.219059706, -118.219048977}}, + {"f1udt102z", Box{55.2888250351, 55.2888679504, -83.4515047073, -83.451461792}}, + {"jjz", Box{-57.65625, -56.25, 54.84375, 56.25}}, + {"1q1dg8peze2", Box{-55.765940398, -55.7659390569, -121.476194859, -121.476193517}}, + {"604unch", Box{-44.2913818359, -44.2900085449, -85.8306884766, -85.8293151855}}, + {"kt", Box{-16.875, -11.25, 22.5, 33.75}}, + {"wpbgc", Box{44.2529296875, 44.296875, 91.0986328125, 91.142578125}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"tz2pky3c", Box{42.0901679993, 42.0903396606, 78.9611434937, 78.9614868164}}, + {"j96v3y", Box{-82.0129394531, -82.0074462891, 71.4440917969, 71.455078125}}, + {"vs974dv", Box{70.8549499512, 70.8563232422, 69.3745422363, 69.3759155273}}, + {"dunwchdt7d6", Box{23.712155968, 23.7121573091, -47.061843574, -47.0618422329}}, + {"h2u2nkksw", Box{-85.7571315765, -85.7570886612, 17.5076580048, 17.5077009201}}, + {"z8qtu1", Box{47.4224853516, 47.4279785156, 166.81640625, 166.827392578}}, + {"2677dsdngh", Box{-31.7026162148, -31.7026108503, -164.066948891, -164.066938162}}, + {"4yy", Box{-52.03125, -50.625, -47.8125, -46.40625}}, + {"uym", Box{80.15625, 81.5625, 40.78125, 42.1875}}, + {"m2mssd", Box{-42.7917480469, -42.7862548828, 64.1821289062, 64.1931152344}}, + {"xed3b", Box{19.9951171875, 20.0390625, 160.6640625, 160.708007812}}, + {"rfp4ky", Box{-33.3215332031, -33.3160400391, 178.802490234, 178.813476562}}, + {"83fmm9e", Box{10.7748413086, 10.7762145996, -165.340118408, -165.338745117}}, + {"tr7z2dqh", Box{42.0687103271, 42.0688819885, 61.5536499023, 61.5539932251}}, + {"crsh", Box{87.890625, 88.06640625, -118.125, -117.7734375}}, + {"f", Box{45.0, 90.0, -90.0, -45.0}}, + {"761m6h", Box{-32.8051757812, -32.7996826172, -31.904296875, -31.8933105469}}, + {"7p", Box{-5.625, 0.0, -45.0, -33.75}}, + {"8qu9r5", Box{38.2049560547, 38.2104492188, -162.114257812, -162.103271484}}, + {"5z", Box{-50.625, -45.0, -11.25, 0.0}}, + {"kbz", Box{-40.78125, -39.375, 43.59375, 45.0}}, + {"zdftsw1rbpsf", Box{61.4698768035, 61.4698769711, 161.21510189, 161.215102226}}, + {"n1m105r", Box{-82.7751159668, -82.7737426758, 97.0408630371, 97.0422363281}}, + {"xf4kktnxsz", Box{12.0258611441, 12.0258665085, 172.120946646, 172.120957375}}, + {"kzqyq4m0qtyu", Box{-3.10768313706, -3.10768296942, 43.5130138323, 43.5130141675}}, + {"1j1vy57w7", Box{-60.8453321457, -60.8452892303, -132.27045536, -132.270412445}}, + {"nq4u3", Box{-55.5029296875, -55.458984375, 105.161132812, 105.205078125}}, + {"bhcy", Box{72.7734375, 72.94921875, -177.5390625, -177.1875}}, + {"vjr", Box{74.53125, 75.9375, 54.84375, 56.25}}, + {"uc99nrdsntv", Box{53.6551974714, 53.6551988125, 36.1377520859, 36.137753427}}, + {"3e8zmnz", Box{-24.0010070801, -23.9996337891, -111.2159729, -111.214599609}}, + {"j0yzdvs9", Box{-84.4325065613, -84.4323348999, 54.6192169189, 54.6195602417}}, + {"8chvm4", Box{6.55883789062, 6.56433105469, -139.350585938, -139.339599609}}, + {"ywf6099wx", Box{83.329668045, 83.3297109604, 115.6883955, 115.688438416}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"zrc", Box{88.59375, 90.0, 147.65625, 149.0625}}, + {"zq", Box{78.75, 84.375, 146.25, 157.5}}, + {"7xznu50ru", Box{-0.201916694641, -0.201873779297, -12.4799537659, -12.4799108505}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"7r", Box{-5.625, 0.0, -33.75, -22.5}}, + {"f27k", Box{47.109375, 47.28515625, -74.1796875, -73.828125}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"b4s13188yv", Box{59.2906218767, 59.2906272411, -174.330078363, -174.330067635}}, + {"q8", Box{-45.0, -39.375, 112.5, 123.75}}, + {"skn7w", Box{23.115234375, 23.1591796875, 20.302734375, 20.3466796875}}, + {"1tzqwzyh5vp", Box{-56.4703863859, -56.4703850448, -101.999646574, -101.999645233}}, + {"r52cmn", Box{-26.4660644531, -26.4605712891, 136.274414062, 136.285400391}}, + {"7bwvbbbru", Box{-41.1713075638, -41.1712646484, -1.72433853149, -1.72429561615}}, + {"ruv0b1n", Box{-18.1439208984, -18.1425476074, 175.789489746, 175.790863037}}, + {"vwf01ujm", Box{82.9915809631, 82.9917526245, 70.3966140747, 70.3969573975}}, + {"0metgxjtrm91", Box{-58.0123747699, -58.0123746023, -163.666450828, -163.666450493}}, + {"2w", Box{-11.25, -5.625, -157.5, -146.25}}, + {"8kmch3", Box{24.0875244141, 24.0930175781, -160.477294922, -160.466308594}}, + {"g6m", Box{57.65625, 59.0625, -26.71875, -25.3125}}, + {"t6v4k2", Box{15.8642578125, 15.8697509766, 63.4680175781, 63.4790039062}}, + {"zr02vfju8hd", Box{84.5186188817, 84.5186202228, 146.862147152, 146.862148494}}, + {"kb8jn751", Box{-41.2919425964, -41.2917709351, 34.0287780762, 34.0291213989}}, + {"mj76", Box{-15.1171875, -14.94140625, 49.5703125, 49.921875}}, + {"f0hwcbkwjnr", Box{46.1889602244, 46.1889615655, -83.5885669291, -83.588565588}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"6rt9btpxj2p1", Box{-2.47621519491, -2.47621502727, -70.9831179678, -70.9831176326}}, + {"wh0s6yb6j", Box{23.2844924927, 23.284535408, 90.8245325089, 90.8245754242}}, + {"65b", Box{-23.90625, -22.5, -90.0, -88.59375}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"z0tpc5d", Box{49.1940307617, 49.1954040527, 142.077941895, 142.079315186}}, + {"fgs2w0", Box{64.775390625, 64.7808837891, -50.009765625, -49.9987792969}}, + {"vfus35qgp", Box{61.2341880798, 61.2342309952, 85.1316404343, 85.1316833496}}, + {"hywnkqdye", Box{-52.3020458221, -52.3020029068, 42.3781728745, 42.3782157898}}, + {"qpwxwun0b", Box{-1.47203922272, -1.47199630737, 99.4454956055, 99.4455385208}}, + {"v88u9rqr24", Box{48.6445963383, 48.6446017027, 68.6182022095, 68.6182129383}}, + {"17x06x4fyw", Box{-70.2295982838, -70.2295929193, -113.792331219, -113.79232049}}, + {"3ykkhjyhrt", Box{-9.1082829237, -9.10827755928, -95.0890946388, -95.08908391}}, + {"mbzkuqw5", Box{-39.910068512, -39.9098968506, 89.1403198242, 89.140663147}}, + {"yqjf0p3mn", Box{79.1422462463, 79.1422891617, 109.337911606, 109.337954521}}, + {"2f", Box{-33.75, -28.125, -146.25, -135.0}}, + {"hqsm3", Box{-52.5146484375, -52.470703125, 17.2705078125, 17.314453125}}, + {"gp1y4wtft1tp", Box{85.4658314399, 85.4658316076, -42.4210815132, -42.4210811779}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"kxxruvh", Box{-1.42272949219, -1.42135620117, 32.9095458984, 32.9109191895}}, + {"08h0ft4vk97r", Box{-89.839789141, -89.8397889733, -151.761162691, -151.761162356}}, + {"y9u8", Box{54.84375, 55.01953125, 118.828125, 119.1796875}}, + {"3zhsk", Box{-4.8779296875, -4.833984375, -94.74609375, -94.7021484375}}, + {"x2", Box{0.0, 5.625, 146.25, 157.5}}, + {"mr7nnvr", Box{-3.13522338867, -3.13385009766, 60.7749938965, 60.7763671875}}, + {"d3g0pn54hr", Box{9.87708985806, 9.87709522247, -74.2193305492, -74.2193198204}}, + {"jv173u", Box{-61.2817382812, -61.2762451172, 80.5847167969, 80.595703125}}, + {"vte", Box{75.9375, 77.34375, 71.71875, 73.125}}, + {"303un61j2s", Box{-42.878715992, -42.8787106276, -132.263009548, -132.262998819}}, + {"m528h", Box{-26.71875, -26.6748046875, 45.87890625, 45.9228515625}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"p8h", Box{-90.0, -88.59375, 163.125, 164.53125}}, + {"tzusgm6v33y", Box{44.4584606588, 44.4584619999, 85.2247855067, 85.2247868478}}, + {"26sre", Box{-29.619140625, -29.5751953125, -162.641601562, -162.59765625}}, + {"q1tcnkmh55b", Box{-36.3626660407, -36.3626646996, 98.3675909042, 98.3675922453}}, + {"xs4t1g3", Box{23.3967590332, 23.3981323242, 161.093902588, 161.095275879}}, + {"td", Box{11.25, 16.875, 67.5, 78.75}}, + {"xdvwxsmsjd", Box{16.6353714466, 16.635376811, 165.571753979, 165.571764708}}, + {"dnw", Box{36.5625, 37.96875, -81.5625, -80.15625}}, + {"u7nu2ff43vx", Box{62.6375922561, 62.6375935972, 20.777977556, 20.7779788971}}, + {"0jy2", Box{-57.65625, -57.48046875, -171.2109375, -170.859375}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"2yx2k", Box{-8.3935546875, -8.349609375, -135.87890625, -135.834960938}}, + {"g3", Box{50.625, 56.25, -33.75, -22.5}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"vrrpw34jfjs", Box{87.1061190963, 87.1061204374, 66.3712459803, 66.3712473214}}, + {"g2gkzeutg", Box{50.0752973557, 50.075340271, -28.8437891006, -28.8437461853}}, + {"z8ett4zh", Box{48.7950897217, 48.7952613831, 162.6512146, 162.651557922}}, + {"cyspv2k0", Box{82.9261779785, 82.9263496399, -95.3887939453, -95.3884506226}}, + {"fqu7qbshmr", Box{83.5435527563, 83.5435581207, -72.471088171, -72.4710774422}}, + {"578u4ww7", Box{-69.5731544495, -69.5729827881, -32.5768661499, -32.5765228271}}, + {"4gzkwxrzb", Box{-68.0740785599, -68.0740356445, -45.7583999634, -45.758357048}}, + {"g0z038npm1ym", Box{49.2639500834, 49.263950251, -35.0818693265, -35.0818689913}}, + {"vrse4ey62b1y", Box{87.7358303592, 87.7358305268, 62.6966058835, 62.6966062188}}, + {"7x2fkbbj6", Box{-3.81822109222, -3.81817817688, -21.2364864349, -21.2364435196}}, + {"tuvfdxy7b", Box{27.2014188766, 27.201461792, 86.9543838501, 86.9544267654}}, + {"9qzggb", Box{38.6279296875, 38.6334228516, -112.686767578, -112.67578125}}, + {"pupf6", Box{-67.1044921875, -67.060546875, 179.736328125, 179.780273438}}, + {"fknyrbe41k0w", Box{68.6017451808, 68.6017453484, -68.9130621403, -68.9130618051}}, + {"591vk5jz7x4v", Box{-83.4343860112, -83.4343858436, -19.8552309349, -19.8552305996}}, + {"n4psdv", Box{-77.9315185547, -77.9260253906, 100.667724609, 100.678710938}}, + {"zyw2", Box{81.5625, 81.73828125, 177.5390625, 177.890625}}, + {"r0e29", Box{-42.099609375, -42.0556640625, 139.614257812, 139.658203125}}, + {"j9cq7f", Box{-79.0466308594, -79.0411376953, 69.4226074219, 69.43359375}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"m42x20h", Box{-31.0693359375, -31.0679626465, 45.7086181641, 45.7099914551}}, + {"405zg3kz2j", Box{-88.6295574903, -88.6295521259, -84.5772171021, -84.5772063732}}, + {"7vu7httsdm", Box{-12.0978945494, -12.097889185, -5.06803393364, -5.0680232048}}, + {"prznz91", Box{-45.2142333984, -45.2128601074, 156.424713135, 156.426086426}}, + {"46dupvqwbsuj", Box{-75.2043508552, -75.2043506876, -74.5332831144, -74.5332827792}}, + {"7s0", Box{-22.5, -21.09375, -22.5, -21.09375}}, + {"5nhz5er2nnm5", Box{-55.0016444363, -55.0016442686, -38.1562833488, -38.1562830135}}, + {"5qqp", Box{-53.61328125, -53.4375, -25.3125, -24.9609375}}, + {"t4dqn2", Box{15.1171875, 15.1226806641, 48.4387207031, 48.4497070312}}, + {"gmxt", Box{76.81640625, 76.9921875, -23.203125, -22.8515625}}, + {"syuh0ke1syh", Box{38.6968839169, 38.696885258, 39.3903154135, 39.3903167546}}, + {"nvr0", Box{-60.46875, -60.29296875, 133.59375, 133.9453125}}, + {"g4", Box{56.25, 61.875, -45.0, -33.75}}, + {"gnd2kb6w0s32", Box{81.6088713706, 81.6088715382, -41.623740904, -41.6237405688}}, + {"x7hptvsgdvu", Box{18.2242034376, 18.2242047787, 152.134332061, 152.134333402}}, + {"2fc8", Box{-29.53125, -29.35546875, -144.140625, -143.7890625}}, + {"e3fsu48yte7", Box{10.693577081, 10.6935784221, -30.057323724, -30.0573223829}}, + {"pbqhjvqh", Box{-87.8610992432, -87.8609275818, 177.448425293, 177.448768616}}, + {"sqx1nwgn", Box{36.7763900757, 36.7765617371, 21.3835144043, 21.3838577271}}, + {"e6g", Box{15.46875, 16.875, -29.53125, -28.125}}, + {"dt", Box{28.125, 33.75, -67.5, -56.25}}, + {"02s64yzk", Box{-86.7981719971, -86.7980003357, -162.642631531, -162.642288208}}, + {"z9", Box{50.625, 56.25, 157.5, 168.75}}, + {"fjv", Box{77.34375, 78.75, -82.96875, -81.5625}}, + {"5yc0", Box{-52.03125, -51.85546875, -9.84375, -9.4921875}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"k3x3j1pmdnfp", Box{-36.3802440651, -36.3802438974, 21.6750839353, 21.6750842705}}, + {"g2pjds", Box{45.9887695312, 45.9942626953, -23.7963867188, -23.7854003906}}, + {"ppw0p3q2gzbk", Box{-47.8054625541, -47.8054623865, 143.764847852, 143.764848188}}, + {"9xwp0kprs0x6", Box{43.4412318841, 43.4412320517, -104.041375928, -104.041375592}}, + {"0gzww7fv7gj", Box{-67.7421551943, -67.7421538532, -135.424522609, -135.424521267}}, + {"sgpt2", Box{17.7978515625, 17.841796875, 44.296875, 44.3408203125}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"rr", Box{-5.625, 0.0, 146.25, 157.5}}, + {"wkqrtvj5", Box{25.2525901794, 25.2527618408, 110.298614502, 110.298957825}}, + {"ngtfyx77u", Box{-69.7886323929, -69.7885894775, 132.126216888, 132.126259804}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"58y", Box{-85.78125, -84.375, -14.0625, -12.65625}}, + {"mvjrxvx", Box{-15.5264282227, -15.5250549316, 86.483001709, 86.484375}}, + {"jgve4ttu", Box{-68.3480072021, -68.3478355408, 86.6021347046, 86.6024780273}}, + {"h9", Box{-84.375, -78.75, 22.5, 33.75}}, + {"ytf9sb6mj27", Box{77.609654814, 77.6096561551, 116.227684468, 116.227685809}}, + {"rk9kv3q", Box{-18.8456726074, -18.8442993164, 148.246765137, 148.248138428}}, + {"kbq8t", Box{-43.505859375, -43.4619140625, 43.1103515625, 43.154296875}}, + {"j8prqp8kx", Box{-88.6836147308, -88.6835718155, 77.9596281052, 77.9596710205}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"mq53k", Box{-11.0302734375, -10.986328125, 60.99609375, 61.0400390625}}, + {"d95fw", Box{6.064453125, 6.1083984375, -61.962890625, -61.9189453125}}, + {"x15j3", Box{6.5478515625, 6.591796875, 139.262695312, 139.306640625}}, + {"x0yg7k2b", Box{4.81338500977, 4.81355667114, 144.636039734, 144.636383057}}, + {"dx8x9yxwprk5", Box{43.5426343046, 43.5426344723, -66.7093545198, -66.7093541846}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"429ptwcscc3", Box{-85.8312396705, -85.8312383294, -77.0999144018, -77.0999130607}}, + {"ncknjt", Box{-81.8865966797, -81.8811035156, 129.616699219, 129.627685547}}, + {"7ce8p70yc7h", Box{-36.5448457003, -36.5448443592, -6.00843250751, -6.00843116641}}, + {"un1", Box{78.75, 80.15625, 1.40625, 2.8125}}, + {"b6rv0shhd5", Box{58.5579174757, 58.5579228401, -157.824010849, -157.82400012}}, + {"qyg8d", Box{-6.943359375, -6.8994140625, 128.759765625, 128.803710938}}, + {"3p7f2mvx6", Box{-3.79041194916, -3.79036903381, -129.707937241, -129.707894325}}, + {"u0vj", Box{50.09765625, 50.2734375, 7.03125, 7.3828125}}, + {"49m", Box{-82.96875, -81.5625, -60.46875, -59.0625}}, + {"7vhu8hp00j2", Box{-16.0619835556, -16.0619822145, -4.56069946289, -4.56069812179}}, + {"3sh", Box{-22.5, -21.09375, -106.875, -105.46875}}, + {"sexg40hc", Box{20.2150154114, 20.2151870728, 33.4928512573, 33.4931945801}}, + {"4uwxw8u", Box{-63.365020752, -63.3636474609, -46.8182373047, -46.8168640137}}, + {"mr", Box{-5.625, 0.0, 56.25, 67.5}}, + {"1kymkgft79d3", Box{-62.3368896358, -62.3368894681, -114.748610817, -114.748610482}}, + {"3tj", Box{-16.875, -15.46875, -105.46875, -104.0625}}, + {"vxfzth5", Box{89.9340820312, 89.9354553223, 71.5910339355, 71.5924072266}}, + {"h", Box{-90.0, -45.0, 0.0, 45.0}}, + {"e5r9", Box{18.45703125, 18.6328125, -34.453125, -34.1015625}}, + {"hq9bc8pmfk", Box{-53.3046555519, -53.3046501875, 13.7869083881, 13.786919117}}, + {"05t", Box{-70.3125, -68.90625, -172.96875, -171.5625}}, + {"ye6yg", Box{64.4677734375, 64.51171875, 116.499023438, 116.54296875}}, + {"gg4k", Box{62.578125, 62.75390625, -8.0859375, -7.734375}}, + {"50q2fcp", Box{-88.4564208984, -88.4550476074, -36.0804748535, -36.0791015625}}, + {"2hu8d3131", Box{-18.1876945496, -18.1876516342, -173.571238518, -173.571195602}}, + {"08gcdmy84ewg", Box{-85.4859731533, -85.4859729856, -152.118642814, -152.118642479}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"t05v4wzmqmet", Box{0.91691667214, 0.916916839778, 50.3935300559, 50.3935303912}}, + {"3nc", Box{-7.03125, -5.625, -133.59375, -132.1875}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"3gvk6zm1869", Box{-23.1190833449, -23.1190820038, -93.7394593656, -93.7394580245}}, + {"39rruq", Box{-36.5734863281, -36.5679931641, -102.117919922, -102.106933594}}, + {"jhkxvqt7q5z", Box{-64.6951617301, -64.6951603889, 51.5663145483, 51.5663158894}}, + {"bb", Box{45.0, 50.625, -146.25, -135.0}}, + {"es26styfpb", Box{24.3776321411, 24.3776375055, -21.9410812855, -21.9410705566}}, + {"500q98r9", Box{-88.8558769226, -88.8557052612, -44.5722198486, -44.5718765259}}, + {"kv", Box{-16.875, -11.25, 33.75, 45.0}}, + {"njf", Box{-57.65625, -56.25, 92.8125, 94.21875}}, + {"whk5vu", Box{24.5874023438, 24.5928955078, 95.8776855469, 95.888671875}}, + {"w9pq4yk4p4qf", Box{6.71437550336, 6.714375671, 122.821964733, 122.821965069}}, + {"yt", Box{73.125, 78.75, 112.5, 123.75}}, + {"ccztdek", Box{55.8283996582, 55.8297729492, -90.5877685547, -90.5863952637}}, + {"ej33gfth2", Box{29.8533296585, 29.8533725739, -43.070526123, -43.0704832077}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"hqyfjm", Box{-51.6522216797, -51.6467285156, 20.9729003906, 20.9838867188}}, + {"njcr", Box{-56.42578125, -56.25, 91.7578125, 92.109375}}, + {"48ejbxwrk6", Box{-86.1343038082, -86.1342984438, -63.2505118847, -63.2505011559}}, + {"78f1r09e5v8", Box{-40.558232367, -40.5582310259, -19.3776619434, -19.3776606023}}, + {"ged8fvuhk31", Box{64.8516565561, 64.8516578972, -18.8578484952, -18.8578471541}}, + {"8ss3fn", Box{25.6530761719, 25.6585693359, -151.435546875, -151.424560547}}, + {"v90sp4e6", Box{51.3422012329, 51.3423728943, 68.5152053833, 68.5155487061}}, + {"bx00h848", Box{84.375, 84.3751716614, -157.298812866, -157.298469543}}, + {"9y3", Box{35.15625, 36.5625, -99.84375, -98.4375}}, + {"ehpkg7", Box{23.3514404297, 23.3569335938, -34.6618652344, -34.6508789062}}, + {"r38623wxhs", Box{-36.1575293541, -36.1575239897, 146.621668339, 146.621679068}}, + {"x6yex2zx", Box{16.0893058777, 16.0894775391, 155.719528198, 155.719871521}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"6w", Box{-11.25, -5.625, -67.5, -56.25}}, + {"mx6g2d", Box{-3.63647460938, -3.63098144531, 71.3891601562, 71.4001464844}}, + {"vmsh9b6f", Box{76.7302322388, 76.7304039001, 61.9556808472, 61.9560241699}}, + {"7uqbsm728bjw", Box{-20.9769334272, -20.9769332595, -1.56654216349, -1.56654182822}}, + {"nvtqqh0jc3", Box{-57.9409021139, -57.9408967495, 131.396538019, 131.396548748}}, + {"x8", Box{0.0, 5.625, 157.5, 168.75}}, + {"nqx5n", Box{-52.91015625, -52.8662109375, 111.357421875, 111.401367188}}, + {"c4wmv60xtud", Box{60.0855401158, 60.0855414569, -125.979288518, -125.979287177}}, + {"t9", Box{5.625, 11.25, 67.5, 78.75}}, + {"e3nqrsk9fqdu", Box{6.74731470644, 6.74731487408, -24.6250675991, -24.6250672638}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"7x9", Box{-2.8125, -1.40625, -21.09375, -19.6875}}, + {"760qb6yxf", Box{-32.5470399857, -32.5469970703, -33.3784389496, -33.3783960342}}, + {"x7skftff", Box{20.5543899536, 20.554561615, 152.340202332, 152.340545654}}, + {"jv774u", Box{-59.9194335938, -59.9139404297, 83.4411621094, 83.4521484375}}, + {"phh7", Box{-66.97265625, -66.796875, 140.9765625, 141.328125}}, + {"hv4tt0pymy", Box{-60.9070980549, -60.9070926905, 37.4962413311, 37.4962520599}}, + {"3zemdehyubj", Box{-1.82806491852, -1.82806357741, -96.563090533, -96.5630891919}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"dep80ett", Box{16.8950843811, 16.8952560425, -56.9235992432, -56.9232559204}}, + {"bbj", Box{45.0, 46.40625, -139.21875, -137.8125}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"9k1t0ecqh3v3", Box{23.4005451389, 23.4005453065, -121.616746299, -121.616745964}}, + {"mm5", Box{-16.875, -15.46875, 60.46875, 61.875}}, + {"jz4zmmv2", Box{-49.3190002441, -49.3188285828, 82.8551101685, 82.8554534912}}, + {"bpshcry", Box{88.065032959, 88.06640625, -174.311828613, -174.310455322}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"e87b", Box{1.40625, 1.58203125, -17.2265625, -16.875}}, + {"pcphekd1ph0", Box{-83.5590720177, -83.5590706766, 178.739619255, 178.739620596}}, + {"nrwq0", Box{-46.7578125, -46.7138671875, 110.0390625, 110.083007812}}, + {"6k3e7rk823cj", Box{-20.4825823568, -20.4825821891, -76.4916108549, -76.4916105196}}, + {"kkv059k5v", Box{-18.2737398148, -18.2736968994, 18.4407663345, 18.4408092499}}, + {"ep7x2t4t6x", Box{42.084068656, 42.0840740204, -40.0526118279, -40.052601099}}, + {"43hh0", Box{-83.671875, -83.6279296875, -73.125, -73.0810546875}}, + {"rdhfdg0qee", Box{-33.2929354906, -33.2929301262, 164.301030636, 164.301041365}}, + {"znku45j7p", Box{80.8763694763, 80.8764123917, 141.77508831, 141.775131226}}, + {"ju", Box{-67.5, -61.875, 78.75, 90.0}}, + {"b6zckuz", Box{60.7145690918, 60.7159423828, -157.633209229, -157.631835938}}, + {"fm7m", Box{75.41015625, 75.5859375, -74.1796875, -73.828125}}, + {"8xg", Box{43.59375, 45.0, -153.28125, -151.875}}, + {"wfk7q", Box{13.2275390625, 13.271484375, 129.990234375, 130.034179688}}, + {"6r1p9yz6z9", Box{-4.26908433437, -4.26907896996, -77.2565674782, -77.2565567493}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"fuzw5", Box{72.7734375, 72.8173828125, -45.5712890625, -45.52734375}}, + {"m33ehjv", Box{-37.4098205566, -37.4084472656, 58.5420227051, 58.5433959961}}, + {"s6stp", Box{14.94140625, 14.9853515625, 17.8857421875, 17.9296875}}, + {"tjxuvxh", Box{31.8109130859, 31.812286377, 56.1456298828, 56.1470031738}}, + {"0vgxezccsf", Box{-56.2950503826, -56.2950450182, -141.160722971, -141.160712242}}, + {"0h", Box{-67.5, -61.875, -180.0, -168.75}}, + {"ge6bb", Box{63.4130859375, 63.45703125, -18.6328125, -18.5888671875}}, + {"h2gyy9", Box{-84.5892333984, -84.5837402344, 16.8090820312, 16.8200683594}}, + {"g0f7xg", Box{49.8504638672, 49.8559570312, -41.4953613281, -41.484375}}, + {"ujfk5", Box{78.046875, 78.0908203125, 3.2958984375, 3.33984375}}, + {"q0b6rwm0", Box{-40.3514099121, -40.3512382507, 90.6880187988, 90.6883621216}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"0nyhwsx2g5", Box{-51.2153702974, -51.215364933, -171.266770363, -171.266759634}}, + {"mjpe", Box{-16.34765625, -16.171875, 55.546875, 55.8984375}}, + {"mt", Box{-16.875, -11.25, 67.5, 78.75}}, + {"z49vj0d4zz", Box{59.9446624517, 59.9446678162, 137.683743238, 137.683753967}}, + {"zry97vd3gs", Box{88.8440108299, 88.8440161943, 155.55866003, 155.558670759}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"v3syrvc9", Box{54.5678901672, 54.5680618286, 63.2723236084, 63.2726669312}}, + {"nrer", Box{-46.58203125, -46.40625, 105.8203125, 106.171875}}, + {"2hqt8nf65v5e", Box{-20.0895036198, -20.0895034522, -170.856119469, -170.856119134}}, + {"wekgdxc", Box{18.9390563965, 18.9404296875, 119.290924072, 119.292297363}}, + {"6bh43q", Box{-44.5715332031, -44.5660400391, -50.5700683594, -50.5590820312}}, + {"d564", Box{18.6328125, 18.80859375, -87.1875, -86.8359375}}, + {"m85", Box{-45.0, -43.59375, 71.71875, 73.125}}, + {"x4tj5rb77r", Box{14.9845737219, 14.9845790863, 142.174555063, 142.174565792}}, + {"0bwycz8vr", Box{-85.9588766098, -85.9588336945, -136.679577827, -136.679534912}}, + {"ehnu2e", Box{23.2635498047, 23.2690429688, -35.4858398438, -35.4748535156}}, + {"jbe8mk", Box{-87.1215820312, -87.1160888672, 83.9025878906, 83.9135742188}}, + {"ss", Box{22.5, 28.125, 22.5, 33.75}}, + {"8edywpv3ygj3", Box{20.8729668148, 20.8729669824, -153.361634128, -153.361633793}}, + {"xpxu1erq390", Box{42.9095560312, 42.9095573723, 145.974376202, 145.974377543}}, + {"4043qb91kj", Box{-89.7772854567, -89.7772800922, -86.5377616882, -86.5377509594}}, + {"2n7q0j0r", Box{-8.76039505005, -8.76022338867, -175.429344177, -175.429000854}}, + {"cm0n96q3vn8t", Box{74.2802738585, 74.2802740261, -123.686270043, -123.686269708}}, + {"50hgpxg", Box{-89.4300842285, -89.4287109375, -37.9866027832, -37.9852294922}}, + {"2m", Box{-16.875, -11.25, -168.75, -157.5}}, + {"w4s1zj8n9", Box{14.4014453888, 14.4014883041, 95.9326601028, 95.9327030182}}, + {"hxzh", Box{-45.703125, -45.52734375, 32.34375, 32.6953125}}, + {"cn", Box{78.75, 84.375, -135.0, -123.75}}, + {"dpt5k8", Box{42.7587890625, 42.7642822266, -82.7709960938, -82.7600097656}}, + {"gz", Box{84.375, 90.0, -11.25, 0.0}}, + {"d09knt", Box{3.54309082031, 3.54858398438, -87.9565429688, -87.9455566406}}, + {"mrucw", Box{-1.142578125, -1.0986328125, 63.193359375, 63.2373046875}}, + {"055egqf4y", Box{-72.4282693863, -72.4282264709, -174.93229866, -174.932255745}}, + {"qphu", Box{-4.921875, -4.74609375, 96.6796875, 97.03125}}, + {"dfeh", Box{14.765625, 14.94140625, -52.03125, -51.6796875}}, + {"00qfn4ctp57", Box{-88.2262055576, -88.2262042165, -170.241776258, -170.241774917}}, + {"q9xx27p2trn", Box{-35.2714830637, -35.2714817226, 123.06805104, 123.068052381}}, + {"10bhfgfsj8m", Box{-84.9250017107, -84.9250003695, -134.875474423, -134.875473082}}, + {"6k", Box{-22.5, -16.875, -78.75, -67.5}}, + {"zyvjr4m37", Box{83.9041757584, 83.9042186737, 176.096205711, 176.096248627}}, + {"0c8k7gb", Box{-80.7948303223, -80.7934570312, -145.733642578, -145.732269287}}, + {"n7k", Box{-71.71875, -70.3125, 106.875, 108.28125}}, + {"fpj4eecz", Box{84.8362541199, 84.8364257812, -82.812538147, -82.8121948242}}, + {"ev", Box{28.125, 33.75, -11.25, 0.0}}, + {"8y", Box{33.75, 39.375, -146.25, -135.0}}, + {"x9xd1q", Box{8.82202148438, 8.82751464844, 168.101806641, 168.112792969}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"d1pmefm0t", Box{6.60424232483, 6.60428524017, -79.6328115463, -79.632768631}}, + {"tmy", Box{32.34375, 33.75, 64.6875, 66.09375}}, + {"phu6jzv0z", Box{-62.8869867325, -62.8869438171, 141.236414909, 141.236457825}}, + {"zv7pjxpuwjbq", Box{75.8009752259, 75.8009753935, 173.221350051, 173.221350387}}, + {"9gegu1", Box{20.3521728516, 20.3576660156, -95.80078125, -95.7897949219}}, + {"tq33", Box{35.33203125, 35.5078125, 58.0078125, 58.359375}}, + {"e", Box{0.0, 45.0, -45.0, 0.0}}, + {"y6z1sy", Box{60.7653808594, 60.7708740234, 111.302490234, 111.313476562}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"q", Box{-45.0, 0.0, 90.0, 135.0}}, + {"mtyyw7", Box{-11.4971923828, -11.4916992188, 77.2668457031, 77.2778320312}}, + {"2sqmb", Box{-20.0830078125, -20.0390625, -148.7109375, -148.666992188}}, + {"yp487k", Box{84.4409179688, 84.4464111328, 93.6584472656, 93.6694335938}}, + {"nyn5k1fc", Box{-55.668926239, -55.6687545776, 132.3670578, 132.367401123}}, + {"gnqk77v", Box{80.9239196777, 80.9252929688, -36.0612487793, -36.0598754883}}, + {"yw12", Box{78.75, 78.92578125, 114.2578125, 114.609375}}, + {"vcgqqmd", Box{55.9725952148, 55.9739685059, 83.5977172852, 83.5990905762}}, + {"27zp3q", Box{-22.5988769531, -22.5933837891, -158.851318359, -158.840332031}}, + {"cg4tb6", Box{62.8967285156, 62.9022216797, -97.7233886719, -97.7124023438}}, + {"w5njb9rnp5e", Box{17.8936573863, 17.8936587274, 98.4693901241, 98.4693914652}}, + {"9y6rp4pptv", Box{36.3990193605, 36.399024725, -97.7684605122, -97.7684497833}}, + {"pjup17j08hup", Box{-56.4091892727, -56.409189105, 140.68680346, 140.686803795}}, + {"vz52f33z9mu", Box{84.5150206983, 84.5150220394, 83.421651721, 83.4216530621}}, + {"zd", Box{56.25, 61.875, 157.5, 168.75}}, + {"q", Box{-45.0, 0.0, 90.0, 135.0}}, + {"dm46s", Box{28.564453125, 28.6083984375, -75.41015625, -75.3662109375}}, + {"d7dnvbu", Box{20.8781433105, 20.8795166016, -75.6793212891, -75.677947998}}, + {"02ercsvx0h", Box{-85.7978796959, -85.7978743315, -164.106216431, -164.106205702}}, + {"hnfx5f", Box{-50.7897949219, -50.7843017578, 3.68041992188, 3.69140625}}, + {"evb677f6t", Box{32.7602863312, 32.7603292465, -10.7523107529, -10.7522678375}}, + {"sg43r7p151", Box{17.1113830805, 17.1113884449, 37.2424077988, 37.2424185276}}, + {"441mdz5", Box{-77.7447509766, -77.7433776855, -88.1172180176, -88.1158447266}}, + {"k2qrsc7hr81", Box{-42.2677946091, -42.267793268, 20.2522458136, 20.2522471547}}, + {"d0ydzxhjwr", Box{4.74158227444, 4.74158763885, -80.5240237713, -80.5240130424}}, + {"4cn", Box{-84.375, -82.96875, -47.8125, -46.40625}}, + {"ds2", Box{23.90625, 25.3125, -67.5, -66.09375}}, + {"rxvywy", Box{-0.230712890625, -0.225219726562, 165.882568359, 165.893554688}}, + {"zs2k", Box{69.609375, 69.78515625, 157.8515625, 158.203125}}, + {"kg63", Box{-26.54296875, -26.3671875, 36.9140625, 37.265625}}, + {"hmxh64j", Box{-58.3044433594, -58.3030700684, 21.1885070801, 21.1898803711}}, + {"d5v3", Box{21.26953125, 21.4453125, -82.6171875, -82.265625}}, + {"ddg1", Box{15.64453125, 15.8203125, -63.28125, -62.9296875}}, + {"stf4tug", Box{32.8092956543, 32.8106689453, 25.5693054199, 25.5706787109}}, + {"vgc", Box{66.09375, 67.5, 80.15625, 81.5625}}, + {"jby3xf", Box{-85.5065917969, -85.5010986328, 87.8796386719, 87.890625}}, + {"9b1f419tdjf", Box{0.360777229071, 0.360778570175, -98.6990234256, -98.6990220845}}, + {"0zqp4", Box{-47.98828125, -47.9443359375, -137.724609375, -137.680664062}}, + {"gg292c4wd", Box{63.5075855255, 63.5076284409, -10.5103969574, -10.5103540421}}, + {"zy96qbt", Box{81.9607543945, 81.9621276855, 170.811309814, 170.812683105}}, + {"tz9x7f5c70", Box{43.4731149673, 43.4731203318, 81.0294485092, 81.0294592381}}, + {"rq", Box{-11.25, -5.625, 146.25, 157.5}}, + {"94tt", Box{14.94140625, 15.1171875, -127.265625, -126.9140625}}, + {"h7vnwf8", Box{-67.7499389648, -67.7485656738, 18.5778808594, 18.5792541504}}, + {"4f", Box{-78.75, -73.125, -56.25, -45.0}}, + {"kj3t", Box{-14.58984375, -14.4140625, 2.109375, 2.4609375}}, + {"qspj", Box{-21.62109375, -21.4453125, 122.34375, 122.6953125}}, + {"4y9", Box{-53.4375, -52.03125, -54.84375, -53.4375}}, + {"b05kqcvsm8", Box{45.7574129105, 45.7574182749, -175.125267506, -175.125256777}}, + {"p5zq4f", Box{-67.8405761719, -67.8350830078, 145.316162109, 145.327148438}}, + {"1cgx", Box{-78.92578125, -78.75, -96.328125, -95.9765625}}, + {"m2", Box{-45.0, -39.375, 56.25, 67.5}}, + {"j150xkd492", Box{-84.2619609833, -84.2619556189, 49.5401537418, 49.5401644707}}, + {"05rs", Box{-71.015625, -70.83984375, -169.453125, -169.1015625}}, + {"ve8", Box{64.6875, 66.09375, 67.5, 68.90625}}, + {"r9tv", Box{-35.68359375, -35.5078125, 165.5859375, 165.9375}}, + {"d71r07", Box{18.1219482422, 18.1274414062, -76.9812011719, -76.9702148438}}, + {"b6hepfqk6", Box{56.79043293, 56.7904758453, -162.072629929, -162.072587013}}, + {"md8y86mqjd", Box{-29.7815215588, -29.7815161943, 68.5731196404, 68.5731303692}}, + {"bcgcyq", Box{55.1843261719, 55.1898193359, -140.701904297, -140.690917969}}, + {"e3hpu9352", Box{6.99472904205, 6.9947719574, -27.9258728027, -27.9258298874}}, + {"hsbsq2rkmg", Box{-62.5320607424, -62.532055378, 23.4879863262, 23.4879970551}}, + {"ub2r", Box{47.63671875, 47.8125, 34.1015625, 34.453125}}, + {"d8", Box{0.0, 5.625, -67.5, -56.25}}, + {"gexm9j6y088", Box{65.6841686368, 65.6841699779, -12.2569441795, -12.2569428384}}, + {"15cdq", Box{-68.5107421875, -68.466796875, -132.626953125, -132.583007812}}, + {"9zud17gy", Box{43.9669418335, 43.9671134949, -94.8617935181, -94.8614501953}}, + {"4q3y", Box{-53.7890625, -53.61328125, -76.2890625, -75.9375}}, + {"gph138", Box{84.5947265625, 84.6002197266, -39.3090820312, -39.2980957031}}, + {"m09d8ju2b", Box{-41.7163324356, -41.7162895203, 47.1152114868, 47.1152544022}}, + {"8mszup4gk", Box{32.3388147354, 32.3388576508, -161.890583038, -161.890540123}}, + {"dyrfvtvqh", Box{35.6722640991, 35.6723070145, -45.102481842, -45.1024389267}}, + {"3h9tgkp", Box{-18.6547851562, -18.6534118652, -132.738189697, -132.736816406}}, + {"66gdty14u", Box{-29.0583658218, -29.0583229065, -73.5738945007, -73.5738515854}}, + {"83zp1d", Box{11.0852050781, 11.0906982422, -158.840332031, -158.829345703}}, + {"e7gp0", Box{22.32421875, 22.3681640625, -29.53125, -29.4873046875}}, + {"s0ykfgceytk", Box{5.07498219609, 5.0749835372, 8.91225636005, 8.91225770116}}, + {"zfe7", Box{59.58984375, 59.765625, 173.3203125, 173.671875}}, + {"cr9", Box{87.1875, 88.59375, -122.34375, -120.9375}}, + {"9ugr3kq", Box{28.0165100098, 28.0178833008, -96.6165161133, -96.6151428223}}, + {"2grcq0", Box{-26.4990234375, -26.4935302734, -135.087890625, -135.076904297}}, + {"50bb31vkds3p", Box{-85.726895202, -85.7268950343, -43.8940487802, -43.8940484449}}, + {"qhxdv1hcqe3", Box{-19.1983763874, -19.1983750463, 100.773404986, 100.773406327}}, + {"k2cv0gp2vk", Box{-39.8857140541, -39.8857086897, 13.7540781498, 13.7540888786}}, + {"y5jd", Box{62.2265625, 62.40234375, 97.734375, 98.0859375}}, + {"gnvg3p", Box{83.5784912109, 83.583984375, -36.8701171875, -36.8591308594}}, + {"9g70w", Box{18.369140625, 18.4130859375, -96.767578125, -96.7236328125}}, + {"9g7qsb", Box{19.423828125, 19.4293212891, -96.4709472656, -96.4599609375}}, + {"1zq2u", Box{-49.0869140625, -49.04296875, -92.28515625, -92.2412109375}}, + {"tr", Box{39.375, 45.0, 56.25, 67.5}}, + {"4wmddz9mgk38", Box{-54.3620882928, -54.3620881252, -59.6429172903, -59.6429169551}}, + {"wkcdsn8nbz", Box{27.1951049566, 27.195110321, 103.535188437, 103.535199165}}, + {"1r198wxj", Box{-50.3247642517, -50.3245925903, -121.609039307, -121.608695984}}, + {"eu", Box{22.5, 28.125, -11.25, 0.0}}, + {"k2y7mk6sd0wz", Box{-40.1858386584, -40.1858384907, 20.2733035013, 20.2733038366}}, + {"gms9ytw4v5", Box{76.2758177519, 76.2758231163, -27.1277761459, -27.1277654171}}, + {"2vdkc", Box{-13.2275390625, -13.18359375, -143.041992188, -142.998046875}}, + {"bke7fx3", Box{71.011505127, 71.012878418, -164.068450928, -164.067077637}}, + {"tvnxu7jt8", Box{29.5047283173, 29.5047712326, 88.0849456787, 88.0849885941}}, + {"f864yp3c", Box{46.9296455383, 46.9298171997, -64.4214248657, -64.421081543}}, + {"g8hxr7x150d7", Box{46.2938149832, 46.2938151509, -15.8435266837, -15.8435263485}}, + {"zmk4kmh", Box{74.9542236328, 74.9555969238, 152.067260742, 152.068634033}}, + {"gtqsvep4", Box{75.3830337524, 75.3832054138, -13.1080627441, -13.1077194214}}, + {"trsvy0", Box{43.1982421875, 43.2037353516, 63.193359375, 63.2043457031}}, + {"bevjfs", Box{67.1264648438, 67.1319580078, -150.358886719, -150.347900391}}, + {"ktrb", Box{-15.46875, -15.29296875, 33.3984375, 33.75}}, + {"dn20q1pv", Box{35.2065467834, 35.2067184448, -89.7256851196, -89.7253417969}}, + {"8n3wy5g2", Box{36.3633728027, 36.3635444641, -177.622489929, -177.622146606}}, + {"vyzft", Box{83.408203125, 83.4521484375, 89.8681640625, 89.912109375}}, + {"gwuedjbs8222", Box{83.6163438857, 83.6163440533, -16.0832866654, -16.0832863301}}, + {"fpb89dkktj83", Box{88.6948023923, 88.6948025599, -89.2249056324, -89.2249052972}}, + {"wjjk3", Box{28.8720703125, 28.916015625, 97.4267578125, 97.470703125}}, + {"wx6", Box{40.78125, 42.1875, 115.3125, 116.71875}}, + {"yzpuuv3w0pw", Box{85.2398702502, 85.2398715913, 134.859245718, 134.859247059}}, + {"k2518ucy", Box{-44.7092056274, -44.7090339661, 15.5041122437, 15.5044555664}}, + {"hkjk1075", Box{-66.7949867249, -66.7948150635, 18.6808776855, 18.6812210083}}, + {"btmd", Box{74.8828125, 75.05859375, -149.765625, -149.4140625}}, + {"ucbvmyuw9z", Box{55.8048337698, 55.8048391342, 35.0636279583, 35.0636386871}}, + {"wf86ytd34", Box{14.5762825012, 14.5763254166, 124.390382767, 124.390425682}}, + {"9zjbws3u", Box{39.4869232178, 39.4870948792, -92.8760147095, -92.8756713867}}, + {"rqc", Box{-7.03125, -5.625, 147.65625, 149.0625}}, + {"pwqw8gh8x74", Box{-53.6845904589, -53.6845891178, 166.680077612, 166.680078954}}, + {"5ekn", Box{-70.6640625, -70.48828125, -16.875, -16.5234375}}, + {"mxx0b3mzwxp", Box{-2.67247259617, -2.67247125506, 77.3629210889, 77.36292243}}, + {"tn", Box{33.75, 39.375, 45.0, 56.25}}, + {"ju59u9b8grr", Box{-67.1826021373, -67.1826007962, 83.8704644144, 83.8704657555}}, + {"hmte6v6jzq84", Box{-58.4613495693, -58.4613494016, 19.1082823277, 19.1082826629}}, + {"t3", Box{5.625, 11.25, 56.25, 67.5}}, + {"es9g6", Box{25.8837890625, 25.927734375, -19.951171875, -19.9072265625}}, + {"2bwcdk6y", Box{-41.8994522095, -41.8992805481, -136.655158997, -136.654815674}}, + {"4ew3jn8umh", Box{-70.1002621651, -70.1002568007, -58.4899663925, -58.4899556637}}, + {"0meekufm4x3", Box{-58.4642212093, -58.4642198682, -163.616186231, -163.61618489}}, + {"02", Box{-90.0, -84.375, -168.75, -157.5}}, + {"9yuv0t43sv0", Box{38.8754063845, 38.8754077256, -94.5450460911, -94.54504475}}, + {"u0g7y8444", Box{49.8782730103, 49.8783159256, 4.85878944397, 4.85883235931}}, + {"r4", Box{-33.75, -28.125, 135.0, 146.25}}, + {"1ps631dwde", Box{-47.4076205492, -47.4076151848, -128.975951672, -128.975940943}}, + {"vbmfdm", Box{46.8731689453, 46.8786621094, 86.9348144531, 86.9458007812}}, + {"s7gmxm8vhn5v", Box{22.0916506089, 22.0916507766, 16.1401226744, 16.1401230097}}, + {"mcwrh68t", Box{-35.317440033, -35.3172683716, 87.7265167236, 87.7268600464}}, + {"9yt5645jq9r9", Box{37.145683486, 37.1456836537, -94.1264504939, -94.1264501587}}, + {"61t1x3z", Box{-36.2892150879, -36.2878417969, -82.6405334473, -82.6391601562}}, + {"wmzkmbm", Box{33.0921936035, 33.0935668945, 111.704864502, 111.706237793}}, + {"jy", Box{-56.25, -50.625, 78.75, 90.0}}, + {"9ctckntmvgkr", Box{8.69393778965, 8.69393795729, -92.9808190092, -92.980818674}}, + {"8rnugbss4gd", Box{40.2134129405, 40.2134142816, -159.086717069, -159.086715728}}, + {"ek8", Box{25.3125, 26.71875, -33.75, -32.34375}}, + {"nvqegbu", Box{-59.8054504395, -59.8040771484, 133.060913086, 133.062286377}}, + {"h0u7v6pujp5", Box{-85.1103597879, -85.1103584468, 6.21813699603, 6.21813833714}}, + {"2rj", Box{-5.625, -4.21875, -161.71875, -160.3125}}, + {"jk38", Box{-66.09375, -65.91796875, 58.359375, 58.7109375}}, + {"c67qe30m", Box{58.8051795959, 58.8053512573, -119.036521912, -119.036178589}}, + {"xhtwk", Box{26.4111328125, 26.455078125, 142.91015625, 142.954101562}}, + {"pw6ue97rjn11", Box{-54.0446339361, -54.0446337685, 161.525675207, 161.525675543}}, + {"xpmw424pk6", Box{41.8371927738, 41.8371981382, 142.836180925, 142.836191654}}, + {"rk", Box{-22.5, -16.875, 146.25, 157.5}}, + {"hmfmn5uwdh2", Box{-56.755605787, -56.7556044459, 14.6840000153, 14.6840013564}}, + {"defvfj", Box{22.1319580078, 22.1374511719, -63.544921875, -63.5339355469}}, + {"sg7jd89w", Box{19.2518234253, 19.2519950867, 38.0806732178, 38.0810165405}}, + {"yn0", Box{78.75, 80.15625, 90.0, 91.40625}}, + {"3n7re6gv2e92", Box{-8.50936442614, -8.5093642585, -130.281692259, -130.281691924}}, + {"vyj", Box{78.75, 80.15625, 85.78125, 87.1875}}, + {"9cntsz", Box{6.63024902344, 6.6357421875, -91.9006347656, -91.8896484375}}, + {"w4uwq3d", Box{16.5756225586, 16.5769958496, 96.6055297852, 96.6069030762}}, + {"zffrf", Box{61.8310546875, 61.875, 172.001953125, 172.045898438}}, + {"hq6142s", Box{-54.665222168, -54.663848877, 14.1668701172, 14.1682434082}}, + {"m7srp977", Box{-24.0746498108, -24.0744781494, 62.5606155396, 62.5609588623}}, + {"8v6hxy3sz", Box{30.3574132919, 30.3574562073, -143.094563484, -143.094520569}}, + {"rf4snrh70h", Box{-33.0078864098, -33.0078810453, 172.54611969, 172.546130419}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"ssr6y", Box{24.3896484375, 24.43359375, 32.958984375, 33.0029296875}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"yz", Box{84.375, 90.0, 123.75, 135.0}}, + {"n2", Box{-90.0, -84.375, 101.25, 112.5}}, + {"vfe", Box{59.0625, 60.46875, 82.96875, 84.375}}, + {"h", Box{-90.0, -45.0, 0.0, 45.0}}, + {"z3", Box{50.625, 56.25, 146.25, 157.5}}, + {"z2web5usz", Box{48.4930944443, 48.4931373596, 155.397105217, 155.397148132}}, + {"8gkc", Box{18.45703125, 18.6328125, -139.5703125, -139.21875}}, + {"17de4", Box{-69.78515625, -69.7412109375, -120.146484375, -120.102539062}}, + {"ky71b3fwd2vv", Box{-9.52539911494, -9.5253989473, 37.9832738265, 37.9832741618}}, + {"e4gfcm", Box{15.9796142578, 15.9851074219, -39.6716308594, -39.6606445312}}, + {"kdgwxnnb", Box{-28.3557128906, -28.3555412292, 27.7387619019, 27.7391052246}}, + {"4nyn2chy", Box{-50.9260940552, -50.9259223938, -81.5230178833, -81.5226745605}}, + {"u6je3kk57", Box{56.8451929092, 56.8452358246, 19.0449285507, 19.0449714661}}, + {"nrs9j1hj5tj", Box{-47.630340457, -47.6303391159, 107.803501636, 107.803502977}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"krqrcc2hm9uq", Box{-2.84883890301, -2.84883873537, 20.116208531, 20.1162088662}}, + {"fmceu", Box{78.0029296875, 78.046875, -76.46484375, -76.4208984375}}, + {"q3w576", Box{-35.9802246094, -35.9747314453, 109.830322266, 109.841308594}}, + {"19ehn", Box{-80.859375, -80.8154296875, -108.017578125, -107.973632812}}, + {"zpkvjk", Box{86.6821289062, 86.6876220703, 141.910400391, 141.921386719}}, + {"7cgwy9jm", Box{-33.9633750916, -33.9632034302, -6.03527069092, -6.03492736816}}, + {"jju1cefk5v1", Box{-57.3273199797, -57.3273186386, 50.6941701472, 50.6941714883}}, + {"5tfpq6", Box{-56.3708496094, -56.3653564453, -19.4128417969, -19.4018554688}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"dm0vh8", Box{29.00390625, 29.0093994141, -77.4975585938, -77.4865722656}}, + {"jt5c8hhd58b", Box{-61.5890081227, -61.5890067816, 72.7797675133, 72.7797688544}}, + {"4ttr", Box{-57.83203125, -57.65625, -60.1171875, -59.765625}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"sp2d3v6wz", Box{41.2067556381, 41.2067985535, 0.783762931824, 0.783805847168}}, + {"up1yhbspqy", Box{85.4337108135, 85.4337161779, 2.67546057701, 2.67547130585}}, + {"1z4bx8dp1ex1", Box{-50.5331422202, -50.5331420526, -97.0504023135, -97.0504019782}}, + {"76qbnz5n1937", Box{-32.3042606749, -32.3042605072, -23.9569957182, -23.9569953829}}, + {"0w1bvcjb40xd", Box{-56.112667881, -56.1126677133, -154.778384641, -154.778384306}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"yqbjwyvsmc", Box{83.9733606577, 83.9733660221, 101.554430723, 101.554441452}}, + {"0hdp2tdy5", Box{-63.3818435669, -63.3818006516, -177.161622047, -177.161579132}}, + {"s8yv34v", Box{5.15670776367, 5.15808105469, 32.0429992676, 32.0443725586}}, + {"uc60k7w6", Box{52.0947647095, 52.0949363708, 36.757850647, 36.7581939697}}, + {"en6h7me8", Box{35.9335327148, 35.9337043762, -42.0398712158, -42.0395278931}}, + {"1bks34", Box{-87.8356933594, -87.8302001953, -94.8779296875, -94.8669433594}}, + {"65", Box{-28.125, -22.5, -90.0, -78.75}}, + {"qwrquvr", Box{-8.62838745117, -8.62701416016, 122.913665771, 122.915039062}}, + {"3dmchcusw83", Box{-32.1575818956, -32.1575805545, -104.198862165, -104.198860824}}, + {"urfeh", Box{89.12109375, 89.1650390625, 14.94140625, 14.9853515625}}, + {"d3g6rs", Box{10.2612304688, 10.2667236328, -73.8500976562, -73.8391113281}}, + {"wx0v", Box{40.25390625, 40.4296875, 113.5546875, 113.90625}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"et", Box{28.125, 33.75, -22.5, -11.25}}, + {"dqtd0", Box{36.9140625, 36.9580078125, -71.015625, -70.9716796875}}, + {"vtzuwhxhgjv8", Box{78.1603311002, 78.1603312679, 78.6718585342, 78.6718588695}}, + {"bn9", Box{81.5625, 82.96875, -178.59375, -177.1875}}, + {"685n", Box{-43.9453125, -43.76953125, -63.28125, -62.9296875}}, + {"fqy4nhy", Box{83.3464050293, 83.3477783203, -70.0405883789, -70.0392150879}}, + {"5q1dw", Box{-55.810546875, -55.7666015625, -31.376953125, -31.3330078125}}, + {"0sv8m11dgkf", Box{-63.2313139737, -63.2313126326, -149.543696344, -149.543695003}}, + {"vp435nn", Box{84.5837402344, 84.5851135254, 48.3041381836, 48.3055114746}}, + {"wydj", Box{37.44140625, 37.6171875, 126.5625, 126.9140625}}, + {"ebg8", Box{4.21875, 4.39453125, -6.328125, -5.9765625}}, + {"ksmb6pfxe4", Box{-21.0059344769, -21.0059291124, 30.6773900986, 30.6774008274}}, + {"8bv63qv96dp", Box{4.65156197548, 4.65156331658, -138.804586083, -138.804584742}}, + {"0s0d53hd", Box{-67.1426010132, -67.1424293518, -156.647872925, -156.647529602}}, + {"1yjf34ubpm", Box{-55.8393591642, -55.8393537998, -93.1132829189, -93.1132721901}}, + {"823y79hmfe", Box{2.51137912273, 2.51138448715, -166.129310131, -166.129299402}}, + {"xynnrv", Box{34.8760986328, 34.8815917969, 177.528076172, 177.5390625}}, + {"9b9ejqcqqdu", Box{3.37801024318, 3.37801158428, -98.9079111814, -98.9079098403}}, + {"cuuhfkd", Box{72.5784301758, 72.5798034668, -95.5233764648, -95.5220031738}}, + {"khwceh", Box{-19.4018554688, -19.3963623047, 9.6240234375, 9.63500976562}}, + {"z8vub32sy6", Box{50.061403513, 50.0614088774, 165.597878695, 165.597889423}}, + {"4s", Box{-67.5, -61.875, -67.5, -56.25}}, + {"bsrb4qeqt7eq", Box{68.9430911466, 68.9430913143, -146.497992687, -146.497992352}}, + {"b1x0sc", Box{53.5308837891, 53.5363769531, -169.947509766, -169.936523438}}, + {"1ngn2bn2vub", Box{-50.9324629605, -50.9324616194, -130.739461184, -130.739459842}}, + {"bsm", Box{68.90625, 70.3125, -150.46875, -149.0625}}, + {"xyzd7", Box{38.3642578125, 38.408203125, 179.428710938, 179.47265625}}, + {"cvvf1tqs", Box{77.7248382568, 77.7250099182, -93.0892181396, -93.0888748169}}, + {"fz2tpb1xj", Box{86.6613578796, 86.661400795, -55.2040243149, -55.2039813995}}, + {"r4zqr7", Box{-28.4161376953, -28.4106445312, 145.513916016, 145.524902344}}, + {"wth", Box{28.125, 29.53125, 118.125, 119.53125}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"4j0ff5xs58h", Box{-61.3716888428, -61.3716875017, -88.8469666243, -88.8469652832}}, + {"c15", Box{50.625, 52.03125, -130.78125, -129.375}}, + {"tnkgyg", Box{35.8319091797, 35.8374023438, 51.9763183594, 51.9873046875}}, + {"e99", Box{8.4375, 9.84375, -21.09375, -19.6875}}, + {"bcrz69", Box{53.3111572266, 53.3166503906, -135.241699219, -135.230712891}}, + {"e5w29f6", Box{19.7877502441, 19.7891235352, -36.1312866211, -36.1299133301}}, + {"gykjjcq656b5", Box{81.0423812829, 81.0423814505, -5.36359190941, -5.36359157413}}, + {"7j62nzypd3be", Box{-15.4248806275, -15.4248804599, -41.5309696645, -41.5309693292}}, + {"rrsgbbvwt9r7", Box{-2.14807743207, -2.14807726443, 152.970445342, 152.970445678}}, + {"93kub", Box{7.8662109375, 7.91015625, -117.0703125, -117.026367188}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"02bcjtw5f84", Box{-85.5746126175, -85.5746112764, -167.445263565, -167.445262223}}, + {"262dqsqs1", Box{-31.9242095947, -31.9241666794, -167.752261162, -167.752218246}}, + {"185ss806c", Box{-89.2085123062, -89.2084693909, -107.379984856, -107.37994194}}, + {"9s6", Box{23.90625, 25.3125, -109.6875, -108.28125}}, + {"ych1d25e", Box{50.8891868591, 50.8893585205, 129.478683472, 129.479026794}}, + {"7f2qcykht0d", Box{-31.1221191287, -31.1221177876, -10.8158227801, -10.815821439}}, + {"5gk7qw", Box{-71.1145019531, -71.1090087891, -4.98779296875, -4.97680664062}}, + {"7kjutjpu98", Box{-21.6807460785, -21.6807407141, -25.4336285591, -25.4336178303}}, + {"h64", Box{-78.75, -77.34375, 14.0625, 15.46875}}, + {"uy57", Box{79.27734375, 79.453125, 38.3203125, 38.671875}}, + {"r5dtqkydk796", Box{-24.3631505594, -24.3631503917, 138.799393661, 138.799393997}}, + {"5j", Box{-61.875, -56.25, -45.0, -33.75}}, + {"xzbszzeu", Box{44.4705963135, 44.4707679749, 169.798851013, 169.799194336}}, + {"wqjz0fj5e", Box{34.9920558929, 34.9920988083, 109.375891685, 109.375934601}}, + {"dekz", Box{19.51171875, 19.6875, -60.8203125, -60.46875}}, + {"bbyux", Box{50.009765625, 50.0537109375, -136.450195312, -136.40625}}, + {"rctysz36", Box{-35.3797531128, -35.3795814514, 177.046394348, 177.046737671}}, + {"xmhvqm1wcw7", Box{29.0765096247, 29.0765109658, 153.206474036, 153.206475377}}, + {"nhw6c", Box{-64.2041015625, -64.16015625, 98.8330078125, 98.876953125}}, + {"u9d3gdu", Box{53.7602233887, 53.7615966797, 25.8233642578, 25.8247375488}}, + {"xenu1tyd0q", Box{17.6100862026, 17.610091567, 167.067042589, 167.067053318}}, + {"qm70t", Box{-15.380859375, -15.3369140625, 105.688476562, 105.732421875}}, + {"3g0", Box{-28.125, -26.71875, -101.25, -99.84375}}, + {"fg", Box{61.875, 67.5, -56.25, -45.0}}, + {"jq1tn6nn0hfs", Box{-55.3590513021, -55.3590511344, 58.642276302, 58.6422766373}}, + {"b9kmw", Box{52.998046875, 53.0419921875, -151.259765625, -151.215820312}}, + {"z7f9pj6", Box{66.2983703613, 66.2997436523, 150.07598877, 150.077362061}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"hqbvrz4x", Box{-51.0687446594, -51.068572998, 12.6486968994, 12.6490402222}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"81es", Box{9.140625, 9.31640625, -175.078125, -174.7265625}}, + {"nyn6mf2", Box{-55.8421325684, -55.8407592773, 132.791748047, 132.793121338}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"46hfqnuv", Box{-78.3165550232, -78.3163833618, -71.8001174927, -71.7997741699}}, + {"7tne", Box{-16.34765625, -16.171875, -13.359375, -13.0078125}}, + {"z7pkduh4g2c", Box{62.6884643734, 62.6884657145, 156.571796089, 156.571797431}}, + {"xgmm", Box{19.16015625, 19.3359375, 176.1328125, 176.484375}}, + {"he054x", Box{-72.5592041016, -72.5537109375, 22.6098632812, 22.6208496094}}, + {"rqnu21r8g", Box{-10.4959344864, -10.495891571, 155.752615929, 155.752658844}}, + {"k8xg8n36", Box{-41.5375900269, -41.5374183655, 33.4001541138, 33.4004974365}}, + {"d60md", Box{12.216796875, 12.2607421875, -78.310546875, -78.2666015625}}, + {"50tqctv", Box{-85.9693908691, -85.9680175781, -37.5444030762, -37.5430297852}}, + {"yxknv54eqnz", Box{86.984847039, 86.9848483801, 118.34842667, 118.348428011}}, + {"5zpczbcjgj", Box{-50.3122490644, -50.3122437, -0.00948429107666, -0.0094735622406}}, + {"yp7nz0rr7d8", Box{86.9704046845, 86.9704060256, 94.5364737511, 94.5364750922}}, + {"kfjb9s772f", Box{-33.6381947994, -33.638189435, 41.9063508511, 41.9063615799}}, + {"q", Box{-45.0, 0.0, 90.0, 135.0}}, + {"cx1f", Box{84.7265625, 84.90234375, -110.0390625, -109.6875}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"hdtcdg62524", Box{-75.6559753418, -75.6559740007, 30.7100191712, 30.7100205123}}, + {"fpy32", Box{88.8134765625, 88.857421875, -81.2109375, -81.1669921875}}, + {"256qs", Box{-25.576171875, -25.5322265625, -176.66015625, -176.616210938}}, + {"6rgzd89v3r48", Box{-0.0842052698135, -0.0842051021755, -73.3642389625, -73.3642386273}}, + {"c8430hd23", Box{45.2005434036, 45.200586319, -109.33280468, -109.332761765}}, + {"xtn", Box{28.125, 29.53125, 165.9375, 167.34375}}, + {"1u2s9", Box{-65.302734375, -65.2587890625, -100.502929688, -100.458984375}}, + {"5c80k35m35p", Box{-81.512144208, -81.5121428668, -11.058716923, -11.0587155819}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"u7c0rfsdjn", Box{66.1518037319, 66.1518090963, 13.0032205582, 13.003231287}}, + {"f4ce8gy", Box{61.1045837402, 61.1059570312, -87.8494262695, -87.8480529785}}, + {"pbd4y02", Box{-86.7027282715, -86.7013549805, 171.826171875, 171.827545166}}, + {"9n", Box{33.75, 39.375, -135.0, -123.75}}, + {"ztybk5c8mw", Box{77.4083697796, 77.408375144, 167.170264721, 167.17027545}}, + {"ks8pv0cv5u6j", Box{-18.3201934956, -18.320193328, 22.7222934365, 22.7222937718}}, + {"nuh", Box{-67.5, -66.09375, 129.375, 130.78125}}, + {"6khcf5xumxz", Box{-22.1723856032, -22.1723842621, -71.9715334475, -71.9715321064}}, + {"nj2", Box{-60.46875, -59.0625, 90.0, 91.40625}}, + {"mdd459nebt64", Box{-30.5797721073, -30.5797719397, 70.4752591252, 70.4752594605}}, + {"e7ujxh", Box{22.0825195312, 22.0880126953, -27.8173828125, -27.8063964844}}, + {"eb1zvy9n", Box{1.39904022217, 1.39921188354, -8.53500366211, -8.53466033936}}, + {"1huxmt", Box{-61.9793701172, -61.9738769531, -128.430175781, -128.419189453}}, + {"v4cyrgyg", Box{61.5884971619, 61.5886688232, 47.8107833862, 47.811126709}}, + {"j6h0dvut", Box{-78.6296653748, -78.6294937134, 62.0020294189, 62.0023727417}}, + {"r8j", Box{-45.0, -43.59375, 164.53125, 165.9375}}, + {"feyj018b7", Box{66.9809389114, 66.9809818268, -59.0613412857, -59.0612983704}}, + {"tw33887htf7j", Box{35.4220805503, 35.422080718, 69.2841558158, 69.2841561511}}, + {"3rbgkj8z4cy8", Box{-0.803537517786, -0.803537350148, -122.518374547, -122.518374212}}, + {"gq30gnn9dg", Box{80.3213185072, 80.3213238716, -32.2028696537, -32.2028589249}}, + {"rek4qy2", Box{-26.2889099121, -26.2875366211, 163.421630859, 163.42300415}}, + {"j8", Box{-90.0, -84.375, 67.5, 78.75}}, + {"f2rev1d7", Box{47.0741844177, 47.0743560791, -67.9803085327, -67.97996521}}, + {"rw1sg2f78x", Box{-10.4102808237, -10.4102754593, 159.755308628, 159.755319357}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"8xfw", Box{44.6484375, 44.82421875, -153.984375, -153.6328125}}, + {"fj20", Box{74.53125, 74.70703125, -90.0, -89.6484375}}, + {"m76w867pt9yj", Box{-25.5625145696, -25.562514402, 59.7809752822, 59.7809756175}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"wdb4v9vwt4ez", Box{15.9628918581, 15.9628920257, 112.749471925, 112.74947226}}, + {"m53rvw", Box{-25.3234863281, -25.3179931641, 46.9995117188, 47.0104980469}}, + {"f33", Box{52.03125, 53.4375, -77.34375, -75.9375}}, + {"0t36bun98", Box{-59.9631214142, -59.9630784988, -155.700302124, -155.700259209}}, + {"ezs1", Box{42.36328125, 42.5390625, -5.625, -5.2734375}}, + {"jb", Box{-90.0, -84.375, 78.75, 90.0}}, + {"vd0k", Box{56.953125, 57.12890625, 67.8515625, 68.203125}}, + {"39cqr9bd", Box{-34.0476608276, -34.0474891663, -110.411911011, -110.411567688}}, + {"n8r", Box{-88.59375, -87.1875, 122.34375, 123.75}}, + {"1b6", Box{-88.59375, -87.1875, -98.4375, -97.03125}}, + {"358wn7v3tch", Box{-24.2369502783, -24.2369489372, -134.014754891, -134.01475355}}, + {"d506n2v77qf", Box{17.2312764823, 17.2312778234, -89.366427362, -89.3664260209}}, + {"qb", Box{-45.0, -39.375, 123.75, 135.0}}, + {"sn", Box{33.75, 39.375, 0.0, 11.25}}, + {"5rj", Box{-50.625, -49.21875, -26.71875, -25.3125}}, + {"0y51c", Box{-55.9423828125, -55.8984375, -141.987304688, -141.943359375}}, + {"r85z8", Box{-43.681640625, -43.6376953125, 162.7734375, 162.817382812}}, + {"rk5z", Box{-21.26953125, -21.09375, 151.5234375, 151.875}}, + {"vzn2", Box{84.375, 84.55078125, 87.5390625, 87.890625}}, + {"bjvk", Box{78.046875, 78.22265625, -172.6171875, -172.265625}}, + {"b9f", Box{54.84375, 56.25, -154.6875, -153.28125}}, + {"q0u1y3mu4k", Box{-40.4660582542, -40.4660528898, 95.907651186, 95.9076619148}}, + {"r7", Box{-28.125, -22.5, 146.25, 157.5}}, + {"cv90dz31", Box{76.0653877258, 76.0655593872, -99.7215270996, -99.7211837769}}, + {"gxfeekgv9sng", Box{89.2360430025, 89.2360431701, -18.8363294676, -18.8363291323}}, + {"92t", Box{2.8125, 4.21875, -116.71875, -115.3125}}, + {"m06", Box{-43.59375, -42.1875, 47.8125, 49.21875}}, + {"n27p3f3c", Box{-87.306804657, -87.3066329956, 105.548057556, 105.548400879}}, + {"jjptjwvt2", Box{-60.9581136703, -60.958070755, 55.7961273193, 55.7961702347}}, + {"u258hxwr", Box{45.0424003601, 45.0425720215, 16.3782119751, 16.3785552979}}, + {"t47", Box{12.65625, 14.0625, 49.21875, 50.625}}, + {"e", Box{0.0, 45.0, -45.0, 0.0}}, + {"0s", Box{-67.5, -61.875, -157.5, -146.25}}, + {"4drwu7v94g3", Box{-76.1364381015, -76.1364367604, -56.758684963, -56.7586836219}}, + {"wk14pc", Box{22.8570556641, 22.8625488281, 102.996826172, 103.0078125}}, + {"w5xg", Box{20.21484375, 20.390625, 100.8984375, 101.25}}, + {"f2z2dvf7", Box{49.3387413025, 49.3389129639, -68.4307479858, -68.4304046631}}, + {"duk0212sgtp", Box{23.9579039812, 23.9579053223, -50.6241537631, -50.624152422}}, + {"h7z", Box{-68.90625, -67.5, 21.09375, 22.5}}, + {"31k02yzy90", Box{-37.8866100311, -37.8866046667, -129.331355095, -129.331344366}}, + {"vus0k", Box{70.3564453125, 70.400390625, 84.55078125, 84.5947265625}}, + {"5m0sjm4rrvf", Box{-61.1431337893, -61.1431324482, -32.8127369285, -32.8127355874}}, + {"4syd3n9", Box{-62.8500366211, -62.8486633301, -58.3140563965, -58.3126831055}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"g", Box{45.0, 90.0, -45.0, 0.0}}, + {"ntn69zcu", Box{-61.392288208, -61.3921165466, 121.368370056, 121.368713379}}, + {"vnc4m", Box{83.3642578125, 83.408203125, 46.6259765625, 46.669921875}}, + {"sy848jtk7wdj", Box{37.0329307951, 37.0329309627, 33.7573626637, 33.757362999}}, + {"ry7y", Box{-8.7890625, -8.61328125, 174.0234375, 174.375}}, + {"5k7gesef08j", Box{-65.453453064, -65.4534517229, -28.3175759017, -28.3175745606}}, + {"n5gnev", Box{-67.7362060547, -67.7307128906, 94.3835449219, 94.39453125}}, + {"kz3eeg1trf7", Box{-3.58612284064, -3.58612149954, 36.0265664756, 36.0265678167}}, + {"t55rxfr7", Box{18.2062339783, 18.2064056396, 49.9208450317, 49.9211883545}}, + {"tm9", Box{30.9375, 32.34375, 57.65625, 59.0625}}, + {"pjw", Box{-59.0625, -57.65625, 143.4375, 144.84375}}, + {"f", Box{45.0, 90.0, -90.0, -45.0}}, + {"2my", Box{-12.65625, -11.25, -160.3125, -158.90625}}, + {"3w6bwyt", Box{-9.72015380859, -9.71878051758, -108.329315186, -108.327941895}}, + {"v4s8r3q", Box{59.1133117676, 59.1146850586, 51.6549682617, 51.6563415527}}, + {"ggx8be488kyn", Box{64.8359277472, 64.8359279148, -0.677700340748, -0.677700005472}}, + {"95w", Box{19.6875, 21.09375, -126.5625, -125.15625}}, + {"9jck7spubf", Box{33.1136190891, 33.1136244535, -133.077703714, -133.077692986}}, + {"f1zfe", Box{55.283203125, 55.3271484375, -78.9697265625, -78.92578125}}, + {"p5ycnk9j", Box{-68.7048912048, -68.7047195435, 144.768218994, 144.768562317}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"zk", Box{67.5, 73.125, 146.25, 157.5}}, + {"rkq7z", Box{-20.4345703125, -20.390625, 155.346679688, 155.390625}}, + {"j5wsc3t4", Box{-69.4689559937, -69.4687843323, 54.2024230957, 54.2027664185}}, + {"56szw", Box{-74.619140625, -74.5751953125, -26.806640625, -26.7626953125}}, + {"6xp21s7", Box{-5.60165405273, -5.60028076172, -57.2346496582, -57.2332763672}}, + {"gbmymdgc5x", Box{47.520198226, 47.5202035904, -2.91706323624, -2.9170525074}}, + {"bmm935md5m", Box{74.7691994905, 74.769204855, -160.963987112, -160.963976383}}, + {"z2v55d8", Box{49.7598266602, 49.7611999512, 153.435058594, 153.436431885}}, + {"q232vgb0", Box{-43.4413146973, -43.4411430359, 103.260498047, 103.26084137}}, + {"b7kf7b5uz4", Box{63.6775839329, 63.6775892973, -161.900067329, -161.900056601}}, + {"y6", Box{56.25, 61.875, 101.25, 112.5}}, + {"3xdf2ts4d", Box{-2.38635063171, -2.38630771637, -108.605260849, -108.605217934}}, + {"0szy1551", Box{-62.2099113464, -62.2097396851, -146.553497314, -146.553153992}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"kk86s", Box{-19.248046875, -19.2041015625, 11.77734375, 11.8212890625}}, + {"tqq", Box{35.15625, 36.5625, 64.6875, 66.09375}}, + {"3gcy97b", Box{-22.7430725098, -22.7416992188, -98.7341308594, -98.7327575684}}, + {"5dnz5z1d", Box{-77.4807357788, -77.4805641174, -12.8409576416, -12.8406143188}}, + {"eumv1pe", Box{24.8263549805, 24.8277282715, -3.11599731445, -3.11462402344}}, + {"2kxmbgqtfc3", Box{-18.6579112709, -18.6579099298, -158.512682766, -158.512681425}}, + {"hc5uf211rpb", Box{-83.5397829115, -83.5397815704, 39.1239881516, 39.1239894927}}, + {"p4khbd21", Box{-76.496257782, -76.4960861206, 140.646972656, 140.647315979}}, + {"xuq", Box{23.90625, 25.3125, 177.1875, 178.59375}}, + {"gn63cf91nhf", Box{80.47779724, 80.4777985811, -41.7573997378, -41.7573983967}}, + {"5hd1nh3", Box{-64.4883728027, -64.4869995117, -41.922454834, -41.921081543}}, + {"ustjn5", Box{71.2078857422, 71.2133789062, 29.794921875, 29.8059082031}}, + {"btv2", Box{77.34375, 77.51953125, -150.1171875, -149.765625}}, + {"7ds5rkh3u1", Box{-30.3439325094, -30.343927145, -16.5503883362, -16.5503776073}}, + {"54", Box{-78.75, -73.125, -45.0, -33.75}}, + {"7sr", Box{-21.09375, -19.6875, -12.65625, -11.25}}, + {"kr552fbb", Box{-5.03860473633, -5.03843307495, 15.5027389526, 15.5030822754}}, + {"8uc453nv4qq4", Box{27.0766978338, 27.0766980015, -144.691553414, -144.691553079}}, + {"6n3m", Box{-8.96484375, -8.7890625, -88.2421875, -87.890625}}, + {"rptzf8", Box{-1.4501953125, -1.44470214844, 143.195800781, 143.206787109}}, + {"x63b", Box{12.65625, 12.83203125, 148.7109375, 149.0625}}, + {"qwj7353", Box{-10.6608581543, -10.6594848633, 119.928131104, 119.929504395}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"v6", Box{56.25, 61.875, 56.25, 67.5}}, + {"5mpwtp", Box{-60.6939697266, -60.6884765625, -22.9833984375, -22.9724121094}}, + {"ukgs", Box{72.421875, 72.59765625, 16.171875, 16.5234375}}, + {"qxyb4g6qf", Box{-1.3872385025, -1.38719558716, 122.116212845, 122.11625576}}, + {"qhww7zdb5v", Box{-18.5476416349, -18.5476362705, 99.3093574047, 99.3093681335}}, + {"wfwffcw5vd", Box{14.5547926426, 14.554798007, 133.37151289, 133.371523619}}, + {"rynp", Box{-10.01953125, -9.84375, 177.1875, 177.5390625}}, + {"fb57ykxryn82", Box{45.6852641702, 45.6852643378, -51.3948151097, -51.3948147744}}, + {"30sq", Box{-41.1328125, -40.95703125, -129.0234375, -128.671875}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"e5gjz7", Box{22.1209716797, 22.1264648438, -40.4626464844, -40.4516601562}}, + {"t6vkbx", Box{16.3421630859, 16.34765625, 63.6547851562, 63.6657714844}}, + {"3e", Box{-28.125, -22.5, -112.5, -101.25}}, + {"th", Box{22.5, 28.125, 45.0, 56.25}}, + {"7j", Box{-16.875, -11.25, -45.0, -33.75}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"1r15z2z5", Box{-49.9611854553, -49.9610137939, -122.015533447, -122.015190125}}, + {"k9e57kg2f3", Box{-35.9649842978, -35.9649789333, 26.866132021, 26.8661427498}}, + {"4w", Box{-56.25, -50.625, -67.5, -56.25}}, + {"82c582qhsx7", Box{4.83616903424, 4.83617037535, -167.324326783, -167.324325442}}, + {"qzdzkwwdc", Box{-1.50190830231, -1.50186538696, 127.823910713, 127.823953629}}, + {"6xn26p6fnr0", Box{-5.54084837437, -5.54084703326, -58.6190021038, -58.6190007627}}, + {"wm4y0xu8ee", Box{29.2223614454, 29.2223668098, 105.14549017, 105.145500898}}, + {"rke5rewv", Box{-19.0961265564, -19.095954895, 150.807609558, 150.807952881}}, + {"0bn3vwcjj", Box{-89.6544456482, -89.6544027328, -137.217650414, -137.217607498}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"red", Box{-25.3125, -23.90625, 160.3125, 161.71875}}, + {"r2", Box{-45.0, -39.375, 146.25, 157.5}}, + {"v7qptt2u5k", Box{64.6291565895, 64.6291619539, 64.9303686619, 64.9303793907}}, + {"pey", Box{-68.90625, -67.5, 165.9375, 167.34375}}, + {"r7cg5cz8", Box{-23.3692932129, -23.3691215515, 148.886032104, 148.886375427}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"qqr5mxz", Box{-9.22988891602, -9.228515625, 111.345062256, 111.346435547}}, + {"wuuvdz7x2", Box{27.7266168594, 27.7266597748, 130.555343628, 130.555386543}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"4mgwkd9yp3r5", Box{-56.5428471006, -56.542846933, -73.6276473105, -73.6276469752}}, + {"dpk6jbemhyvh", Box{41.1364542693, 41.1364544369, -83.7660782039, -83.7660778686}}, + {"768py6th4818", Box{-29.5607757568, -29.5607755892, -33.4683660418, -33.4683657065}}, + {"q8", Box{-45.0, -39.375, 112.5, 123.75}}, + {"xmgvmq9cf", Box{33.3026075363, 33.3026504517, 151.756639481, 151.756682396}}, + {"6rhhdy3", Box{-4.79965209961, -4.79827880859, -73.0027770996, -73.0014038086}}, + {"05dd6myj4xqj", Box{-69.884508457, -69.8845082894, -176.377142966, -176.377142631}}, + {"yw6", Box{80.15625, 81.5625, 115.3125, 116.71875}}, + {"6mt6h4jrw", Box{-13.6986637115, -13.6986207962, -71.1839389801, -71.1838960648}}, + {"86ymqv2s", Box{16.4211273193, 16.4212989807, -159.663619995, -159.663276672}}, + {"vygq9vr6umc", Box{84.1406701505, 84.1406714916, 83.4073568881, 83.4073582292}}, + {"89g6sp8p81", Box{10.3256946802, 10.3257000446, -152.75390625, -152.753895521}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"6st", Box{-19.6875, -18.28125, -60.46875, -59.0625}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"sk", Box{22.5, 28.125, 11.25, 22.5}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"f3pf69r61v4", Box{51.0277444124, 51.0277457535, -67.7316650748, -67.7316637337}}, + {"r9qde0cj", Box{-37.5243186951, -37.5241470337, 166.773834229, 166.774177551}}, + {"nbm5pqh", Box{-88.0334472656, -88.0320739746, 131.10534668, 131.106719971}}, + {"u7f9fh2dzpw9", Box{66.4252256043, 66.425225772, 14.8545113951, 14.8545117304}}, + {"pnzr19j29", Box{-50.7952022552, -50.7951593399, 145.268483162, 145.268526077}}, + {"3e7rf8ww4fe5", Box{-25.3526548482, -25.3526546806, -107.810775787, -107.810775451}}, + {"1x", Box{-50.625, -45.0, -112.5, -101.25}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"4h4swejxpzv", Box{-66.6912616789, -66.6912603378, -86.1908380687, -86.1908367276}}, + {"hy6gnf0u", Box{-54.3047332764, -54.304561615, 37.9148483276, 37.9151916504}}, + {"xnycujudf", Box{38.3084249496, 38.308467865, 144.67423439, 144.674277306}}, + {"t6bypmux9z54", Box{16.5563485399, 16.5563487075, 57.6295499504, 57.6295502856}}, + {"ufw1c6xp2t", Box{59.3851214647, 59.3851268291, 42.2520661354, 42.2520768642}}, + {"42jpxwe9byn", Box{-88.6456024647, -88.6456011236, -71.3843134046, -71.3843120635}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"tj0de", Box{28.564453125, 28.6083984375, 45.8349609375, 45.87890625}}, + {"e0kqd", Box{2.548828125, 2.5927734375, -38.935546875, -38.8916015625}}, + {"bzysgj0rr", Box{89.4574213028, 89.4574642181, -136.976895332, -136.976852417}}, + {"bxdp8ycgtcqt", Box{88.543546591, 88.5435467586, -154.651882276, -154.651881941}}, + {"k0kx2785", Box{-42.2995948792, -42.2994232178, 6.33911132812, 6.33945465088}}, + {"75ugg", Box{-23.2470703125, -23.203125, -38.1884765625, -38.14453125}}, + {"sbbsbv2r", Box{5.08375167847, 5.08392333984, 34.4864273071, 34.4867706299}}, + {"u7vunvq7rn", Box{66.8263041973, 66.8263095617, 19.6414518356, 19.6414625645}}, + {"w4m7uexcx", Box{13.3349132538, 13.3349561691, 97.591509819, 97.5915527344}}, + {"350g", Box{-27.59765625, -27.421875, -133.9453125, -133.59375}}, + {"p", Box{-90.0, -45.0, 135.0, 180.0}}, + {"t8w2g1m1", Box{2.95137405396, 2.95154571533, 76.4277648926, 76.4281082153}}, + {"96k738f", Box{13.2316589355, 13.2330322266, -117.704772949, -117.703399658}}, + {"c26nv174", Box{47.5999832153, 47.6001548767, -120.713653564, -120.713310242}}, + {"s67g9ehvds", Box{13.2889294624, 13.2889348269, 16.5959858894, 16.5959966183}}, + {"4ybt4sw", Box{-51.1276245117, -51.1262512207, -55.4287719727, -55.4273986816}}, + {"5jqnz9zqyk12", Box{-59.2714333534, -59.2714331858, -36.2226838991, -36.2226835638}}, + {"31d5nguy", Box{-36.0135269165, -36.0133552551, -131.884346008, -131.884002686}}, + {"m4bbcg", Box{-29.3829345703, -29.3774414062, 46.1315917969, 46.142578125}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"mkx0hzzq", Box{-19.6438980103, -19.6437263489, 66.3124465942, 66.312789917}}, + {"9bv7x0", Box{4.833984375, 4.83947753906, -93.5595703125, -93.5485839844}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"0x7pv0083h", Box{-47.8563809395, -47.8563755751, -153.060793877, -153.060783148}}, + {"ruqzw0ztgw", Box{-19.7702515125, -19.7702461481, 178.516309261, 178.51631999}}, + {"xpy5c3c", Box{44.2625427246, 44.2639160156, 143.493804932, 143.495178223}}, + {"bnn6fxqm", Box{79.2740821838, 79.2742538452, -171.09249115, -171.092147827}}, + {"fnm9u1t", Box{80.4721069336, 80.4734802246, -82.0829772949, -82.0816040039}}, + {"jfvn54", Box{-73.4655761719, -73.4600830078, 85.9130859375, 85.9240722656}}, + {"dj", Box{28.125, 33.75, -90.0, -78.75}}, + {"mxfstk2s", Box{-0.591201782227, -0.59103012085, 71.2470245361, 71.2473678589}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"4xy4p", Box{-46.0546875, -46.0107421875, -58.7548828125, -58.7109375}}, + {"cbdg47d", Box{48.3590698242, 48.3604431152, -97.2811889648, -97.2798156738}}, + {"1ddv9", Box{-74.970703125, -74.9267578125, -108.588867188, -108.544921875}}, + {"4cn07cd", Box{-84.3228149414, -84.3214416504, -47.6449584961, -47.6435852051}}, + {"dq8fjtv64n", Box{36.9460237026, 36.946029067, -77.4463176727, -77.4463069439}}, + {"gx2qc4", Box{86.9787597656, 86.9842529297, -22.1044921875, -22.0935058594}}, + {"yx", Box{84.375, 90.0, 112.5, 123.75}}, + {"44", Box{-78.75, -73.125, -90.0, -78.75}}, + {"zz679sbs", Box{86.4232635498, 86.4234352112, 171.980667114, 171.981010437}}, + {"2fdh2u769u7y", Box{-30.1666307822, -30.1666306145, -143.399997689, -143.399997354}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"11cxx", Box{-78.837890625, -78.7939453125, -132.583007812, -132.5390625}}, + {"ygf2", Box{66.09375, 66.26953125, 126.9140625, 127.265625}}, + {"m0uscc", Box{-39.9407958984, -39.9353027344, 51.4050292969, 51.416015625}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"kn2m", Box{-8.96484375, -8.7890625, 0.3515625, 0.703125}}, + {"k56d043m45", Box{-26.3539534807, -26.3539481163, 3.51742744446, 3.51743817329}}, + {"m7", Box{-28.125, -22.5, 56.25, 67.5}}, + {"pwe", Box{-53.4375, -52.03125, 161.71875, 163.125}}, + {"1j7q0hs8u", Box{-59.3892145157, -59.3891716003, -130.423336029, -130.423293114}}, + {"f264077wzn", Box{46.776856184, 46.7768615484, -75.9214067459, -75.9213960171}}, + {"2jvn", Box{-11.6015625, -11.42578125, -172.96875, -172.6171875}}, + {"4d4ghmzzsjb9", Box{-78.1897520833, -78.1897519156, -63.4352295846, -63.4352292493}}, + {"1h2n6vef0we", Box{-64.9645265937, -64.9645252526, -134.873975068, -134.873973727}}, + {"vnfc1v2yh", Box{83.1744003296, 83.1744432449, 48.9452934265, 48.9453363419}}, + {"2b5brk", Box{-44.9340820312, -44.9285888672, -140.657958984, -140.646972656}}, + {"yntb", Box{81.5625, 81.73828125, 98.0859375, 98.4375}}, + {"5fj85yy", Box{-78.7129211426, -78.7115478516, -3.34259033203, -3.34121704102}}, + {"cm36wevqn", Box{74.9923324585, 74.9923753738, -121.699075699, -121.699032784}}, + {"7hf52n", Box{-17.6770019531, -17.6715087891, -42.1875, -42.1765136719}}, + {"dh1sz", Box{23.3349609375, 23.37890625, -87.5830078125, -87.5390625}}, + {"h", Box{-90.0, -45.0, 0.0, 45.0}}, + {"7ny5k0k28", Box{-6.4585018158, -6.45845890045, -36.3808822632, -36.3808393478}}, + {"w7", Box{16.875, 22.5, 101.25, 112.5}}, + {"f9j0296wjz", Box{50.6768792868, 50.6768846512, -60.443097353, -60.4430866241}}, + {"v316n9gu7y5", Box{50.9869372845, 50.9869386256, 58.2987718284, 58.2987731695}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"0jt97bxt", Box{-58.8391685486, -58.8389968872, -172.090530396, -172.090187073}}, + {"cfdbvrc", Box{59.236907959, 59.23828125, -97.1507263184, -97.1493530273}}, + {"95d", Box{19.6875, 21.09375, -132.1875, -130.78125}}, + {"my", Box{-11.25, -5.625, 78.75, 90.0}}, + {"5t", Box{-61.875, -56.25, -22.5, -11.25}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"k2j", Box{-45.0, -43.59375, 18.28125, 19.6875}}, + {"rjygh", Box{-12.12890625, -12.0849609375, 144.66796875, 144.711914062}}, + {"xs79f", Box{24.2138671875, 24.2578125, 162.509765625, 162.553710938}}, + {"nmz3x305vedq", Box{-57.3864214495, -57.3864212818, 111.764155068, 111.764155403}}, + {"u1hq", Box{51.6796875, 51.85546875, 5.9765625, 6.328125}}, + {"2v8jpgs92zxm", Box{-13.1641120277, -13.1641118601, -145.903202109, -145.903201774}}, + {"f54n04uq", Box{62.9458236694, 62.9459953308, -87.1816635132, -87.1813201904}}, + {"8zkv13p8", Box{41.6656494141, 41.6658210754, -139.505081177, -139.504737854}}, + {"z03j6ktm2", Box{47.354722023, 47.3547649384, 136.512336731, 136.512379646}}, + {"qd266u", Box{-31.9262695312, -31.9207763672, 112.972412109, 112.983398438}}, + {"783q5", Box{-42.5390625, -42.4951171875, -20.6103515625, -20.56640625}}, + {"ynqe7fy5s9m6", Box{80.7432531193, 80.7432532869, 99.3138598278, 99.3138601631}}, + {"pp", Box{-50.625, -45.0, 135.0, 146.25}}, + {"bd", Box{56.25, 61.875, -157.5, -146.25}}, + {"8573xzmt2qy", Box{18.5856847465, 18.5856860876, -175.081539452, -175.081538111}}, + {"gwhbzrftu0", Box{78.9253950119, 78.9254003763, -15.4981040955, -15.4980933666}}, + {"h42w9e805", Box{-76.1819458008, -76.1819028854, 0.769171714783, 0.769214630127}}, + {"uzbhd66135d1", Box{89.397358764, 89.3973589316, 33.8516691327, 33.851669468}}, + {"zf", Box{56.25, 61.875, 168.75, 180.0}}, + {"6q2r", Box{-8.61328125, -8.4375, -78.3984375, -78.046875}}, + {"qtxy7v4w9", Box{-12.9352855682, -12.9352426529, 123.566708565, 123.56675148}}, + {"58", Box{-90.0, -84.375, -22.5, -11.25}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"8u9qjb5qw", Box{26.368303299, 26.3683462143, -144.234781265, -144.23473835}}, + {"48sx", Box{-85.95703125, -85.78125, -61.171875, -60.8203125}}, + {"690tdt6", Box{-38.3793640137, -38.3779907227, -66.6842651367, -66.6828918457}}, + {"qm8", Box{-14.0625, -12.65625, 101.25, 102.65625}}, + {"2mj", Box{-16.875, -15.46875, -161.71875, -160.3125}}, + {"3e5", Box{-28.125, -26.71875, -108.28125, -106.875}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"f6dg1tndqxy", Box{59.6177373827, 59.6177387238, -74.8076811433, -74.8076798022}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"9f3eqkhmf", Box{13.2504987717, 13.250541687, -98.8600444794, -98.860001564}}, + {"yuqghk1x5z", Box{69.4568055868, 69.4568109512, 133.431175947, 133.431186676}}, + {"w30rynrjmy", Box{7.02257037163, 7.02257573605, 101.875094175, 101.875104904}}, + {"m25ergh6", Box{-44.4118881226, -44.4117164612, 61.5182876587, 61.5186309814}}, + {"jqznhq", Box{-50.9436035156, -50.9381103516, 66.2805175781, 66.2915039062}}, + {"3u4", Box{-22.5, -21.09375, -98.4375, -97.03125}}, + {"fzr", Box{85.78125, 87.1875, -46.40625, -45.0}}, + {"je", Box{-73.125, -67.5, 67.5, 78.75}}, + {"pmztf5q7e7d4", Box{-56.6270351037, -56.6270349361, 156.893490851, 156.893491186}}, + {"xdknsz", Box{13.8372802734, 13.8427734375, 163.333740234, 163.344726562}}, + {"736h1c7d5h", Box{-37.2583937645, -37.2583884001, -30.8556604385, -30.8556497097}}, + {"c57pmmv9u", Box{64.5875501633, 64.5875930786, -130.542812347, -130.542769432}}, + {"80qnjsh", Box{2.48291015625, 2.48428344727, -171.315307617, -171.313934326}}, + {"kp", Box{-5.625, 0.0, 0.0, 11.25}}, + {"u6yufbthbdw", Box{61.3072863221, 61.3072876632, 20.8699330688, 20.8699344099}}, + {"c4yj93f16ys", Box{61.4454093575, 61.4454106987, -126.504698396, -126.504697055}}, + {"ygkp", Box{64.51171875, 64.6875, 129.375, 129.7265625}}, + {"h1ypf", Box{-78.7939453125, -78.75, 8.525390625, 8.5693359375}}, + {"hz76my5h4qe", Box{-48.7895616889, -48.7895603478, 38.5772185028, 38.5772198439}}, + {"zh1cw", Box{67.763671875, 67.8076171875, 137.724609375, 137.768554688}}, + {"00", Box{-90.0, -84.375, -180.0, -168.75}}, + {"h9be5u0k", Box{-79.6062469482, -79.6060752869, 23.3682632446, 23.3686065674}}, + {"4btv", Box{-86.30859375, -86.1328125, -48.1640625, -47.8125}}, + {"42hz7pm83dt", Box{-88.6857041717, -88.6857028306, -71.9308523834, -71.9308510423}}, + {"qv49", Box{-16.69921875, -16.5234375, 127.265625, 127.6171875}}, + {"0nwzwd", Box{-52.1081542969, -52.1026611328, -170.222167969, -170.211181641}}, + {"jhkmc", Box{-65.0830078125, -65.0390625, 51.0205078125, 51.064453125}}, + {"sysens0py", Box{37.1131467819, 37.1131896973, 40.3640270233, 40.3640699387}}, + {"5q792kbf", Box{-54.5975875854, -54.5974159241, -28.8161087036, -28.8157653809}}, + {"624gbe", Box{-44.3243408203, -44.3188476562, -74.8608398438, -74.8498535156}}, + {"gtkjfqg", Box{75.5790710449, 75.5804443359, -16.7720031738, -16.7706298828}}, + {"nv4", Box{-61.875, -60.46875, 126.5625, 127.96875}}, + {"dcwv6uu0", Box{9.3864440918, 9.38661575317, -46.6314697266, -46.6311264038}}, + {"vvgtyfv9", Box{78.36977005, 78.3699417114, 83.97605896, 83.9764022827}}, + {"53rjpqr6zt9", Box{-82.0550099015, -82.0550085604, -23.5773669183, -23.5773655772}}, + {"vmyp4dnemp6", Box{78.5858018696, 78.5858032107, 64.8065069318, 64.8065082729}}, + {"t9xqjxjpv", Box{9.53197002411, 9.53201293945, 77.9440927505, 77.9441356659}}, + {"sby32e", Box{4.45495605469, 4.46044921875, 42.5610351562, 42.5720214844}}, + {"sjfgy", Box{33.0029296875, 33.046875, 4.130859375, 4.1748046875}}, + {"k7q0z2b2du", Box{-26.5826869011, -26.5826815367, 20.0065648556, 20.0065755844}}, + {"nt", Box{-61.875, -56.25, 112.5, 123.75}}, + {"1", Box{-90.0, -45.0, -135.0, -90.0}}, + {"mpfpfd32e", Box{-0.0314998626709, -0.0314569473267, 47.9242086411, 47.9242515564}}, + {"hqjqn", Box{-55.1953125, -55.1513671875, 18.896484375, 18.9404296875}}, + {"9q7chj6u2uw", Box{35.3616240621, 35.3616254032, -118.296964467, -118.296963125}}, + {"0wsf47v9c", Box{-53.0650377274, -53.064994812, -150.713839531, -150.713796616}}, + {"kdv72env8xke", Box{-28.9424979128, -28.9424977452, 29.9140823632, 29.9140826985}}, + {"trfx", Box{44.82421875, 45.0, 59.765625, 60.1171875}}, + {"02uttm", Box{-84.7869873047, -84.7814941406, -162.191162109, -162.180175781}}, + {"hhjgb5s3vv39", Box{-66.8212655, -66.8212653324, 8.0920227617, 8.09202309698}}, + {"r16", Box{-37.96875, -36.5625, 137.8125, 139.21875}}, + {"4xy44eer4t3", Box{-46.0342316329, -46.0342302918, -58.9480648935, -58.9480635524}}, + {"8b", Box{0.0, 5.625, -146.25, -135.0}}, + {"zd", Box{56.25, 61.875, 157.5, 168.75}}, + {"z0x", Box{47.8125, 49.21875, 144.84375, 146.25}}, + {"4967", Box{-82.44140625, -82.265625, -64.3359375, -63.984375}}, + {"2vf4", Box{-12.3046875, -12.12890625, -143.4375, -143.0859375}}, + {"tzp3t0rtg", Box{39.6410322189, 39.6410751343, 89.1754674911, 89.1755104065}}, + {"75yry", Box{-22.5439453125, -22.5, -35.947265625, -35.9033203125}}, + {"bdgtu", Box{61.4794921875, 61.5234375, -152.40234375, -152.358398438}}, + {"u1", Box{50.625, 56.25, 0.0, 11.25}}, + {"rz2bgp7hds", Box{-4.04629468918, -4.04628932476, 169.940750599, 169.940761328}}, + {"g", Box{45.0, 90.0, -45.0, 0.0}}, + {"psptppx32e", Box{-66.5796643496, -66.5796589851, 168.364470005, 168.364480734}}, + {"gshfctpwf1", Box{68.0120283365, 68.0120337009, -15.7440090179, -15.7439982891}}, + {"3yq", Box{-9.84375, -8.4375, -92.8125, -91.40625}}, + {"685zv36e0fd2", Box{-43.6303004622, -43.6303002946, -61.9923811778, -61.9923808426}}, + {"7gf5fd", Box{-23.2360839844, -23.2305908203, -8.32763671875, -8.31665039062}}, + {"bmmzfq", Box{75.9265136719, 75.9320068359, -160.565185547, -160.554199219}}, + {"m40", Box{-33.75, -32.34375, 45.0, 46.40625}}, + {"tx45501g7v", Box{39.9029284716, 39.902933836, 70.4469001293, 70.4469108582}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"ej054jn", Box{28.6798095703, 28.6811828613, -44.9038696289, -44.9024963379}}, + {"n1g7d", Box{-79.541015625, -79.4970703125, 94.658203125, 94.7021484375}}, + {"nn6ejehuf", Box{-54.2991113663, -54.2990684509, 93.7639331818, 93.7639760971}}, + {"qs8e93xwrrc", Box{-19.0629114211, -19.06291008, 113.268668801, 113.268670142}}, + {"f2", Box{45.0, 50.625, -78.75, -67.5}}, + {"gm", Box{73.125, 78.75, -33.75, -22.5}}, + {"npp4rnm", Box{-50.1951599121, -50.1937866211, 100.158233643, 100.159606934}}, + {"6t", Box{-16.875, -11.25, -67.5, -56.25}}, + {"2f4fe3d5", Box{-33.3017921448, -33.3016204834, -142.237243652, -142.23690033}}, + {"s7r00k0196", Box{18.3034908772, 18.3034962416, 21.1047899723, 21.1048007011}}, + {"st084", Box{28.125, 28.1689453125, 23.291015625, 23.3349609375}}, + {"p6f3bv9f1", Box{-74.1930770874, -74.1930341721, 149.449467659, 149.449510574}}, + {"fgk5j", Box{63.80859375, 63.8525390625, -50.4052734375, -50.361328125}}, + {"yeu22jjtp", Box{66.1660194397, 66.166062355, 118.484416008, 118.484458923}}, + {"3bcn7gkusn25", Box{-39.6639578976, -39.6639577299, -99.6722602844, -99.6722599491}}, + {"1", Box{-90.0, -45.0, -135.0, -90.0}}, + {"6q4dh71sqn", Box{-10.8811962605, -10.881190896, -75.0452899933, -75.0452792645}}, + {"p", Box{-90.0, -45.0, 135.0, 180.0}}, + {"6uy2kbef9yg", Box{-18.2340927422, -18.2340914011, -47.2469682992, -47.246966958}}, + {"58j", Box{-90.0, -88.59375, -15.46875, -14.0625}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"mh6x", Box{-19.86328125, -19.6875, 48.515625, 48.8671875}}, + {"cgq4xrjz", Box{63.7603569031, 63.7605285645, -92.486000061, -92.4856567383}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"ppqg570sk3n", Box{-48.6741918325, -48.6741904914, 144.635886848, 144.635888189}}, + {"1suyu1k", Box{-62.0878601074, -62.0864868164, -105.639038086, -105.637664795}}, + {"xm4xkzsnvux", Box{29.4417956471, 29.4417969882, 149.980114549, 149.980115891}}, + {"8p3xj", Box{42.01171875, 42.0556640625, -177.670898438, -177.626953125}}, + {"ef92nkk", Box{14.0858459473, 14.0872192383, -9.21203613281, -9.2106628418}}, + {"qnrf101", Box{-9.4921875, -9.49081420898, 100.943756104, 100.945129395}}, + {"2qt", Box{-8.4375, -7.03125, -161.71875, -160.3125}}, + {"c2q7e", Box{47.021484375, 47.0654296875, -114.829101562, -114.78515625}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"j6t0bwpjpusg", Box{-75.7718221284, -75.7718219608, 63.3131746575, 63.3131749928}}, + {"wrq", Box{40.78125, 42.1875, 109.6875, 111.09375}}, + {"xvf2jk", Box{32.3657226562, 32.3712158203, 172.144775391, 172.155761719}}, + {"xy0p5y", Box{35.0134277344, 35.0189208984, 168.914794922, 168.92578125}}, + {"bsh9xbd", Box{67.766418457, 67.767791748, -150.828552246, -150.827178955}}, + {"g675yc", Box{58.3209228516, 58.3264160156, -29.2346191406, -29.2236328125}}, + {"dkrnq7", Box{25.0213623047, 25.0268554688, -68.6315917969, -68.6206054688}}, + {"6q4uk3dyk5", Box{-10.4936009645, -10.4935956001, -74.6920967102, -74.6920859814}}, + {"t58tp1kxb54p", Box{20.5746203475, 20.5746205151, 46.0169246793, 46.0169250146}}, + {"bbw73yzeuy", Box{48.4215438366, 48.421549201, -137.373529673, -137.373518944}}, + {"gnq", Box{80.15625, 81.5625, -36.5625, -35.15625}}, + {"3j", Box{-16.875, -11.25, -135.0, -123.75}}, + {"7dx2c", Box{-30.8056640625, -30.76171875, -12.2607421875, -12.216796875}}, + {"vn9", Box{81.5625, 82.96875, 46.40625, 47.8125}}, + {"4kj", Box{-67.5, -66.09375, -71.71875, -70.3125}}, + {"cuvj4trg5nb8", Box{72.6270465553, 72.6270467229, -94.0981142968, -94.0981139615}}, + {"uetmbuswe", Box{65.7240772247, 65.7241201401, 29.92208004, 29.9221229553}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"f", Box{45.0, 90.0, -90.0, -45.0}}, + {"jg", Box{-73.125, -67.5, 78.75, 90.0}}, + {"ycz", Box{54.84375, 56.25, 133.59375, 135.0}}, + {"pevtd", Box{-67.939453125, -67.8955078125, 165.322265625, 165.366210938}}, + {"gf7fm3hmb8", Box{58.0582380295, 58.0582433939, -5.73999166489, -5.73998093605}}, + {"w7zwjnh24qd", Box{22.1814313531, 22.1814326942, 112.022537291, 112.022538632}}, + {"nfesgy", Box{-75.0695800781, -75.0640869141, 128.836669922, 128.84765625}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"efq0q0dg0g0n", Box{12.7034739777, 12.7034741454, -2.5450193882, -2.54501905292}}, + {"kkucr8pk", Box{-18.060836792, -18.0606651306, 18.2692337036, 18.2695770264}}, + {"5zdg4zvz", Box{-47.2413825989, -47.2412109375, -7.25406646729, -7.25372314453}}, + {"fuw", Box{70.3125, 71.71875, -47.8125, -46.40625}}, + {"x51mnftp8", Box{17.7689266205, 17.7689695358, 137.061309814, 137.06135273}}, + {"y0", Box{45.0, 50.625, 90.0, 101.25}}, + {"ndufku4sr", Box{-74.1130399704, -74.1129970551, 119.392161369, 119.392204285}}, + {"ydwndhywhg8", Box{60.232219398, 60.2322207391, 121.034520864, 121.034522206}}, + {"gj6ehkq0", Box{75.0819396973, 75.0821113586, -41.2893676758, -41.289024353}}, + {"m3hfct0", Box{-38.8641357422, -38.8627624512, 62.9956054688, 62.9969787598}}, + {"6745yupp70qu", Box{-27.4426010996, -27.442600932, -75.631118305, -75.6311179698}}, + {"d7b0m9dzj213", Box{21.1471368559, 21.1471370235, -78.504297249, -78.5042969137}}, + {"py", Box{-56.25, -50.625, 168.75, 180.0}}, + {"4vhrpypw", Box{-60.6105422974, -60.610370636, -49.9225616455, -49.9222183228}}, + {"xwyyj1xz5kzw", Box{39.0329053625, 39.0329055302, 167.222706601, 167.222706936}}, + {"18ht0j2w8ste", Box{-89.0911141969, -89.0911140293, -106.171159521, -106.171159185}}, + {"vynwqurve", Box{79.8729228973, 79.8729658127, 88.1980276108, 88.1980705261}}, + {"s77hhn", Box{19.0173339844, 19.0228271484, 15.64453125, 15.6555175781}}, + {"hj66tgs86", Box{-60.0100278854, -60.0099849701, 3.42301368713, 3.42305660248}}, + {"e5nh4k", Box{17.6000976562, 17.6055908203, -36.4636230469, -36.4526367188}}, + {"jk", Box{-67.5, -61.875, 56.25, 67.5}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"f0p3v5", Box{45.3240966797, 45.3295898438, -79.5849609375, -79.5739746094}}, + {"numc175r1", Box{-65.9002876282, -65.9002447128, 131.895375252, 131.895418167}}, + {"7pc", Box{-1.40625, 0.0, -43.59375, -42.1875}}, + {"b7qw82mfqc4", Box{64.4255930185, 64.4255943596, -159.590199888, -159.590198547}}, + {"qfe", Box{-30.9375, -29.53125, 127.96875, 129.375}}, + {"mw9kj6nrue61", Box{-7.72204069421, -7.72204052657, 69.4973042607, 69.497304596}}, + {"6en5d6psj", Box{-27.4980926514, -27.498049736, -58.9531087875, -58.9530658722}}, + {"mk80", Box{-19.6875, -19.51171875, 56.25, 56.6015625}}, + {"d2fbpmpyjv", Box{4.24727261066, 4.24727797508, -74.5533192158, -74.5533084869}}, + {"pf84wbwguh9", Box{-75.4946324229, -75.4946310818, 169.056073576, 169.056074917}}, + {"ncj8287", Box{-84.3296813965, -84.3283081055, 131.510467529, 131.51184082}}, + {"smd4t", Box{31.376953125, 31.4208984375, 14.2822265625, 14.326171875}}, + {"4ryj3jjxrd", Box{-45.4546773434, -45.454671979, -70.2606797218, -70.260668993}}, + {"udffsxnn8", Box{60.9477710724, 60.9478139877, 26.5731811523, 26.5732240677}}, + {"cub7vr", Box{72.4163818359, 72.421875, -100.667724609, -100.656738281}}, + {"y7c6s4", Box{66.5441894531, 66.5496826172, 103.18359375, 103.194580078}}, + {"t253", Box{0.17578125, 0.3515625, 60.8203125, 61.171875}}, + {"1e2bhmk9ybw", Box{-71.6896077991, -71.6896064579, -111.252067387, -111.252066046}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"99r75", Box{7.55859375, 7.6025390625, -102.172851562, -102.12890625}}, + {"knbr2kzz1791", Box{-5.72952283546, -5.72952266783, 0.373246818781, 0.373247154057}}, + {"v8h", Box{45.0, 46.40625, 73.125, 74.53125}}, + {"sm6xvf3bc", Box{30.9060430527, 30.906085968, 15.0207567215, 15.0207996368}}, + {"vu", Box{67.5, 73.125, 78.75, 90.0}}, + {"w56htc6", Box{19.0791320801, 19.0805053711, 93.0679321289, 93.0693054199}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"5hm57j8", Box{-65.4922485352, -65.4908752441, -37.8369140625, -37.8355407715}}, + {"k8qwr0e", Box{-42.4923706055, -42.4909973145, 31.9523620605, 31.9537353516}}, + {"716e0", Box{-37.44140625, -37.3974609375, -41.484375, -41.4404296875}}, + {"wz71b", Box{41.0888671875, 41.1328125, 127.96875, 128.012695312}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"89x2", Box{8.4375, 8.61328125, -147.3046875, -146.953125}}, + {"rcr37p", Box{-37.7105712891, -37.705078125, 179.077148438, 179.088134766}}, + {"4xzjc7vmstr", Box{-45.3739361465, -45.3739348054, -57.5939060748, -57.5939047337}}, + {"tv07ndf8", Box{28.6674499512, 28.6676216125, 79.3906402588, 79.3909835815}}, + {"qb2z", Box{-42.36328125, -42.1875, 124.8046875, 125.15625}}, + {"xjq0fjferr", Box{29.6952670813, 29.6952724457, 143.529134989, 143.529145718}}, + {"zwn6", Box{79.1015625, 79.27734375, 166.2890625, 166.640625}}, + {"7xc", Box{-1.40625, 0.0, -21.09375, -19.6875}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"vw4sy8z", Box{79.5890808105, 79.5904541016, 71.3108825684, 71.3122558594}}, + {"djg8s3pre", Box{32.4384212494, 32.4384641647, -84.881272316, -84.8812294006}}, + {"vpn8t", Box{84.462890625, 84.5068359375, 54.3603515625, 54.404296875}}, + {"1sse8x6", Box{-64.0324401855, -64.0310668945, -106.147155762, -106.145782471}}, + {"snm4", Box{35.5078125, 35.68359375, 7.03125, 7.3828125}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"5rb", Box{-46.40625, -45.0, -33.75, -32.34375}}, + {"q7", Box{-28.125, -22.5, 101.25, 112.5}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"hn5sw9rbvjk3", Box{-55.4519608431, -55.4519606754, 5.21838281304, 5.21838314831}}, + {"y0f7w5tep1f", Box{49.8537348211, 49.8537361622, 93.4355905652, 93.4355919063}}, + {"xts0uk", Box{31.0913085938, 31.0968017578, 163.311767578, 163.322753906}}, + {"ftybqfey", Box{77.4024581909, 77.4026298523, -57.7060317993, -57.7056884766}}, + {"eqvhf", Box{38.8037109375, 38.84765625, -26.630859375, -26.5869140625}}, + {"ctpbp73", Box{73.1428527832, 73.1442260742, -101.281585693, -101.280212402}}, + {"15czhy35", Box{-67.6409339905, -67.6407623291, -132.328948975, -132.328605652}}, + {"1", Box{-90.0, -45.0, -135.0, -90.0}}, + {"vk6mwwrysbf", Box{69.9084989727, 69.9085003138, 59.7105565667, 59.7105579078}}, + {"4yfcbqvevqy", Box{-51.6858740151, -51.685872674, -52.3640397191, -52.364038378}}, + {"d6qm1q41g", Box{13.5684156418, 13.5684585571, -69.9031305313, -69.903087616}}, + {"kjqew1cty", Box{-14.842915535, -14.8428726196, 9.40661430359, 9.40665721893}}, + {"hf9zn39ewerv", Box{-74.6981724165, -74.6981722489, 36.4879449829, 36.4879453182}}, + {"1j", Box{-61.875, -56.25, -135.0, -123.75}}, + {"u41", Box{56.25, 57.65625, 1.40625, 2.8125}}, + {"pd8sbu5sk", Box{-75.0798368454, -75.0797939301, 158.241062164, 158.24110508}}, + {"k7", Box{-28.125, -22.5, 11.25, 22.5}}, + {"fx6xcm", Box{87.1710205078, 87.1765136719, -63.9294433594, -63.9184570312}}, + {"k1nwc4mun", Box{-38.1754302979, -38.1753873825, 9.19272422791, 9.19276714325}}, + {"nechx1mg", Box{-68.1078529358, -68.1076812744, 114.221763611, 114.222106934}}, + {"8et6dbj4g", Box{20.1274251938, 20.1274681091, -149.98934269, -149.989299774}}, + {"7e", Box{-28.125, -22.5, -22.5, -11.25}}, + {"vqcthybtw0", Box{83.885679245, 83.8856846094, 58.5690593719, 58.5690701008}}, + {"r6qdv32n", Box{-31.8524551392, -31.8522834778, 155.621337891, 155.621681213}}, + {"tbhh", Box{0.703125, 0.87890625, 84.375, 84.7265625}}, + {"0c5fpu", Box{-84.0014648438, -83.9959716797, -140.635986328, -140.625}}, + {"7b", Box{-45.0, -39.375, -11.25, 0.0}}, + {"9vzmkfvug", Box{33.2825231552, 33.2825660706, -90.8379220963, -90.8378791809}}, + {"68t", Box{-42.1875, -40.78125, -60.46875, -59.0625}}, + {"ef1szshm45h", Box{12.1078079939, 12.107809335, -8.80510747433, -8.80510613322}}, + {"21dgj4", Box{-36.0241699219, -36.0186767578, -175.913085938, -175.902099609}}, + {"109q9yt", Box{-86.0092163086, -86.0078430176, -133.158416748, -133.157043457}}, + {"nhj3b9vc", Box{-67.182598114, -67.1824264526, 97.4126815796, 97.4130249023}}, + {"nye5uzgb", Box{-52.735748291, -52.7355766296, 128.182640076, 128.182983398}}, + {"dhz5f", Box{27.3779296875, 27.421875, -80.068359375, -80.0244140625}}, + {"g1verehd", Box{55.4318618774, 55.4320335388, -36.9298553467, -36.9295120239}}, + {"jtr", Box{-60.46875, -59.0625, 77.34375, 78.75}}, + {"m5nbruj", Box{-28.0590820312, -28.0577087402, 54.839630127, 54.841003418}}, + {"p", Box{-90.0, -45.0, 135.0, 180.0}}, + {"h", Box{-90.0, -45.0, 0.0, 45.0}}, + {"bm3gr", Box{75.1025390625, 75.146484375, -165.981445312, -165.9375}}, + {"e7my1m0qp", Box{19.3644332886, 19.3644762039, -25.6084871292, -25.6084442139}}, + {"fzue", Box{89.12109375, 89.296875, -49.921875, -49.5703125}}, + {"q70", Box{-28.125, -26.71875, 101.25, 102.65625}}, + {"sjeed", Box{31.552734375, 31.5966796875, 5.009765625, 5.0537109375}}, + {"cvsuyyw", Box{76.8081665039, 76.8095397949, -94.2654418945, -94.2640686035}}, + {"7dnp", Box{-32.51953125, -32.34375, -14.0625, -13.7109375}}, + {"tf9kr1u", Box{14.8191833496, 14.8205566406, 80.8209228516, 80.8222961426}}, + {"j38nduwqew", Box{-80.3940546513, -80.3940492868, 56.3795828819, 56.3795936108}}, + {"444y82r", Box{-77.606048584, -77.604675293, -86.1122131348, -86.1108398438}}, + {"1rwzsww", Box{-46.4584350586, -46.4570617676, -114.051818848, -114.050445557}}, + {"98vu", Box{4.921875, 5.09765625, -104.4140625, -104.0625}}, + {"f0hu79k2y84", Box{45.7540655136, 45.7540668547, -83.1603857875, -83.1603844464}}, + {"35399zm2gnn", Box{-26.415091753, -26.4150904119, -132.806374133, -132.806372792}}, + {"qzxy", Box{-1.7578125, -1.58203125, 134.6484375, 135.0}}, + {"7gpr25", Box{-26.8341064453, -26.8286132812, -1.0546875, -1.04370117188}}, + {"xucdp", Box{27.0703125, 27.1142578125, 171.166992188, 171.2109375}}, + {"db3mpnz89zq", Box{2.32235983014, 2.32236117125, -54.1741874814, -54.1741861403}}, + {"p", Box{-90.0, -45.0, 135.0, 180.0}}, + {"94m52", Box{13.2275390625, 13.271484375, -127.96875, -127.924804688}}, + {"u7ucp", Box{66.26953125, 66.3134765625, 18.2373046875, 18.28125}}, + {"81qq43p", Box{8.09143066406, 8.09280395508, -171.10244751, -171.101074219}}, + {"f80w8", Box{46.142578125, 46.1865234375, -66.796875, -66.7529296875}}, + {"8j5z", Box{29.35546875, 29.53125, -174.7265625, -174.375}}, + {"56q", Box{-77.34375, -75.9375, -25.3125, -23.90625}}, + {"b72vvhj", Box{64.3139648438, 64.3153381348, -167.468719482, -167.467346191}}, + {"5j", Box{-61.875, -56.25, -45.0, -33.75}}, + {"42hm9tj0rn", Box{-89.0056622028, -89.0056568384, -72.7003526688, -72.7003419399}}, + {"cbxx9q2c89", Box{49.1654545069, 49.1654598713, -90.6471419334, -90.6471312046}}, + {"43", Box{-84.375, -78.75, -78.75, -67.5}}, + {"rvmw", Box{-14.4140625, -14.23828125, 176.484375, 176.8359375}}, + {"jwmeyr4hj7", Box{-54.1454154253, -54.1454100609, 75.5120050907, 75.5120158195}}, + {"b3y", Box{54.84375, 56.25, -160.3125, -158.90625}}, + {"4y3n0e7u8j", Box{-53.7704104185, -53.7704050541, -54.8166275024, -54.8166167736}}, + {"m0k0x", Box{-43.505859375, -43.4619140625, 50.9326171875, 50.9765625}}, + {"2zc1v219ev8z", Box{-1.09834464267, -1.09834447503, -144.610815234, -144.610814899}}, + {"3rvj3ezyffez", Box{-0.46162577346, -0.461625605822, -116.64206598, -116.642065644}}, + {"35bdpq", Box{-23.5217285156, -23.5162353516, -133.978271484, -133.967285156}}, + {"qdqrzp2e7b", Box{-30.9410619736, -30.9410566092, 121.597527266, 121.597537994}}, + {"vmrsrejf", Box{75.2951431274, 75.2953147888, 67.1343612671, 67.1347045898}}, + {"up", Box{84.375, 90.0, 0.0, 11.25}}, + {"bzy", Box{88.59375, 90.0, -137.8125, -136.40625}}, + {"3rnm42gs62", Box{-4.7412443161, -4.74123895168, -114.857157469, -114.85714674}}, + {"yhekty3621c", Box{71.1382435262, 71.1382448673, 94.8247160017, 94.8247173429}}, + {"ektx", Box{26.54296875, 26.71875, -26.015625, -25.6640625}}, + {"9nxkb4u9f6", Box{37.4128782749, 37.4128836393, -124.798411131, -124.798400402}}, + {"fg", Box{61.875, 67.5, -56.25, -45.0}}, + {"66e4x10k68", Box{-30.4918241501, -30.4918187857, -74.2231822014, -74.2231714725}}, + {"me", Box{-28.125, -22.5, 67.5, 78.75}}, + {"r385f5q9", Box{-35.8852958679, -35.8851242065, 146.346817017, 146.347160339}}, + {"xbdc8wgn0", Box{3.11428070068, 3.11432361603, 172.643280029, 172.643322945}}, + {"74s", Box{-30.9375, -29.53125, -39.375, -37.96875}}, + {"dg8t7", Box{20.6103515625, 20.654296875, -55.4150390625, -55.37109375}}, + {"nf7", Box{-77.34375, -75.9375, 127.96875, 129.375}}, + {"6nzfqpxy", Box{-6.59351348877, -6.59334182739, -78.8272476196, -78.8269042969}}, + {"0ux06p9jktht", Box{-64.6014270745, -64.6014269069, -136.31678693, -136.316786595}}, + {"nb8pxznpjh", Box{-85.8294653893, -85.8294600248, 124.099030495, 124.099041224}}, + {"6qks2x97hzm", Box{-9.05492708087, -9.05492573977, -72.3979751766, -72.3979738355}}, + {"us6qrd43em", Box{70.0161534548, 70.0161588192, 25.9968817234, 25.9968924522}}, + {"tp2eh", Box{41.30859375, 41.3525390625, 45.87890625, 45.9228515625}}, + {"vcgf16q2", Box{55.2076721191, 55.2078437805, 84.0869522095, 84.0872955322}}, + {"qt15nkc82kz", Box{-16.3214953244, -16.3214939833, 114.182988256, 114.182989597}}, + {"t6t", Box{14.0625, 15.46875, 63.28125, 64.6875}}, + {"yx53b3kj32", Box{84.6903848648, 84.6903902292, 117.086845636, 117.086856365}}, + {"twqdxev4cx", Box{35.6168121099, 35.6168174744, 76.9771456718, 76.9771564007}}, + {"p", Box{-90.0, -45.0, 135.0, 180.0}}, + {"4gty084hgddy", Box{-69.2569826916, -69.2569825239, -48.13918937, -48.1391890347}}, + {"c7", Box{61.875, 67.5, -123.75, -112.5}}, + {"ywffc641", Box{83.463306427, 83.4634780884, 116.424865723, 116.425209045}}, + {"k0km", Box{-42.71484375, -42.5390625, 5.9765625, 6.328125}}, + {"17k4fg", Box{-71.2188720703, -71.2133789062, -118.004150391, -117.993164062}}, + {"9fr", Box{12.65625, 14.0625, -91.40625, -90.0}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"h08scsx", Box{-86.3278198242, -86.3264465332, 0.778656005859, 0.780029296875}}, + {"8f8nq48", Box{15.1748657227, 15.1762390137, -145.986328125, -145.984954834}}, + {"hecr6qx49k0", Box{-67.59567976, -67.5956784189, 24.3663561344, 24.3663574755}}, + {"jn", Box{-56.25, -50.625, 45.0, 56.25}}, + {"qwx7j", Box{-7.91015625, -7.8662109375, 122.915039062, 122.958984375}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"wxcj4", Box{44.47265625, 44.5166015625, 113.994140625, 114.038085938}}, + {"gw63h", Box{80.33203125, 80.3759765625, -19.16015625, -19.1162109375}}, + {"hp7b6f27tq6p", Box{-49.1618095525, -49.1618093848, 5.3948584199, 5.39485875517}}, + {"kd", Box{-33.75, -28.125, 22.5, 33.75}}, + {"fbweu", Box{48.4716796875, 48.515625, -46.93359375, -46.8896484375}}, + {"m1fcuue7nm1g", Box{-34.8233712651, -34.8233710974, 49.080661498, 49.0806618333}}, + {"h9j", Box{-84.375, -82.96875, 29.53125, 30.9375}}, + {"n9d3g5uv", Box{-81.2334251404, -81.233253479, 115.80242157, 115.802764893}}, + {"nhpp1spf", Box{-66.247215271, -66.2470436096, 99.9203109741, 99.9206542969}}, + {"7jg2w13b23h", Box{-12.5614446402, -12.5614432991, -40.1635962725, -40.1635949314}}, + {"6q4z0ebf3", Box{-9.99854564667, -9.99850273132, -74.8597669601, -74.8597240448}}, + {"sv9vqgeecr", Box{31.8802589178, 31.8802642822, 36.5124285221, 36.5124392509}}, + {"wqd4", Box{36.9140625, 37.08984375, 104.0625, 104.4140625}}, + {"bwqgj", Box{80.68359375, 80.7275390625, -147.788085938, -147.744140625}}, + {"hk73qx", Box{-65.8355712891, -65.830078125, 16.1059570312, 16.1169433594}}, + {"gdx1d6hr76v", Box{59.3384175003, 59.3384188414, -12.5513903797, -12.5513890386}}, + {"47czd", Box{-67.587890625, -67.5439453125, -76.201171875, -76.1572265625}}, + {"1kpebdk50", Box{-66.8279457092, -66.8279027939, -113.17565918, -113.175616264}}, + {"g3z7", Box{55.37109375, 55.546875, -23.5546875, -23.203125}}, + {"8x", Box{39.375, 45.0, -157.5, -146.25}}, + {"nczvrs", Box{-79.2114257812, -79.2059326172, 134.978027344, 134.989013672}}, + {"fbyjmwc6", Box{50.1790237427, 50.1791954041, -47.5690841675, -47.5687408447}}, + {"yhz41tus5wm", Box{72.1026183665, 72.1026197076, 99.9160046875, 99.9160060287}}, + {"uktff1pp0", Box{70.8025932312, 70.8026361465, 19.4334411621, 19.4334840775}}, + {"hrt8", Box{-47.8125, -47.63671875, 18.984375, 19.3359375}}, + {"b5vkzheshz3", Box{66.9541557133, 66.9541570544, -172.304558605, -172.304557264}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"h6mvgwv1kmp", Box{-76.2956875563, -76.2956862152, 19.4968043268, 19.4968056679}}, + {"etzq7udz", Box{33.4683036804, 33.4684753418, -12.1361160278, -12.1357727051}}, + {"rf2x5pd6", Box{-31.0717391968, -31.0715675354, 169.588050842, 169.588394165}}, + {"k8kquxqbt2", Box{-42.3673152924, -42.3673099279, 28.6838114262, 28.683822155}}, + {"bz91jncb2c7", Box{87.4004097283, 87.4004110694, -144.621583968, -144.621582627}}, + {"3uk4y5r6sjwr", Box{-20.5920389481, -20.5920387805, -95.3511917219, -95.3511913866}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"y95n1hd", Box{51.7044067383, 51.7057800293, 116.765441895, 116.766815186}}, + {"629k5b3kbkc", Box{-41.4821608365, -41.4821594954, -76.8256638944, -76.8256625533}}, + {"pp42st7vp5", Box{-50.5073958635, -50.5073904991, 138.367266655, 138.367277384}}, + {"u17e", Box{52.55859375, 52.734375, 4.921875, 5.2734375}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"ex5w6znubms", Box{40.5129298568, 40.5129311979, -17.447989583, -17.4479882419}}, + {"8jsn", Box{31.9921875, 32.16796875, -174.375, -174.0234375}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"mht9efkj", Box{-19.410610199, -19.4104385376, 52.9046630859, 52.9050064087}}, + {"kkbcqqfcsz", Box{-18.0241495371, -18.0241441727, 12.5833261013, 12.5833368301}}, + {"866rppwznm", Box{13.9291459322, 13.9291512966, -165.268782377, -165.268771648}}, + {"96wj53wczp0c", Box{14.9499841221, 14.9499842897, -115.160106607, -115.160106272}}, + {"9ctzrj", Box{9.73937988281, 9.74487304688, -92.8564453125, -92.8454589844}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"wfq5hd", Box{13.1945800781, 13.2000732422, 132.385253906, 132.396240234}}, + {"9y6vu2v8wm5", Box{36.1712247133, 36.1712260544, -97.1882195771, -97.188218236}}, + {"6xcpg", Box{-0.0439453125, 0.0, -65.9619140625, -65.91796875}}, + {"rxqgqmc", Box{-3.61587524414, -3.61450195312, 167.268218994, 167.269592285}}, + {"yye", Box{81.5625, 82.96875, 127.96875, 129.375}}, + {"r3", Box{-39.375, -33.75, 146.25, 157.5}}, + {"x7t", Box{19.6875, 21.09375, 153.28125, 154.6875}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"g9mmh9sku8h", Box{52.9192113876, 52.9192127287, -14.9133986235, -14.9133972824}}, + {"v2q6qu3", Box{46.8251037598, 46.8264770508, 65.3370666504, 65.3384399414}}, + {"7j9ckmu00j", Box{-13.8111609221, -13.8111555576, -42.3468017578, -42.346791029}}, + {"4q3td", Box{-53.876953125, -53.8330078125, -76.552734375, -76.5087890625}}, + {"9ve2c92z33ke", Box{31.077454146, 31.0774543136, -96.6126798838, -96.6126795486}}, + {"9sscvm0mw1kw", Box{25.6485348567, 25.6485350244, -105.58899276, -105.588992424}}, + {"9u", Box{22.5, 28.125, -101.25, -90.0}}, + {"nv4j7yb8mjjy", Box{-60.9149988368, -60.9149986692, 126.728203855, 126.728204191}}, + {"80w8", Box{2.8125, 2.98828125, -170.859375, -170.5078125}}, + {"q06ch78z1", Box{-43.3975410461, -43.3974981308, 94.0550279617, 94.0550708771}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"gzw", Box{87.1875, 88.59375, -2.8125, -1.40625}}, + {"1", Box{-90.0, -45.0, -135.0, -90.0}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"furb", Box{68.90625, 69.08203125, -45.3515625, -45.0}}, + {"xen8pyc36", Box{16.9122934341, 16.9123363495, 166.983003616, 166.983046532}}, + {"n1gc29sd", Box{-79.9279403687, -79.9277687073, 95.3015899658, 95.3019332886}}, + {"cjvu", Box{78.046875, 78.22265625, -126.9140625, -126.5625}}, + {"w7x53f7fb0", Box{20.2716207504, 20.2716261148, 111.175804138, 111.175814867}}, + {"hz", Box{-50.625, -45.0, 33.75, 45.0}}, + {"8tz14", Box{32.51953125, 32.5634765625, -147.568359375, -147.524414062}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"pyfunm", Box{-51.3006591797, -51.2951660156, 172.891845703, 172.902832031}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"7xre21ceuxv", Box{-3.63716259599, -3.63716125488, -11.9508652389, -11.9508638978}}, + {"17", Box{-73.125, -67.5, -123.75, -112.5}}, + {"ru", Box{-22.5, -16.875, 168.75, 180.0}}, + {"bdjs6y77m", Box{57.0319604874, 57.0320034027, -149.640097618, -149.640054703}}, + {"u1vsgx2", Box{55.718536377, 55.719909668, 7.88818359375, 7.88955688477}}, + {"pj5e80uz", Box{-61.2544441223, -61.2542724609, 139.928398132, 139.928741455}}, + {"ju01xcg9", Box{-67.2265434265, -67.2263717651, 79.0953826904, 79.0957260132}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"k05kz1", Box{-44.1595458984, -44.1540527344, 4.8779296875, 4.88891601562}}, + {"ru2h1xc", Box{-20.3480529785, -20.3466796875, 168.81729126, 168.818664551}}, + {"ud2s6zr", Box{58.443145752, 58.444519043, 23.3335876465, 23.3349609375}}, + {"mrq5fm1zrqp", Box{-3.5308277607, -3.53082641959, 64.7891007364, 64.7891020775}}, + {"0y2u0dg2rg", Box{-54.1254597902, -54.1254544258, -145.168544054, -145.168533325}}, + {"9nkt0rnyx3", Box{36.0747295618, 36.0747349262, -128.651307821, -128.651297092}}, + {"77q", Box{-26.71875, -25.3125, -25.3125, -23.90625}}, + {"ng76t7", Box{-71.2628173828, -71.2573242188, 128.551025391, 128.562011719}}, + {"4ewypr0y27up", Box{-69.2182661779, -69.2182660103, -57.6881629229, -57.6881625876}}, + {"ge7vkm2x73", Box{64.2341905832, 64.2341959476, -17.0389688015, -17.0389580727}}, + {"3qm4d", Box{-9.404296875, -9.3603515625, -116.630859375, -116.586914062}}, + {"6gqw9", Box{-25.576171875, -25.5322265625, -47.0654296875, -47.021484375}}, + {"32", Box{-45.0, -39.375, -123.75, -112.5}}, + {"ns85", Box{-64.16015625, -63.984375, 112.5, 112.8515625}}, + {"hzy00b4j5tcj", Box{-46.4053600095, -46.4053598419, 42.2233571112, 42.2233574465}}, + {"7qrk5nt", Box{-9.10491943359, -9.10354614258, -23.4159851074, -23.4146118164}}, + {"vd4219t7th", Box{56.2588620186, 56.258867383, 70.7374048233, 70.7374155521}}, + {"g", Box{45.0, 90.0, -45.0, 0.0}}, + {"pq1p16t", Box{-55.0057983398, -55.0044250488, 147.718048096, 147.719421387}}, + {"gryfsc3wgn", Box{89.0412604809, 89.0412658453, -24.0468835831, -24.0468728542}}, + {"np0u", Box{-49.921875, -49.74609375, 91.0546875, 91.40625}}, + {"u87", Box{46.40625, 47.8125, 26.71875, 28.125}}, + {"9qz2", Box{37.96875, 38.14453125, -113.5546875, -113.203125}}, + {"xunf", Box{22.8515625, 23.02734375, 178.2421875, 178.59375}}, + {"ve", Box{61.875, 67.5, 67.5, 78.75}}, + {"s2c1tf2bvp4s", Box{4.49494846165, 4.49494862929, 12.9101834446, 12.9101837799}}, + {"5znd0f", Box{-50.2624511719, -50.2569580078, -2.07641601562, -2.0654296875}}, + {"dn90ug", Box{36.7108154297, 36.7163085938, -88.3850097656, -88.3740234375}}, + {"24bg3", Box{-28.9599609375, -28.916015625, -178.901367188, -178.857421875}}, + {"x46xpb2", Box{13.888092041, 13.889465332, 138.856201172, 138.857574463}}, + {"83q", Box{7.03125, 8.4375, -160.3125, -158.90625}}, + {"2pup20sqj", Box{-0.128059387207, -0.128016471863, -174.368948936, -174.368906021}}, + {"07", Box{-73.125, -67.5, -168.75, -157.5}}, + {"jj7nh21g4zk", Box{-59.4135086238, -59.4135072827, 49.408044219, 49.4080455601}}, + {"19up4rw9", Box{-78.8844108582, -78.8842391968, -106.767196655, -106.766853333}}, + {"2j", Box{-16.875, -11.25, -180.0, -168.75}}, + {"14uexty", Box{-73.8844299316, -73.8830566406, -128.33404541, -128.332672119}}, + {"t3wtb", Box{9.4482421875, 9.4921875, 65.390625, 65.4345703125}}, + {"wv0z", Box{29.35546875, 29.53125, 124.8046875, 125.15625}}, + {"jj6gcte19u", Box{-59.7790789604, -59.779073596, 48.9373004436, 48.9373111725}}, + {"xz0wc0te1bbe", Box{40.5647895299, 40.5647896975, 169.504699185, 169.504699521}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"zyejp1um86c", Box{82.4519781768, 82.4519795179, 173.282215744, 173.282217085}}, + {"ft915xruxj8n", Box{76.1539096758, 76.1539098434, -65.9289979935, -65.9289976582}}, + {"vchvx0yp5c", Box{51.5971237421, 51.5971291065, 85.7457053661, 85.745716095}}, + {"x5x", Box{19.6875, 21.09375, 144.84375, 146.25}}, + {"0ykju58", Box{-53.8137817383, -53.8124084473, -140.44921875, -140.447845459}}, + {"d2yk35fd", Box{4.98676300049, 4.98693466187, -69.91355896, -69.9132156372}}, + {"6ymr7k8", Box{-8.54461669922, -8.5432434082, -48.7243652344, -48.7229919434}}, + {"pjsxb9f", Box{-57.6905822754, -57.6892089844, 141.352844238, 141.354217529}}, + {"trydkh27gn2t", Box{44.0132818557, 44.0132820234, 65.5668789893, 65.5668793246}}, + {"k72c0uqqw2", Box{-26.5185070038, -26.5185016394, 12.3464977741, 12.346508503}}, + {"s8trjfz", Box{4.05807495117, 4.05944824219, 30.145111084, 30.146484375}}, + {"57ffkwfnrh1", Box{-68.4725689888, -68.4725676477, -29.6820102632, -29.6820089221}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"u2k", Box{46.40625, 47.8125, 16.875, 18.28125}}, + {"nndqt", Box{-52.294921875, -52.2509765625, 93.3837890625, 93.427734375}}, + {"w7", Box{16.875, 22.5, 101.25, 112.5}}, + {"r6c7x00p6", Box{-28.91477108, -28.9147281647, 148.315515518, 148.315558434}}, + {"mdrgz", Box{-31.6845703125, -31.640625, 78.7060546875, 78.75}}, + {"f1dzhud0j", Box{54.6926879883, 54.6927309036, -85.9211111069, -85.9210681915}}, + {"yqh", Box{78.75, 80.15625, 106.875, 108.28125}}, + {"9jp43kj", Box{28.5424804688, 28.5438537598, -125.094451904, -125.093078613}}, + {"14pb4v5q", Box{-78.7215042114, -78.72133255, -123.976249695, -123.975906372}}, + {"bjzkjzr9hsvc", Box{78.0868977495, 78.0868979171, -169.54150144, -169.541501105}}, + {"svjbhs9e9g8", Box{28.1503388286, 28.1503401697, 42.0358264446, 42.0358277857}}, + {"guuxc8c5v", Box{73.0858182907, 73.0858612061, -4.85436916351, -4.85432624817}}, + {"utu2603h0ru5", Box{77.3897973262, 77.3897974938, 28.5658425093, 28.5658428445}}, + {"bq", Box{78.75, 84.375, -168.75, -157.5}}, + {"kk", Box{-22.5, -16.875, 11.25, 22.5}}, + {"6vxq65mhx", Box{-12.9452419281, -12.9451990128, -45.9596300125, -45.9595870972}}, + {"f4sb", Box{59.0625, 59.23828125, -83.3203125, -82.96875}}, + {"y5p4", Box{62.2265625, 62.40234375, 99.84375, 100.1953125}}, + {"bs6cju", Box{69.1040039062, 69.1094970703, -153.380126953, -153.369140625}}, + {"5j", Box{-61.875, -56.25, -45.0, -33.75}}, + {"4e8z0qpsppx", Box{-69.048345387, -69.0483440459, -66.4237166941, -66.423715353}}, + {"nbyg", Box{-85.25390625, -85.078125, 133.2421875, 133.59375}}, + {"8jn2dnxvbd", Box{28.2495939732, 28.2495993376, -171.112382412, -171.112371683}}, + {"0h6ej9g", Box{-65.5567932129, -65.5554199219, -176.238555908, -176.237182617}}, + {"9j18njf9mc", Box{28.1568056345, 28.1568109989, -132.623273134, -132.623262405}}, + {"pf93jtqd2xm9", Box{-75.7324543409, -75.7324541733, 170.758466944, 170.758467279}}, + {"hc2", Box{-82.96875, -81.5625, 33.75, 35.15625}}, + {"g", Box{45.0, 90.0, -45.0, 0.0}}, + {"cewhub", Box{65.5224609375, 65.5279541016, -103.853759766, -103.842773438}}, + {"2vcrfbgsv2sx", Box{-11.2890061922, -11.2890060246, -144.366300032, -144.366299696}}, + {"mxdsnv", Box{-2.08190917969, -2.07641601562, 71.3122558594, 71.3232421875}}, + {"03", Box{-84.375, -78.75, -168.75, -157.5}}, + {"u73kybuxbq", Box{64.1216933727, 64.1216987371, 13.3106338978, 13.3106446266}}, + {"uz2", Box{85.78125, 87.1875, 33.75, 35.15625}}, + {"3w", Box{-11.25, -5.625, -112.5, -101.25}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"q8kttcg9t", Box{-42.6170825958, -42.6170396805, 119.085831642, 119.085874557}}, + {"j8w8vhmh9d", Box{-87.0315349102, -87.0315295458, 76.8672823906, 76.8672931194}}, + {"qxn54fn91g", Box{-5.08648216724, -5.08647680283, 121.067351103, 121.067361832}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"3h2p4vmu", Box{-19.8337554932, -19.8335838318, -134.871253967, -134.870910645}}, + {"j4re6xcw", Box{-76.7288589478, -76.7286872864, 55.6587982178, 55.6591415405}}, + {"ugkmrc1vj", Box{64.2104530334, 64.2104959488, 40.0697565079, 40.0697994232}}, + {"n1mj98dq", Box{-81.9981765747, -81.9980049133, 97.1002578735, 97.1006011963}}, + {"r6c", Box{-29.53125, -28.125, 147.65625, 149.0625}}, + {"9uksmr", Box{24.6917724609, 24.697265625, -94.6911621094, -94.6801757812}}, + {"dmve", Box{32.87109375, 33.046875, -71.015625, -70.6640625}}, + {"jgrdkvuq", Box{-71.2906265259, -71.2904548645, 89.5114517212, 89.5117950439}}, + {"94g", Box{15.46875, 16.875, -130.78125, -129.375}}, + {"2vj4gt", Box{-16.3641357422, -16.3586425781, -139.064941406, -139.053955078}}, + {"q39q2pm5kxt", Box{-35.4234436154, -35.4234422743, 103.01487878, 103.014880121}}, + {"qcuy493hpwdf", Box{-34.0939741954, -34.0939740278, 130.541249625, 130.541249961}}, + {"nhfpjqpyqnv3", Box{-62.0167130046, -62.0167128369, 93.0541204289, 93.0541207641}}, + {"838kk", Box{9.1845703125, 9.228515625, -168.22265625, -168.178710938}}, + {"zx2fdg", Box{86.2371826172, 86.2426757812, 158.675537109, 158.686523438}}, + {"j7ktd1g4", Box{-70.7419967651, -70.7418251038, 62.670135498, 62.6704788208}}, + {"yzp", Box{84.375, 85.78125, 133.59375, 135.0}}, + {"76kf7tcsnb94", Box{-31.9159668311, -31.9159666635, -26.91415295, -26.9141526148}}, + {"9jb", Box{32.34375, 33.75, -135.0, -133.59375}}, + {"6w", Box{-11.25, -5.625, -67.5, -56.25}}, + {"f2zs58", Box{49.921875, 49.9273681641, -68.0493164062, -68.0383300781}}, + {"f0", Box{45.0, 50.625, -90.0, -78.75}}, + {"mnqum74bk", Box{-9.08015727997, -9.08011436462, 54.7268486023, 54.7268915176}}, + {"t6rhggbn5", Box{13.512840271, 13.5128831863, 66.2586736679, 66.2587165833}}, + {"g9q", Box{52.03125, 53.4375, -14.0625, -12.65625}}, + {"7vr", Box{-15.46875, -14.0625, -1.40625, 0.0}}, + {"t6sr47h", Box{15.3094482422, 15.3108215332, 62.3309326172, 62.3323059082}}, + {"076vzrw", Box{-70.666809082, -70.665435791, -164.555969238, -164.554595947}}, + {"s2", Box{0.0, 5.625, 11.25, 22.5}}, + {"gd7350xxrrs", Box{57.8360626101, 57.8360639513, -17.7872353792, -17.7872340381}}, + {"0p8sgbg", Box{-46.9734191895, -46.9720458984, -179.127960205, -179.126586914}}, + {"5zsn39", Box{-46.7083740234, -46.7028808594, -5.55908203125, -5.54809570312}}, + {"80f", Box{4.21875, 5.625, -177.1875, -175.78125}}, + {"cymr4xm05c0", Box{81.4265495539, 81.426550895, -93.7502968311, -93.75029549}}, + {"qmjmz", Box{-15.8642578125, -15.8203125, 108.940429688, 108.984375}}, + {"39c0", Box{-35.15625, -34.98046875, -111.09375, -110.7421875}}, + {"pgxnue0jpfn", Box{-69.1086280346, -69.1086266935, 178.791844547, 178.791845888}}, + {"nytxjp", Box{-52.1685791016, -52.1630859375, 131.704101562, 131.715087891}}, + {"q1mvpgze", Box{-37.0687294006, -37.0685577393, 98.4368133545, 98.4371566772}}, + {"tqjgn4h", Box{34.2883300781, 34.2897033691, 64.6051025391, 64.6064758301}}, + {"tjn18qrtdk", Box{28.4239697456, 28.4239751101, 53.4588825703, 53.4588932991}}, + {"qq3w12r", Box{-8.78768920898, -8.78631591797, 103.423919678, 103.425292969}}, + {"zzdu8g6", Box{87.9963684082, 87.9977416992, 172.652893066, 172.654266357}}, + {"mz72xeccms3t", Box{-4.11002179608, -4.11002162844, 83.6525436491, 83.6525439844}}, + {"s7fnw5nzhbs4", Box{22.2540122643, 22.2540124319, 14.3356508017, 14.3356511369}}, + {"76rtxb6nkdm", Box{-31.3744948804, -31.3744935393, -22.8596024215, -22.8596010804}}, + {"znbejer93dr", Box{83.5141731799, 83.514174521, 135.955197662, 135.955199003}}, + {"smk7u7bz27", Box{30.212289691, 30.2122950554, 17.4143707752, 17.4143815041}}, + {"yvmurs", Box{75.3002929688, 75.3057861328, 132.165527344, 132.176513672}}, + {"tnjn6dgd8", Box{34.8641681671, 34.8642110825, 52.1459197998, 52.1459627151}}, + {"gyee1hnu", Box{82.1125030518, 82.1126747131, -6.27490997314, -6.27456665039}}, + {"gjwh9n02wfmc", Box{76.7615726776, 76.7615728453, -36.5179139748, -36.5179136395}}, + {"n6jh51hrnfws", Box{-78.0401661247, -78.0401659571, 108.41922082, 108.419221155}}, + {"shgszu46pudj", Box{27.5760518946, 27.5760520622, 5.26587635279, 5.26587668806}}, + {"3c", Box{-39.375, -33.75, -101.25, -90.0}}, + {"fy", Box{78.75, 84.375, -56.25, -45.0}}, + {"s75d5tn3m", Box{17.254242897, 17.2542858124, 16.3344812393, 16.3345241547}}, + {"2c1c9kkw8dk5", Box{-39.0868538059, -39.0868536383, -143.727924228, -143.727923892}}, + {"5yugurey8e2", Box{-51.3297383487, -51.3297370076, -4.37837362289, -4.37837228179}}, + {"rd", Box{-33.75, -28.125, 157.5, 168.75}}, + {"um", Box{73.125, 78.75, 11.25, 22.5}}, + {"bkgc", Box{71.89453125, 72.0703125, -163.4765625, -163.125}}, + {"8cfxnrphhu0", Box{11.1133790016, 11.1133803427, -142.449899912, -142.449898571}}, + {"tdm", Box{12.65625, 14.0625, 74.53125, 75.9375}}, + {"y9usehucn02q", Box{55.6610321626, 55.6610323302, 118.966741897, 118.966742232}}, + {"vk7kbfk0vf33", Box{69.7537115403, 69.7537117079, 60.859013088, 60.8590134233}}, + {"ewyx82", Box{39.287109375, 39.2926025391, -13.3483886719, -13.3374023438}}, + {"ev8c3", Box{31.1572265625, 31.201171875, -10.1513671875, -10.107421875}}, + {"37p4fhuc", Box{-27.6153373718, -27.6151657104, -113.811836243, -113.81149292}}, + {"0yvh2p9wbu", Box{-51.2418007851, -51.2417954206, -139.216657877, -139.216647148}}, + {"81k", Box{7.03125, 8.4375, -174.375, -172.96875}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"5c67rs", Box{-82.3754882812, -82.3699951172, -7.75634765625, -7.74536132812}}, + {"udjvtg", Box{57.2332763672, 57.2387695312, 30.8386230469, 30.849609375}}, + {"8b56vfqrppu", Box{0.497001260519, 0.497002601624, -141.418113112, -141.418111771}}, + {"xv50", Box{28.125, 28.30078125, 172.96875, 173.3203125}}, + {"7ep", Box{-28.125, -26.71875, -12.65625, -11.25}}, + {"bxzp4746", Box{89.8410415649, 89.8412132263, -147.554283142, -147.553939819}}, + {"5d54r", Box{-78.3544921875, -78.310546875, -17.9736328125, -17.9296875}}, + {"hhknsytff", Box{-64.9149942398, -64.9149513245, 5.8417224884, 5.84176540375}}, + {"gvjjq75", Box{74.0643310547, 74.0657043457, -3.93997192383, -3.93859863281}}, + {"6ryrt5", Box{-0.0714111328125, -0.06591796875, -69.7412109375, -69.7302246094}}, + {"tykj1vq1gj", Box{36.0643225908, 36.0643279552, 84.460272789, 84.4602835178}}, + {"20tdw84b1w", Box{-41.7480146885, -41.7480093241, -171.976139545, -171.976128817}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"sbwdq1c4355", Box{3.21802318096, 3.21802452207, 43.1557171047, 43.1557184458}}, + {"rwhkzg", Box{-10.3985595703, -10.3930664062, 163.817138672, 163.828125}}, + {"wrzphwjw57", Box{44.8582237959, 44.8582291603, 111.299196482, 111.299207211}}, + {"674", Box{-28.125, -26.71875, -75.9375, -74.53125}}, + {"z8kb", Box{46.40625, 46.58203125, 164.1796875, 164.53125}}, + {"pmudq9vs33", Box{-57.2503942251, -57.2503888607, 152.871376276, 152.871387005}}, + {"j1br4n9", Box{-78.8900756836, -78.8887023926, 45.440826416, 45.442199707}}, + {"ccc", Box{54.84375, 56.25, -99.84375, -98.4375}}, + {"src", Box{43.59375, 45.0, 12.65625, 14.0625}}, + {"cc51keq", Box{50.8625793457, 50.8639526367, -96.8252563477, -96.8238830566}}, + {"pr", Box{-50.625, -45.0, 146.25, 157.5}}, + {"tvd9", Box{31.11328125, 31.2890625, 82.265625, 82.6171875}}, + {"489bdms44x", Box{-87.069016099, -87.0690107346, -64.9345850945, -64.9345743656}}, + {"cmn23svsyv", Box{73.1958800554, 73.1958854198, -114.887176752, -114.887166023}}, + {"dm9vug1194", Box{31.9649899006, 31.964995265, -76.0789060593, -76.0788953304}}, + {"45f7", Box{-68.37890625, -68.203125, -86.8359375, -86.484375}}, + {"mxuxkdtgy50", Box{-0.117443203926, -0.117441862822, 74.0340328217, 74.0340341628}}, + {"tdwd7", Box{14.4580078125, 14.501953125, 76.7724609375, 76.81640625}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"jss4y", Box{-64.2041015625, -64.16015625, 73.388671875, 73.4326171875}}, + {"tmcs", Box{33.046875, 33.22265625, 58.359375, 58.7109375}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"gc61ncnxuec", Box{52.2138749063, 52.2138762474, -8.13174828887, -8.13174694777}}, + {"jwpc", Box{-56.07421875, -55.8984375, 78.3984375, 78.75}}, + {"yq5gn", Box{79.27734375, 79.3212890625, 106.787109375, 106.831054688}}, + {"uhtmex", Box{71.3177490234, 71.3232421875, 7.53662109375, 7.54760742188}}, + {"n0hbvyc", Box{-89.8310852051, -89.8297119141, 96.9337463379, 96.9351196289}}, + {"kp8q0gk0h", Box{-1.7399597168, -1.73991680145, 0.390186309814, 0.390229225159}}, + {"yjzduj46p", Box{77.8549575806, 77.8550004959, 100.726046562, 100.726089478}}, + {"8hhkjyy4v5s", Box{23.2406947017, 23.2406960428, -173.762292266, -173.762290925}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"wwu63", Box{38.3642578125, 38.408203125, 118.520507812, 118.564453125}}, + {"z9rnym", Box{53.2452392578, 53.2507324219, 167.618408203, 167.629394531}}, + {"78fnfc", Box{-39.5892333984, -39.5837402344, -19.5666503906, -19.5556640625}}, + {"8dc1mqyer", Box{15.7261133194, 15.7261562347, -155.85381031, -155.853767395}}, + {"b5", Box{61.875, 67.5, -180.0, -168.75}}, + {"q3zq3gz6w7", Box{-34.0365725756, -34.0365672112, 111.532441378, 111.532452106}}, + {"xx6", Box{40.78125, 42.1875, 160.3125, 161.71875}}, + {"r3", Box{-39.375, -33.75, 146.25, 157.5}}, + {"dytz0swq74e", Box{37.8187742829, 37.818775624, -48.1333740056, -48.1333726645}}, + {"gpwu", Box{87.890625, 88.06640625, -35.5078125, -35.15625}}, + {"9ywdf6cr2w7", Box{37.0622827113, 37.0622840524, -92.0087559521, -92.008754611}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"ues", Box{64.6875, 66.09375, 28.125, 29.53125}}, + {"qggvs5q7pr", Box{-22.9210478067, -22.9210424423, 129.208112955, 129.208123684}}, + {"fy9d8y2mv2", Box{82.0372724533, 82.0372778177, -54.1070973873, -54.1070866585}}, + {"bxx92bb60s", Box{87.411711216, 87.4117165804, -146.919801235, -146.919790506}}, + {"uugkmp4jkm0", Box{72.5052005053, 72.5052018464, 38.5429680347, 38.5429693758}}, + {"7mmd13sd30", Box{-15.1085615158, -15.1085561514, -25.9544706345, -25.9544599056}}, + {"5nn2", Box{-56.25, -56.07421875, -36.2109375, -35.859375}}, + {"jf9sz2r", Box{-75.1011657715, -75.0997924805, 81.1875915527, 81.1889648438}}, + {"3r", Box{-5.625, 0.0, -123.75, -112.5}}, + {"yw", Box{78.75, 84.375, 112.5, 123.75}}, + {"yt31y5hwc3c5", Box{74.8565152846, 74.8565154523, 114.17615667, 114.176157005}}, + {"7vgv9xhmn", Box{-11.6501426697, -11.6500997543, -5.90455055237, -5.90450763702}}, + {"f10tgm4q6", Box{51.6642808914, 51.6643238068, -89.1508769989, -89.1508340836}}, + {"hepj84tfcj", Box{-72.143971324, -72.1439659595, 32.3516893387, 32.3517000675}}, + {"zg", Box{61.875, 67.5, 168.75, 180.0}}, + {"by12", Box{78.75, 78.92578125, -144.4921875, -144.140625}}, + {"51", Box{-84.375, -78.75, -45.0, -33.75}}, + {"w44s78ur", Box{12.0023918152, 12.0025634766, 93.6752700806, 93.6756134033}}, + {"2tcr0", Box{-11.42578125, -11.3818359375, -155.7421875, -155.698242188}}, + {"p0n", Box{-90.0, -88.59375, 143.4375, 144.84375}}, + {"u1", Box{50.625, 56.25, 0.0, 11.25}}, + {"nygu1mshqxku", Box{-51.2971434742, -51.2971433066, 129.084147625, 129.08414796}}, + {"3khrs6gty5", Box{-21.1655312777, -21.1655259132, -117.581605911, -117.581595182}}, + {"4n", Box{-56.25, -50.625, -90.0, -78.75}}, + {"tj8pjxb5", Box{32.2110557556, 32.211227417, 45.2416992188, 45.2420425415}}, + {"7nhpg3stdjgu", Box{-9.87847991288, -9.87847974524, -39.225907065, -39.2259067297}}, + {"rhtcg24t2s50", Box{-19.3789601326, -19.378959965, 143.232218474, 143.232218809}}, + {"5vx7c75x", Box{-58.3856391907, -58.3854675293, -0.99494934082, -0.994606018066}}, + {"cs4kgc65z", Box{68.3424711227, 68.3425140381, -109.168095589, -109.168052673}}, + {"k3sn2mb", Box{-35.4322814941, -35.4309082031, 16.8859863281, 16.8873596191}}, + {"ud7s8v", Box{58.4747314453, 58.4802246094, 27.4548339844, 27.4658203125}}, + {"8qqg2ue1mr6b", Box{35.7525117695, 35.7525119372, -159.220504649, -159.220504314}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"kwtg55d3me", Box{-7.89069950581, -7.89069414139, 30.7210993767, 30.7211101055}}, + {"mqsedtkq3", Box{-7.79235363007, -7.79231071472, 62.6938676834, 62.6939105988}}, + {"94j6tg", Box{11.7059326172, 11.7114257812, -127.364501953, -127.353515625}}, + {"wd45s", Box{11.865234375, 11.9091796875, 115.48828125, 115.532226562}}, + {"fwgxjq0n2", Box{84.233250618, 84.2332935333, -62.3474121094, -62.347369194}}, + {"1k8fh1", Box{-64.3304443359, -64.3249511719, -122.51953125, -122.508544922}}, + {"h", Box{-90.0, -45.0, 0.0, 45.0}}, + {"f", Box{45.0, 90.0, -90.0, -45.0}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"qh", Box{-22.5, -16.875, 90.0, 101.25}}, + {"24nszw5", Box{-32.8820800781, -32.8807067871, -170.525665283, -170.524291992}}, + {"p8rfy", Box{-88.1103515625, -88.06640625, 168.662109375, 168.706054688}}, + {"st23m", Box{29.7509765625, 29.794921875, 23.0712890625, 23.115234375}}, + {"zgg3", Box{66.26953125, 66.4453125, 173.3203125, 173.671875}}, + {"zgsgk7bcz8k", Box{65.2796901762, 65.2796915174, 175.617812276, 175.617813617}}, + {"js0", Box{-67.5, -66.09375, 67.5, 68.90625}}, + {"9zs3bh", Box{42.5170898438, 42.5225830078, -95.2734375, -95.2624511719}}, + {"k8qd0d6mqfz", Box{-43.2289119065, -43.2289105654, 31.6659866273, 31.6659879684}}, + {"xrj", Box{39.375, 40.78125, 153.28125, 154.6875}}, + {"0mbxv2r4", Box{-56.2922286987, -56.2920570374, -167.806549072, -167.80620575}}, + {"bdf8wz8g1", Box{60.5983543396, 60.5983972549, -153.686671257, -153.686628342}}, + {"emgeh3mkyu", Box{32.8787970543, 32.8788024187, -28.6338579655, -28.6338472366}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"156p3nxfwq", Box{-70.4081690311, -70.4081636667, -132.132643461, -132.132632732}}, + {"stduvpcnp4", Box{31.8160736561, 31.8160790205, 26.5885877609, 26.5885984898}}, + {"shk8evrmmbgr", Box{24.0238861553, 24.023886323, 6.50312740356, 6.50312773883}}, + {"7ynw", Box{-10.1953125, -10.01953125, -2.109375, -1.7578125}}, + {"r4zgvkp", Box{-28.8500976562, -28.8487243652, 146.138763428, 146.140136719}}, + {"5b9p", Box{-85.95703125, -85.78125, -9.84375, -9.4921875}}, + {"gsp", Box{67.5, 68.90625, -12.65625, -11.25}}, + {"ek4mmvr", Box{23.4516906738, 23.4530639648, -30.323638916, -30.322265625}}, + {"hd2nu", Box{-76.1572265625, -76.11328125, 22.67578125, 22.7197265625}}, + {"xzp0t", Box{39.462890625, 39.5068359375, 178.813476562, 178.857421875}}, + {"fycjx9", Box{83.9410400391, 83.9465332031, -54.5141601562, -54.5031738281}}, + {"x4ygy62x5", Box{16.1414909363, 16.1415338516, 144.767661095, 144.76770401}}, + {"37", Box{-28.125, -22.5, -123.75, -112.5}}, + {"m40", Box{-33.75, -32.34375, 45.0, 46.40625}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"b5", Box{61.875, 67.5, -180.0, -168.75}}, + {"zwd", Box{81.5625, 82.96875, 160.3125, 161.71875}}, + {"qgnb2g", Box{-28.0645751953, -28.0590820312, 133.275146484, 133.286132812}}, + {"7w", Box{-11.25, -5.625, -22.5, -11.25}}, + {"v6x", Box{59.0625, 60.46875, 66.09375, 67.5}}, + {"018v8j6y", Box{-80.5658340454, -80.565662384, -178.94153595, -178.941192627}}, + {"mm4p02h18xc", Box{-15.6442321837, -15.6442308426, 59.079002291, 59.0790036321}}, + {"6cty1v7", Box{-35.4789733887, -35.4776000977, -48.0830383301, -48.0816650391}}, + {"g6rzs", Box{58.974609375, 59.0185546875, -22.67578125, -22.6318359375}}, + {"58qb", Box{-88.59375, -88.41796875, -13.0078125, -12.65625}}, + {"n8v", Box{-85.78125, -84.375, 119.53125, 120.9375}}, + {"h1nj4b1uv", Box{-83.4952783585, -83.4952354431, 8.56096744537, 8.56101036072}}, + {"qt4ukphd", Box{-16.0891342163, -16.0889625549, 116.54914856, 116.549491882}}, + {"n2", Box{-90.0, -84.375, 101.25, 112.5}}, + {"50sux01hejpj", Box{-86.3956842385, -86.3956840709, -38.0111838877, -38.0111835524}}, + {"4exy", Box{-69.2578125, -69.08203125, -56.6015625, -56.25}}, + {"x9t8r", Box{8.4814453125, 8.525390625, 165.541992188, 165.5859375}}, + {"785m2wrxgw2", Box{-44.0414522588, -44.0414509177, -17.8972649574, -17.8972636163}}, + {"0exegdddqf", Box{-69.6391904354, -69.639185071, -146.7955935, -146.795582771}}, + {"ynrw", Box{81.2109375, 81.38671875, 100.546875, 100.8984375}}, + {"uqdmuzrz6", Box{82.6143121719, 82.6143550873, 14.6335315704, 14.6335744858}}, + {"gchp3yu15c", Box{51.9366699457, 51.9366753101, -5.54244160652, -5.54243087769}}, + {"415qzwpj2r", Box{-83.154578805, -83.1545734406, -85.0904738903, -85.0904631615}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"vzq07z4", Box{85.8636474609, 85.865020752, 87.3550415039, 87.3564147949}}, + {"w43s", Box{13.359375, 13.53515625, 92.109375, 92.4609375}}, + {"b7td", Box{65.0390625, 65.21484375, -161.015625, -160.6640625}}, + {"2fpe", Box{-33.22265625, -33.046875, -135.703125, -135.3515625}}, + {"nyt", Box{-53.4375, -52.03125, 130.78125, 132.1875}}, + {"g", Box{45.0, 90.0, -45.0, 0.0}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"dkzk", Box{27.421875, 27.59765625, -68.5546875, -68.203125}}, + {"r3925m207w", Box{-36.5335857868, -36.5335804224, 148.150784969, 148.150795698}}, + {"0zy5gndwbzt3", Box{-45.710165631, -45.7101654634, -137.677191608, -137.677191272}}, + {"46ex1dmu9", Box{-74.6938991547, -74.6938562393, -73.7542676926, -73.7542247772}}, + {"jf74h9rrvnz8", Box{-76.9839544594, -76.9839542918, 83.1766849011, 83.1766852364}}, + {"6vj1jtgv", Box{-16.6667747498, -16.6666030884, -48.9719009399, -48.9715576172}}, + {"ntsx", Box{-57.83203125, -57.65625, 118.828125, 119.1796875}}, + {"ehr3ejb", Box{24.2015075684, 24.2028808594, -34.6728515625, -34.6714782715}}, + {"p7", Box{-73.125, -67.5, 146.25, 157.5}}, + {"re7", Box{-26.71875, -25.3125, 161.71875, 163.125}}, + {"66x6j7sc0t", Box{-30.5665129423, -30.5665075779, -68.3174300194, -68.3174192905}}, + {"mywcjb2q", Box{-8.25931549072, -8.25914382935, 88.4952163696, 88.4955596924}}, + {"f88jrw", Box{48.7683105469, 48.7738037109, -67.1704101562, -67.1594238281}}, + {"bjty3ef9n3y6", Box{77.0569135621, 77.0569137298, -171.844434701, -171.844434366}}, + {"jhz66rh4fj7s", Box{-62.8467891365, -62.8467889689, 55.2997731417, 55.299773477}}, + {"u16r3nm", Box{53.3399963379, 53.3413696289, 3.21487426758, 3.21624755859}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"q5rwnj4", Box{-25.6365966797, -25.6352233887, 100.813293457, 100.814666748}}, + {"dsqd4vc85c", Box{24.2894035578, 24.2894089222, -58.2363045216, -58.2362937927}}, + {"bzpu7z2qxf9", Box{85.1630249619, 85.1630263031, -135.18609032, -135.186088979}}, + {"e805cvqt", Box{0.688877105713, 0.68904876709, -22.4141693115, -22.4138259888}}, + {"y9su01vg", Box{54.1507530212, 54.1509246826, 119.187583923, 119.187927246}}, + {"41", Box{-84.375, -78.75, -90.0, -78.75}}, + {"vu3f", Box{69.2578125, 69.43359375, 81.2109375, 81.5625}}, + {"86", Box{11.25, 16.875, -168.75, -157.5}}, + {"wpuksnn65", Box{44.4180679321, 44.4181108475, 96.1610555649, 96.1610984802}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"nrqk483fq0kp", Box{-48.5138629563, -48.5138627887, 110.151591897, 110.151592232}}, + {"cg0mc", Box{62.8857421875, 62.9296875, -100.854492188, -100.810546875}}, + {"myt75g", Box{-7.89367675781, -7.88818359375, 86.2976074219, 86.30859375}}, + {"ugxe27b", Box{65.2793884277, 65.2807617188, 44.3078613281, 44.3092346191}}, + {"wd845dtbhs8", Box{14.42781955, 14.4278208911, 112.661898136, 112.661899477}}, + {"c8ef9h6mr", Box{48.2762002945, 48.2762432098, -107.179226875, -107.17918396}}, + {"bx", Box{84.375, 90.0, -157.5, -146.25}}, + {"qduv3", Box{-28.6083984375, -28.564453125, 119.223632812, 119.267578125}}, + {"j86depxm", Box{-88.1122398376, -88.1120681763, 71.1574172974, 71.1577606201}}, + {"4semjs5hr9p", Box{-63.7858861685, -63.7858848274, -62.6835371554, -62.6835358143}}, + {"ee", Box{16.875, 22.5, -22.5, -11.25}}, + {"jxv25m96n", Box{-46.3756942749, -46.3756513596, 75.0276088715, 75.0276517868}}, + {"vx7gj0006xwe", Box{86.3086774014, 86.308677569, 72.993280068, 72.9932804033}}, + {"6c", Box{-39.375, -33.75, -56.25, -45.0}}, + {"ukstfgt78f", Box{71.3430798054, 71.3430851698, 17.7062165737, 17.7062273026}}, + {"g1h0ye", Box{50.7733154297, 50.7788085938, -39.0893554688, -39.0783691406}}, + {"5s3j5er", Box{-65.1969909668, -65.1956176758, -20.9303283691, -20.9289550781}}, + {"6yrnnwq1", Box{-8.75455856323, -8.75438690186, -46.1123657227, -46.1120223999}}, + {"20fjy", Box{-39.7705078125, -39.7265625, -176.923828125, -176.879882812}}, + {"1tt417q", Box{-58.6930847168, -58.6917114258, -105.405578613, -105.404205322}}, + {"hh68", Box{-66.09375, -65.91796875, 3.515625, 3.8671875}}, + {"85s823r7j", Box{19.7388267517, 19.7388696671, -173.650717735, -173.65067482}}, + {"3u6n", Box{-20.0390625, -19.86328125, -98.4375, -98.0859375}}, + {"7w5y8", Box{-10.107421875, -10.0634765625, -17.2265625, -17.1826171875}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"zk8j", Box{71.19140625, 71.3671875, 146.25, 146.6015625}}, + {"mgm1evw0", Box{-26.4248657227, -26.4246940613, 85.954284668, 85.9546279907}}, + {"m7nzv", Box{-26.7626953125, -26.71875, 65.9619140625, 66.005859375}}, + {"ev89re48", Box{31.1737060547, 31.1738777161, -10.2138519287, -10.213508606}}, + {"dnc7mrxvb3", Box{38.5822302103, 38.5822355747, -88.0008208752, -88.0008101463}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"wbksmz281", Box{2.19314575195, 2.1931886673, 130.331540108, 130.331583023}}, + {"0x3zqt", Box{-47.9168701172, -47.9113769531, -154.753417969, -154.742431641}}, + {"h97q", Box{-81.9140625, -81.73828125, 27.0703125, 27.421875}}, + {"ypjt38wtc", Box{85.3015851974, 85.3016281128, 97.8092622757, 97.809305191}}, + {"d3ey054b7p", Box{9.50874745846, 9.50875282288, -73.4726572037, -73.4726464748}}, + {"zpbkps3qd9r", Box{89.3213434517, 89.3213447928, 135.682985634, 135.682986975}}, + {"sqxhhhs68mxy", Box{37.2908039019, 37.2908040695, 21.2753888592, 21.2753891945}}, + {"293cyj4g6q", Box{-37.6330769062, -37.6330715418, -154.771517515, -154.771506786}}, + {"w3", Box{5.625, 11.25, 101.25, 112.5}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"q69s", Box{-30.234375, -30.05859375, 103.359375, 103.7109375}}, + {"fy1qerq2ven", Box{79.9325484037, 79.9325497448, -54.3405380845, -54.3405367434}}, + {"62", Box{-45.0, -39.375, -78.75, -67.5}}, + {"yke1jyek0fg", Box{70.5246882141, 70.5246895552, 105.725934952, 105.725936294}}, + {"2rcbq1z5c", Box{-1.35204792023, -1.35200500488, -166.015734673, -166.015691757}}, + {"gue", Box{70.3125, 71.71875, -7.03125, -5.625}}, + {"t8kqv", Box{2.5927734375, 2.63671875, 73.6962890625, 73.740234375}}, + {"bgtecc1b", Box{65.3521728516, 65.3523445129, -138.436317444, -138.435974121}}, + {"8s550", Box{23.02734375, 23.0712890625, -153.28125, -153.237304688}}, + {"j655kpm1w0b", Box{-78.1386239827, -78.1386226416, 60.6516551971, 60.6516565382}}, + {"980h", Box{0.703125, 0.87890625, -112.5, -112.1484375}}, + {"ssywj", Box{27.7734375, 27.8173828125, 31.8603515625, 31.904296875}}, + {"hrvu", Box{-45.703125, -45.52734375, 19.3359375, 19.6875}}, + {"3ftuv", Box{-30.1025390625, -30.05859375, -92.9443359375, -92.900390625}}, + {"zcphg93jux", Box{51.4678519964, 51.4678573608, 178.749125004, 178.749135733}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"vjxk", Box{76.640625, 76.81640625, 55.1953125, 55.546875}}, + {"t6cgynpnh4", Box{16.161929369, 16.1619347334, 58.9843940735, 58.9844048023}}, + {"jspc", Box{-67.32421875, -67.1484375, 78.3984375, 78.75}}, + {"6w7k2v", Box{-9.06921386719, -9.06372070312, -62.8967285156, -62.8857421875}}, + {"n6z4", Box{-74.1796875, -74.00390625, 111.09375, 111.4453125}}, + {"y507hx0", Box{62.4407958984, 62.4421691895, 90.5493164062, 90.5506896973}}, + {"z17p35rnsc6v", Box{53.3246401884, 53.324640356, 139.272515886, 139.272516221}}, + {"p8uj5760", Box{-84.8844909668, -84.8843193054, 163.270568848, 163.27091217}}, + {"8pcszdrxwp", Box{44.4423955679, 44.4424009323, -177.550477982, -177.550467253}}, + {"mckjc7q4r", Box{-36.9397687912, -36.9397258759, 84.4384717941, 84.4385147095}}, + {"p6sngm70", Box{-74.7221374512, -74.7219657898, 152.021942139, 152.022285461}}, + {"59gcptbk", Box{-79.9481964111, -79.9480247498, -16.8966293335, -16.8962860107}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"fmnv3ug5m", Box{74.0745019913, 74.0745449066, -69.1765737534, -69.176530838}}, + {"qc8kkbc0xev", Box{-35.8112038672, -35.8112025261, 124.312004596, 124.312005937}}, + {"b4", Box{56.25, 61.875, -180.0, -168.75}}, + {"vkxeh", Box{70.83984375, 70.8837890625, 66.97265625, 67.0166015625}}, + {"399qcn2zv", Box{-35.3403139114, -35.3402709961, -110.696997643, -110.696954727}}, + {"ybz3", Box{49.39453125, 49.5703125, 133.9453125, 134.296875}}, + {"d5e25", Box{19.6875, 19.7314453125, -85.2978515625, -85.25390625}}, + {"5n", Box{-56.25, -50.625, -45.0, -33.75}}, + {"wdw", Box{14.0625, 15.46875, 120.9375, 122.34375}}, + {"29q7", Box{-37.44140625, -37.265625, -148.7109375, -148.359375}}, + {"tqe1y4trw", Box{36.885137558, 36.8851804733, 60.7398891449, 60.7399320602}}, + {"zfwy172d", Box{60.135383606, 60.1355552673, 178.297805786, 178.298149109}}, + {"tfd57f", Box{14.6447753906, 14.6502685547, 81.7272949219, 81.73828125}}, + {"27f6s6h", Box{-23.4558105469, -23.4544372559, -165.393676758, -165.392303467}}, + {"zk2q2ph7db", Box{70.0439357758, 70.0439411402, 146.607517004, 146.607527733}}, + {"ptp", Box{-61.875, -60.46875, 167.34375, 168.75}}, + {"7pcgp042wz", Box{-0.878782868385, -0.878777503967, -42.2280657291, -42.2280550003}}, + {"9t", Box{28.125, 33.75, -112.5, -101.25}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"qd0pwby4y", Box{-32.4270486832, -32.4270057678, 112.805128098, 112.805171013}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"yet", Box{64.6875, 66.09375, 119.53125, 120.9375}}, + {"v4pbsh7cp", Box{56.3614082336, 56.361451149, 56.0796689987, 56.0797119141}}, + {"kvr7u7pm7jeu", Box{-14.7921594232, -14.7921592556, 44.1421702132, 44.1421705484}}, + {"687jj", Box{-42.71484375, -42.6708984375, -63.0615234375, -63.017578125}}, + {"4w29", Box{-54.66796875, -54.4921875, -66.796875, -66.4453125}}, + {"6bz45dve", Box{-40.4140663147, -40.4138946533, -46.2448883057, -46.2445449829}}, + {"gfysykk0nw29", Box{61.32709058, 61.3270907477, -1.82894401252, -1.82894367725}}, + {"pdw4scw", Box{-75.4898071289, -75.4884338379, 166.15447998, 166.155853271}}, + {"65f4", Box{-23.5546875, -23.37890625, -87.1875, -86.8359375}}, + {"jc9g6tpd08j", Box{-80.9634017944, -80.9634004533, 81.3311286271, 81.3311299682}}, + {"ckw1tvf18r09", Box{70.608052779, 70.6080529466, -115.057056472, -115.057056136}}, + {"2cbtp", Box{-34.27734375, -34.2333984375, -145.239257812, -145.1953125}}, + {"54myf", Box{-76.1572265625, -76.11328125, -36.826171875, -36.7822265625}}, + {"kw", Box{-11.25, -5.625, 22.5, 33.75}}, + {"mw", Box{-11.25, -5.625, 67.5, 78.75}}, + {"2bjvee8sgek", Box{-44.0131442249, -44.0131428838, -138.009411693, -138.009410352}}, + {"yj6r4eyxuv1", Box{75.783675313, 75.7836766541, 93.2830573618, 93.2830587029}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"pkec4ng", Box{-64.4746398926, -64.4732666016, 151.615447998, 151.616821289}}, + {"uvpynzd0v", Box{74.2210149765, 74.2210578918, 44.9480295181, 44.9480724335}}, + {"grxrc9by8g4", Box{88.5605496168, 88.5605509579, -23.4877046943, -23.4877033532}}, + {"6z0", Box{-5.625, -4.21875, -56.25, -54.84375}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"6npe6", Box{-10.6787109375, -10.634765625, -79.365234375, -79.3212890625}}, + {"04wzmgz23", Box{-74.6424436569, -74.6424007416, -170.245127678, -170.245084763}}, + {"dmfzk9", Box{33.6236572266, 33.6291503906, -74.6850585938, -74.6740722656}}, + {"eeu", Box{21.09375, 22.5, -16.875, -15.46875}}, + {"84bd9k3t", Box{15.9324073792, 15.9325790405, -179.239883423, -179.2395401}}, + {"7q6ywwxq2kc", Box{-8.664367944, -8.6643666029, -29.5871995389, -29.5871981978}}, + {"bve6jyuwk", Box{76.327214241, 76.3272571564, -141.420650482, -141.420607567}}, + {"h558", Box{-73.125, -72.94921875, 4.921875, 5.2734375}}, + {"sk4f3d2h1vq", Box{22.9085822403, 22.9085835814, 15.1831886172, 15.1831899583}}, + {"tffnvn", Box{16.6882324219, 16.6937255859, 81.7822265625, 81.7932128906}}, + {"eppvmm886m", Box{40.3281337023, 40.3281390667, -33.8700664043, -33.8700556755}}, + {"d2w", Box{2.8125, 4.21875, -70.3125, -68.90625}}, + {"sd3", Box{12.65625, 14.0625, 23.90625, 25.3125}}, + {"q73kk", Box{-25.9716796875, -25.927734375, 103.18359375, 103.227539062}}, + {"wtz5yx", Box{33.0413818359, 33.046875, 122.629394531, 122.640380859}}, + {"xw8z", Box{37.79296875, 37.96875, 158.5546875, 158.90625}}, + {"dyhuchkt7qr", Box{34.6092416346, 34.6092429757, -49.5200385153, -49.5200371742}}, + {"pyb1es47f", Box{-51.7449617386, -51.7449188232, 168.906984329, 168.907027245}}, + {"v6jcgfj1nus", Box{56.5687993169, 56.568800658, 64.5078939199, 64.507895261}}, + {"xxe", Box{42.1875, 43.59375, 161.71875, 163.125}}, + {"7xdvcext7rq", Box{-1.78159162402, -1.78159028292, -18.5564473271, -18.556445986}}, + {"1f35b7w", Box{-76.6653442383, -76.6639709473, -99.8245239258, -99.8231506348}}, + {"he675b8pjek", Box{-71.187440604, -71.1874392629, 25.8290988207, 25.8291001618}}, + {"pzj3", Box{-50.44921875, -50.2734375, 176.1328125, 176.484375}}, + {"1s1m8j10zwq3", Box{-66.5055748634, -66.5055746958, -110.740483962, -110.740483627}}, + {"sk4xsg4ufxm", Box{23.8356931508, 23.8356944919, 14.9782557786, 14.9782571197}}, + {"r3m2ker83", Box{-37.906908989, -37.9068660736, 153.840909004, 153.84095192}}, + {"p7bj", Box{-68.02734375, -67.8515625, 146.25, 146.6015625}}, + {"4wws597u", Box{-52.7268218994, -52.726650238, -58.2004165649, -58.2000732422}}, + {"u9rk", Box{52.734375, 52.91015625, 32.6953125, 33.046875}}, + {"2mv856bp9uj4", Box{-12.6398345456, -12.6398343779, -160.872720927, -160.872720592}}, + {"57th4543xnbs", Box{-69.5926011354, -69.5926009677, -26.6274683923, -26.627468057}}, + {"8pct", Box{44.47265625, 44.6484375, -177.890625, -177.5390625}}, + {"me0skjqbk8", Box{-27.3490476608, -27.3490422964, 68.3883690834, 68.3883798122}}, + {"30s74", Box{-41.66015625, -41.6162109375, -128.935546875, -128.891601562}}, + {"9r0h9fedn", Box{40.1800918579, 40.1801347733, -123.668031693, -123.667988777}}, + {"9v6gvte7r", Box{30.2211999893, 30.2212429047, -97.136349678, -97.1363067627}}, + {"j5d", Box{-70.3125, -68.90625, 47.8125, 49.21875}}, + {"hf8ge", Box{-75.322265625, -75.2783203125, 34.9365234375, 34.98046875}}, + {"hf2hmz", Box{-76.5582275391, -76.552734375, 34.0026855469, 34.013671875}}, + {"5wc405", Box{-51.6632080078, -51.6577148438, -21.09375, -21.0827636719}}, + {"x2dk49y7p8", Box{3.52575302124, 3.52575838566, 149.532830715, 149.532841444}}, + {"350jjfux7dbk", Box{-27.2297275811, -27.2297274135, -134.740984105, -134.740983769}}, + {"04wd", Box{-75.5859375, -75.41015625, -170.859375, -170.5078125}}, + {"1zxmhstk", Box{-46.9081878662, -46.9080162048, -90.8497238159, -90.8493804932}}, + {"b2sj26ddce3", Box{48.7495739758, 48.7495753169, -163.11051473, -163.110513389}}, + {"vznd8mz363", Box{84.8462587595, 84.8462641239, 87.9116642475, 87.9116749763}}, + {"f", Box{45.0, 90.0, -90.0, -45.0}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"kw", Box{-11.25, -5.625, 22.5, 33.75}}, + {"1s0nq579", Box{-66.3833427429, -66.3831710815, -112.231521606, -112.231178284}}, + {"mzky2scd", Box{-3.09368133545, -3.09350967407, 85.4537200928, 85.4540634155}}, + {"kctpzwx2rxg", Box{-35.1644052565, -35.1644039154, 41.121122092, 41.1211234331}}, + {"19", Box{-84.375, -78.75, -112.5, -101.25}}, + {"pmg4wc8pn", Box{-57.2073554993, -57.2073125839, 150.765638351, 150.765681267}}, + {"sxcn0yd0jck", Box{44.6841497719, 44.684151113, 23.9422076941, 23.9422090352}}, + {"000dsj4g", Box{-89.5325660706, -89.5323944092, -179.1173172, -179.116973877}}, + {"pgv0", Box{-68.90625, -68.73046875, 175.78125, 176.1328125}}, + {"dhw0935d8", Box{25.4063129425, 25.4063558578, -81.5027618408, -81.5027189255}}, + {"4gvz08kbk9", Box{-67.6743596792, -67.6743543148, -48.1353735924, -48.1353628635}}, + {"djz4g1m", Box{32.8340148926, 32.8353881836, -80.0175476074, -80.0161743164}}, + {"x4yhn1h5m1z", Box{16.1779354513, 16.1779367924, 143.706889004, 143.706890345}}, + {"9t", Box{28.125, 33.75, -112.5, -101.25}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"4fgytpvp2v", Box{-73.3448284864, -73.344823122, -50.7499372959, -50.7499265671}}, + {"jggxx", Box{-67.587890625, -67.5439453125, 83.9794921875, 84.0234375}}, + {"mye8t1", Box{-8.34411621094, -8.33862304688, 83.8916015625, 83.9025878906}}, + {"bssun7dstj", Box{71.0356503725, 71.0356557369, -150.542006493, -150.541995764}}, + {"ehhv", Box{23.37890625, 23.5546875, -38.3203125, -37.96875}}, + {"484tc", Box{-88.9892578125, -88.9453125, -63.9404296875, -63.896484375}}, + {"d4tjtv", Box{15.0567626953, 15.0622558594, -82.7160644531, -82.705078125}}, + {"2z", Box{-5.625, 0.0, -146.25, -135.0}}, + {"t5ey32", Box{20.7861328125, 20.7916259766, 50.3283691406, 50.3393554688}}, + {"um", Box{73.125, 78.75, 11.25, 22.5}}, + {"pe", Box{-73.125, -67.5, 157.5, 168.75}}, + {"2x05zw6z", Box{-4.93028640747, -4.93011474609, -157.166633606, -157.166290283}}, + {"67", Box{-28.125, -22.5, -78.75, -67.5}}, + {"dxfr3yndexbg", Box{44.9015942775, 44.9015944451, -64.249955602, -64.2499552667}}, + {"y87", Box{46.40625, 47.8125, 116.71875, 118.125}}, + {"2h29w", Box{-20.830078125, -20.7861328125, -179.033203125, -178.989257812}}, + {"cv", Box{73.125, 78.75, -101.25, -90.0}}, + {"jcx2m1mjy9e", Box{-81.5106931329, -81.5106917918, 89.1721884906, 89.1721898317}}, + {"ryj9w", Box{-10.986328125, -10.9423828125, 176.748046875, 176.791992188}}, + {"g5sftxrhf40", Box{65.1676046848, 65.1676060259, -38.0689144135, -38.0689130723}}, + {"nhs95z98z", Box{-64.4703912735, -64.4703483582, 96.4952802658, 96.4953231812}}, + {"s7n00se8u3n", Box{16.8998533487, 16.8998546898, 19.7144696116, 19.7144709527}}, + {"4310r6m", Box{-84.3186950684, -84.3173217773, -77.0182800293, -77.0169067383}}, + {"7kkmp", Box{-20.21484375, -20.1708984375, -27.4658203125, -27.421875}}, + {"3dvnpf13665", Box{-28.4653508663, -28.4653495252, -105.126356632, -105.12635529}}, + {"1cv2hegqd1s7", Box{-80.1345262863, -80.1345261186, -93.6648788676, -93.6648785323}}, + {"uy0yttxwk65", Box{79.9238741398, 79.9238754809, 35.0568728149, 35.056874156}}, + {"166cs2etxffy", Box{-77.0763716474, -77.0763714798, -119.690902121, -119.690901786}}, + {"bm7n7", Box{75.6298828125, 75.673828125, -164.399414062, -164.35546875}}, + {"5r7q", Box{-48.1640625, -47.98828125, -29.1796875, -28.828125}}, + {"ymjqjv6", Box{74.2085266113, 74.2098999023, 108.888244629, 108.88961792}}, + {"jx95kpvmv9", Box{-47.1976464987, -47.1976411343, 69.0894770622, 69.0894877911}}, + {"f1m4m9", Box{52.4322509766, 52.4377441406, -82.7270507812, -82.7160644531}}, + {"0cdr1wfep", Box{-80.2944374084, -80.2943944931, -143.016285896, -143.016242981}}, + {"fe4q2v7", Box{63.0024719238, 63.0038452148, -64.2988586426, -64.2974853516}}, + {"9pxfyb9p", Box{42.6748466492, 42.6750183105, -123.80355835, -123.803215027}}, + {"8w088r", Box{33.8763427734, 33.8818359375, -156.785888672, -156.774902344}}, + {"u569fj", Box{63.6163330078, 63.6218261719, 3.603515625, 3.61450195312}}, + {"smvm0syj0kst", Box{33.2496320643, 33.2496322319, 18.6630416662, 18.6630420014}}, + {"vx", Box{84.375, 90.0, 67.5, 78.75}}, + {"zj", Box{73.125, 78.75, 135.0, 146.25}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"69n85w0", Box{-39.3420410156, -39.3406677246, -58.2055664062, -58.2041931152}}, + {"ywmj", Box{81.03515625, 81.2109375, 119.53125, 119.8828125}}, + {"2717c52t", Box{-27.4471092224, -27.446937561, -166.947555542, -166.947212219}}, + {"t1h", Box{5.625, 7.03125, 50.625, 52.03125}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"5xphd2", Box{-49.833984375, -49.8284912109, -12.5573730469, -12.5463867188}}, + {"xy8", Box{36.5625, 37.96875, 168.75, 170.15625}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"3pet007v7qb", Box{-1.93128302693, -1.93128168583, -130.072835684, -130.072834343}}, + {"dyuz", Box{39.19921875, 39.375, -49.5703125, -49.21875}}, + {"6r", Box{-5.625, 0.0, -78.75, -67.5}}, + {"y8v06s5", Box{49.2846679688, 49.2860412598, 119.645233154, 119.646606445}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"mfuy2m", Box{-28.4051513672, -28.3996582031, 85.4406738281, 85.4516601562}}, + {"dnp2u", Box{33.8818359375, 33.92578125, -79.62890625, -79.5849609375}}, + {"g4u3ehx6", Box{60.757484436, 60.7576560974, -38.8816452026, -38.8813018799}}, + {"q14h9tg88tx", Box{-38.5522833467, -38.5522820055, 92.8832553327, 92.8832566738}}, + {"d4m6rn", Box{13.0847167969, 13.0902099609, -82.3095703125, -82.2985839844}}, + {"64", Box{-33.75, -28.125, -90.0, -78.75}}, + {"y2nzb", Box{46.3623046875, 46.40625, 110.7421875, 110.786132812}}, + {"yhs5m8ytcgxb", Box{70.8889147639, 70.8889149316, 95.8757111058, 95.875711441}}, + {"ytp9j8f0dv8", Box{73.305016458, 73.3050177991, 123.291438818, 123.291440159}}, + {"pxcpm2h", Box{-45.1318359375, -45.1304626465, 159.142456055, 159.143829346}}, + {"5zyf9", Box{-45.966796875, -45.9228515625, -1.7138671875, -1.669921875}}, + {"wmz7v2", Box{33.0029296875, 33.0084228516, 111.676025391, 111.687011719}}, + {"3fb7hkc8wus", Box{-28.9777037501, -28.977702409, -100.709314942, -100.709313601}}, + {"ssstmsrv", Box{26.2595558167, 26.259727478, 29.0804672241, 29.0808105469}}, + {"21", Box{-39.375, -33.75, -180.0, -168.75}}, + {"w0du7r8257", Box{3.60078513622, 3.60079050064, 94.0104925632, 94.0105032921}}, + {"fhx", Box{70.3125, 71.71875, -80.15625, -78.75}}, + {"1xd", Box{-47.8125, -46.40625, -109.6875, -108.28125}}, + {"s040p9v3shb2", Box{0.00989601016045, 0.00989617779851, 3.14947161824, 3.14947195351}}, + {"gq", Box{78.75, 84.375, -33.75, -22.5}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"17b4wsgdq0q", Box{-68.4403167665, -68.4403154254, -123.459283412, -123.45928207}}, + {"x3qs", Box{7.734375, 7.91015625, 155.390625, 155.7421875}}, + {"rc", Box{-39.375, -33.75, 168.75, 180.0}}, + {"sfxjv06b9", Box{15.0747013092, 15.0747442245, 43.8172960281, 43.8173389435}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"ngveyg", Box{-68.2305908203, -68.2250976562, 131.781005859, 131.791992188}}, + {"pmxd5bd39qp", Box{-58.7079012394, -58.7078998983, 156.964822859, 156.9648242}}, + {"xhw6", Box{25.6640625, 25.83984375, 143.7890625, 144.140625}}, + {"bc6vx6", Box{53.0090332031, 53.0145263672, -142.064208984, -142.053222656}}, + {"uuxy5sbu", Box{71.3939666748, 71.3941383362, 44.803276062, 44.8036193848}}, + {"yu", Box{67.5, 73.125, 123.75, 135.0}}, + {"610pf9c3ebh", Box{-38.0028247833, -38.0028234422, -89.888253808, -89.8882524669}}, + {"9d", Box{11.25, 16.875, -112.5, -101.25}}, + {"1s0tk4yp", Box{-66.5608406067, -66.5606689453, -111.612854004, -111.612510681}}, + {"ss7yjqhs5su", Box{24.9946086109, 24.994609952, 28.0104857683, 28.0104871094}}, + {"krqww1d0kp", Box{-3.06785166264, -3.06784629822, 20.6572151184, 20.6572258472}}, + {"4yzg18c07qnm", Box{-51.4997104369, -51.4997102693, -45.2841233835, -45.2841230482}}, + {"5wf241", Box{-52.0257568359, -52.0202636719, -19.248046875, -19.2370605469}}, + {"gxrh60ce", Box{86.5329551697, 86.5331268311, -12.5662994385, -12.5659561157}}, + {"5rzrgr9qf", Box{-45.0015878677, -45.0015449524, -23.4100627899, -23.4100198746}}, + {"u3", Box{50.625, 56.25, 11.25, 22.5}}, + {"8tm4uz7c", Box{30.0546455383, 30.0548171997, -150.254859924, -150.254516602}}, + {"wz5f3", Box{39.7705078125, 39.814453125, 129.067382812, 129.111328125}}, + {"ckz", Box{71.71875, 73.125, -113.90625, -112.5}}, + {"h4s31", Box{-75.76171875, -75.7177734375, 6.0205078125, 6.064453125}}, + {"hguje", Box{-67.939453125, -67.8955078125, 39.5068359375, 39.55078125}}, + {"4rnfer", Box{-50.1470947266, -50.1416015625, -69.1149902344, -69.1040039062}}, + {"gj", Box{73.125, 78.75, -45.0, -33.75}}, + {"04", Box{-78.75, -73.125, -180.0, -168.75}}, + {"kvj", Box{-16.875, -15.46875, 40.78125, 42.1875}}, + {"7c3p", Box{-36.73828125, -36.5625, -9.84375, -9.4921875}}, + {"rdw55", Box{-30.41015625, -30.3662109375, 166.069335938, 166.11328125}}, + {"7spe18wk094b", Box{-21.969217658, -21.9692174904, -11.8785988167, -11.8785984814}}, + {"uxumm", Box{89.5166015625, 89.560546875, 28.6962890625, 28.740234375}}, + {"1n0sh0", Box{-55.546875, -55.5413818359, -134.12109375, -134.110107422}}, + {"cphmy", Box{85.3857421875, 85.4296875, -128.759765625, -128.715820312}}, + {"sd", Box{11.25, 16.875, 22.5, 33.75}}, + {"h6jbb2gxyd0", Box{-78.6127030849, -78.6127017438, 19.3520092964, 19.3520106375}}, + {"x3", Box{5.625, 11.25, 146.25, 157.5}}, + {"yv289c0nbs", Box{74.625813961, 74.6258193254, 124.530050755, 124.530061483}}, + {"g1fxbp5", Box{56.2445068359, 56.245880127, -41.480255127, -41.4788818359}}, + {"bqdh", Box{82.265625, 82.44140625, -165.9375, -165.5859375}}, + {"w558neznn3", Box{16.8966346979, 16.8966400623, 95.2174007893, 95.2174115181}}, + {"up", Box{84.375, 90.0, 0.0, 11.25}}, + {"pmy73pm2r", Box{-57.0450925827, -57.0450496674, 155.090517998, 155.090560913}}, + {"jzerkufsjnh", Box{-46.5112745762, -46.5112732351, 83.5327059031, 83.5327072442}}, + {"8eks", Box{18.984375, 19.16015625, -151.171875, -150.8203125}}, + {"3rryyvfxc", Box{-2.99931049347, -2.99926757812, -112.551455498, -112.551412582}}, + {"vn0cz", Box{79.0576171875, 79.1015625, 46.3623046875, 46.40625}}, + {"skb5cnez", Box{27.4148368835, 27.4150085449, 11.2990951538, 11.2994384766}}, + {"3p5cu0yju", Box{-5.31227588654, -5.31223297119, -129.542369843, -129.542326927}}, + {"8yrwss1y2s", Box{36.3218951225, 36.3219004869, -135.502946377, -135.502935648}}, + {"7x9kys4ydp", Box{-1.95441305637, -1.95440769196, -20.4526805878, -20.4526698589}}, + {"u7gscks3", Box{66.9536018372, 66.9537734985, 16.2326431274, 16.2329864502}}, + {"30c8pz7", Box{-40.7414245605, -40.7400512695, -132.545928955, -132.544555664}}, + {"umertwj6wxuc", Box{77.2892892547, 77.2892894223, 16.0695068166, 16.0695071518}}, + {"n6", Box{-78.75, -73.125, 101.25, 112.5}}, + {"br8dqrhg058f", Box{87.6219940558, 87.6219942234, -167.765692659, -167.765692323}}, + {"tp", Box{39.375, 45.0, 45.0, 56.25}}, + {"33uy", Box{-34.1015625, -33.92578125, -117.0703125, -116.71875}}, + {"u3ps1jnxg", Box{51.356921196, 51.3569641113, 21.8498754501, 21.8499183655}}, + {"nqwmqymbyz", Box{-52.4801498652, -52.4801445007, 110.343879461, 110.34389019}}, + {"wfugcgd", Box{16.1471557617, 16.1485290527, 130.509338379, 130.51071167}}, + {"vp7g2eg", Box{86.3731384277, 86.3745117188, 50.2995300293, 50.3009033203}}, + {"zz", Box{84.375, 90.0, 168.75, 180.0}}, + {"859t0xmm6gwk", Box{20.6071523577, 20.6071525253, -177.861316167, -177.861315832}}, + {"bvjnf49wp1hu", Box{74.3262923509, 74.3262925185, -139.128492661, -139.128492326}}, + {"08ybs", Box{-85.693359375, -85.6494140625, -147.83203125, -147.788085938}}, + {"dr908p7", Box{42.3152160645, 42.3165893555, -77.339630127, -77.3382568359}}, + {"gufpbsu", Box{73.1071472168, 73.1085205078, -8.41003417969, -8.40866088867}}, + {"623c388eg2t", Box{-43.3706304431, -43.370629102, -76.2223117054, -76.2223103642}}, + {"8rc", Box{43.59375, 45.0, -167.34375, -165.9375}}, + {"h4", Box{-78.75, -73.125, 0.0, 11.25}}, + {"x84", Box{0.0, 1.40625, 160.3125, 161.71875}}, + {"bbxgcx6nyzk", Box{48.5127027333, 48.5127040744, -135.282602906, -135.282601565}}, + {"tqg06239nrht", Box{38.014278654, 38.0142788216, 60.5699611455, 60.5699614808}}, + {"05fd", Box{-68.5546875, -68.37890625, -176.484375, -176.1328125}}, + {"wuc", Box{26.71875, 28.125, 125.15625, 126.5625}}, + {"m3hh", Box{-38.671875, -38.49609375, 61.875, 62.2265625}}, + {"m2w4ru", Box{-41.7700195312, -41.7645263672, 65.0280761719, 65.0390625}}, + {"4cz3k", Box{-79.9365234375, -79.892578125, -45.87890625, -45.8349609375}}, + {"jqefz9v", Box{-52.9444885254, -52.9431152344, 61.8598937988, 61.8612670898}}, + {"qqcnf60wjf", Box{-5.83269953728, -5.83269417286, 102.756060362, 102.756071091}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"n8qen549x9vr", Box{-88.0496587045, -88.0496585369, 121.908059008, 121.908059344}}, + {"g01nwm2wyj", Box{46.1726027727, 46.1726081371, -43.3181476593, -43.3181369305}}, + {"6xc", Box{-1.40625, 0.0, -66.09375, -64.6875}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"nmjjnhz67", Box{-60.9696149826, -60.9695720673, 108.555006981, 108.555049896}}, + {"2gzwv1zggqg", Box{-22.7094335854, -22.7094322443, -135.472611934, -135.472610593}}, + {"dt", Box{28.125, 33.75, -67.5, -56.25}}, + {"v33ysf2y0s", Box{53.1872391701, 53.1872445345, 58.9207291603, 58.9207398891}}, + {"fwy6tccjp3", Box{83.4186798334, 83.4186851978, -58.4565675259, -58.456556797}}, + {"qug6fjx4ue8k", Box{-17.7671476454, -17.7671474777, 128.418009616, 128.418009952}}, + {"8y59duq21", Box{34.0370178223, 34.0370607376, -141.198649406, -141.198606491}}, + {"pxgfrpkhdnk", Box{-45.9701107442, -45.9701094031, 163.086639047, 163.086640388}}, + {"91b", Box{9.84375, 11.25, -135.0, -133.59375}}, + {"v83mnvugmh", Box{47.3173213005, 47.3173266649, 69.5611810684, 69.5611917973}}, + {"k76cem9", Box{-26.4248657227, -26.4234924316, 15.2613830566, 15.2627563477}}, + {"ydjq0mk", Box{57.3335266113, 57.3348999023, 119.899291992, 119.900665283}}, + {"cdsuf", Box{59.8974609375, 59.94140625, -105.732421875, -105.688476562}}, + {"tuyz3", Box{27.9931640625, 28.037109375, 88.2861328125, 88.330078125}}, + {"r7r2skwfj00", Box{-26.605796814, -26.6057954729, 156.641564369, 156.64156571}}, + {"8hx507p9f5x", Box{25.8566424251, 25.8566437662, -170.134868771, -170.13486743}}, + {"cc", Box{50.625, 56.25, -101.25, -90.0}}, + {"pkuuvxz1z3", Box{-62.4034112692, -62.4034059048, 153.181310892, 153.181321621}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"7vvz9gbf", Box{-11.316947937, -11.3167762756, -3.08612823486, -3.08578491211}}, + {"54r", Box{-77.34375, -75.9375, -35.15625, -33.75}}, + {"5r0mm0pwj1j", Box{-49.7011131048, -49.7011117637, -33.1681899726, -33.1681886315}}, + {"mx", Box{-5.625, 0.0, 67.5, 78.75}}, + {"rm39jwc", Box{-15.2558898926, -15.2545166016, 148.60244751, 148.603820801}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"4gqrkyvqc", Box{-70.4060983658, -70.4060554504, -47.2449445724, -47.2449016571}}, + {"9s36btfr9", Box{24.4225215912, 24.4225645065, -110.717082024, -110.717039108}}, + {"dxt5", Box{42.71484375, 42.890625, -60.46875, -60.1171875}}, + {"21x8tven9", Box{-36.4432811737, -36.4432382584, -169.196276665, -169.196233749}}, + {"7hh", Box{-22.5, -21.09375, -39.375, -37.96875}}, + {"v7nm8q7s", Box{62.8768157959, 62.8769874573, 65.0548553467, 65.0551986694}}, + {"e9qxh", Box{8.26171875, 8.3056640625, -13.18359375, -13.1396484375}}, + {"273", Box{-26.71875, -25.3125, -167.34375, -165.9375}}, + {"p99qpkcvb", Box{-80.4807329178, -80.4806900024, 159.578819275, 159.57886219}}, + {"q2062f9csybw", Box{-44.5904645696, -44.590464402, 101.637129262, 101.637129597}}, + {"btsq32", Box{77.0361328125, 77.0416259766, -151.468505859, -151.457519531}}, + {"1cj5", Box{-83.84765625, -83.671875, -94.21875, -93.8671875}}, + {"j71pemzwqxt9", Box{-71.7739416473, -71.7739414796, 57.8096582741, 57.8096586093}}, + {"qnqcbndu0nf", Box{-9.49970439076, -9.49970304966, 99.4959667325, 99.4959680736}}, + {"2v7cm6junqb", Box{-15.237314254, -15.2373129129, -140.737684965, -140.737683624}}, + {"e7", Box{16.875, 22.5, -33.75, -22.5}}, + {"s91p45", Box{6.87194824219, 6.87744140625, 23.994140625, 24.0051269531}}, + {"xwk1de36", Box{35.438117981, 35.4382896423, 163.236579895, 163.236923218}}, + {"gf2", Box{57.65625, 59.0625, -11.25, -9.84375}}, + {"d1m", Box{7.03125, 8.4375, -82.96875, -81.5625}}, + {"0b", Box{-90.0, -84.375, -146.25, -135.0}}, + {"rw4", Box{-11.25, -9.84375, 160.3125, 161.71875}}, + {"74pk9xsb", Box{-32.9177856445, -32.9176139832, -34.7322463989, -34.7319030762}}, + {"4ghe0cn8s", Box{-72.5920772552, -72.5920343399, -49.8798179626, -49.8797750473}}, + {"tw4", Box{33.75, 35.15625, 70.3125, 71.71875}}, + {"gevx3", Box{67.3681640625, 67.412109375, -14.7216796875, -14.677734375}}, + {"kw8w6yqc1ks", Box{-7.30433911085, -7.30433776975, 23.3333033323, 23.3333046734}}, + {"vbdjwzpg7", Box{48.8183069229, 48.8183498383, 81.8699026108, 81.8699455261}}, + {"tp", Box{39.375, 45.0, 45.0, 56.25}}, + {"751q", Box{-27.0703125, -26.89453125, -43.2421875, -42.890625}}, + {"yeurzu", Box{67.4780273438, 67.4835205078, 118.817138672, 118.828125}}, + {"svx", Box{30.9375, 32.34375, 43.59375, 45.0}}, + {"4yrg1w", Box{-54.2834472656, -54.2779541016, -45.2856445312, -45.2746582031}}, + {"h6xmxenmzkc", Box{-74.9532110989, -74.9532097578, 21.7837978899, 21.7837992311}}, + {"j5uzmqughwj1", Box{-67.5942097418, -67.5942095742, 51.9171233475, 51.9171236828}}, + {"26trngxu", Box{-29.6871185303, -29.6869468689, -161.059913635, -161.059570312}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"h58jxs7g", Box{-69.3218421936, -69.3216705322, 0.334739685059, 0.335083007812}}, + {"tg2kgbk3", Box{19.1177558899, 19.1179275513, 79.2721939087, 79.2725372314}}, + {"x34hfu", Box{6.48193359375, 6.48742675781, 149.183349609, 149.194335938}}, + {"774j", Box{-27.24609375, -27.0703125, -30.9375, -30.5859375}}, + {"yf", Box{56.25, 61.875, 123.75, 135.0}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"e273k4gje9s", Box{1.64203494787, 1.64203628898, -28.9996308088, -28.9996294677}}, + {"e", Box{0.0, 45.0, -45.0, 0.0}}, + {"hx", Box{-50.625, -45.0, 22.5, 33.75}}, + {"wz", Box{39.375, 45.0, 123.75, 135.0}}, + {"w7p94b", Box{17.05078125, 17.0562744141, 111.917724609, 111.928710938}}, + {"69sgzyb46pbs", Box{-35.8658129722, -35.8658128045, -60.4796498269, -60.4796494916}}, + {"7f7m8k2u5", Box{-31.3529205322, -31.3528776169, -6.66754245758, -6.66749954224}}, + {"gq4n7m", Box{79.8760986328, 79.8815917969, -30.7946777344, -30.7836914062}}, + {"c1srg1pz8kt", Box{54.8066094518, 54.8066107929, -128.880941123, -128.880939782}}, + {"b2h1dy", Box{45.2966308594, 45.3021240234, -163.004150391, -162.993164062}}, + {"7170zy", Box{-37.8039550781, -37.7984619141, -40.4406738281, -40.4296875}}, + {"p09", Box{-87.1875, -85.78125, 136.40625, 137.8125}}, + {"sw970ux6", Box{37.114906311, 37.1150779724, 24.3007278442, 24.301071167}}, + {"rc8hsdptqn", Box{-35.7595646381, -35.7595592737, 168.958311081, 168.95832181}}, + {"qp3jujqc1", Box{-3.17899703979, -3.17895412445, 91.5913438797, 91.591386795}}, + {"dn7n9krj2tgg", Box{36.3231066428, 36.3231068105, -85.7166788355, -85.7166785002}}, + {"23e7vwt", Box{-35.8676147461, -35.8662414551, -163.931121826, -163.929748535}}, + {"um", Box{73.125, 78.75, 11.25, 22.5}}, + {"p8ws1", Box{-86.484375, -86.4404296875, 166.684570312, 166.728515625}}, + {"vqt63tm6xx", Box{81.9873136282, 81.9873189926, 63.7062621117, 63.7062728405}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"bm20d", Box{74.619140625, 74.6630859375, -168.662109375, -168.618164062}}, + {"p3t4k9c0", Box{-81.1573791504, -81.157207489, 153.480377197, 153.48072052}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"8rsnr3k0", Box{43.2929992676, 43.293170929, -162.80090332, -162.800559998}}, + {"b739vykqw", Box{63.6243152618, 63.6243581772, -166.381845474, -166.381802559}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"13zrrq", Box{-78.8488769531, -78.8433837891, -113.236083984, -113.225097656}}, + {"6yk9jt", Box{-9.64050292969, -9.63500976562, -49.6801757812, -49.6691894531}}, + {"zmn0", Box{73.125, 73.30078125, 154.6875, 155.0390625}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"ps151cxkk8", Box{-66.9636869431, -66.9636815786, 158.993303776, 158.993314505}}, + {"tn", Box{33.75, 39.375, 45.0, 56.25}}, + {"u7", Box{61.875, 67.5, 11.25, 22.5}}, + {"55yuz", Box{-68.0712890625, -68.02734375, -35.2001953125, -35.15625}}, + {"x0p3grv8k", Box{0.350232124329, 0.350275039673, 145.345859528, 145.345902443}}, + {"y5c6gjhshg", Box{66.6053169966, 66.605322361, 91.896032095, 91.8960428238}}, + {"6fd2j", Box{-30.9375, -30.8935546875, -52.8662109375, -52.822265625}}, + {"gegbfkt19cj6", Box{66.2505683675, 66.2505685352, -17.1207369491, -17.1207366139}}, + {"k7", Box{-28.125, -22.5, 11.25, 22.5}}, + {"y1ydbr", Box{55.3656005859, 55.37109375, 99.1516113281, 99.1625976562}}, + {"byqsd", Box{80.947265625, 80.9912109375, -137.021484375, -136.977539062}}, + {"mfcbxhf", Box{-29.4172668457, -29.4158935547, 81.5213012695, 81.5226745605}}, + {"yxwd5901", Box{87.5447273254, 87.5448989868, 121.794433594, 121.794776917}}, + {"24k7s7y0gqgj", Box{-31.7077504657, -31.7077502981, -173.828286678, -173.828286342}}, + {"9031fbu", Box{1.71798706055, 1.71936035156, -133.467407227, -133.466033936}}, + {"xqusuduvmc", Box{38.8197237253, 38.8197290897, 152.782648802, 152.782659531}}, + {"5bxw", Box{-86.1328125, -85.95703125, -0.703125, -0.3515625}}, + {"xw593h52f", Box{33.9918279648, 33.9918708801, 162.470369339, 162.470412254}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"9xxm2s", Box{43.1323242188, 43.1378173828, -102.282714844, -102.271728516}}, + {"byf7t", Box{83.583984375, 83.6279296875, -142.866210938, -142.822265625}}, + {"v6", Box{56.25, 61.875, 56.25, 67.5}}, + {"yh", Box{67.5, 73.125, 90.0, 101.25}}, + {"d6k43xp", Box{13.0902099609, 13.091583252, -73.0494689941, -73.0480957031}}, + {"k4m5h", Box{-31.81640625, -31.7724609375, 7.20703125, 7.2509765625}}, + {"r1t7", Box{-36.03515625, -35.859375, 142.3828125, 142.734375}}, + {"cs4kvrjdkm7", Box{68.3738274872, 68.3738288283, -109.097485095, -109.097483754}}, + {"unu", Box{82.96875, 84.375, 5.625, 7.03125}}, + {"59xp", Box{-80.33203125, -80.15625, -12.65625, -12.3046875}}, + {"542p4hbm", Box{-76.0863304138, -76.0861587524, -44.9117660522, -44.9114227295}}, + {"5j", Box{-61.875, -56.25, -45.0, -33.75}}, + {"v3", Box{50.625, 56.25, 56.25, 67.5}}, + {"mstr13c0", Box{-18.4474182129, -18.4472465515, 74.9391174316, 74.9394607544}}, + {"wcvtdg61swv8", Box{10.8286933601, 10.8286935277, 131.608171687, 131.608172022}}, + {"3cm40m0w91z3", Box{-37.5885963254, -37.5885961577, -94.207024388, -94.2070240527}}, + {"m9fup0", Box{-34.453125, -34.4476318359, 71.6748046875, 71.6857910156}}, + {"jx", Box{-50.625, -45.0, 67.5, 78.75}}, + {"7myut8cg", Box{-11.8605995178, -11.8604278564, -24.013710022, -24.0133666992}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"5dgu11uuf12g", Box{-73.8176893629, -73.8176891953, -17.1760072187, -17.1760068834}}, + {"qp1ens1zqg", Box{-5.07442295551, -5.07441759109, 92.3977124691, 92.3977231979}}, + {"vusxu8ewwbrz", Box{71.6786695831, 71.6786697507, 85.2809854969, 85.2809858322}}, + {"8qjtgd1q", Box{34.7727584839, 34.7729301453, -160.860099792, -160.85975647}}, + {"60dppvw", Box{-40.9268188477, -40.9254455566, -86.838684082, -86.837310791}}, + {"tygxz83s2", Box{39.3331575394, 39.3332004547, 84.0035247803, 84.0035676956}}, + {"e0qwrc", Box{2.51037597656, 2.51586914062, -35.5187988281, -35.5078125}}, + {"5wyh2qbz", Box{-51.2458992004, -51.2457275391, -14.0504837036, -14.0501403809}}, + {"0zqs", Box{-48.515625, -48.33984375, -137.109375, -136.7578125}}, + {"n5ss", Box{-69.609375, -69.43359375, 96.328125, 96.6796875}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"v9q84x9n8ktx", Box{52.0735898428, 52.0735900104, 76.7518796772, 76.7518800125}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"2eh8wf4ktz", Box{-28.0253130198, -28.0253076553, -150.871907473, -150.871896744}}, + {"cr96dg281x", Box{87.6448434591, 87.6448488235, -121.870586872, -121.870576143}}, + {"56u", Box{-74.53125, -73.125, -28.125, -26.71875}}, + {"628vt", Box{-41.220703125, -41.1767578125, -77.4755859375, -77.431640625}}, + {"0heb", Box{-64.6875, -64.51171875, -174.7265625, -174.375}}, + {"8q5skg4mk", Box{34.5144510269, 34.5144939423, -163.616123199, -163.616080284}}, + {"7x0pw97495hw", Box{-4.2993279174, -4.29932774976, -22.2101866454, -22.2101863101}}, + {"1j4vjyz1dqx", Box{-60.9587225318, -60.9587211907, -130.870407969, -130.870406628}}, + {"u4q1szh", Box{57.9583740234, 57.9597473145, 8.65173339844, 8.65310668945}}, + {"3v3dbm2uv2", Box{-14.9556970596, -14.9556916952, -99.1283833981, -99.1283726692}}, + {"te3jtfwjnq", Box{19.2626702785, 19.262675643, 69.1674435139, 69.1674542427}}, + {"sgvgx3ey6qe", Box{21.7183318734, 21.7183332145, 42.1597914398, 42.1597927809}}, + {"2sw", Box{-19.6875, -18.28125, -149.0625, -147.65625}}, + {"wzy2pqu8e", Box{43.6309146881, 43.6309576035, 132.863974571, 132.864017487}}, + {"dk2849", Box{23.9117431641, 23.9172363281, -77.9370117188, -77.9260253906}}, + {"0d65", Box{-76.81640625, -76.640625, -154.6875, -154.3359375}}, + {"84", Box{11.25, 16.875, -180.0, -168.75}}, + {"q17", Box{-37.96875, -36.5625, 94.21875, 95.625}}, + {"x9wzer", Box{9.79431152344, 9.7998046875, 167.135009766, 167.145996094}}, + {"7xk2s7425n6", Box{-4.1143463552, -4.1143450141, -16.3334485888, -16.3334472477}}, + {"jv", Box{-61.875, -56.25, 78.75, 90.0}}, + {"u5juvw", Box{62.7429199219, 62.7484130859, 8.32763671875, 8.33862304688}}, + {"cnuczt0c", Box{83.3040046692, 83.3041763306, -127.989692688, -127.989349365}}, + {"7f3jv330zuh", Box{-31.3259911537, -31.3259898126, -9.61132586002, -9.61132451892}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"70jqgb8uv", Box{-43.8099145889, -43.8098716736, -37.4511480331, -37.4511051178}}, + {"u5t5d155", Box{65.3087425232, 65.3089141846, 7.12326049805, 7.1236038208}}, + {"r9hy1gz8m4p", Box{-38.2996594906, -38.2996581495, 164.267115444, 164.267116785}}, + {"qwmurb9920", Box{-9.09371852875, -9.09371316433, 120.928573608, 120.928584337}}, + {"d2pt693fw4", Box{0.930157899857, 0.930163264275, -68.0906009674, -68.0905902386}}, + {"zfgbx", Box{60.556640625, 60.6005859375, 174.331054688, 174.375}}, + {"c7mtdqkfuuc", Box{64.2828767002, 64.2828780413, -115.910019726, -115.910018384}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"w89eqd", Box{3.39477539062, 3.40026855469, 114.895019531, 114.906005859}}, + {"75mtem68vx", Box{-25.7229477167, -25.7229423523, -37.1191334724, -37.1191227436}}, + {"12e9fwr", Box{-86.8455505371, -86.8441772461, -118.708648682, -118.707275391}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"nq", Box{-56.25, -50.625, 101.25, 112.5}}, + {"svnx", Box{29.35546875, 29.53125, 42.890625, 43.2421875}}, + {"0e1kd5pxdgt", Box{-72.316198647, -72.3161973059, -155.64387247, -155.643871129}}, + {"pgk", Box{-71.71875, -70.3125, 174.375, 175.78125}}, + {"vry9q", Box{88.8134765625, 88.857421875, 65.654296875, 65.6982421875}}, + {"6f2x91u", Box{-31.0157775879, -31.0144042969, -55.4974365234, -55.4960632324}}, + {"3hefw", Box{-19.248046875, -19.2041015625, -129.462890625, -129.418945312}}, + {"g", Box{45.0, 90.0, -45.0, 0.0}}, + {"m66erp", Box{-31.7340087891, -31.728515625, 60.0732421875, 60.0842285156}}, + {"5nwny", Box{-52.2509765625, -52.20703125, -36.298828125, -36.2548828125}}, + {"d5", Box{16.875, 22.5, -90.0, -78.75}}, + {"wuphy8", Box{23.3349609375, 23.3404541016, 133.879394531, 133.890380859}}, + {"b8u631kf", Box{49.6214675903, 49.6216392517, -151.472969055, -151.472625732}}, + {"34d8j", Box{-30.9375, -30.8935546875, -131.264648438, -131.220703125}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"he", Box{-73.125, -67.5, 22.5, 33.75}}, + {"yec1yepjd", Box{66.4187908173, 66.4188337326, 114.201593399, 114.201636314}}, + {"h0p58hpz", Box{-89.3615913391, -89.3614196777, 9.85439300537, 9.85473632812}}, + {"8u", Box{22.5, 28.125, -146.25, -135.0}}, + {"hg9sxf0mu", Box{-69.509510994, -69.5094680786, 36.200466156, 36.2005090714}}, + {"7zdg4c9868", Box{-2.27687358856, -2.27686822414, -7.25979566574, -7.2597849369}}, + {"1h", Box{-67.5, -61.875, -135.0, -123.75}}, + {"k7h239zr4097", Box{-28.0702368356, -28.070236668, 17.3025243357, 17.302524671}}, + {"9h", Box{22.5, 28.125, -135.0, -123.75}}, + {"fx", Box{84.375, 90.0, -67.5, -56.25}}, + {"sf66h05", Box{13.0078125, 13.009185791, 37.093963623, 37.0953369141}}, + {"4ge39", Box{-70.048828125, -70.0048828125, -51.6357421875, -51.591796875}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"ypd", Box{87.1875, 88.59375, 92.8125, 94.21875}}, + {"gbzh2r", Box{50.0042724609, 50.009765625, -1.39526367188, -1.38427734375}}, + {"h0", Box{-90.0, -84.375, 0.0, 11.25}}, + {"nk7wkxwvku2", Box{-64.952994436, -64.9529930949, 106.379102468, 106.37910381}}, + {"23qu", Box{-37.265625, -37.08984375, -159.2578125, -158.90625}}, + {"n15b3", Box{-84.3310546875, -84.287109375, 95.3173828125, 95.361328125}}, + {"8x9c6f1cjkn", Box{42.4184060097, 42.4184073508, -154.915576279, -154.915574938}}, + {"5dr", Box{-77.34375, -75.9375, -12.65625, -11.25}}, + {"022q4", Box{-87.5390625, -87.4951171875, -168.310546875, -168.266601562}}, + {"52", Box{-90.0, -84.375, -33.75, -22.5}}, + {"s0j8hb", Box{0.0, 0.0054931640625, 7.94311523438, 7.9541015625}}, + {"58ygg9vn9nj", Box{-85.1113092899, -85.1113079488, -12.8470878303, -12.8470864892}}, + {"ztzqs1", Box{78.4918212891, 78.4973144531, 167.87109375, 167.882080078}}, + {"n5x", Box{-70.3125, -68.90625, 99.84375, 101.25}}, + {"jh593g3fsf", Box{-67.261980772, -67.2619754076, 50.001386404, 50.0013971329}}, + {"8vjg52364", Box{28.6540603638, 28.6541032791, -138.01943779, -138.019394875}}, + {"dqeh1", Box{37.265625, 37.3095703125, -74.4873046875, -74.443359375}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"m1mjx", Box{-37.001953125, -36.9580078125, 52.3388671875, 52.3828125}}, + {"w4fr", Box{16.69921875, 16.875, 93.1640625, 93.515625}}, + {"k2v0kj", Box{-40.7098388672, -40.7043457031, 18.45703125, 18.4680175781}}, + {"ytmdb30u", Box{75.0208282471, 75.0209999084, 120.246391296, 120.246734619}}, + {"wzuhv", Box{44.4287109375, 44.47265625, 129.594726562, 129.638671875}}, + {"84m2ue733hx1", Box{12.8061776049, 12.8061777726, -172.414918095, -172.41491776}}, + {"02", Box{-90.0, -84.375, -168.75, -157.5}}, + {"x8j5vd68s", Box{0.671625137329, 0.671668052673, 164.776554108, 164.776597023}}, + {"7k", Box{-22.5, -16.875, -33.75, -22.5}}, + {"4xtrffmbtxr", Box{-46.4377109706, -46.4377096295, -59.9881960452, -59.9881947041}}, + {"k8x", Box{-42.1875, -40.78125, 32.34375, 33.75}}, + {"yyxsh", Box{82.265625, 82.3095703125, 134.47265625, 134.516601562}}, + {"f2rqpzxu", Box{47.502822876, 47.5029945374, -68.2034683228, -68.203125}}, + {"d6h4jg", Box{11.6180419922, 11.6235351562, -72.8723144531, -72.861328125}}, + {"e7d", Box{19.6875, 21.09375, -30.9375, -29.53125}}, + {"bbs0tcr5wc9", Box{47.9078659415, 47.9078672826, -140.362410396, -140.362409055}}, + {"7fuf", Box{-29.1796875, -29.00390625, -4.5703125, -4.21875}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"tt9p6y", Box{32.2448730469, 32.2503662109, 69.0270996094, 69.0380859375}}, + {"xypsv42g6pr", Box{34.5979173481, 34.5979186893, 179.517726749, 179.51772809}}, + {"p4gvh3sr97h", Box{-73.6428004503, -73.6427991092, 140.466100574, 140.466101915}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"6ecy4rk", Box{-22.8117370605, -22.8103637695, -64.9346923828, -64.9333190918}}, + {"045xe38uj", Box{-77.4227142334, -77.4226713181, -174.934058189, -174.934015274}}, + {"xyunrc48", Box{39.0728759766, 39.0730476379, 174.719009399, 174.719352722}}, + {"enm03", Box{35.2001953125, 35.244140625, -37.9248046875, -37.880859375}}, + {"n7r9k6ecbhm", Box{-71.4849673212, -71.4849659801, 111.988799125, 111.988800466}}, + {"2bpxnf2", Box{-43.7571716309, -43.7557983398, -135.406494141, -135.40512085}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"vs65p7h9m", Box{69.4502878189, 69.4503307343, 70.6374979019, 70.6375408173}}, + {"j6vt6yyjmms", Box{-73.5703888535, -73.5703875124, 64.1136950254, 64.1136963665}}, + {"pp7z8s7", Box{-47.8770446777, -47.8756713867, 140.299530029, 140.30090332}}, + {"skxsqyzdgn", Box{26.0971534252, 26.0971587896, 22.103934288, 22.1039450169}}, + {"y1x7mdy", Box{54.0238952637, 54.0252685547, 100.445251465, 100.446624756}}, + {"ck9n9vptne", Box{71.4834183455, 71.4834237099, -122.256267071, -122.256256342}}, + {"fq7u12930mgt", Box{80.862324927, 80.8623250946, -73.4198988229, -73.4198984876}}, + {"zs50j6", Box{67.5109863281, 67.5164794922, 161.949462891, 161.960449219}}, + {"9jccmq0j", Box{32.5972938538, 32.5974655151, -132.308349609, -132.308006287}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"hhtn7", Box{-63.5888671875, -63.544921875, 7.1630859375, 7.20703125}}, + {"480spdnxu", Box{-89.2845582962, -89.2845153809, -66.4581871033, -66.4581441879}}, + {"ghz87yh", Box{71.7956542969, 71.7970275879, -34.2828369141, -34.281463623}}, + {"cf", Box{56.25, 61.875, -101.25, -90.0}}, + {"gd1n5pnc8p7", Box{57.3434360325, 57.3434373736, -20.9526403248, -20.9526389837}}, + {"hef9dvw3q", Box{-68.6121511459, -68.6121082306, 26.1453151703, 26.1453580856}}, + {"wbjwhe8rkyf0", Box{1.07519432902, 1.07519449666, 131.682678759, 131.682679094}}, + {"pndb3wg", Box{-53.3564758301, -53.3551025391, 138.937225342, 138.938598633}}, + {"bh2k52ewybs", Box{69.6132829785, 69.6132843196, -179.500513673, -179.500512332}}, + {"t4r", Box{12.65625, 14.0625, 54.84375, 56.25}}, + {"s7215", Box{18.45703125, 18.5009765625, 11.3818359375, 11.42578125}}, + {"u8", Box{45.0, 50.625, 22.5, 33.75}}, + {"f2", Box{45.0, 50.625, -78.75, -67.5}}, + {"3hwb5r0etbt", Box{-19.6484443545, -19.6484430134, -125.36405012, -125.364048779}}, + {"wendsfh63", Box{17.3258256912, 17.3258686066, 121.855244637, 121.855287552}}, + {"90n", Box{0.0, 1.40625, -126.5625, -125.15625}}, + {"e5mx13zs4t2", Box{19.5220465958, 19.5220479369, -37.2002863884, -37.2002850473}}, + {"6kngstqxn", Box{-21.854724884, -21.8546819687, -69.0508747101, -69.0508317947}}, + {"rgs", Box{-25.3125, -23.90625, 174.375, 175.78125}}, + {"sbd1n1z90", Box{2.99806594849, 2.99810886383, 36.8364715576, 36.836514473}}, + {"693c7m26y6", Box{-37.7197015285, -37.7196961641, -64.8956286907, -64.8956179619}}, + {"ynu", Box{82.96875, 84.375, 95.625, 97.03125}}, + {"68t74uch2444", Box{-41.6333230957, -41.6333229281, -59.9949619174, -59.9949615821}}, + {"jcmv2zscc", Box{-82.0043992996, -82.0043563843, 86.875462532, 86.8755054474}}, + {"3c", Box{-39.375, -33.75, -101.25, -90.0}}, + {"r76ymc", Box{-25.6146240234, -25.6091308594, 150.369873047, 150.380859375}}, + {"kj9vj024cd", Box{-13.1817376614, -13.1817322969, 2.68072843552, 2.68073916435}}, + {"scr", Box{7.03125, 8.4375, 43.59375, 45.0}}, + {"57g8wvk", Box{-68.7895202637, -68.7881469727, -28.5260009766, -28.5246276855}}, + {"8qde6", Box{37.1337890625, 37.177734375, -165.146484375, -165.102539062}}, + {"7ve260", Box{-14.0185546875, -14.0130615234, -6.591796875, -6.58081054688}}, + {"trcxzhy6", Box{44.9824905396, 44.9826622009, 58.6755752563, 58.6759185791}}, + {"syqw7fyhx", Box{36.2707614899, 36.2708044052, 43.0639600754, 43.0640029907}}, + {"tb0", Box{0.0, 1.40625, 78.75, 80.15625}}, + {"dttugd1xtj5p", Box{31.7847627215, 31.7847628891, -59.2579753697, -59.2579750344}}, + {"v899zsbyh7f2", Box{48.1472598016, 48.1472599693, 69.9401802197, 69.940180555}}, + {"8e", Box{16.875, 22.5, -157.5, -146.25}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"ny4017r8x", Box{-56.2320613861, -56.2320184708, 126.628031731, 126.628074646}}, + {"8ued", Box{25.6640625, 25.83984375, -141.328125, -140.9765625}}, + {"bqpsbj0h9p1t", Box{79.6132376231, 79.6132377908, -158.203080073, -158.203079738}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"b5m", Box{63.28125, 64.6875, -172.96875, -171.5625}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"w722c", Box{18.4130859375, 18.45703125, 101.645507812, 101.689453125}}, + {"ey", Box{33.75, 39.375, -11.25, 0.0}}, + {"6ndrutd", Box{-7.04498291016, -7.04360961914, -86.6354370117, -86.6340637207}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"47jtykh", Box{-72.0922851562, -72.0909118652, -70.7354736328, -70.7341003418}}, + {"krb5qvkb", Box{-0.806121826172, -0.805950164795, 11.5531539917, 11.5534973145}}, + {"mtpgnes7uugs", Box{-16.3277602941, -16.3277601264, 78.6901270598, 78.6901273951}}, + {"6062z", Box{-43.4619140625, -43.41796875, -86.5283203125, -86.484375}}, + {"241quukp", Box{-32.5389289856, -32.5387573242, -178.027954102, -178.027610779}}, + {"g4m49", Box{58.095703125, 58.1396484375, -37.9248046875, -37.880859375}}, + {"wwq", Box{35.15625, 36.5625, 120.9375, 122.34375}}, + {"beujb1c", Box{67.1141052246, 67.1154785156, -151.873626709, -151.872253418}}, + {"h4", Box{-78.75, -73.125, 0.0, 11.25}}, + {"69xq1n9v", Box{-35.4712486267, -35.4710769653, -57.2583389282, -57.2579956055}}, + {"cjpu", Box{73.828125, 74.00390625, -124.1015625, -123.75}}, + {"pmks2611hw", Box{-59.7104895115, -59.7104841471, 152.590677738, 152.590688467}}, + {"zd78cuj300z8", Box{57.8102342784, 57.8102344461, 162.505999133, 162.505999468}}, + {"g9h460p7719", Box{51.0210737586, 51.0210750997, -16.777022928, -16.7770215869}}, + {"crncwf7g0c", Box{84.6515518427, 84.6515572071, -113.955999613, -113.955988884}}, + {"mp4", Box{-5.625, -4.21875, 47.8125, 49.21875}}, + {"3ghgz3gsjgr", Box{-27.4555031955, -27.4555018544, -94.2466463149, -94.2466449738}}, + {"m2g9", Box{-40.60546875, -40.4296875, 61.171875, 61.5234375}}, + {"ngt309w", Box{-70.1284790039, -70.1271057129, 131.163024902, 131.164398193}}, + {"hgn9u3537p", Box{-72.8116375208, -72.8116321564, 43.08198452, 43.0819952488}}, + {"6spnhu", Box{-21.4233398438, -21.4178466797, -57.4475097656, -57.4365234375}}, + {"z22v4r0d82", Box{47.3240375519, 47.3240429163, 147.404261827, 147.404272556}}, + {"ytypn", Box{78.57421875, 78.6181640625, 121.201171875, 121.245117188}}, + {"qstc", Box{-19.51171875, -19.3359375, 120.5859375, 120.9375}}, + {"y8r48c5", Box{46.8511962891, 46.8525695801, 122.380828857, 122.382202148}}, + {"uq2xybqqg", Box{81.5210866928, 81.5211296082, 12.2584676743, 12.2585105896}}, + {"95xm8dtz5u42", Box{20.6692528725, 20.6692530401, -124.77465447, -124.774654135}}, + {"x0uqxwj", Box{5.39428710938, 5.39566040039, 141.313018799, 141.31439209}}, + {"0kve9v", Box{-62.6385498047, -62.6330566406, -160.938720703, -160.927734375}}, + {"szwv1er87", Box{43.0843019485, 43.0843448639, 43.3185338974, 43.3185768127}}, + {"88zscymedp", Box{5.08868157864, 5.08868694305, -146.868581772, -146.868571043}}, + {"pd", Box{-78.75, -73.125, 157.5, 168.75}}, + {"g6", Box{56.25, 61.875, -33.75, -22.5}}, + {"3pg6r201vv51", Box{-1.01041479036, -1.01041462272, -130.110833198, -130.110832863}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"g02", Box{46.40625, 47.8125, -45.0, -43.59375}}, + {"jx", Box{-50.625, -45.0, 67.5, 78.75}}, + {"zksxs2nz2j3", Box{71.6321320832, 71.6321334243, 152.774163634, 152.774164975}}, + {"erqyg8ff4d", Box{41.9722473621, 41.9722527266, -24.1001200676, -24.1001093388}}, + {"n3wm1r3p4jev", Box{-80.6425363384, -80.6425361708, 110.095458291, 110.095458627}}, + {"sqfbq13pjp9", Box{38.0208036304, 38.0208049715, 15.3824485838, 15.3824499249}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"y0", Box{45.0, 50.625, 90.0, 101.25}}, + {"04q3tpcc68bq", Box{-77.0372864977, -77.03728633, -170.988700055, -170.988699719}}, + {"81", Box{5.625, 11.25, -180.0, -168.75}}, + {"nj", Box{-61.875, -56.25, 90.0, 101.25}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"8bxs0rfgp3n7", Box{3.55871787295, 3.55871804059, -135.688042603, -135.688042268}}, + {"j0rm6", Box{-87.6708984375, -87.626953125, 55.283203125, 55.3271484375}}, + {"86v9", Box{15.64453125, 15.8203125, -161.015625, -160.6640625}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"nmcsv", Box{-56.8212890625, -56.77734375, 103.579101562, 103.623046875}}, + {"cqytkn1d56", Box{83.9249145985, 83.9249199629, -114.431394339, -114.43138361}}, + {"jfpcxxud86r9", Box{-78.4433147125, -78.4433145449, 89.9842279404, 89.9842282757}}, + {"zf8cm8fkrg", Box{59.2870920897, 59.2870974541, 170.049809217, 170.049819946}}, + {"98x", Box{2.8125, 4.21875, -102.65625, -101.25}}, + {"e5mbzuu", Box{18.4391784668, 18.4405517578, -36.5679931641, -36.566619873}}, + {"03g5kqkcp", Box{-79.5504570007, -79.5504140854, -164.337658882, -164.337615967}}, + {"b362q0b79x2", Box{52.0799548924, 52.0799562335, -165.321857929, -165.321856588}}, + {"m3pq", Box{-38.3203125, -38.14453125, 66.4453125, 66.796875}}, + {"6564m2w5b0", Box{-26.3198518753, -26.3198465109, -86.9485473633, -86.9485366344}}, + {"m9puw71wrf", Box{-38.5664212704, -38.566415906, 78.6754882336, 78.6754989624}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"9t34u", Box{30.0146484375, 30.05859375, -110.91796875, -110.874023438}}, + {"hewq3", Box{-69.2138671875, -69.169921875, 31.3330078125, 31.376953125}}, + {"2m2uy1tgg0", Box{-14.6249055862, -14.6249002218, -167.423615456, -167.423604727}}, + {"qu8dxgebd9wz", Box{-19.22872575, -19.2287255824, 124.798967354, 124.798967689}}, + {"scwmj", Box{9.31640625, 9.3603515625, 42.7587890625, 42.802734375}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"zm02460g", Box{73.1365013123, 73.1366729736, 146.701469421, 146.701812744}}, + {"cgtd84ky", Box{65.1403427124, 65.1405143738, -93.5091018677, -93.5087585449}}, + {"eq", Box{33.75, 39.375, -33.75, -22.5}}, + {"ht26dn8h", Box{-59.9929046631, -59.9927330017, 22.939453125, 22.9397964478}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"j70dzsu", Box{-72.6155090332, -72.6141357422, 57.2882080078, 57.2895812988}}, + {"y241djby0dk", Box{45.2962996066, 45.2963009477, 104.151447415, 104.151448756}}, + {"un9", Box{81.5625, 82.96875, 1.40625, 2.8125}}, + {"2kzdj9", Box{-17.9241943359, -17.9187011719, -157.961425781, -157.950439453}}, + {"m7b3gj70", Box{-23.5697937012, -23.5696220398, 56.7375183105, 56.7378616333}}, + {"vvpmy3", Box{74.1412353516, 74.1467285156, 89.2199707031, 89.2309570312}}, + {"ym0n0", Box{74.1796875, 74.2236328125, 101.25, 101.293945312}}, + {"k6", Box{-33.75, -28.125, 11.25, 22.5}}, + {"d96nj5sqmjfn", Box{8.10626830906, 8.10626847669, -64.4617196918, -64.4617193565}}, + {"9yfrdwk", Box{39.3214416504, 39.3228149414, -97.9705810547, -97.9692077637}}, + {"8vj712jd0kv", Box{28.6527125537, 28.6527138948, -138.804685324, -138.804683983}}, + {"bk86yhd", Box{70.8206176758, 70.8219909668, -168.132019043, -168.130645752}}, + {"6e3pb2s87q10", Box{-25.3536236286, -25.353623461, -66.0764430463, -66.0764427111}}, + {"nxbn2n7yn", Box{-45.2722549438, -45.2722120285, 112.505407333, 112.505450249}}, + {"cg4n", Box{62.9296875, 63.10546875, -98.4375, -98.0859375}}, + {"4de6s37sk467", Box{-75.4904382862, -75.4904381186, -62.7379387245, -62.7379383892}}, + {"pzegg", Box{-47.1533203125, -47.109375, 174.155273438, 174.19921875}}, + {"xytyx62", Box{37.7174377441, 37.7188110352, 177.154541016, 177.155914307}}, + {"2x4", Box{-5.625, -4.21875, -154.6875, -153.28125}}, + {"j11m8j1eywcu", Box{-83.3800566941, -83.3800565265, 46.7601537332, 46.7601540685}}, + {"k2xw57e", Box{-41.1135864258, -41.1122131348, 21.9438171387, 21.9451904297}}, + {"2q74", Box{-9.4921875, -9.31640625, -164.53125, -164.1796875}}, + {"9tp1960t8", Box{28.4006023407, 28.400645256, -102.600631714, -102.600588799}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"2b2mwb", Box{-42.626953125, -42.6214599609, -145.601806641, -145.590820312}}, + {"b7ts80s", Box{65.481262207, 65.482635498, -161.010131836, -161.008758545}}, + {"10x0", Box{-87.1875, -87.01171875, -125.15625, -124.8046875}}, + {"c5p9s65qqch", Box{62.1507364511, 62.1507377923, -124.261599183, -124.261597842}}, + {"n1j", Box{-84.375, -82.96875, 97.03125, 98.4375}}, + {"gjy9e2g7hcvc", Box{77.6120662875, 77.6120664552, -35.7118779793, -35.7118776441}}, + {"d0nngw26hnc0", Box{1.22123524547, 1.2212354131, -81.408175081, -81.4081747457}}, + {"sqqh99ewn", Box{35.9565353394, 35.9565782547, 19.7584819794, 19.7585248947}}, + {"27", Box{-28.125, -22.5, -168.75, -157.5}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"uv", Box{73.125, 78.75, 33.75, 45.0}}, + {"nu6", Box{-66.09375, -64.6875, 126.5625, 127.96875}}, + {"hjg9s97mjbfp", Box{-57.3848481663, -57.3848479986, 5.12434154749, 5.12434188277}}, + {"cekd", Box{63.6328125, 63.80859375, -106.171875, -105.8203125}}, + {"fx2s", Box{86.484375, 86.66015625, -66.796875, -66.4453125}}, + {"zxx5n4wh37", Box{87.7293223143, 87.7293276787, 167.615715265, 167.615725994}}, + {"k0redc", Box{-42.9730224609, -42.9675292969, 10.6677246094, 10.6787109375}}, + {"xhj810f1r7", Box{22.504350543, 22.5043559074, 142.781378031, 142.78138876}}, + {"9j", Box{28.125, 33.75, -135.0, -123.75}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"z59900erg", Box{64.8673582077, 64.867401123, 137.113966942, 137.114009857}}, + {"6dnccesgx8ts", Box{-33.4225525707, -33.4225524031, -57.9350421578, -57.9350418225}}, + {"jx", Box{-50.625, -45.0, 67.5, 78.75}}, + {"312y4y56", Box{-36.8807601929, -36.8805885315, -133.819999695, -133.819656372}}, + {"32vk3g9np", Box{-40.013923645, -40.0138807297, -116.288609505, -116.288566589}}, + {"kn39", Box{-9.66796875, -9.4921875, 2.109375, 2.4609375}}, + {"eed4ybdn4mpp", Box{20.1747029833, 20.174703151, -19.3880166113, -19.3880162761}}, + {"st", Box{28.125, 33.75, 22.5, 33.75}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"0t8kw4xyhu5x", Box{-58.2566988654, -58.2566986978, -156.873914078, -156.873913743}}, + {"dcy", Box{9.84375, 11.25, -47.8125, -46.40625}}, + {"p3r", Box{-82.96875, -81.5625, 156.09375, 157.5}}, + {"wdc0w9tbd6", Box{15.5649769306, 15.564982295, 114.199887514, 114.199898243}}, + {"j3jc", Box{-84.19921875, -84.0234375, 64.3359375, 64.6875}}, + {"k5cw3t", Box{-22.7801513672, -22.7746582031, 2.17529296875, 2.18627929688}}, + {"pd6m", Box{-76.46484375, -76.2890625, 160.6640625, 161.015625}}, + {"hnfqkjqrqnh", Box{-50.9025013447, -50.9025000036, 3.34868967533, 3.34869101644}}, + {"6qkwd", Box{-8.701171875, -8.6572265625, -72.333984375, -72.2900390625}}, + {"1q2x", Box{-53.61328125, -53.4375, -123.046875, -122.6953125}}, + {"3nps1et4nde", Box{-10.527292192, -10.5272908509, -124.380057603, -124.380056262}}, + {"gq2c8qnkx2", Box{80.4536533356, 80.4536587, -32.6754319668, -32.6754212379}}, + {"q7d", Box{-25.3125, -23.90625, 104.0625, 105.46875}}, + {"560", Box{-78.75, -77.34375, -33.75, -32.34375}}, + {"j855ffmuxv4s", Box{-89.3276607245, -89.3276605569, 71.8478319794, 71.8478323147}}, + {"sumfzt8z4", Box{24.4210624695, 24.4211053848, 42.1666431427, 42.166686058}}, + {"bje9d2n0", Box{76.201171875, 76.2013435364, -174.971008301, -174.970664978}}, + {"y943", Box{50.80078125, 50.9765625, 115.6640625, 116.015625}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"gmn", Box{73.125, 74.53125, -25.3125, -23.90625}}, + {"1djwe5s", Box{-77.5881958008, -77.5868225098, -104.628295898, -104.626922607}}, + {"tb5wv3", Box{1.19201660156, 1.19750976562, 83.9025878906, 83.9135742188}}, + {"58h3qnfv9m", Box{-89.7422236204, -89.742218256, -16.2559354305, -16.2559247017}}, + {"f0du0sguugkg", Box{48.5425508581, 48.5425510257, -86.1054797843, -86.105479449}}, + {"h6zyvwk", Box{-73.3103942871, -73.3090209961, 22.3956298828, 22.3970031738}}, + {"vg2nu", Box{64.4677734375, 64.51171875, 78.92578125, 78.9697265625}}, + {"j7r7de", Box{-71.0870361328, -71.0815429688, 66.5551757812, 66.5661621094}}, + {"hjshn", Box{-58.359375, -58.3154296875, 5.888671875, 5.9326171875}}, + {"46khrs1t", Box{-76.5738487244, -76.573677063, -72.7933502197, -72.793006897}}, + {"g5p5p", Box{62.40234375, 62.4462890625, -34.8486328125, -34.8046875}}, + {"7pxgy2sf", Box{-2.15023040771, -2.15005874634, -33.8203811646, -33.8200378418}}, + {"vrz", Box{88.59375, 90.0, 66.09375, 67.5}}, + {"bry3ngyuq", Box{88.7908601761, 88.7909030914, -159.654779434, -159.654736519}}, + {"9cu9t1g3cs", Box{10.1173567772, 10.1173621416, -94.6976208687, -94.6976101398}}, + {"82", Box{0.0, 5.625, -168.75, -157.5}}, + {"m9yzmzsmxx", Box{-33.8396555185, -33.8396501541, 77.2510313988, 77.2510421276}}, + {"4wv4wwxb2dr2", Box{-51.5560363233, -51.5560361557, -60.1724312827, -60.1724309474}}, + {"j7ufy8", Box{-68.4228515625, -68.4173583984, 63.2153320312, 63.2263183594}}, + {"hzrjzg9xjj", Box{-48.1875532866, -48.1875479221, 43.9366006851, 43.936611414}}, + {"z5eh3ghtm7", Box{65.4519671202, 65.4519724846, 139.302059412, 139.302070141}}, + {"6sjhet2cts", Box{-21.6798663139, -21.6798609495, -60.3136754036, -60.3136646748}}, + {"vjyn6v", Box{78.4698486328, 78.4753417969, 53.5583496094, 53.5693359375}}, + {"hysxqy94", Box{-52.1270370483, -52.126865387, 40.3761291504, 40.3764724731}}, + {"8yd4qxm0d", Box{36.9979190826, 36.997961998, -143.144903183, -143.144860268}}, + {"w43b42f", Box{12.660369873, 12.6617431641, 92.5625610352, 92.5639343262}}, + {"7suc291x", Box{-18.0548286438, -18.0546569824, -15.7962799072, -15.7959365845}}, + {"pq", Box{-56.25, -50.625, 146.25, 157.5}}, + {"frd", Box{87.1875, 88.59375, -75.9375, -74.53125}}, + {"r2", Box{-45.0, -39.375, 146.25, 157.5}}, + {"5rs1x50fe5m", Box{-47.531902045, -47.5319007039, -27.8162173927, -27.8162160516}}, + {"zjv30682s21k", Box{77.5333506614, 77.533350829, 142.394326217, 142.394326553}}, + {"zbbtmw", Box{50.1745605469, 50.1800537109, 169.694824219, 169.705810547}}, + {"e56k", Box{18.984375, 19.16015625, -41.8359375, -41.484375}}, + {"v7dzp", Box{65.91796875, 65.9619140625, 60.4248046875, 60.46875}}, + {"n2z8qt2hnzss", Box{-85.707738027, -85.7077378593, 112.082815245, 112.08281558}}, + {"zbp8bt07bb5", Box{45.159945488, 45.1599468291, 179.319227189, 179.31922853}}, + {"s551t", Box{17.138671875, 17.1826171875, 4.4384765625, 4.482421875}}, + {"5zp7trjp", Box{-49.9701118469, -49.9699401855, -0.817108154297, -0.816764831543}}, + {"81n2z7dz", Box{5.77726364136, 5.77743530273, -170.888557434, -170.888214111}}, + {"dw", Box{33.75, 39.375, -67.5, -56.25}}, + {"vvw35", Box{76.11328125, 76.1572265625, 87.6708984375, 87.71484375}}, + {"zhtuc3b8bg5n", Box{71.1572198197, 71.1572199874, 143.141591996, 143.141592331}}, + {"042w4qgx", Box{-76.2507820129, -76.2506103516, -179.193191528, -179.192848206}}, + {"9sntb7", Box{23.5272216797, 23.5327148438, -103.348388672, -103.337402344}}, + {"wt6x5nxk", Box{30.7981109619, 30.7982826233, 116.157417297, 116.15776062}}, + {"spzwehn", Box{44.7583007812, 44.7596740723, 10.6869506836, 10.6883239746}}, + {"hv70sbbpw64", Box{-60.3754413128, -60.3754399717, 38.1777611375, 38.1777624786}}, + {"wxt", Box{42.1875, 43.59375, 119.53125, 120.9375}}, + {"7dvqsskj8", Box{-28.3643817902, -28.3643388748, -14.9139404297, -14.9138975143}}, + {"wm22", Box{29.53125, 29.70703125, 101.6015625, 101.953125}}, + {"5t4kf", Box{-61.0400390625, -60.99609375, -19.248046875, -19.2041015625}}, + {"5sy67z47fz1", Box{-62.846608758, -62.8466074169, -13.542933315, -13.5429319739}}, + {"c36nw2msv0t", Box{53.1760194898, 53.1760208309, -120.655067414, -120.655066073}}, + {"311v", Box{-38.49609375, -38.3203125, -132.5390625, -132.1875}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"xcykeys", Box{10.6704711914, 10.6718444824, 177.709350586, 177.710723877}}, + {"gtmvbbv", Box{75.5461120605, 75.5474853516, -14.3742370605, -14.3728637695}}, + {"5n1", Box{-56.25, -54.84375, -43.59375, -42.1875}}, + {"y08uf", Box{48.6474609375, 48.69140625, 91.142578125, 91.1865234375}}, + {"ds4", Box{22.5, 23.90625, -64.6875, -63.28125}}, + {"1t49n9", Box{-61.6937255859, -61.6882324219, -108.698730469, -108.687744141}}, + {"v81d55x758", Box{45.3713035583, 45.3713089228, 69.7513175011, 69.7513282299}}, + {"39j80vxmjh", Box{-39.3439078331, -39.3439024687, -104.722495079, -104.72248435}}, + {"x3vd66k2vj46", Box{10.251773335, 10.2517735027, 154.089306034, 154.089306369}}, + {"vg", Box{61.875, 67.5, 78.75, 90.0}}, + {"06q04jnnuyz", Box{-77.3150892556, -77.3150879145, -160.216156393, -160.216155052}}, + {"cvws", Box{76.640625, 76.81640625, -92.109375, -91.7578125}}, + {"9d5j84gmgbv", Box{12.2328941524, 12.2328954935, -108.276619166, -108.276617825}}, + {"mjg", Box{-12.65625, -11.25, 49.21875, 50.625}}, + {"k7gspu", Box{-23.1811523438, -23.1756591797, 16.5124511719, 16.5234375}}, + {"13nrmggfx", Box{-83.0795574188, -83.0795145035, -114.702801704, -114.702758789}}, + {"ypj759", Box{84.9078369141, 84.9133300781, 97.5366210938, 97.5476074219}}, + {"hhy3z0zjn", Box{-62.9686546326, -62.9686117172, 9.10655021667, 9.10659313202}}, + {"b0xhjv", Box{48.5430908203, 48.5485839844, -169.903564453, -169.892578125}}, + {"xucn7t7t11", Box{27.8470855951, 27.8470909595, 170.314908028, 170.314918756}}, + {"f0yqwpw6", Box{50.4028701782, 50.4030418396, -80.9386825562, -80.9383392334}}, + {"hcud164f7z", Box{-79.7932773829, -79.7932720184, 40.1369941235, 40.1370048523}}, + {"k7b09t", Box{-23.7908935547, -23.7854003906, 11.3159179688, 11.3269042969}}, + {"gzttr69", Box{88.1240844727, 88.1254577637, -3.19564819336, -3.19427490234}}, + {"z1", Box{50.625, 56.25, 135.0, 146.25}}, + {"mt", Box{-16.875, -11.25, 67.5, 78.75}}, + {"vgpm3pe", Box{62.839050293, 62.840423584, 88.9933776855, 88.9947509766}}, + {"xc2xk", Box{8.3056640625, 8.349609375, 169.62890625, 169.672851562}}, + {"7gpegjrrn", Box{-27.4357795715, -27.4357366562, -0.561075210571, -0.561032295227}}, + {"5m00s41bg21m", Box{-61.7759934627, -61.775993295, -33.5716743395, -33.5716740042}}, + {"ybz9un", Box{49.5593261719, 49.5648193359, 134.47265625, 134.483642578}}, + {"5cxhpu5jy6y", Box{-80.8364005387, -80.8363991976, -1.06127768755, -1.06127634645}}, + {"0gk7412", Box{-71.1845397949, -71.1831665039, -140.185546875, -140.184173584}}, + {"zj2", Box{74.53125, 75.9375, 135.0, 136.40625}}, + {"6jj5vt6zv5", Box{-16.1856347322, -16.1856293678, -82.7230596542, -82.7230489254}}, + {"mdp7x0cy8x", Box{-33.1294924021, -33.1294870377, 78.0053544044, 78.0053651333}}, + {"515", Box{-84.375, -82.96875, -40.78125, -39.375}}, + {"rcrpxqxje", Box{-36.613740921, -36.6136980057, 178.922095299, 178.922138214}}, + {"ydd5n3qr6tu", Box{59.5979855955, 59.5979869366, 115.595853925, 115.595855266}}, + {"hd", Box{-78.75, -73.125, 22.5, 33.75}}, + {"rj4uc", Box{-16.0400390625, -15.99609375, 138.911132812, 138.955078125}}, + {"0r3x", Box{-47.98828125, -47.8125, -166.640625, -166.2890625}}, + {"490rp8g4y", Box{-83.1399393082, -83.1398963928, -66.8144702911, -66.8144273758}}, + {"v19nuj", Box{54.6514892578, 54.6569824219, 46.58203125, 46.5930175781}}, + {"wrg3hx7", Box{43.8093566895, 43.8107299805, 106.022186279, 106.02355957}}, + {"ntzjry4", Box{-56.7004394531, -56.6990661621, 122.687072754, 122.688446045}}, + {"3s5c85t2d", Box{-22.2170162201, -22.2169733047, -107.219266891, -107.219223976}}, + {"wyy", Box{37.96875, 39.375, 132.1875, 133.59375}}, + {"6wk17", Box{-9.6240234375, -9.580078125, -61.7431640625, -61.69921875}}, + {"7m5wqccz7meg", Box{-15.7654795982, -15.7654794306, -28.5289463773, -28.5289460421}}, + {"sk8n5z1qd2", Box{26.4067554474, 26.4067608118, 11.4166080952, 11.416618824}}, + {"kw8hyjjj384", Box{-7.57417201996, -7.57417067885, 22.7706053853, 22.7706067264}}, + {"14rw6bt3vx9v", Box{-76.2420291267, -76.2420289591, -124.324827231, -124.324826896}}, + {"9cdu", Box{9.140625, 9.31640625, -97.3828125, -97.03125}}, + {"0cptj3e6crgm", Box{-83.4873395227, -83.4873393551, -135.467890911, -135.467890576}}, + {"0m", Box{-61.875, -56.25, -168.75, -157.5}}, + {"mx", Box{-5.625, 0.0, 67.5, 78.75}}, + {"p1sfuc", Box{-81.0736083984, -81.0681152344, 141.888427734, 141.899414062}}, + {"03nduncm", Box{-83.8536643982, -83.8534927368, -159.431877136, -159.431533813}}, + {"0m0j", Box{-60.99609375, -60.8203125, -168.75, -168.3984375}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"fn", Box{78.75, 84.375, -90.0, -78.75}}, + {"srddrb0", Box{42.5830078125, 42.5843811035, 15.1062011719, 15.1075744629}}, + {"wpbf1dnj", Box{43.957157135, 43.9573287964, 91.1288452148, 91.1291885376}}, + {"nv8k1f98", Box{-58.3456420898, -58.3454704285, 124.180526733, 124.180870056}}, + {"7upj4ct1", Box{-21.6126823425, -21.6125106812, -1.27853393555, -1.27819061279}}, + {"k312nzxpwm", Box{-39.3324869871, -39.3324816227, 13.3143246174, 13.3143353462}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"qure08zn2n", Box{-20.5611813068, -20.5611759424, 134.328460693, 134.328471422}}, + {"m1", Box{-39.375, -33.75, 45.0, 56.25}}, + {"q7wbtv20e", Box{-25.195684433, -25.1956415176, 110.995001793, 110.995044708}}, + {"fsqx0ywr3s", Box{70.1736903191, 70.1736956835, -58.3177685738, -58.3177578449}}, + {"rn9k7k2927vd", Box{-7.66684871167, -7.66684854403, 136.901339516, 136.901339851}}, + {"8ypg", Box{34.27734375, 34.453125, -135.3515625, -135.0}}, + {"42ru", Box{-87.890625, -87.71484375, -67.8515625, -67.5}}, + {"z3efhtnprhw", Box{53.8177970052, 53.8177983463, 151.729739606, 151.729740947}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"fu", Box{67.5, 73.125, -56.25, -45.0}}, + {"uz9", Box{87.1875, 88.59375, 35.15625, 36.5625}}, + {"bqdnsr4y4", Box{82.7445602417, 82.744603157, -165.746870041, -165.746827126}}, + {"rshpnngvd", Box{-21.231508255, -21.2314653397, 163.393907547, 163.393950462}}, + {"4egtrvjfe", Box{-67.9555034637, -67.9554605484, -62.2295236588, -62.2294807434}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"r2m2yc", Box{-43.4564208984, -43.4509277344, 153.929443359, 153.940429688}}, + {"v9vps2z7", Box{56.1667442322, 56.1669158936, 74.727973938, 74.7283172607}}, + {"dh", Box{22.5, 28.125, -90.0, -78.75}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"7", Box{-45.0, 0.0, -45.0, 0.0}}, + {"rrtp", Box{-1.58203125, -1.40625, 153.28125, 153.6328125}}, + {"57wdet", Box{-69.8455810547, -69.8400878906, -24.4555664062, -24.4445800781}}, + {"sxm3rvsc1x0y", Box{41.031399183, 41.0313993506, 30.229977183, 30.2299775183}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"fmwp70q5", Box{77.2138023376, 77.213973999, -70.1724243164, -70.1720809937}}, + {"vq3whey", Box{81.2315368652, 81.2329101562, 58.5653686523, 58.5667419434}}, + {"vm55829xumn", Box{73.7443381548, 73.7443394959, 60.4819867015, 60.4819880426}}, + {"pc", Box{-84.375, -78.75, 168.75, 180.0}}, + {"76j", Box{-33.75, -32.34375, -26.71875, -25.3125}}, + {"du5md", Box{23.466796875, 23.5107421875, -51.591796875, -51.5478515625}}, + {"0b800r9", Box{-87.1463012695, -87.1449279785, -146.237640381, -146.23626709}}, + {"r96suj", Box{-37.1063232422, -37.1008300781, 161.19140625, 161.202392578}}, + {"dqn86", Box{33.7939453125, 33.837890625, -69.521484375, -69.4775390625}}, + {"62jjysq7fun", Box{-43.9652466774, -43.9652453363, -71.4243963361, -71.424394995}}, + {"s623s4p3v", Box{12.9312086105, 12.9312515259, 11.7875146866, 11.7875576019}}, + {"j9w5", Box{-81.03515625, -80.859375, 75.9375, 76.2890625}}, + {"cku2qh5ee64", Box{71.7852795124, 71.7852808535, -117.504816949, -117.504815608}}, + {"ypmy864vvgs", Box{86.9358202815, 86.9358216226, 98.1009525061, 98.1009538472}}, + {"kwe", Box{-8.4375, -7.03125, 26.71875, 28.125}}, + {"gmq7083dvewj", Box{75.0604587235, 75.0604588911, -24.9366608262, -24.9366604909}}, + {"9er", Box{18.28125, 19.6875, -102.65625, -101.25}}, + {"5p89tmjs9j5", Box{-47.5205630064, -47.5205616653, -44.0585620701, -44.058560729}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"ewy", Box{37.96875, 39.375, -14.0625, -12.65625}}, + {"jtgef", Box{-56.9970703125, -56.953125, 72.509765625, 72.5537109375}}, + {"9yjjw", Box{34.716796875, 34.7607421875, -93.955078125, -93.9111328125}}, + {"926", Box{1.40625, 2.8125, -120.9375, -119.53125}}, + {"bz1", Box{84.375, 85.78125, -144.84375, -143.4375}}, + {"yjjpq0ecnve", Box{74.4023618102, 74.4023631513, 97.3003654182, 97.3003667593}}, + {"w5e", Box{19.6875, 21.09375, 94.21875, 95.625}}, + {"hqcn9wtcr", Box{-50.8527517319, -50.8527088165, 12.7303647995, 12.7304077148}}, + {"qfh6xphngs", Box{-33.2709145546, -33.2709091902, 130.039823055, 130.039833784}}, + {"1he586fypp", Box{-64.0560919046, -64.0560865402, -130.766186714, -130.766175985}}, + {"4cc5sh9n3s", Box{-79.5152020454, -79.515196681, -54.666531086, -54.6665203571}}, + {"9y5wfm", Box{34.9639892578, 34.9694824219, -96.2292480469, -96.2182617188}}, + {"c97809", Box{52.0367431641, 52.0422363281, -107.556152344, -107.545166016}}, + {"k9g2nkbm3j5h", Box{-35.1292287558, -35.1292285882, 27.3453609645, 27.3453612998}}, + {"thdwugw196t", Box{26.5185204148, 26.5185217559, 48.7326653302, 48.7326666713}}, + {"34nm41n89c8v", Box{-32.8655058704, -32.8655057028, -126.114044376, -126.11404404}}, + {"buf7qgu", Box{72.3106384277, 72.3120117188, -142.783813477, -142.782440186}}, + {"mhvh0u7f4", Box{-17.55443573, -17.5543928146, 52.0694446564, 52.0694875717}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"f0vdwj1bu", Box{49.6857976913, 49.6858406067, -81.9993782043, -81.999335289}}, + {"kcke59", Box{-37.4359130859, -37.4304199219, 40.2319335938, 40.2429199219}}, + {"9rws4p0", Box{42.9290771484, 42.9304504395, -114.521484375, -114.520111084}}, + {"fhj1u03epu", Box{67.8095269203, 67.8095322847, -82.7905762196, -82.7905654907}}, + {"13296d9gwq1", Box{-82.734657526, -82.7346561849, -122.934338897, -122.934337556}}, + {"4j", Box{-61.875, -56.25, -90.0, -78.75}}, + {"gk5u1y2", Box{68.2374572754, 68.2388305664, -28.3996582031, -28.3982849121}}, + {"9v6yrwx00", Box{30.6655883789, 30.6656312943, -97.0436096191, -97.0435667038}}, + {"mc92", Box{-36.5625, -36.38671875, 80.5078125, 80.859375}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"vtzr1we0jh5", Box{78.6099457741, 78.6099471152, 77.7655689418, 77.7655702829}}, + {"ytmmrjr08p", Box{75.4830640554, 75.4830694199, 120.200042725, 120.200053453}}, + {"y7q525c0mgkz", Box{63.8731999509, 63.8732001185, 109.689126424, 109.68912676}}, + {"s5nc", Box{17.05078125, 17.2265625, 9.4921875, 9.84375}}, + {"wk2", Box{23.90625, 25.3125, 101.25, 102.65625}}, + {"f4beky4z04y", Box{61.0742144287, 61.0742157698, -89.0843501687, -89.0843488276}}, + {"ywdu5yj95", Box{82.2987556458, 82.2987985611, 116.539664268, 116.539707184}}, + {"n3", Box{-84.375, -78.75, 101.25, 112.5}}, + {"0334vnb6", Box{-82.4479293823, -82.4477577209, -167.123680115, -167.123336792}}, + {"xg65", Box{18.80859375, 18.984375, 171.5625, 171.9140625}}, + {"0ebmse71br", Box{-67.9212623835, -67.921257019, -156.946552992, -156.946542263}}, + {"ycwd9fc", Box{53.8920593262, 53.8934326172, 132.968902588, 132.970275879}}, + {"0z2gsvd0tfzy", Box{-48.573201634, -48.5732014664, -144.983568527, -144.983568192}}, + {"e041", Box{0.17578125, 0.3515625, -42.1875, -41.8359375}}, + {"ntzdfpdcphj7", Box{-57.1314592101, -57.1314590424, 123.138849624, 123.138849959}}, + {"jx1bfyyhgds1", Box{-50.4552562349, -50.4552560672, 70.0901824236, 70.0901827589}}, + {"dhzuvhgspbew", Box{27.5804938003, 27.580493968, -78.8766921312, -78.8766917959}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"teyjc173t", Box{22.1116161346, 22.11165905, 75.986123085, 75.9861660004}}, + {"bg57uz4rxw5", Box{62.5739514828, 62.5739528239, -141.467531472, -141.467530131}}, + {"52dtfpdc", Box{-86.1353874207, -86.1352157593, -30.1427078247, -30.142364502}}, + {"vx1j39e", Box{85.3060913086, 85.3074645996, 68.9762878418, 68.9776611328}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"psmpz5", Box{-64.7149658203, -64.7094726562, 164.838867188, 164.849853516}}, + {"4xr95eeee", Box{-49.023141861, -49.0230989456, -56.7943811417, -56.7943382263}}, + {"5j", Box{-61.875, -56.25, -45.0, -33.75}}, + {"kpb", Box{-1.40625, 0.0, 0.0, 1.40625}}, + {"dsub48epk", Box{26.722741127, 26.7227840424, -60.7061576843, -60.706114769}}, + {"2urtnwtdw17", Box{-20.1787023246, -20.1787009835, -135.409665853, -135.409664512}}, + {"e6s30gwjxm", Box{14.2584782839, 14.2584836483, -27.7319276333, -27.7319169044}}, + {"qtx", Box{-14.0625, -12.65625, 122.34375, 123.75}}, + {"qj0qvndweq3k", Box{-15.651620999, -15.6516208313, 90.5748634413, 90.5748637766}}, + {"ffetyh28uyj", Box{60.0967490673, 60.0967504084, -51.0635559261, -51.063554585}}, + {"z56t8nwqq7", Box{64.2848414183, 64.2848467827, 138.52447629, 138.524487019}}, + {"7h", Box{-22.5, -16.875, -45.0, -33.75}}, + {"9tuuw1pkyh", Box{33.1410956383, 33.1411010027, -105.546426773, -105.546416044}}, + {"2m", Box{-16.875, -11.25, -168.75, -157.5}}, + {"h7qt", Box{-70.83984375, -70.6640625, 20.390625, 20.7421875}}, + {"t832ztb6psn", Box{1.57003641129, 1.57003775239, 69.5880755782, 69.5880769193}}, + {"wk", Box{22.5, 28.125, 101.25, 112.5}}, + {"ndjbb8w3n", Box{-78.6152458191, -78.6152029037, 120.616750717, 120.616793633}}, + {"14pf3eqg4zd5", Box{-78.3360836841, -78.3360835165, -124.026254117, -124.026253782}}, + {"9j", Box{28.125, 33.75, -135.0, -123.75}}, + {"fr6ng34", Box{86.9732666016, 86.9746398926, -75.7919311523, -75.7905578613}}, + {"p3ggurx2c", Box{-79.455742836, -79.4556999207, 151.720204353, 151.720247269}}, + {"1h1pg1myn06", Box{-66.1297975481, -66.129796207, -133.453757465, -133.453756124}}, + {"cqsue", Box{82.353515625, 82.3974609375, -116.938476562, -116.89453125}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"s8jkw", Box{0.791015625, 0.8349609375, 30.146484375, 30.1904296875}}, + {"67", Box{-28.125, -22.5, -78.75, -67.5}}, + {"ywe4mn", Box{81.9909667969, 81.9964599609, 116.938476562, 116.949462891}}, + {"0f5te71q9g", Box{-77.7655917406, -77.7655863762, -141.183511019, -141.18350029}}, + {"v9s6tw70swwv", Box{53.911406938, 53.9114071056, 73.7225837633, 73.7225840986}}, + {"0jbutv", Box{-56.8377685547, -56.8322753906, -178.692626953, -178.681640625}}, + {"bn271bp", Box{80.68359375, 80.684967041, -179.561920166, -179.560546875}}, + {"1vvyth", Box{-56.4916992188, -56.4862060547, -92.9443359375, -92.9333496094}}, + {"7ruk94vup", Box{-0.59944152832, -0.599398612976, -27.7212953568, -27.7212524414}}, + {"3hf", Box{-18.28125, -16.875, -132.1875, -130.78125}}, + {"741rwgds3m4k", Box{-32.4116574973, -32.4116573296, -42.9420667514, -42.9420664161}}, + {"2pye", Box{-0.87890625, -0.703125, -170.859375, -170.5078125}}, + {"2", Box{-45.0, 0.0, -180.0, -135.0}}, + {"e7", Box{16.875, 22.5, -33.75, -22.5}}, + {"f", Box{45.0, 90.0, -90.0, -45.0}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"4c5p5ke", Box{-83.1198120117, -83.1184387207, -51.8843078613, -51.8829345703}}, + {"h7q", Box{-71.71875, -70.3125, 19.6875, 21.09375}}, + {"4fjp8", Box{-77.431640625, -77.3876953125, -49.21875, -49.1748046875}}, + {"p2cbvvvdt8", Box{-85.6173992157, -85.6173938513, 148.971412182, 148.971422911}}, + {"xxjtqz46qmm", Box{40.3367181122, 40.3367194533, 165.534370691, 165.534372032}}, + {"w1e", Box{8.4375, 9.84375, 94.21875, 95.625}}, + {"fxpg4v3e", Box{84.9316978455, 84.9318695068, -56.4786529541, -56.4783096313}}, + {"3be6u", Box{-41.7041015625, -41.66015625, -96.50390625, -96.4599609375}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"seqqvkuphz", Box{19.4951051474, 19.4951105118, 31.5254724026, 31.5254831314}}, + {"txy2t7xx", Box{43.7020683289, 43.7022399902, 76.5300750732, 76.530418396}}, + {"s2hc2d2s", Box{0.232772827148, 0.232944488525, 17.9523468018, 17.9526901245}}, + {"8zr0n4f62k", Box{40.7967638969, 40.7967692614, -136.139477491, -136.139466763}}, + {"th1vxpfxnp9", Box{23.5106107593, 23.5106121004, 47.7722467482, 47.7722480893}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"33", Box{-39.375, -33.75, -123.75, -112.5}}, + {"gu", Box{67.5, 73.125, -11.25, 0.0}}, + {"9vq49", Box{29.970703125, 30.0146484375, -92.7685546875, -92.724609375}}, + {"tm", Box{28.125, 33.75, 56.25, 67.5}}, + {"dpzw0p", Box{44.6868896484, 44.6923828125, -79.453125, -79.4421386719}}, + {"gwg12", Box{83.1884765625, 83.232421875, -18.28125, -18.2373046875}}, + {"b8vphv0m5k", Box{50.4775643349, 50.4775696993, -150.259526968, -150.259516239}}, + {"pgpffhw1", Box{-72.6167106628, -72.6165390015, 179.744567871, 179.744911194}}, + {"3r3w", Box{-3.1640625, -2.98828125, -121.640625, -121.2890625}}, + {"u1d", Box{53.4375, 54.84375, 2.8125, 4.21875}}, + {"mznb8v5xu6", Box{-5.50830245018, -5.50829708576, 88.2801353931, 88.280146122}}, + {"8mb57vjrex4", Box{32.9438298941, 32.9438312352, -168.577842414, -168.577841073}}, + {"zm", Box{73.125, 78.75, 146.25, 157.5}}, + {"c9ef6tm74sg", Box{53.8623873889, 53.86238873, -107.109378129, -107.109376788}}, + {"spww", Box{43.2421875, 43.41796875, 9.140625, 9.4921875}}, + {"snp97n", Box{34.0026855469, 34.0081787109, 10.6787109375, 10.6896972656}}, + {"zp9r6emsk8xx", Box{88.4805002622, 88.4805004299, 136.875432059, 136.875432394}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"18zsh9bg", Box{-85.0679969788, -85.0678253174, -101.754341125, -101.753997803}}, + {"v28", Box{47.8125, 49.21875, 56.25, 57.65625}}, + {"4e", Box{-73.125, -67.5, -67.5, -56.25}}, + {"evn0wp56", Box{28.2516860962, 28.2518577576, -2.5443649292, -2.54402160645}}, + {"uyf9v", Box{83.2763671875, 83.3203125, 37.4853515625, 37.529296875}}, + {"d7", Box{16.875, 22.5, -78.75, -67.5}}, + {"05", Box{-73.125, -67.5, -180.0, -168.75}}, + {"ujj8", Box{73.125, 73.30078125, 7.734375, 8.0859375}}, + {"wcb7n8", Box{10.37109375, 10.3765869141, 124.387207031, 124.398193359}}, + {"r35s2y4e2", Box{-38.5944128036, -38.5943698883, 151.208267212, 151.208310127}}, + {"k", Box{-45.0, 0.0, 0.0, 45.0}}, + {"8tm3h7b1f", Box{29.7279310226, 29.727973938, -149.930334091, -149.930291176}}, + {"3xecw9gsguw3", Box{-2.53837538883, -2.53837522119, -106.935942136, -106.9359418}}, + {"hqs10v", Box{-53.2342529297, -53.2287597656, 16.9079589844, 16.9189453125}}, + {"b21g", Box{45.52734375, 45.703125, -166.2890625, -165.9375}}, + {"vphhpnjt5b", Box{85.1119422913, 85.1119476557, 50.9403312206, 50.9403419495}}, + {"kbd", Box{-42.1875, -40.78125, 36.5625, 37.96875}}, + {"2c", Box{-39.375, -33.75, -146.25, -135.0}}, + {"07ur", Box{-67.67578125, -67.5, -162.7734375, -162.421875}}, + {"8e5ky1", Box{17.7154541016, 17.7209472656, -152.666015625, -152.655029297}}, + {"k2w84t", Box{-42.1600341797, -42.1545410156, 20.5004882812, 20.5114746094}}, + {"p9t4ncex81m", Box{-81.2014035881, -81.201402247, 164.832694083, 164.832695425}}, + {"q67rduzsu6uz", Box{-30.9984667785, -30.9984666109, 105.951650552, 105.951650888}}, + {"udwkypp0v", Box{59.936041832, 59.9360847473, 31.5625619888, 31.5626049042}}, + {"pjsu1q9qg", Box{-58.3225107193, -58.322467804, 141.7364645, 141.736507416}}, + {"2kj2w9b021b", Box{-22.4024440348, -22.4024426937, -161.081542969, -161.081541628}}, + {"5k0", Box{-67.5, -66.09375, -33.75, -32.34375}}, + {"t626vs8j", Box{13.1652259827, 13.165397644, 56.8432617188, 56.8436050415}}, + {"hd0z4zr73", Box{-77.4791479111, -77.4791049957, 23.6855363846, 23.6855792999}}, + {"79gjppfekhhk", Box{-34.2341917008, -34.2341915332, -17.9700222239, -17.9700218886}}, + {"u9u", Box{54.84375, 56.25, 28.125, 29.53125}}, + {"5zbfmj3n30", Box{-45.9808301926, -45.9808248281, -9.97416973114, -9.9741590023}}, + {"1w1nt3g4t9pp", Box{-55.0973731466, -55.0973729789, -110.858671814, -110.858671479}}, + {"f6bh910940", Box{61.2654304504, 61.2654358149, -78.7052822113, -78.7052714825}}, + {"r65q38x", Box{-32.6486206055, -32.6472473145, 150.895843506, 150.897216797}}, + {"xq2", Box{35.15625, 36.5625, 146.25, 147.65625}}, + {"q87xbntvdv8d", Box{-42.1947657689, -42.1947656013, 117.429890111, 117.429890446}}, + {"w1zhgmbpw", Box{10.7115840912, 10.7116270065, 99.9868297577, 99.986872673}}, + {"5n", Box{-56.25, -50.625, -45.0, -33.75}}, + {"9dz", Box{15.46875, 16.875, -102.65625, -101.25}}, + {"n8r794hh15gv", Box{-87.9668216966, -87.966821529, 122.744798921, 122.744799256}}, + {"px78re9", Box{-49.1555786133, -49.1542053223, 162.752838135, 162.754211426}}, + {"3pps", Box{-4.921875, -4.74609375, -124.453125, -124.1015625}}, + {"3s6um", Box{-20.3466796875, -20.302734375, -108.413085938, -108.369140625}}, + {"9dj7zre6t7", Box{11.9508236647, 11.9508290291, -104.793895483, -104.793884754}}, + {"4v1b", Box{-61.875, -61.69921875, -53.7890625, -53.4375}}, + {"1k35z", Box{-65.4345703125, -65.390625, -122.036132812, -121.9921875}}, + {"7z9n57", Box{-1.74133300781, -1.73583984375, -9.70092773438, -9.68994140625}}, + {"3gzg", Box{-23.37890625, -23.203125, -90.3515625, -90.0}}, + {"hy", Box{-56.25, -50.625, 33.75, 45.0}}, + {"2rj6t", Box{-5.185546875, -5.1416015625, -161.147460938, -161.103515625}}, + {"h", Box{-90.0, -45.0, 0.0, 45.0}}, + {"dp44nc1t", Box{39.7329139709, 39.7330856323, -86.8888092041, -86.8884658813}}, + {"0x1", Box{-50.625, -49.21875, -156.09375, -154.6875}}, + {"dmwxxf", Box{32.2668457031, 32.2723388672, -69.2687988281, -69.2578125}}, + {"khy29", Box{-18.193359375, -18.1494140625, 8.8330078125, 8.876953125}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"phn4", Box{-67.1484375, -66.97265625, 143.4375, 143.7890625}}, + {"qzhvp", Box{-4.74609375, -4.7021484375, 130.737304688, 130.78125}}, + {"3n", Box{-11.25, -5.625, -135.0, -123.75}}, + {"0nx", Box{-53.4375, -52.03125, -170.15625, -168.75}}, + {"19uwx04h21", Box{-79.0129369497, -79.0129315853, -105.86151123, -105.861500502}}, + {"7ur1q", Box{-20.8740234375, -20.830078125, -1.142578125, -1.0986328125}}, + {"8yn6q9vmm", Box{34.1560220718, 34.1560649872, -137.167868614, -137.167825699}}, + {"m4zk", Box{-28.828125, -28.65234375, 55.1953125, 55.546875}}, + {"9bgzpypspd0", Box{5.48287510872, 5.48287644982, -95.6253647804, -95.6253634393}}, + {"y1s", Box{53.4375, 54.84375, 95.625, 97.03125}}, + {"qsyp207nvy", Box{-17.0042717457, -17.0042663813, 120.941866636, 120.941877365}}, + {"rfb", Box{-29.53125, -28.125, 168.75, 170.15625}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"5exm5p63", Box{-69.3935966492, -69.3934249878, -12.1697616577, -12.169418335}}, + {"cnv22fdkruw", Box{83.0271819234, 83.0271832645, -127.58079797, -127.580796629}}, + {"n7vg", Box{-68.37890625, -68.203125, 109.3359375, 109.6875}}, + {"whvgd2h3sz9", Box{27.3342821002, 27.3342834413, 98.1908561289, 98.19085747}}, + {"shbfuzk8vr", Box{27.2421401739, 27.2421455383, 1.2698328495, 1.26984357834}}, + {"44vmk", Box{-73.6083984375, -73.564453125, -82.44140625, -82.3974609375}}, + {"uhd1mfq", Box{70.5445861816, 70.5459594727, 3.07342529297, 3.07479858398}}, + {"7bz", Box{-40.78125, -39.375, -1.40625, 0.0}}, + {"h5b2wkdqpz", Box{-68.7925726175, -68.7925672531, 0.629643201828, 0.629653930664}}, + {"h1", Box{-84.375, -78.75, 0.0, 11.25}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"408bm1", Box{-87.1380615234, -87.1325683594, -88.7255859375, -88.7145996094}}, + {"ggysyy5e2be6", Box{66.9622308388, 66.9622310065, -1.80790107697, -1.8079007417}}, + {"w4u7dn8m9ndw", Box{16.1206699535, 16.1206701212, 96.0648427159, 96.0648430511}}, + {"yq", Box{78.75, 84.375, 101.25, 112.5}}, + {"2nwuht4w", Box{-7.70587921143, -7.70570755005, -170.306625366, -170.306282043}}, + {"v5gqe", Box{67.236328125, 67.2802734375, 49.7021484375, 49.74609375}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"cmehghzjm1", Box{76.7994600534, 76.7994654179, -119.389586449, -119.38957572}}, + {"u207q361d", Box{45.5784130096, 45.578455925, 11.8790531158, 11.8790960312}}, + {"n4pgq345pp", Box{-78.1726652384, -78.172659874, 101.176142693, 101.176153421}}, + {"b2mn8", Box{47.548828125, 47.5927734375, -161.71875, -161.674804688}}, + {"qbe6mp0g8et3", Box{-41.7529202811, -41.7529201135, 128.541097529, 128.541097865}}, + {"sr04m", Box{39.7705078125, 39.814453125, 11.4697265625, 11.513671875}}, + {"hfr0y7u988jx", Box{-77.1910560317, -77.1910558641, 43.8746168464, 43.8746171817}}, + {"jrgxg5qkg7p", Box{-45.0252610445, -45.0252597034, 61.3124428689, 61.3124442101}}, + {"gryut", Box{89.384765625, 89.4287109375, -24.0380859375, -23.994140625}}, + {"14d5b766ppxg", Box{-75.2600834705, -75.2600833029, -132.173112966, -132.173112631}}, + {"0ede2rgwt2", Box{-69.6975231171, -69.6975177526, -153.968356848, -153.968346119}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"dj", Box{28.125, 33.75, -90.0, -78.75}}, + {"xf", Box{11.25, 16.875, 168.75, 180.0}}, + {"szf3p5k9rmx", Box{43.7876281142, 43.7876294553, 37.228180021, 37.2281813622}}, + {"b9fqkhfem7", Box{55.9690493345, 55.9690546989, -154.156497717, -154.156486988}}, + {"t7zw8x5c", Box{22.2749519348, 22.2751235962, 66.8239974976, 66.8243408203}}, + {"f87dmh", Box{46.8237304688, 46.8292236328, -62.3583984375, -62.3474121094}}, + {"yrd1swq12", Box{87.4857187271, 87.4857616425, 104.268493652, 104.268536568}}, + {"s2", Box{0.0, 5.625, 11.25, 22.5}}, + {"q9dhkgwy4kum", Box{-35.7951473258, -35.7951471582, 115.530612208, 115.530612543}}, + {"7dr", Box{-32.34375, -30.9375, -12.65625, -11.25}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"2rpk2ey43dw", Box{-4.85693067312, -4.85692933202, -158.524402678, -158.524401337}}, + {"3wfeg", Box{-6.3720703125, -6.328125, -108.852539062, -108.80859375}}, + {"ke5k4j", Box{-27.3944091797, -27.3889160156, 27.158203125, 27.1691894531}}, + {"z0xq", Box{48.8671875, 49.04296875, 145.1953125, 145.546875}}, + {"w1sy", Box{9.4921875, 9.66796875, 96.6796875, 97.03125}}, + {"eqm14", Box{35.33203125, 35.3759765625, -26.630859375, -26.5869140625}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"hjp9d9f", Box{-61.6017150879, -61.6003417969, 10.6594848633, 10.6608581543}}, + {"p92v0", Box{-82.08984375, -82.0458984375, 158.5546875, 158.598632812}}, + {"36m7g02m", Box{-31.6823387146, -31.6821670532, -116.23500824, -116.234664917}}, + {"5g70e57zjrf", Box{-71.6117633879, -71.6117620468, -6.89403623343, -6.89403489232}}, + {"65rkyfq4se", Box{-25.8709841967, -25.8709788322, -79.4996237755, -79.4996130466}}, + {"eev1", Box{21.26953125, 21.4453125, -15.46875, -15.1171875}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"m75926t", Box{-27.8915405273, -27.8901672363, 61.1897277832, 61.1911010742}}, + {"1kjyeb", Box{-66.357421875, -66.3519287109, -115.499267578, -115.48828125}}, + {"fb8rk2yfwmrp", Box{49.0914924257, 49.0914925933, -55.7021225989, -55.7021222636}}, + {"y2qhd0j8x", Box{47.1973514557, 47.197394371, 109.783244133, 109.783287048}}, + {"m2", Box{-45.0, -39.375, 56.25, 67.5}}, + {"0543np5pgd23", Box{-72.9094239883, -72.9094238207, -176.567995213, -176.567994878}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"d4h5zdhe5gy", Box{11.9207011163, 11.9207024574, -84.0390613675, -84.0390600264}}, + {"9rcd", Box{43.9453125, 44.12109375, -121.640625, -121.2890625}}, + {"ne9nrh75tq3", Box{-69.1898868978, -69.1898855567, 114.218213707, 114.218215048}}, + {"7wk7", Box{-9.31640625, -9.140625, -16.5234375, -16.171875}}, + {"995f97e", Box{6.08367919922, 6.08505249023, -107.167510986, -107.166137695}}, + {"60kmung", Box{-42.5459289551, -42.5445556641, -83.843536377, -83.8421630859}}, + {"845", Box{11.25, 12.65625, -175.78125, -174.375}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"jehdxn0", Box{-72.6525878906, -72.6512145996, 74.1357421875, 74.1371154785}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"1d", Box{-78.75, -73.125, -112.5, -101.25}}, + {"rbjy", Box{-43.9453125, -43.76953125, 176.8359375, 177.1875}}, + {"r8qgzf4r9uy", Box{-42.9222710431, -42.922269702, 167.335936725, 167.335938066}}, + {"k5p", Box{-28.125, -26.71875, 9.84375, 11.25}}, + {"f4z7", Box{60.99609375, 61.171875, -79.8046875, -79.453125}}, + {"7rp35b", Box{-5.44921875, -5.44372558594, -23.3898925781, -23.37890625}}, + {"zn71yyn0pbc", Box{80.4968301952, 80.4968315363, 139.52395454, 139.523955882}}, + {"ppj7", Box{-50.09765625, -49.921875, 142.3828125, 142.734375}}, + {"mqv3q", Box{-6.8115234375, -6.767578125, 63.896484375, 63.9404296875}}, + {"tsdtmfq", Box{26.2477111816, 26.2490844727, 71.276550293, 71.277923584}}, + {"72ey8b14uynx", Box{-41.0444164462, -41.0444162786, -28.4420176595, -28.4420173243}}, + {"7qrgb", Box{-9.1845703125, -9.140625, -22.8515625, -22.8076171875}}, + {"w7zmkdpezcm", Box{22.0282383263, 22.0282396674, 111.653705388, 111.653706729}}, + {"kqwr1dh9jdbc", Box{-7.19585834071, -7.19585817307, 20.1113973185, 20.1113976538}}, + {"kv9jx", Box{-13.095703125, -13.0517578125, 35.4638671875, 35.5078125}}, + {"09", Box{-84.375, -78.75, -157.5, -146.25}}, + {"f8ztmmp0", Box{50.1690673828, 50.1692390442, -56.7127990723, -56.7124557495}}, + {"k5dj8cuwbxjg", Box{-24.3348933198, -24.3348931521, 2.85166796297, 2.85166829824}}, + {"xd72j5qndwhn", Box{12.6752517745, 12.6752519421, 162.298391461, 162.298391797}}, + {"esp42d", Box{22.9064941406, 22.9119873047, -12.6342773438, -12.6232910156}}, + {"5sbfys", Box{-62.7758789062, -62.7703857422, -21.1596679688, -21.1486816406}}, + {"8wsz02n", Box{37.79296875, 37.794342041, -150.801086426, -150.799713135}}, + {"zeghw8", Box{66.884765625, 66.8902587891, 162.004394531, 162.015380859}}, + {"u0xg7ug", Box{48.4098815918, 48.4112548828, 11.0673522949, 11.0687255859}}, + {"0jb11", Box{-57.48046875, -57.4365234375, -179.956054688, -179.912109375}}, + {"xv8cwtybm", Box{31.2328004837, 31.232843399, 170.099816322, 170.099859238}}, + {"ef0cwqt7", Box{11.5498924255, 11.5500640869, -9.91344451904, -9.91310119629}}, + {"hrh5k", Box{-50.0537109375, -50.009765625, 17.05078125, 17.0947265625}}, + {"pnpdsx4eb", Box{-55.7714509964, -55.7714080811, 145.748062134, 145.748105049}}, + {"8g2sx4gn", Box{19.0884017944, 19.0885734558, -145.235137939, -145.234794617}}, + {"tsue3yr4z", Box{27.3248434067, 27.324886322, 73.9149427414, 73.9149856567}}, + {"k4vq", Box{-28.4765625, -28.30078125, 7.3828125, 7.734375}}, + {"mr1f1d430h", Box{-5.26225805283, -5.26225268841, 58.7799453735, 58.7799561024}}, + {"dtuqkjybm", Box{33.4740114212, 33.4740543365, -61.3381719589, -61.3381290436}}, + {"p00zpfbj5350", Box{-88.7535613775, -88.7535612099, 136.39540717, 136.395407505}}, + {"n16jy8wrg38", Box{-81.9539228082, -81.9539214671, 93.106867075, 93.1068684161}}, + {"3ckf9t6", Box{-37.5004577637, -37.4990844727, -94.5016479492, -94.5002746582}}, + {"vvch78h7q7", Box{78.0913943052, 78.0913996696, 80.3161633015, 80.3161740303}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"qkr8dj44", Box{-20.9780502319, -20.9778785706, 111.887512207, 111.88785553}}, + {"s5dw7s", Box{20.8081054688, 20.8135986328, 3.66943359375, 3.68041992188}}, + {"tpt", Box{42.1875, 43.59375, 52.03125, 53.4375}}, + {"6vqn07ep", Box{-14.3936347961, -14.3934631348, -47.7973937988, -47.7970504761}}, + {"7zbup2", Box{-0.703125, -0.697631835938, -9.87670898438, -9.86572265625}}, + {"xd0j0wrn39f8", Box{12.1643207967, 12.1643209644, 157.531653419, 157.531653754}}, + {"254kywz4", Box{-27.2526168823, -27.2524452209, -176.540679932, -176.540336609}}, + {"6pkmr1875rp", Box{-3.28710615635, -3.28710481524, -83.7153281271, -83.715326786}}, + {"69bmhmbw0de", Box{-34.2447146773, -34.2447133362, -66.9609577954, -66.9609564543}}, + {"47jd", Box{-72.7734375, -72.59765625, -71.015625, -70.6640625}}, + {"mw3ngtnj", Box{-8.6289024353, -8.62873077393, 69.0682983398, 69.0686416626}}, + {"v", Box{45.0, 90.0, 45.0, 90.0}}, + {"4uyq1", Box{-62.2265625, -62.1826171875, -47.4169921875, -47.373046875}}, + {"9748v3e", Box{17.0150756836, 17.0164489746, -119.999542236, -119.998168945}}, + {"sjy7", Box{32.87109375, 33.046875, 8.7890625, 9.140625}}, + {"nc1jb2kb", Box{-83.3628845215, -83.3627128601, 125.17375946, 125.174102783}}, + {"ffryw", Box{58.798828125, 58.8427734375, -45.087890625, -45.0439453125}}, + {"3qfr7scg5s", Box{-5.7302069664, -5.73020160198, -120.429575443, -120.429564714}}, + {"1x", Box{-50.625, -45.0, -112.5, -101.25}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"n4wk", Box{-75.234375, -75.05859375, 98.7890625, 99.140625}}, + {"bw2d2", Box{80.5517578125, 80.595703125, -156.796875, -156.752929688}}, + {"ztgehr9mpt", Box{77.9131776094, 77.9131829739, 162.610681057, 162.610691786}}, + {"bnkb", Box{80.15625, 80.33203125, -173.3203125, -172.96875}}, + {"q0fmcn", Box{-39.7375488281, -39.7320556641, 93.2080078125, 93.2189941406}}, + {"e0e1sxt9gwm", Box{3.11770454049, 3.1177058816, -40.5757860839, -40.5757847428}}, + {"9qc", Box{37.96875, 39.375, -122.34375, -120.9375}}, + {"0cybm9snr", Box{-80.1029920578, -80.1029491425, -136.51031971, -136.510276794}}, + {"fp", Box{84.375, 90.0, -90.0, -78.75}}, + {"7u69k7", Box{-20.8575439453, -20.8520507812, -7.54760742188, -7.53662109375}}, + {"guh3mbvnwv7y", Box{67.7249914035, 67.7249915712, -5.01359079033, -5.01359045506}}, + {"vgw4wgnrd58e", Box{65.1447393559, 65.1447395235, 87.4928004295, 87.4928007647}}, + {"rzk732w", Box{-3.64471435547, -3.64334106445, 174.789733887, 174.791107178}}, + {"kf", Box{-33.75, -28.125, 33.75, 45.0}}, + {"rcfr28t0", Box{-33.8790893555, -33.8789176941, 171.942901611, 171.943244934}}, + {"5bqnms", Box{-87.4731445312, -87.4676513672, -2.57080078125, -2.55981445312}}, + {"fs84w", Box{70.751953125, 70.7958984375, -67.236328125, -67.1923828125}}, + {"mcjrsmx", Box{-38.0264282227, -38.0250549316, 86.3291931152, 86.3305664062}}, + {"u84", Box{45.0, 46.40625, 25.3125, 26.71875}}, + {"gkv4g14m", Box{72.2084999084, 72.2086715698, -26.5838241577, -26.583480835}}, + {"27dhxu", Box{-24.4995117188, -24.4940185547, -165.596923828, -165.5859375}}, + {"0v", Box{-61.875, -56.25, -146.25, -135.0}}, + {"bpurn", Box{89.82421875, 89.8681640625, -173.759765625, -173.715820312}}, + {"p5", Box{-73.125, -67.5, 135.0, 146.25}}, + {"f3ffsuh", Box{55.3051757812, 55.3065490723, -74.6685791016, -74.6672058105}}, + {"j0zbr0tb", Box{-85.7345581055, -85.7343864441, 56.2139511108, 56.2142944336}}, + {"vyz", Box{82.96875, 84.375, 88.59375, 90.0}}, + {"082p96b5ey", Box{-87.2596514225, -87.2596460581, -157.444907427, -157.444896698}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"g030qs", Box{46.4721679688, 46.4776611328, -43.3081054688, -43.2971191406}}, + {"54", Box{-78.75, -73.125, -45.0, -33.75}}, + {"fp5rcptn2gc", Box{85.7795964181, 85.7795977592, -85.3788422048, -85.3788408637}}, + {"dk8z85s6516h", Box{26.650436148, 26.6504363157, -77.6893445849, -77.6893442497}}, + {"3v1ebh18qujh", Box{-16.1937826127, -16.193782445, -99.1382686794, -99.1382683441}}, + {"un50j3xf9", Box{78.7586688995, 78.7587118149, 4.46014881134, 4.46019172668}}, + {"4b8y3gh", Box{-86.0723876953, -86.0710144043, -55.1129150391, -55.111541748}}, + {"efdgh", Box{14.58984375, 14.6337890625, -7.20703125, -7.1630859375}}, + {"1xxuk2", Box{-47.0654296875, -47.0599365234, -101.414794922, -101.403808594}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"1z6", Box{-49.21875, -47.8125, -98.4375, -97.03125}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"mxkjr1jdu5ru", Box{-3.28991509974, -3.2899149321, 73.440352343, 73.4403526783}}, + {"x", Box{0.0, 45.0, 135.0, 180.0}}, + {"3kpcn3sm", Box{-22.315120697, -22.3149490356, -112.57106781, -112.570724487}}, + {"buk3t0ctrmt0", Box{69.1749724746, 69.1749726422, -140.051333159, -140.051332824}}, + {"pp", Box{-50.625, -45.0, 135.0, 146.25}}, + {"4h", Box{-67.5, -61.875, -90.0, -78.75}}, + {"fjw1kcg4", Box{76.1671829224, 76.1673545837, -81.3496398926, -81.3492965698}}, + {"877wsvjfz5", Box{19.4517821074, 19.4517874718, -163.611187935, -163.611177206}}, + {"ru3", Box{-21.09375, -19.6875, 170.15625, 171.5625}}, + {"yr", Box{84.375, 90.0, 101.25, 112.5}}, + {"cu5x6cxq", Box{68.7836837769, 68.7838554382, -96.1973190308, -96.196975708}}, + {"w04vuf4bdzjm", Box{1.02185273543, 1.02185290307, 94.0798293427, 94.0798296779}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"4zdcmmp2p4", Box{-47.5652968884, -47.5652915239, -52.1418428421, -52.1418321133}}, + {"eft02s1hu", Box{14.1292333603, 14.1292762756, -4.19523239136, -4.19518947601}}, + {"zk4v9qdeg5q1", Box{68.5031637736, 68.5031639412, 150.175689161, 150.175689496}}, + {"8xr", Box{40.78125, 42.1875, -147.65625, -146.25}}, + {"3pxyrt", Box{-1.68640136719, -1.68090820312, -123.771972656, -123.760986328}}, + {"cmh39xszs8", Box{73.4311580658, 73.4311634302, -117.70080328, -117.700792551}}, + {"xrm9d0wb48", Box{41.047668457, 41.0476738214, 154.081642628, 154.081653357}}, + {"d4bh0k", Box{16.1938476562, 16.1993408203, -89.9890136719, -89.9780273438}}, + {"hk8", Box{-64.6875, -63.28125, 11.25, 12.65625}}, + {"9hxqk54m0wn", Box{26.4285027981, 26.4285041392, -124.625786841, -124.6257855}}, + {"mygnv0", Box{-5.8447265625, -5.83923339844, 83.1884765625, 83.1994628906}}, + {"yrjmvs", Box{85.4077148438, 85.4132080078, 108.874511719, 108.885498047}}, + {"52csyemvf12", Box{-84.9274425209, -84.9274411798, -31.3469982147, -31.3469968736}}, + {"4jrvjj", Box{-59.5623779297, -59.5568847656, -78.8818359375, -78.8708496094}}, + {"ys1", Box{67.5, 68.90625, 113.90625, 115.3125}}, + {"unf91", Box{83.14453125, 83.1884765625, 3.5595703125, 3.603515625}}, + {"h5che0vnt", Box{-68.109998703, -68.1099557877, 1.5451669693, 1.54520988464}}, + {"ugrk3", Box{64.0283203125, 64.072265625, 43.9892578125, 44.033203125}}, + {"9ush8c", Box{26.1090087891, 26.1145019531, -95.5920410156, -95.5810546875}}, + {"q92pzb", Box{-36.6064453125, -36.6009521484, 112.840576172, 112.8515625}}, + {"0e", Box{-73.125, -67.5, -157.5, -146.25}}, + {"dbt1mchu", Box{3.03840637207, 3.03857803345, -48.9595413208, -48.959197998}}, + {"98xv2m", Box{3.76281738281, 3.76831054688, -101.590576172, -101.579589844}}, + {"rqd8u195kgu", Box{-8.29684630036, -8.29684495926, 149.942988753, 149.942990094}}, + {"504wk58ccv", Box{-88.8818138838, -88.8818085194, -41.3074886799, -41.307477951}}, + {"0dzjhbn", Box{-73.65234375, -73.650970459, -147.43927002, -147.437896729}}, + {"sgcn", Box{22.1484375, 22.32421875, 35.15625, 35.5078125}}, + {"46k78jw0x65w", Box{-76.6982056573, -76.6982054897, -72.7648819238, -72.7648815885}}, + {"6w2cbxx3nf9", Box{-9.49474900961, -9.4947476685, -66.4130924642, -66.4130911231}}, + {"zxmf4", Box{86.1328125, 86.1767578125, 165.673828125, 165.717773438}}, + {"unf", Box{82.96875, 84.375, 2.8125, 4.21875}}, + {"m4p", Box{-33.75, -32.34375, 54.84375, 56.25}}, + {"dsc1rqss2w", Box{26.9749438763, 26.9749492407, -65.7689452171, -65.7689344883}}, + {"cxp", Box{84.375, 85.78125, -102.65625, -101.25}}, + {"zmh", Box{73.125, 74.53125, 151.875, 153.28125}}, + {"tynvnjc8hdb", Box{34.6605066955, 34.6605080366, 88.5081124306, 88.5081137717}}, + {"uk8hb", Box{71.1474609375, 71.19140625, 11.25, 11.2939453125}}, + {"34d", Box{-30.9375, -29.53125, -132.1875, -130.78125}}, + {"ts39vet4rzw5", Box{24.2335202359, 24.2335204035, 69.8582813144, 69.8582816496}}, + {"3rt1fx5", Box{-2.46643066406, -2.46505737305, -116.604766846, -116.603393555}}, + {"ujn8yhfpg", Box{73.2842588425, 73.2843017578, 9.40717220306, 9.40721511841}}, + {"pdbvhzj", Box{-73.6138916016, -73.6125183105, 158.770294189, 158.77166748}}, + {"q35", Box{-39.375, -37.96875, 105.46875, 106.875}}, + {"szh5424hc", Box{39.9031591415, 39.9032020569, 39.4766664505, 39.4767093658}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"tt1wjkr44e", Box{29.2033928633, 29.2033982277, 69.8498082161, 69.8498189449}}, + {"1u3hdkn", Box{-65.2807617188, -65.2793884277, -99.7366333008, -99.7352600098}}, + {"jc9", Box{-81.5625, -80.15625, 80.15625, 81.5625}}, + {"627pp", Box{-42.36328125, -42.3193359375, -74.2236328125, -74.1796875}}, + {"g46wqb4z", Box{58.7560844421, 58.7562561035, -41.1839675903, -41.1836242676}}, + {"2407674", Box{-33.1622314453, -33.1608581543, -179.546813965, -179.545440674}}, + {"3vbsrcxu", Box{-11.9002532959, -11.9000816345, -100.195655823, -100.1953125}}, + {"u0mr9fpy", Box{47.7366256714, 47.7367973328, 7.47035980225, 7.470703125}}, + {"p1s1", Box{-81.38671875, -81.2109375, 140.625, 140.9765625}}, + {"ce7y6s1ugjpu", Box{64.4026983529, 64.4026985206, -107.11415682, -107.114156485}}, + {"tujn", Box{23.5546875, 23.73046875, 85.78125, 86.1328125}}, + {"fes", Box{64.6875, 66.09375, -61.875, -60.46875}}, + {"28te871t29y", Box{-41.5548755229, -41.5548741817, -149.752549231, -149.75254789}}, + {"2z9j0591", Box{-1.9141960144, -1.91402435303, -144.842376709, -144.842033386}}, + {"e", Box{0.0, 45.0, -45.0, 0.0}}, + {"90", Box{0.0, 5.625, -135.0, -123.75}}, + {"jbfm12r", Box{-84.900970459, -84.899597168, 81.9786071777, 81.9799804688}}, + {"y0ws", Box{48.515625, 48.69140625, 99.140625, 99.4921875}}, + {"m2", Box{-45.0, -39.375, 56.25, 67.5}}, + {"gpspv95sz", Box{88.5561132431, 88.5561561584, -39.1281938553, -39.1281509399}}, + {"7k8u95cyjdx6", Box{-18.8748412952, -18.8748411275, -32.6487181708, -32.6487178355}}, + {"c1fe0r", Box{55.4095458984, 55.4150390625, -131.473388672, -131.462402344}}, + {"668wjecj2d", Box{-29.8613011837, -29.8612958193, -77.8037810326, -77.8037703037}}, + {"dnq3", Box{35.33203125, 35.5078125, -81.2109375, -80.859375}}, + {"m3sxdxnvmrr", Box{-35.2047483623, -35.2047470212, 62.6974926889, 62.69749403}}, + {"zz3qpfvqzu", Box{86.8522238731, 86.8522292376, 170.855931044, 170.855941772}}, + {"98mjjx8bu", Box{2.3264837265, 2.32652664185, -105.225849152, -105.225806236}}, + {"pkmusy0e4j35", Box{-65.2692317404, -65.2692315727, 154.545451552, 154.545451887}}, + {"j3f9dtm5r5n", Box{-79.8631650209, -79.8631636798, 59.8826631904, 59.8826645315}}, + {"67up3c0uh9jn", Box{-22.6256497577, -22.62564959, -73.0468659103, -73.046865575}}, + {"6q0fd9wn2", Box{-10.8012342453, -10.80119133, -77.5772094727, -77.5771665573}}, + {"t82e5zrs", Box{1.97410583496, 1.97427749634, 68.3782196045, 68.3785629272}}, + {"0hstxh", Box{-63.6987304688, -63.6932373047, -173.364257812, -173.353271484}}, + {"qe1egcuetqe", Box{-27.4555715919, -27.4555702507, 114.78057906, 114.780580401}}, + {"yhp25wc4v", Box{67.5375509262, 67.5375938416, 100.350708961, 100.350751877}}, + {"z6uvby2nrt4k", Box{61.5149248391, 61.5149250068, 152.962971367, 152.962971702}}, + {"29sd0863cx", Box{-36.2092262506, -36.2092208862, -151.146748066, -151.146737337}}, + {"kvnx614", Box{-15.5950927734, -15.5937194824, 42.981262207, 42.982635498}}, + {"mu1srk07", Box{-21.7304420471, -21.7302703857, 81.1783218384, 81.1786651611}}, + {"5bz5bmq", Box{-85.0932312012, -85.0918579102, -1.38702392578, -1.38565063477}}, + {"fu4yx9fr8gtk", Box{68.6534980685, 68.6534982361, -52.0500935242, -52.0500931889}}, + {"3hyhj92rn", Box{-17.5700569153, -17.5700139999, -126.320199966, -126.320157051}}, + {"345nw", Box{-32.607421875, -32.5634765625, -130.517578125, -130.473632812}}, + {"q5f2p327mhy", Box{-23.8988001645, -23.8987988234, 93.4832319617, 93.4832333028}}, + {"0wmufb9", Box{-54.0060424805, -54.0046691895, -149.2918396, -149.290466309}}, + {"r", Box{-45.0, 0.0, 135.0, 180.0}}, + {"07d2sde", Box{-70.2108764648, -70.2095031738, -165.384063721, -165.38269043}}, + {"d0r2", Box{1.40625, 1.58203125, -79.8046875, -79.453125}}, + {"znegsexfs23h", Box{82.1973916143, 82.197391782, 140.482018143, 140.482018478}}, + {"sfr69qxg", Box{13.1319236755, 13.1320953369, 44.010887146, 44.0112304688}}, + {"tr44b8brc", Box{39.8638486862, 39.8638916016, 59.0848588943, 59.0849018097}}, + {"tbnqctecsf", Box{1.21700406075, 1.21700942516, 87.6103341579, 87.6103448868}}, + {"jpfy538qu", Box{-45.3421640396, -45.3421211243, 49.0105247498, 49.0105676651}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"gskrg0z5e", Box{70.2732753754, 70.2733182907, -16.3818597794, -16.381816864}}, + {"6cz", Box{-35.15625, -33.75, -46.40625, -45.0}}, + {"u67hm7b47423", Box{58.4243181534, 58.424318321, 15.6995919719, 15.6995923072}}, + {"j154zhnnkyt3", Box{-83.8685209863, -83.8685208187, 49.5348178223, 49.5348181576}}, + {"muqdpev4smv", Box{-20.7211281359, -20.7211267948, 88.2272703946, 88.2272717357}}, + {"47h3upynmsru", Box{-72.7737144381, -72.7737142704, -72.589170076, -72.5891697407}}, + {"g6j200", Box{56.25, 56.2554931641, -26.3671875, -26.3562011719}}, + {"tw", Box{33.75, 39.375, 67.5, 78.75}}, + {"c0pjhr520q", Box{45.9173905849, 45.9173959494, -124.965008497, -124.964997768}}, + {"8nx", Box{36.5625, 37.96875, -170.15625, -168.75}}, + {"47b2wvtns", Box{-68.7870311737, -68.7869882584, -78.0947685242, -78.0947256088}}, + {"vrsbq", Box{87.2314453125, 87.275390625, 63.193359375, 63.2373046875}}, + {"sz", Box{39.375, 45.0, 33.75, 45.0}}, + {"xe61b0bnw", Box{18.5941028595, 18.5941457748, 160.312757492, 160.312800407}}, + {"dky6qedz3w", Box{27.1347606182, 27.1347659826, -69.6714520454, -69.6714413166}}, + {"vmvkqx2hb", Box{78.1314611435, 78.1315040588, 63.9184570312, 63.9184999466}}, + {"t96m49xgr1y", Box{7.9189632833, 7.9189646244, 70.7848772407, 70.7848785818}}, + {"brw2urrqcfex", Box{87.3603346758, 87.3603348434, -159.764133766, -159.764133431}}, + {"z7m8", Box{63.28125, 63.45703125, 153.984375, 154.3359375}}, + {"wm6w7f38", Box{30.6422424316, 30.642414093, 104.932479858, 104.932823181}}, + {"rxj23rtt4y", Box{-5.53896546364, -5.53896009922, 164.945415258, 164.945425987}}, + {"sfr9xsyzfn", Box{12.9473769665, 12.9473823309, 44.6358203888, 44.6358311176}}, + {"9ubf9uq02e", Box{27.1816080809, 27.1816134453, -100.110146999, -100.110136271}}, + {"kj25zp1gb6j", Box{-14.7704637051, -14.770462364, 0.310037881136, 0.31003922224}}, + {"x4f", Box{15.46875, 16.875, 137.8125, 139.21875}}, + {"xnn27kkf4c", Box{33.8176399469, 33.8176453114, 143.938525915, 143.938536644}}, + {"61bhs9byn4", Box{-34.3545806408, -34.3545752764, -89.8009586334, -89.8009479046}}, + {"rv2sve92mngr", Box{-14.6144826896, -14.614482522, 169.696759768, 169.696760103}}, + {"zkvq2w", Box{72.8503417969, 72.8558349609, 153.654785156, 153.665771484}}, + {"qprmp68h7kcd", Box{-3.32535546273, -3.32535529509, 100.514057502, 100.514057837}}, + {"77pmzubu", Box{-27.0874786377, -27.0873069763, -23.2130813599, -23.2127380371}}, + {"q73t2sumh3b", Box{-25.7689382136, -25.7689368725, 103.387366533, 103.387367874}}, + {"3kxch9c", Box{-19.5021057129, -19.5007324219, -112.652435303, -112.651062012}}, + {"t", Box{0.0, 45.0, 45.0, 90.0}}, + {"3um1y618chw", Box{-20.7749935985, -20.7749922574, -93.9419808984, -93.9419795573}}, + {"45nj7sxww", Box{-72.1763134003, -72.1762704849, -81.3981342316, -81.3980913162}}, + {"rnkyjdv404", Box{-8.77360224724, -8.77359688282, 141.928253174, 141.928263903}}, + {"p3", Box{-84.375, -78.75, 146.25, 157.5}}, + {"sxbz", Box{44.82421875, 45.0, 23.5546875, 23.90625}}, + {"xuj2k", Box{22.5439453125, 22.587890625, 176.30859375, 176.352539062}}, + {"yhp9", Box{67.67578125, 67.8515625, 100.546875, 100.8984375}}, + {"1yq4", Box{-54.4921875, -54.31640625, -92.8125, -92.4609375}}, + {"u4m2jkw", Box{57.6809692383, 57.6823425293, 7.62176513672, 7.62313842773}}, + {"xb9", Box{2.8125, 4.21875, 170.15625, 171.5625}}, + {"ebf4e478jp", Box{4.67060029507, 4.67060565948, -8.30064296722, -8.30063223839}}, + {"y7venx9", Box{66.6622924805, 66.6636657715, 109.271392822, 109.272766113}}, + {"8qu", Box{37.96875, 39.375, -163.125, -161.71875}}, + {"jw2jbzms66", Box{-53.7924420834, -53.7924367189, 67.5406086445, 67.5406193733}}, + {"n", Box{-90.0, -45.0, 90.0, 135.0}}, + {"jbx", Box{-87.1875, -85.78125, 88.59375, 90.0}}, + {"3v4n", Box{-15.8203125, -15.64453125, -98.4375, -98.0859375}}, + {"0z1theg", Box{-49.7254943848, -49.7241210938, -143.938751221, -143.93737793}}, + {"zbz00jf21m", Box{49.2503625154, 49.2503678799, 178.596893549, 178.596904278}}, + {"dfpq2eg2", Box{12.3692321777, 12.3694038391, -46.0282516479, -46.0279083252}}, + {"z2j5bc1ph562", Box{45.6658919156, 45.6658920832, 153.315756954, 153.31575729}}, + {"3p3g", Box{-3.69140625, -3.515625, -132.5390625, -132.1875}}, + {"4rfgeu3", Box{-45.7676696777, -45.7662963867, -74.7166442871, -74.7152709961}}, + {"nykq", Box{-53.7890625, -53.61328125, 129.7265625, 130.078125}}, + {"h", Box{-90.0, -45.0, 0.0, 45.0}}, + {"85", Box{16.875, 22.5, -180.0, -168.75}}, + {"bdsdxr", Box{59.5404052734, 59.5458984375, -150.853271484, -150.842285156}}, + {"wsyt3duqg2", Box{27.657866478, 27.6578718424, 121.71251893, 121.712529659}}, + {"90", Box{0.0, 5.625, -135.0, -123.75}}, + {"butw", Box{71.3671875, 71.54296875, -138.515625, -138.1640625}}, + {"ddhpjv6b7tqh", Box{12.5093796104, 12.5093797781, -61.6183796525, -61.6183793172}}, + {"18ueqgd", Box{-85.1907348633, -85.1893615723, -105.872497559, -105.871124268}}, + {"v2g8jh1", Box{49.2407226562, 49.2420959473, 61.3929748535, 61.3943481445}}, + {"84umeh3gmupk", Box{16.45947285, 16.4594730176, -173.888941817, -173.888941482}}, + {"s4g900", Box{15.64453125, 15.6500244141, 4.921875, 4.93286132812}}, + {"0b313fz2", Box{-88.3589172363, -88.358745575, -144.756889343, -144.756546021}}, + {"4q", Box{-56.25, -50.625, -78.75, -67.5}}, + {"d61", Box{11.25, 12.65625, -77.34375, -75.9375}}, + {"w5q5298pq", Box{18.8620233536, 18.8620662689, 98.4597301483, 98.4597730637}}, + {"ushgx399", Box{68.1236457825, 68.1238174438, 29.5003509521, 29.5006942749}}, + {"73ngt", Box{-38.759765625, -38.7158203125, -24.0380859375, -23.994140625}}, + {"2f4smcem", Box{-32.9938316345, -32.9936599731, -142.477226257, -142.476882935}}, + {"0", Box{-90.0, -45.0, -180.0, -135.0}}, + {"8", Box{0.0, 45.0, -180.0, -135.0}}, + {"5u14weqgz", Box{-67.0420503616, -67.0420074463, -9.54853534698, -9.54849243164}}, + {"xxhuu8y4xb", Box{40.214509964, 40.2145153284, 164.386013746, 164.386024475}}, + {"272xeqmj", Box{-25.3652000427, -25.3650283813, -167.897186279, -167.896842957}}, + {"2trrhunrd1", Box{-14.215015769, -14.2150104046, -147.087278366, -147.087267637}}, + {"e4", Box{11.25, 16.875, -45.0, -33.75}}, + {"p5duz8tp", Box{-69.4735908508, -69.4734191895, 139.203643799, 139.203987122}}, + {"5qprz78e", Box{-54.8679542542, -54.8677825928, -23.2353973389, -23.2350540161}}, + {"ch5yq40qtu3", Box{68.6107577384, 68.6107590795, -129.462299198, -129.462297857}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"7qmv9c5", Box{-8.87145996094, -8.87008666992, -25.5830383301, -25.5816650391}}, + {"c", Box{45.0, 90.0, -135.0, -90.0}}, + {"hp8s7", Box{-47.0654296875, -47.021484375, 0.8349609375, 0.87890625}}, + {"9e4y04d17", Box{17.9436349869, 17.9436779022, -108.629937172, -108.629894257}}, + {"39nh", Box{-38.671875, -38.49609375, -104.0625, -103.7109375}}, + {"6", Box{-45.0, 0.0, -90.0, -45.0}}, + {"pjpxe1pvyzhm", Box{-60.5501220189, -60.5501218513, 145.689649321, 145.689649656}}, + {"drx", Box{42.1875, 43.59375, -68.90625, -67.5}}, + {"zu1c5qg0s", Box{67.7129459381, 67.7129888535, 171.3580513, 171.358094215}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"t8d", Box{2.8125, 4.21875, 70.3125, 71.71875}}, + {"d47w70rwe23h", Box{13.7573739141, 13.7573740818, -84.9358485639, -84.9358482286}}, + {"3t617", Box{-15.2490234375, -15.205078125, -109.555664062, -109.51171875}}, + {"qnkq1pz", Box{-8.74649047852, -8.7451171875, 96.0301208496, 96.0314941406}}, + {"fu", Box{67.5, 73.125, -56.25, -45.0}}, + {"7vs", Box{-14.0625, -12.65625, -5.625, -4.21875}}, + {"bztqz0h", Box{88.3740234375, 88.3753967285, -138.554077148, -138.552703857}}, + {"b8j", Box{45.0, 46.40625, -150.46875, -149.0625}}, + {"cetkxmq73", Box{65.5079126358, 65.5079555511, -104.789958, -104.789915085}}, + {"p91", Box{-84.375, -82.96875, 158.90625, 160.3125}}, + {"z4g7bn4w38", Box{61.1619615555, 61.1619669199, 139.573810101, 139.573820829}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"g2eej64jbwj", Box{48.3518493176, 48.3518506587, -28.5946373641, -28.594636023}}, + {"fwshzb30mj", Box{82.398903966, 82.3989093304, -61.5328359604, -61.5328252316}}, + {"fv2mqt", Box{75.4815673828, 75.4870605469, -55.6127929688, -55.6018066406}}, + {"bzr6m3zdun5", Box{86.1868751049, 86.186876446, -135.813499242, -135.813497901}}, + {"et5rq77j8b", Box{29.4182109833, 29.4182163477, -17.6508772373, -17.6508665085}}, + {"1c", Box{-84.375, -78.75, -101.25, -90.0}}, + {"y1hyumh10jq", Box{51.8391890824, 51.8391904235, 96.8719562888, 96.8719576299}}, + {"qd42djnqxq", Box{-33.6334955692, -33.6334902048, 115.76084733, 115.760858059}}, + {"hsd9s", Box{-64.423828125, -64.3798828125, 26.19140625, 26.2353515625}}, + {"8289gq947", Box{3.156208992, 3.15625190735, -167.902550697, -167.902507782}}, + {"em37sw72zq4", Box{30.1809775829, 30.180978924, -31.7896565795, -31.7896552384}}, + {"zms25", Box{75.9375, 75.9814453125, 152.358398438, 152.40234375}}, + {"h25d54", Box{-89.6374511719, -89.6319580078, 16.3037109375, 16.3146972656}}, + {"6qc7y2t4bb", Box{-6.36885166168, -6.36884629726, -76.7106306553, -76.7106199265}}, + {"06vt5z8j", Box{-73.6102867126, -73.6101150513, -160.850830078, -160.850486755}}, + {"37q3", Box{-26.54296875, -26.3671875, -114.9609375, -114.609375}}, + {"sey9wu", Box{21.3793945312, 21.3848876953, 31.9372558594, 31.9482421875}}, + {"qk0jrj", Box{-21.5496826172, -21.5441894531, 101.557617188, 101.568603516}}, + {"8x6jjpm0", Box{41.6999816895, 41.7001533508, -154.460906982, -154.46056366}}, + {"5j1etu", Box{-61.2377929688, -61.2322998047, -42.6379394531, -42.626953125}}, + {"r6b", Box{-29.53125, -28.125, 146.25, 147.65625}}, + {"ddu3vyj07", Box{15.8093690872, 15.8094120026, -61.263756752, -61.2637138367}}, + {"m9fm5q2d91", Box{-34.2425769567, -34.2425715923, 70.8076143265, 70.8076250553}}, + {"0pxdx", Box{-47.373046875, -47.3291015625, -169.145507812, -169.1015625}}, + {"w", Box{0.0, 45.0, 90.0, 135.0}}, + {"q1e", Box{-36.5625, -35.15625, 94.21875, 95.625}}, + {"h3vxhm8tu", Box{-78.8945817947, -78.8945388794, 19.172000885, 19.1720438004}}, + {"bcxsz", Box{54.2724609375, 54.31640625, -135.395507812, -135.3515625}}, + {"crjh", Box{85.078125, 85.25390625, -116.71875, -116.3671875}}, + {"bdqejqqgwj", Box{58.2185536623, 58.2185590267, -148.119134903, -148.119124174}}, + {"x7zhc480u7", Box{21.9425886869, 21.9425940514, 156.137877703, 156.137888432}}, + {"xhr7c9nd6js9", Box{24.5713387616, 24.5713389292, 145.270248726, 145.270249061}}, + {"f25r3r", Box{46.3128662109, 46.318359375, -74.1247558594, -74.1137695312}}, + {"b4v1e8zek", Box{60.7370996475, 60.7371425629, -172.804470062, -172.804427147}}, + {"95cwh1k", Box{22.1553039551, 22.1566772461, -132.709350586, -132.707977295}}, + {"kh1r", Box{-21.26953125, -21.09375, 1.7578125, 2.109375}}, + {"7p", Box{-5.625, 0.0, -45.0, -33.75}}, + {"mgsvj", Box{-24.43359375, -24.3896484375, 85.6494140625, 85.693359375}}, + {"k70", Box{-28.125, -26.71875, 11.25, 12.65625}}, + {"pxjr5g", Box{-49.3780517578, -49.3725585938, 165.047607422, 165.05859375}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"81pv", Box{6.50390625, 6.6796875, -169.1015625, -168.75}}, + {"jjg", Box{-57.65625, -56.25, 49.21875, 50.625}}, + {"732kjtvw", Box{-37.2330093384, -37.232837677, -33.1491851807, -33.1488418579}}, + {"kuc2", Box{-18.28125, -18.10546875, 35.5078125, 35.859375}}, + {"wn91fmw18yp", Box{36.9006192684, 36.9006206095, 91.5134082735, 91.5134096146}}, + {"5wdnzyz5r", Box{-52.2133398056, -52.2132968903, -19.3370103836, -19.3369674683}}, + {"m682wkeu80", Box{-30.8241176605, -30.8241122961, 56.8813705444, 56.8813812733}}, + {"r18jv9k", Box{-35.5448913574, -35.5435180664, 135.247192383, 135.248565674}}, + {"zr079yhvttr", Box{85.0241656601, 85.0241670012, 146.685235351, 146.685236692}}, + {"r4umz4vhvm", Box{-28.5045593977, -28.5045540333, 141.291271448, 141.291282177}}, + {"58gdwpzc3zs", Box{-85.2989700437, -85.2989687026, -17.3037296534, -17.3037283123}}, + {"64frgqpt0yj", Box{-28.1350958347, -28.1350944936, -86.6827766597, -86.6827753186}}, + {"8n18eckkw5", Box{33.8455456495, 33.8455510139, -177.719736099, -177.71972537}}, + {"mz326c81b1", Box{-4.16625916958, -4.16625380516, 80.6286621094, 80.6286728382}}, + {"hx", Box{-50.625, -45.0, 22.5, 33.75}}, + {"ush2juq", Box{67.5233459473, 67.5247192383, 28.737487793, 28.738861084}}, + {"bp6h7m8fe", Box{86.5589618683, 86.5590047836, -177.04351902, -177.043476105}}, + {"111", Box{-84.375, -82.96875, -133.59375, -132.1875}}, + {"m9hwzz", Box{-38.1500244141, -38.14453125, 74.1687011719, 74.1796875}}, + {"100u6e92zuk", Box{-89.2335520685, -89.2335507274, -133.833394647, -133.833393306}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"9wbmdmhu", Box{38.9636993408, 38.9638710022, -112.043037415, -112.042694092}}, + {"9p1jg8k", Box{40.3871154785, 40.3884887695, -133.434448242, -133.433074951}}, + {"vqf6kw", Box{83.3972167969, 83.4027099609, 59.6118164062, 59.6228027344}}, + {"gw", Box{78.75, 84.375, -22.5, -11.25}}, + {"h49v9", Box{-74.970703125, -74.9267578125, 2.5048828125, 2.548828125}}, + {"23cmz", Box{-34.1455078125, -34.1015625, -166.684570312, -166.640625}}, + {"71", Box{-39.375, -33.75, -45.0, -33.75}}, + {"5x2kvmbu", Box{-48.3515167236, -48.3513450623, -21.9166946411, -21.9163513184}}, + {"1nywfjs8e", Box{-50.8144283295, -50.8143854141, -125.765175819, -125.765132904}}, + {"7u4vm3b9qy", Box{-21.5672886372, -21.5672832727, -7.15112328529, -7.15111255646}}, + {"rx4n750gn", Box{-4.50937271118, -4.50932979584, 160.445623398, 160.445666313}}, + {"9", Box{0.0, 45.0, -135.0, -90.0}}, + {"nxfyqng3", Box{-45.2703666687, -45.2701950073, 116.635322571, 116.635665894}}, + {"tgnt", Box{17.75390625, 17.9296875, 87.890625, 88.2421875}}, + {"qe2k5jtbm2c", Box{-25.985365659, -25.9853643179, 112.991521508, 112.991522849}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"1jhq", Box{-60.8203125, -60.64453125, -129.0234375, -128.671875}}, + {"p874n4ubjm", Box{-88.2270544767, -88.2270491123, 161.989170313, 161.989181042}}, + {"91h2qc", Box{5.67443847656, 5.67993164062, -128.726806641, -128.715820312}}, + {"mzp8s0p", Box{-5.537109375, -5.53573608398, 89.4822692871, 89.4836425781}}, + {"ptp0rem", Box{-61.8132019043, -61.8118286133, 167.680206299, 167.68157959}}, + {"14", Box{-78.75, -73.125, -135.0, -123.75}}, + {"s4dq", Box{15.1171875, 15.29296875, 3.1640625, 3.515625}}, + {"uvs7", Box{76.46484375, 76.640625, 39.7265625, 40.078125}}, + {"wh9xq3mqh", Box{26.5948104858, 26.5948534012, 92.3914146423, 92.3914575577}}, + {"kz", Box{-5.625, 0.0, 33.75, 45.0}}, + {"s8t4hkb3", Box{3.19032669067, 3.19049835205, 29.7183609009, 29.7187042236}}, + {"3ry9w", Box{-1.142578125, -1.0986328125, -114.345703125, -114.301757812}}, + {"mf1wt5", Box{-32.5909423828, -32.5854492188, 81.0791015625, 81.0900878906}}, + {"e", Box{0.0, 45.0, -45.0, 0.0}}, + {"mh", Box{-22.5, -16.875, 45.0, 56.25}}, + {"75y3665k", Box{-23.6748504639, -23.6746788025, -36.1075973511, -36.1072540283}}, + {"sts", Box{30.9375, 32.34375, 28.125, 29.53125}}, + {"6fdmxb", Box{-29.970703125, -29.9652099609, -52.7453613281, -52.734375}}, + {"xcvf9qbx13jx", Box{10.3214901499, 10.3214903176, 176.891616806, 176.891617142}}, + {"n1r6pkxu86t", Box{-82.5916823745, -82.5916810334, 100.524576455, 100.524577796}}, + {"1m2p", Box{-59.23828125, -59.0625, -123.75, -123.3984375}}, + {"fz", Box{84.375, 90.0, -56.25, -45.0}}, + {"hgw", Box{-70.3125, -68.90625, 42.1875, 43.59375}}, + {"ssktp8e5hsub", Box{24.7884432971, 24.7884434648, 29.1620342061, 29.1620345414}}, + {"8wbw4", Box{39.0234375, 39.0673828125, -156.708984375, -156.665039062}}, + {"wbcdsqtpnkh9", Box{4.69513194636, 4.69513211399, 126.053283289, 126.053283624}}, + {"f0md", Box{46.7578125, 46.93359375, -82.265625, -81.9140625}}, + {"hngnmbt2pe4", Box{-50.9298545122, -50.9298531711, 4.478969872, 4.4789712131}}, + {"gbkn8cgjewxc", Box{47.559420336, 47.5594205037, -5.58776054531, -5.58776021004}}, + {"u", Box{45.0, 90.0, 0.0, 45.0}}, + {"b5", Box{61.875, 67.5, -180.0, -168.75}}, + {"w1r042nrqyk", Box{7.0325280726, 7.0325294137, 99.951505065, 99.9515064061}}, + {"tv6r1uuh1h", Box{30.7885193825, 30.7885247469, 81.9965028763, 81.9965136051}}, + {"r1hw8pqpr3", Box{-38.1913465261, -38.1913411617, 141.336675882, 141.336686611}}, + {"j2bcyrxdgj", Box{-85.4319351912, -85.4319298267, 57.5897741318, 57.5897848606}}, + {"m", Box{-45.0, 0.0, 45.0, 90.0}}, + {"qtxvgfvmrbf", Box{-13.0357463658, -13.0357450247, 123.570777476, 123.570778817}}, + {"jp", Box{-50.625, -45.0, 45.0, 56.25}}, + {"f76", Box{63.28125, 64.6875, -75.9375, -74.53125}}, + {"vz9", Box{87.1875, 88.59375, 80.15625, 81.5625}}, + {"wm", Box{28.125, 33.75, 101.25, 112.5}}, + {"c0wn5", Box{48.8671875, 48.9111328125, -126.430664062, -126.38671875}}, + {"7pn7whg", Box{-4.9836730957, -4.98229980469, -35.943145752, -35.9417724609}}, + {"s", Box{0.0, 45.0, 0.0, 45.0}}, + {"txwr3", Box{43.4619140625, 43.505859375, 76.3330078125, 76.376953125}}, + {"zc0", Box{50.625, 52.03125, 168.75, 170.15625}}, + {"sq7pru6gr", Box{36.4545679092, 36.4546108246, 15.8134031296, 15.8134460449}}, + {"nu", Box{-67.5, -61.875, 123.75, 135.0}}, + {"7dkt6vr", Box{-31.3920593262, -31.3906860352, -16.0414123535, -16.0400390625}}, + {"xm2uwefdyf1", Box{30.3433477879, 30.343349129, 147.594056278, 147.59405762}}, + {"mgmnc0", Box{-25.5322265625, -25.5267333984, 85.8251953125, 85.8361816406}}, + {"jj1shq", Box{-61.1389160156, -61.1334228516, 47.2961425781, 47.3071289062}}, + {"3", Box{-45.0, 0.0, -135.0, -90.0}}, + {"0p4y3p8tgr", Box{-49.4841438532, -49.4841384888, -176.088041067, -176.088030338}}, + {"gu", Box{67.5, 73.125, -11.25, 0.0}}, + {"e94", Box{5.625, 7.03125, -19.6875, -18.28125}}, + {"u7khr", Box{64.0283203125, 64.072265625, 17.1826171875, 17.2265625}}, + {"k1k", Box{-37.96875, -36.5625, 5.625, 7.03125}}, + {"wks48m7f", Box{25.7811355591, 25.7813072205, 106.891136169, 106.891479492}}, + {"z91w3", Box{51.7236328125, 51.767578125, 159.653320312, 159.697265625}}, + {"c2d6xmbp1", Box{48.284740448, 48.2847833633, -120.267291069, -120.267248154}}, + {"s9yur", Box{10.5908203125, 10.634765625, 32.2998046875, 32.34375}}, + {"7u09b46", Box{-22.1800231934, -22.1786499023, -10.544128418, -10.542755127}}, + {"8sndxb", Box{22.939453125, 22.9449462891, -148.018798828, -148.0078125}}, + {"j2g761bhsex", Box{-85.1995566487, -85.1995553076, 60.9084056318, 60.9084069729}}, + {"5wg", Box{-52.03125, -50.625, -18.28125, -16.875}}, + {"fzn", Box{84.375, 85.78125, -47.8125, -46.40625}}, + {"ugdpz8p4mu", Box{66.0502123833, 66.0502177477, 36.9019496441, 36.9019603729}}, + {"nx", Box{-50.625, -45.0, 112.5, 123.75}}, + {"d", Box{0.0, 45.0, -90.0, -45.0}}, + {"crr9e8mz59se", Box{86.0475053452, 86.0475055128, -113.041263744, -113.041263409}}, + {"dgmpsg", Box{19.6160888672, 19.6215820312, -49.0100097656, -48.9990234375}}, + {"jcfk52m03", Box{-79.4517087936, -79.4516658783, 82.063794136, 82.0638370514}}, + {"d8trpccuk", Box{4.05331134796, 4.05335426331, -59.7740364075, -59.7739934921}}, + {"93g9665", Box{10.0744628906, 10.0758361816, -118.725128174, -118.723754883}}, + {"sqt7cuf8d0f", Box{37.2478620708, 37.2478634119, 18.7132385373, 18.7132398784}}, + {"f9", Box{50.625, 56.25, -67.5, -56.25}}, + {"k90", Box{-39.375, -37.96875, 22.5, 23.90625}}, + {"k8xdhcv", Box{-41.8263244629, -41.8249511719, 33.2624816895, 33.2638549805}}, + {"4989w4r926t7", Box{-81.2862400152, -81.2862398475, -66.5228856727, -66.5228853375}}, + {"c3", Box{50.625, 56.25, -123.75, -112.5}}, + {"bd908pg0", Box{59.1929626465, 59.1931343079, -156.089630127, -156.089286804}}, + {"bq", Box{78.75, 84.375, -168.75, -157.5}}, + {"chcdt", Box{72.158203125, 72.2021484375, -132.670898438, -132.626953125}}, + {"hff8vsrzhy39", Box{-74.3748327903, -74.3748326227, 37.5181730837, 37.5181734189}}, + {"9gef7g6ezj", Box{20.101531148, 20.1015365124, -95.8080339432, -95.8080232143}}, + {"yc0u2dp", Box{51.3830566406, 51.3844299316, 124.836273193, 124.837646484}}, + {"w0b41f7", Box{4.58267211914, 4.58404541016, 90.0810241699, 90.0823974609}}, + {"8cdmwjc", Box{9.43588256836, 9.43725585938, -142.820892334, -142.819519043}}, + {"p4ngqtjm2", Box{-78.150343895, -78.1503009796, 144.785041809, 144.785084724}}, + {"5", Box{-90.0, -45.0, -45.0, 0.0}}, + {"3qtzqdknx", Box{-7.14961051941, -7.14956760406, -115.372624397, -115.372581482}}, + {"gzjhuv9xmkg5", Box{85.2414438687, 85.2414440364, -4.00772050023, -4.00772016495}}, + {"8g8t7eh4y0fh", Box{20.6273078173, 20.627307985, -145.387313068, -145.387312733}}, + {"39w", Box{-36.5625, -35.15625, -104.0625, -102.65625}}, + {"z34rj8", Box{51.85546875, 51.8609619141, 149.655761719, 149.666748047}}, + {"c9p0zv2", Box{50.7856750488, 50.7870483398, -102.315673828, -102.314300537}}, + {"vh871y", Box{70.8728027344, 70.8782958984, 45.4284667969, 45.439453125}}, + {"7ggt8b4yfw", Box{-22.9382622242, -22.9382568598, -6.29128217697, -6.29127144814}}, + {"3qz3d324z", Box{-6.76023960114, -6.76019668579, -113.455510139, -113.455467224}}, + {"4sm2463w0w", Box{-66.0803282261, -66.0803228617, -60.0162291527, -60.0162184238}}, + {"26ewbu0gw3", Box{-29.728397727, -29.7283923626, -163.793867826, -163.793857098}}, + {"bre7js2w9z", Box{87.7393430471, 87.7393484116, -163.937226534, -163.937215805}}, + {"sy5ug08bn", Box{34.5877075195, 34.5877504349, 39.1565608978, 39.1566038132}}, + {"p4r", Box{-77.34375, -75.9375, 144.84375, 146.25}}, + {"qb", Box{-45.0, -39.375, 123.75, 135.0}}, + {"f4hj", Box{57.12890625, 57.3046875, -84.375, -84.0234375}}, + {"5r0f5t7d5", Box{-50.2442550659, -50.2442121506, -32.5365686417, -32.5365257263}}, + {"2j7n4r6r", Box{-14.3730354309, -14.3728637695, -175.679283142, -175.678939819}}, + {"wu92egv2wsy", Box{25.4211013019, 25.421102643, 125.680104196, 125.680105537}}, + {"vgtwey347bg", Box{65.8648006618, 65.8648020029, 86.6507081687, 86.6507095098}}, + {"q2meny7pbd18", Box{-43.0307328701, -43.0307327025, 109.285149202, 109.285149537}}, + {"rpe", Box{-2.8125, -1.40625, 139.21875, 140.625}}, + {"m69", Box{-30.9375, -29.53125, 57.65625, 59.0625}}, + {"w1zwd8", Box{10.986328125, 10.9918212891, 100.656738281, 100.667724609}}, + {"fzpf", Box{84.7265625, 84.90234375, -45.3515625, -45.0}}, + {"t3w", Box{8.4375, 9.84375, 64.6875, 66.09375}}, + {"zb11", Box{45.17578125, 45.3515625, 170.15625, 170.5078125}}, + {"r2prkmxpgtww", Box{-43.6940126494, -43.6940124817, 156.641852036, 156.641852371}}, + {"zr34g1zcj", Box{86.274433136, 86.2744760513, 147.79894352, 147.798986435}}, + {"19mgdk8", Box{-82.3287963867, -82.3274230957, -104.315185547, -104.313812256}}, + {"mkp", Box{-22.5, -21.09375, 66.09375, 67.5}}, + {"934qy86ssc", Box{6.81367456913, 6.81367993355, -120.296655893, -120.296645164}}, + {"byydj4mrm8", Box{83.3339166641, 83.3339220285, -136.882202625, -136.882191896}}, + {"j", Box{-90.0, -45.0, 45.0, 90.0}}, + {"9cjzqv5n6", Box{6.92795276642, 6.92799568176, -92.8632259369, -92.8631830215}}, + {"vkg1wg6s", Box{72.0009613037, 72.0011329651, 60.7688140869, 60.7691574097}}, + {"ynp42e9x", Box{79.1659355164, 79.1661071777, 99.8677825928, 99.8681259155}}, + {"uv9zwddtwn", Box{77.2705686092, 77.2705739737, 36.5002727509, 36.5002834797}}, + {"t17zkzszpm", Box{8.3480912447, 8.34809660912, 50.4890120029, 50.4890227318}}, + {"tuw779c04ukm", Box{25.8934257366, 25.8934259042, 87.6943681017, 87.6943684369}}, + {"37rm598", Box{-25.8316040039, -25.8302307129, -113.400878906, -113.399505615}}, + {"ymf18", Box{77.607421875, 77.6513671875, 104.0625, 104.106445312}}, + {"gd", Box{56.25, 61.875, -22.5, -11.25}}, + {"smz", Box{32.34375, 33.75, 21.09375, 22.5}}, + {"p", Box{-90.0, -45.0, 135.0, 180.0}}, + {"muzkh95w2u42", Box{-17.5715374947, -17.571537327, 89.1479081288, 89.1479084641}}, + {"hh53c48eg", Box{-67.1780061722, -67.1779632568, 4.61507320404, 4.61511611938}}, + {"739ewv", Box{-35.9197998047, -35.9143066406, -31.3439941406, -31.3330078125}}, + {"cw883", Box{81.6064453125, 81.650390625, -111.752929688, -111.708984375}}, + {"41xu1w37yf", Box{-80.8243882656, -80.8243829012, -79.0336382389, -79.0336275101}}, + {"0y750v2k", Box{-54.2868804932, -54.2867088318, -141.997947693, -141.99760437}}, + {"gqbgw", Box{83.583984375, 83.6279296875, -32.431640625, -32.3876953125}}, + {"pej", Box{-73.125, -71.71875, 164.53125, 165.9375}}, + {"r05t", Box{-44.12109375, -43.9453125, 139.921875, 140.2734375}}, + {"qfuew7wk4f", Box{-28.8960921764, -28.896086812, 130.361484289, 130.361495018}}, + {"nu357yhp72y", Box{-65.4882533848, -65.4882520437, 125.326685607, 125.326686949}}, + {"8qt7rnggz", Box{37.1715116501, 37.1715545654, -161.054120064, -161.054077148}}, + {"dhjq2nz", Box{23.6357116699, 23.6370849609, -82.6075744629, -82.6062011719}}, + {"5s4", Box{-67.5, -66.09375, -19.6875, -18.28125}}, + {"ge8nq842", Box{65.7861328125, 65.7863044739, -22.211265564, -22.2109222412}}, + {"71", Box{-39.375, -33.75, -45.0, -33.75}}, + {"sz59kteks87q", Box{39.625713788, 39.6257139556, 38.8742895797, 38.874289915}}, + {"ur2bh", Box{85.78125, 85.8251953125, 12.48046875, 12.5244140625}}, + {"w140u2f", Box{5.76095581055, 5.76232910156, 93.0020141602, 93.0033874512}}, + {"fpd3zkt6whz4", Box{87.5202913955, 87.5202915631, -86.5098573267, -86.5098569915}}, + {"zmej764h8kb8", Box{76.8721358478, 76.8721360154, 150.614330247, 150.614330582}}, + {"k4p6z", Box{-33.2666015625, -33.22265625, 10.5029296875, 10.546875}}, + {"f8", Box{45.0, 50.625, -67.5, -56.25}}, + {"utsy6pv17m", Box{77.0789462328, 77.0789515972, 29.2745840549, 29.2745947838}}, + {"6z5", Box{-5.625, -4.21875, -52.03125, -50.625}}, + {"mjdc1", Box{-13.88671875, -13.8427734375, 48.9111328125, 48.955078125}}, + {"gjks4c2", Box{75.2412414551, 75.2426147461, -38.5510253906, -38.5496520996}}, + {"fvkvvrh42ju", Box{75.5808614194, 75.5808627605, -49.3341010809, -49.3340997398}}, + {"yp63x30p7u7", Box{86.0516823828, 86.0516837239, 93.4828309715, 93.4828323126}}, + {"6rw", Box{-2.8125, -1.40625, -70.3125, -68.90625}}, + {"28vsqm8sb6", Box{-40.0031411648, -40.0031358004, -149.490269423, -149.490258694}}, + {"be72g2zcek5", Box{63.4174847603, 63.4174861014, -152.776078731, -152.77607739}}, + {"xry6fn699f", Box{44.1117489338, 44.1117542982, 155.130461454, 155.130472183}}, + {"3sf7bw01y4", Box{-17.5888001919, -17.5887948275, -109.313707352, -109.313696623}}, + {"k729yrqr77", Box{-26.3700467348, -26.3700413704, 12.2365057468, 12.2365164757}}, + {"e", Box{0.0, 45.0, -45.0, 0.0}}, + {"63x6dd", Box{-36.1120605469, -36.1065673828, -68.4448242188, -68.4338378906}}, + {"z", Box{45.0, 90.0, 135.0, 180.0}}, + {"mzyj", Box{-0.52734375, -0.3515625, 87.1875, 87.5390625}}, + {"3j6r", Box{-14.23828125, -14.0625, -131.8359375, -131.484375}}, + {"u3q", Box{52.03125, 53.4375, 19.6875, 21.09375}}, + {"nueu7mbnbn4", Box{-63.9076530933, -63.9076517522, 129.166262448, 129.166263789}}, + {"cyq2pkr", Box{80.1795959473, 80.1809692383, -92.1327209473, -92.1313476562}}, + {"gptzzke9hvh", Box{88.5747224092, 88.5747237504, -36.5904432535, -36.5904419124}}, + {"khd", Box{-19.6875, -18.28125, 2.8125, 4.21875}}, + {"ghm92hg6p5", Box{69.1524285078, 69.1524338722, -37.2608613968, -37.260850668}}, + {"n9e20w0", Box{-81.5295410156, -81.5281677246, 117.092285156, 117.093658447}}, + {"826dzs0k", Box{1.91230773926, 1.91247940063, -164.904441833, -164.904098511}}, + {"0d5f2", Box{-78.3544921875, -78.310546875, -152.2265625, -152.182617188}}, + {"70zsyg3", Box{-39.9284362793, -39.9270629883, -34.1551208496, -34.1537475586}}, + {"zykh8gwy", Box{80.9675216675, 80.9676933289, 174.417228699, 174.417572021}}, + {"4spd3s", Box{-67.0825195312, -67.0770263672, -56.8872070312, -56.8762207031}}, + {"r9p6f2t", Box{-38.8888549805, -38.8874816895, 167.801055908, 167.802429199}}, + {"6q3merq7w", Box{-8.83652687073, -8.83648395538, -76.8405246735, -76.8404817581}}, + {"qx1zr32", Box{-4.34371948242, -4.34234619141, 115.279541016, 115.280914307}}, + {"3zfnk", Box{-0.3076171875, -0.263671875, -98.26171875, -98.2177734375}}, + {"kd2e3", Box{-31.7724609375, -31.728515625, 23.2470703125, 23.291015625}}, + {"stkhr6", Box{30.2893066406, 30.2947998047, 28.4436035156, 28.4545898438}}, + {"nh4pzbm", Box{-66.1363220215, -66.1349487305, 93.159942627, 93.161315918}}, + {"zt8tf", Box{76.9482421875, 76.9921875, 158.291015625, 158.334960938}}, + {"gd37", Box{58.18359375, 58.359375, -20.7421875, -20.390625}}, + {"gnx5b45dd", Box{82.2330951691, 82.2331380844, -35.1513576508, -35.1513147354}}, + {"2qm7", Box{-9.31640625, -9.140625, -161.3671875, -161.015625}}, + {"4g0zf0kq9cpp", Box{-71.7601996846, -71.760199517, -55.1015008986, -55.1015005633}}, + {"977tgt", Box{19.3194580078, 19.3249511719, -118.674316406, -118.663330078}}, + {"md0k", Box{-33.046875, -32.87109375, 67.8515625, 68.203125}}, + {"v1q3nvfb3h", Box{52.2386813164, 52.2386866808, 54.089512825, 54.0895235538}}, + {"z96pt6u96w", Box{53.3649623394, 53.3649677038, 160.549499989, 160.549510717}}, + {"pu", Box{-67.5, -61.875, 168.75, 180.0}}, + {"6uydh", Box{-17.9296875, -17.8857421875, -46.93359375, -46.8896484375}}, + {"nx5mt4sk", Box{-49.6437835693, -49.643611908, 117.295875549, 117.296218872}}, + {"nk8jt", Box{-63.720703125, -63.6767578125, 101.469726562, 101.513671875}}, + {"kec1015b", Box{-23.7249755859, -23.7248039246, 23.9113998413, 23.9117431641}}, + {"fk388b", Box{68.994140625, 68.9996337891, -76.6076660156, -76.5966796875}}, + {"nsb", Box{-63.28125, -61.875, 112.5, 113.90625}}, + {"ndbws", Box{-73.388671875, -73.3447265625, 113.37890625, 113.422851562}}, + {"fs5", Box{67.5, 68.90625, -63.28125, -61.875}}, + {"h6x0kbec6p15", Box{-75.8905554749, -75.8905553073, 21.3077272475, 21.3077275828}}, + {"hy78", Box{-54.84375, -54.66796875, 38.671875, 39.0234375}}, + {"vpun9j4ce9", Box{89.7640568018, 89.7640621662, 50.6728720665, 50.6728827953}}, + {"6tyevvqrj665", Box{-11.9670169987, -11.967016831, -58.0978783965, -58.0978780612}}, + {"d3ktekr48n", Box{8.02185416222, 8.02185952663, -72.2694396973, -72.2694289684}}, + {"heyfm2up5", Box{-68.5054206848, -68.5053777695, 32.2285223007, 32.2285652161}}, + {"mn3f7qjr22v", Box{-9.41403463483, -9.41403329372, 47.6109869778, 47.6109883189}}, + {"1wngqx9b", Box{-55.637512207, -55.6373405457, -102.719764709, -102.719421387}}, + {"xc9jv49jej", Box{9.46294605732, 9.46295142174, 170.3774786, 170.377489328}}, + {"27", Box{-28.125, -22.5, -168.75, -157.5}}, + {"6yhqnw", Box{-10.1623535156, -10.1568603516, -49.9877929688, -49.9768066406}}, + {"rmhhu3qd", Box{-16.0328292847, -16.0326576233, 152.07069397, 152.071037292}}, + {"y00b8mhby", Box{45.1154851913, 45.1155281067, 91.0724544525, 91.0724973679}}, + {"yq5tr", Box{79.6728515625, 79.716796875, 106.479492188, 106.5234375}}, + {"cuvxw", Box{73.037109375, 73.0810546875, -93.251953125, -93.2080078125}}, + {"exvb4bcn", Box{43.5988998413, 43.5990715027, -14.2918395996, -14.2914962769}}, + {"uhdpsyx75n0", Box{71.667112112, 71.6671134531, 3.03132534027, 3.03132668138}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"qvwbzgw7q", Box{-13.9108800888, -13.9108371735, 133.591604233, 133.591647148}}, + {"u0rqp0bres", Box{47.466366291, 47.4663716555, 10.503423214, 10.5034339428}}, + {"43k2u0vbnrbd", Box{-82.8327522799, -82.8327521123, -72.5894909352, -72.5894905999}}, + {"7r7kwz7xxr", Box{-3.38658392429, -3.38657855988, -28.8779389858, -28.877928257}}, + {"fu2f", Box{69.2578125, 69.43359375, -55.1953125, -54.84375}}, + {"9tkjsvzxw6", Box{30.5309307575, 30.5309361219, -106.655691862, -106.655681133}}, + {"y9x0z", Box{53.5693359375, 53.61328125, 122.651367188, 122.6953125}}, + {"y4rk4b25g", Box{58.3613920212, 58.3614349365, 100.316290855, 100.316333771}}, + {"sxmdmu9xcn", Box{41.202839613, 41.2028449774, 30.4891633987, 30.4891741276}}, + {"fb0", Box{45.0, 46.40625, -56.25, -54.84375}}, + {"7ffkxt5", Box{-28.7127685547, -28.7113952637, -7.7522277832, -7.75085449219}}, + {"n1x5jn4dr", Box{-81.0018110275, -81.0017681122, 100.067210197, 100.067253113}}, + {"uxcdctgmj", Box{89.1095924377, 89.1096353531, 24.6799707413, 24.6800136566}}, + {"h7", Box{-73.125, -67.5, 11.25, 22.5}}, + {"b", Box{45.0, 90.0, -180.0, -135.0}}, + {"us1n4udk", Box{68.5800933838, 68.5802650452, 24.0301895142, 24.0305328369}}, + {"zjtptsj2vn", Box{77.2779929638, 77.2779983282, 142.280373573, 142.280384302}}, + {"x6utsqz", Box{16.4726257324, 16.4739990234, 152.774505615, 152.775878906}}, + {"cs9dn901rqn", Box{70.6698024273, 70.6698037684, -110.104661286, -110.104659945}}, + {"sjbn", Box{33.3984375, 33.57421875, 0.0, 0.3515625}}, + {"0fwxjyc3g9q", Box{-74.6696452796, -74.6696439385, -136.854814589, -136.854813248}}, + {"fk", Box{67.5, 73.125, -78.75, -67.5}}, + {"75hq9jh", Box{-26.9549560547, -26.9535827637, -38.9739990234, -38.9726257324}}, + {"kr3hg1q1", Box{-3.37675094604, -3.37657928467, 12.7963256836, 12.7966690063}}, + {"hfq4d2wu", Box{-76.9008636475, -76.9006919861, 42.2956466675, 42.2959899902}}, + {"rg6ygh", Box{-25.5102539062, -25.5047607422, 172.749023438, 172.760009766}}, + {"995pvrg", Box{7.02987670898, 7.03125, -108.046417236, -108.045043945}}, + {"s5ys", Box{21.796875, 21.97265625, 9.140625, 9.4921875}}, + {"289ucubzj6", Box{-41.3252341747, -41.3252288103, -154.960902929, -154.9608922}}, + {"4", Box{-90.0, -45.0, -90.0, -45.0}}, + {"7g0e3gp7cz", Box{-27.5365501642, -27.5365447998, -10.4599392414, -10.4599285126}}, + {"9suuudg", Box{27.5688171387, 27.5701904297, -105.618438721, -105.61706543}}, + {"8vdt3j8zb0", Box{31.8918943405, 31.8918997049, -142.689399719, -142.68938899}}, + {"cf", Box{56.25, 61.875, -101.25, -90.0}}, + {"jnp33f5pr9", Box{-56.0180372, -56.0180318356, 55.276658535, 55.2766692638}}, + {"czgmgyb", Box{89.6415710449, 89.6429443359, -96.5148925781, -96.5135192871}}, + {"c1kk", Box{52.734375, 52.91015625, -129.0234375, -128.671875}}, + {"kfm4hfe8cp4s", Box{-31.9782876223, -31.9782874547, 40.994843021, 40.9948433563}}, + {"9mnws4hc8h", Box{29.2788434029, 29.2788487673, -114.427070618, -114.427059889}}, + {"t0j7chwg", Box{0.684413909912, 0.684585571289, 52.4360275269, 52.4363708496}}, + {"y", Box{45.0, 90.0, 90.0, 135.0}}, + {"suj", Box{22.5, 23.90625, 40.78125, 42.1875}}, +} diff --git a/vendor/github.com/mmcloughlin/geohash/example_test.go b/vendor/github.com/mmcloughlin/geohash/example_test.go new file mode 100644 index 00000000..d2222d85 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/example_test.go @@ -0,0 +1,72 @@ +package geohash_test + +import ( + "fmt" + + "github.com/mmcloughlin/geohash" +) + +func Example() { + // Uluru in Australian Outback + lat, lng := -25.345457, 131.036192 + + // Encode a full 12 character string geohash + fmt.Println(geohash.Encode(lat, lng)) + + // Or at lower precision + fmt.Println(geohash.EncodeWithPrecision(lat, lng, 6)) + + // As an integer + fmt.Printf("%016x\n", geohash.EncodeInt(lat, lng)) + + // Decode to a point + fmt.Println(geohash.Decode("qgmpvf18")) + + // or to a bounding box + fmt.Println(geohash.BoundingBox("qgmpvf18")) + + // Output: + // qgmpvf18h86e + // qgmpvf + // b3e75db828820cd5 + // -25.3454 131.036 + // {-25.345458984375 -25.345287322998047 131.03599548339844 131.03633880615234} +} + +func ExampleEncode() { + fmt.Println(geohash.Encode(48.858, 2.294)) + // Output: u09tunq6qp66 +} + +func ExampleEncodeInt() { + fmt.Printf("%016x\n", geohash.EncodeInt(48.858, 2.294)) + // Output: d0139d52c6b54c69 +} + +func ExampleEncodeIntWithPrecision() { + fmt.Printf("%08x\n", geohash.EncodeIntWithPrecision(48.858, 2.294, 32)) + // Output: d0139d52 +} + +func ExampleEncodeWithPrecision() { + fmt.Println(geohash.EncodeWithPrecision(48.858, 2.294, 5)) + // Output: u09tu +} + +func ExampleDecode() { + lat, lng := geohash.Decode("u09tunq6") + fmt.Printf("%.3f %.3f\n", lat, lng) + // Output: 48.858 2.294 +} + +func ExampleDecodeInt() { + lat, lng := geohash.DecodeInt(0xd0139d52c6b54c69) + fmt.Printf("%.3f %.3f\n", lat, lng) + // Output: 48.858 2.294 +} + +func ExampleDecodeIntWithPrecision() { + lat, lng := geohash.DecodeIntWithPrecision(0xd013, uint(16)) + fmt.Printf("%.3f %.3f\n", lat, lng) + // Output: 48.600 2.000 +} diff --git a/vendor/github.com/mmcloughlin/geohash/extensive_test.go b/vendor/github.com/mmcloughlin/geohash/extensive_test.go new file mode 100644 index 00000000..c42bdedc --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/extensive_test.go @@ -0,0 +1,122 @@ +package geohash + +import ( + "math" + "testing" +) + +// TestCase objects are generated from independent code to verify we get the +// same results. See testcases_test.go. +type TestCase struct { + hashInt uint64 + hash string + lat, lng float64 +} + +// Test we get the same string geohashes. +func TestEncode(t *testing.T) { + for _, c := range testcases { + hash := Encode(c.lat, c.lng) + if c.hash != hash { + t.Errorf("incorrect encode string result for (%v,%v): %s != %s", + c.lat, c.lng, c.hash, hash) + } + } +} + +// Test we get the same integer geohashes. +func TestEncodeInt(t *testing.T) { + for _, c := range testcases { + hashInt := EncodeInt(c.lat, c.lng) + if c.hashInt != hashInt { + t.Errorf("incorrect encode integer result for (%v,%v): %016x != %016x xor %016x", + c.lat, c.lng, c.hashInt, hashInt, c.hashInt^hashInt) + } + } +} + +// Verify the prefix property. +func TestPrefixProperty(t *testing.T) { + for _, c := range testcases { + for chars := uint(1); chars <= 12; chars++ { + hash := EncodeWithPrecision(c.lat, c.lng, chars) + pre := c.hash[:chars] + if pre != hash { + t.Errorf("incorrect encode string result for (%v,%v) at precision %d: %s != %s", + c.lat, c.lng, chars, pre, hash) + } + } + } +} + +// Test bounding boxes for string geohashes. +func TestBoundingBox(t *testing.T) { + for _, c := range testcases { + box := BoundingBox(c.hash) + if !box.Contains(c.lat, c.lng) { + t.Errorf("incorrect bounding box for %s", c.hash) + } + } +} + +// Test bounding boxes for integer geohashes. +func TestBoundingBoxInt(t *testing.T) { + for _, c := range testcases { + box := BoundingBoxInt(c.hashInt) + if !box.Contains(c.lat, c.lng) { + t.Errorf("incorrect bounding box for 0x%x", c.hashInt) + } + } +} + +// Crude test of integer decoding. +func TestDecodeInt(t *testing.T) { + for _, c := range testcases { + lat, lng := DecodeInt(c.hashInt) + if math.Abs(lat-c.lat) > 0.0000001 { + t.Errorf("large error in decoded latitude for 0x%x", c.hashInt) + } + if math.Abs(lng-c.lng) > 0.0000001 { + t.Errorf("large error in decoded longitude for 0x%x", c.hashInt) + } + } +} + +type DecodeTestCase struct { + hash string + box Box +} + +// Test decoding at various precisions. +func TestDecode(t *testing.T) { + for _, c := range decodecases { + lat, lng := Decode(c.hash) + if !c.box.Contains(lat, lng) { + t.Errorf("hash %s decoded to %f,%f should lie in %+v", + c.hash, lat, lng, c.box) + } + } +} + +// Test decoding at various precisions. +func TestDecodeCenter(t *testing.T) { + for _, c := range decodecases { + lat, lng := DecodeCenter(c.hash) + if !c.box.Contains(lat, lng) { + t.Errorf("hash %s decoded to %f,%f should lie in %+v", + c.hash, lat, lng, c.box) + } + } +} + +// Test roundtrip decoding then encoding again. +func TestDecodeThenEncode(t *testing.T) { + for _, c := range decodecases { + precision := uint(len(c.hash)) + lat, lng := Decode(c.hash) + rehashed := EncodeWithPrecision(lat, lng, precision) + if c.hash != rehashed { + t.Errorf("hash %s decoded and re-encoded to %s", c.hash, rehashed) + } + } +} diff --git a/vendor/github.com/mmcloughlin/geohash/geohash.go b/vendor/github.com/mmcloughlin/geohash/geohash.go new file mode 100644 index 00000000..85633049 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/geohash.go @@ -0,0 +1,292 @@ +// Package geohash provides encoding and decoding of string and integer +// geohashes. +package geohash + +import ( + "math" +) + +// Direction represents directions in the latitute/longitude space. +type Direction int + +// Cardinal and intercardinal directions +const ( + North Direction = iota + NorthEast + East + SouthEast + South + SouthWest + West + NorthWest +) + +// Encode the point (lat, lng) as a string geohash with the standard 12 +// characters of precision. +func Encode(lat, lng float64) string { + return EncodeWithPrecision(lat, lng, 12) +} + +// EncodeWithPrecision encodes the point (lat, lng) as a string geohash with +// the specified number of characters of precision (max 12). +func EncodeWithPrecision(lat, lng float64, chars uint) string { + bits := 5 * chars + inthash := EncodeIntWithPrecision(lat, lng, bits) + enc := base32encoding.Encode(inthash) + return enc[12-chars:] +} + +// EncodeInt encodes the point (lat, lng) to a 64-bit integer geohash. +func EncodeInt(lat, lng float64) uint64 + +// encodeInt provides a Go implementation of integer geohash. This is the +// default implementation of EncodeInt, but optimized versions are provided +// for certain architectures. +func encodeInt(lat, lng float64) uint64 { + latInt := encodeRange(lat, 90) + lngInt := encodeRange(lng, 180) + return interleave(latInt, lngInt) +} + +// EncodeIntWithPrecision encodes the point (lat, lng) to an integer with the +// specified number of bits. +func EncodeIntWithPrecision(lat, lng float64, bits uint) uint64 { + hash := EncodeInt(lat, lng) + return hash >> (64 - bits) +} + +// Box represents a rectangle in latitude/longitude space. +type Box struct { + MinLat float64 + MaxLat float64 + MinLng float64 + MaxLng float64 +} + +// Center returns the center of the box. +func (b Box) Center() (lat, lng float64) { + lat = (b.MinLat + b.MaxLat) / 2.0 + lng = (b.MinLng + b.MaxLng) / 2.0 + return +} + +// Contains decides whether (lat, lng) is contained in the box. The +// containment test is inclusive of the edges and corners. +func (b Box) Contains(lat, lng float64) bool { + return (b.MinLat <= lat && lat <= b.MaxLat && + b.MinLng <= lng && lng <= b.MaxLng) +} + +// minDecimalPlaces returns the minimum number of decimal places such that +// there must exist an number with that many places within any range of width +// r. This is intended for returning minimal precision coordinates inside a +// box. +func maxDecimalPower(r float64) float64 { + m := int(math.Floor(math.Log10(r))) + return math.Pow10(m) +} + +// Round returns a point inside the box, making an effort to round to minimal +// precision. +func (b Box) Round() (lat, lng float64) { + x := maxDecimalPower(b.MaxLat - b.MinLat) + lat = math.Ceil(b.MinLat/x) * x + x = maxDecimalPower(b.MaxLng - b.MinLng) + lng = math.Ceil(b.MinLng/x) * x + return +} + +// errorWithPrecision returns the error range in latitude and longitude for in +// integer geohash with bits of precision. +func errorWithPrecision(bits uint) (latErr, lngErr float64) { + b := int(bits) + latBits := b / 2 + lngBits := b - latBits + latErr = math.Ldexp(180.0, -latBits) + lngErr = math.Ldexp(360.0, -lngBits) + return +} + +// BoundingBox returns the region encoded by the given string geohash. +func BoundingBox(hash string) Box { + bits := uint(5 * len(hash)) + inthash := base32encoding.Decode(hash) + return BoundingBoxIntWithPrecision(inthash, bits) +} + +// BoundingBoxIntWithPrecision returns the region encoded by the integer +// geohash with the specified precision. +func BoundingBoxIntWithPrecision(hash uint64, bits uint) Box { + fullHash := hash << (64 - bits) + latInt, lngInt := deinterleave(fullHash) + lat := decodeRange(latInt, 90) + lng := decodeRange(lngInt, 180) + latErr, lngErr := errorWithPrecision(bits) + return Box{ + MinLat: lat, + MaxLat: lat + latErr, + MinLng: lng, + MaxLng: lng + lngErr, + } +} + +// BoundingBoxInt returns the region encoded by the given 64-bit integer +// geohash. +func BoundingBoxInt(hash uint64) Box { + return BoundingBoxIntWithPrecision(hash, 64) +} + +// Decode the string geohash to a (lat, lng) point. +func Decode(hash string) (lat, lng float64) { + box := BoundingBox(hash) + return box.Round() +} + +// DecodeCenter decodes the string geohash to the central point of the bounding box. +func DecodeCenter(hash string) (lat, lng float64) { + box := BoundingBox(hash) + return box.Center() +} + +// DecodeIntWithPrecision decodes the provided integer geohash with bits of +// precision to a (lat, lng) point. +func DecodeIntWithPrecision(hash uint64, bits uint) (lat, lng float64) { + box := BoundingBoxIntWithPrecision(hash, bits) + return box.Round() +} + +// DecodeInt decodes the provided 64-bit integer geohash to a (lat, lng) point. +func DecodeInt(hash uint64) (lat, lng float64) { + return DecodeIntWithPrecision(hash, 64) +} + +// Neighbors returns a slice of geohash strings that correspond to the provided +// geohash's neighbors. +func Neighbors(hash string) []string { + box := BoundingBox(hash) + lat, lng := box.Center() + latDelta := box.MaxLat - box.MinLat + lngDelta := box.MaxLng - box.MinLng + precision := uint(len(hash)) + return []string{ + // N + EncodeWithPrecision(lat+latDelta, lng, precision), + // NE, + EncodeWithPrecision(lat+latDelta, lng+lngDelta, precision), + // E, + EncodeWithPrecision(lat, lng+lngDelta, precision), + // SE, + EncodeWithPrecision(lat-latDelta, lng+lngDelta, precision), + // S, + EncodeWithPrecision(lat-latDelta, lng, precision), + // SW, + EncodeWithPrecision(lat-latDelta, lng-lngDelta, precision), + // W, + EncodeWithPrecision(lat, lng-lngDelta, precision), + // NW + EncodeWithPrecision(lat+latDelta, lng-lngDelta, precision), + } +} + +// NeighborsInt returns a slice of uint64s that correspond to the provided hash's +// neighbors at 64-bit precision. +func NeighborsInt(hash uint64) []uint64 { + return NeighborsIntWithPrecision(hash, 64) +} + +// NeighborsIntWithPrecision returns a slice of uint64s that correspond to the +// provided hash's neighbors at the given precision. +func NeighborsIntWithPrecision(hash uint64, bits uint) []uint64 { + box := BoundingBoxIntWithPrecision(hash, bits) + lat, lng := box.Center() + latDelta := box.MaxLat - box.MinLat + lngDelta := box.MaxLng - box.MinLng + return []uint64{ + // N + EncodeIntWithPrecision(lat+latDelta, lng, bits), + // NE, + EncodeIntWithPrecision(lat+latDelta, lng+lngDelta, bits), + // E, + EncodeIntWithPrecision(lat, lng+lngDelta, bits), + // SE, + EncodeIntWithPrecision(lat-latDelta, lng+lngDelta, bits), + // S, + EncodeIntWithPrecision(lat-latDelta, lng, bits), + // SW, + EncodeIntWithPrecision(lat-latDelta, lng-lngDelta, bits), + // W, + EncodeIntWithPrecision(lat, lng-lngDelta, bits), + // NW + EncodeIntWithPrecision(lat+latDelta, lng-lngDelta, bits), + } +} + +// Neighbor returns a geohash string that corresponds to the provided +// geohash's neighbor in the provided direction +func Neighbor(hash string, direction Direction) string { + return Neighbors(hash)[direction] +} + +// NeighborInt returns a uint64 that corresponds to the provided hash's +// neighbor in the provided direction at 64-bit precision. +func NeighborInt(hash uint64, direction Direction) uint64 { + return NeighborsIntWithPrecision(hash, 64)[direction] +} + +// NeighborIntWithPrecision returns a uint64s that corresponds to the +// provided hash's neighbor in the provided direction at the given precision. +func NeighborIntWithPrecision(hash uint64, bits uint, direction Direction) uint64 { + return NeighborsIntWithPrecision(hash, bits)[direction] +} + +// precalculated for performance +var exp232 = math.Exp2(32) + +// Encode the position of x within the range -r to +r as a 32-bit integer. +func encodeRange(x, r float64) uint32 { + p := (x + r) / (2 * r) + return uint32(p * exp232) +} + +// Decode the 32-bit range encoding X back to a value in the range -r to +r. +func decodeRange(X uint32, r float64) float64 { + p := float64(X) / exp232 + x := 2*r*p - r + return x +} + +// Spread out the 32 bits of x into 64 bits, where the bits of x occupy even +// bit positions. +func spread(x uint32) uint64 { + X := uint64(x) + X = (X | (X << 16)) & 0x0000ffff0000ffff + X = (X | (X << 8)) & 0x00ff00ff00ff00ff + X = (X | (X << 4)) & 0x0f0f0f0f0f0f0f0f + X = (X | (X << 2)) & 0x3333333333333333 + X = (X | (X << 1)) & 0x5555555555555555 + return X +} + +// Interleave the bits of x and y. In the result, x and y occupy even and odd +// bitlevels, respectively. +func interleave(x, y uint32) uint64 { + return spread(x) | (spread(y) << 1) +} + +// Squash the even bitlevels of X into a 32-bit word. Odd bitlevels of X are +// ignored, and may take any value. +func squash(X uint64) uint32 { + X &= 0x5555555555555555 + X = (X | (X >> 1)) & 0x3333333333333333 + X = (X | (X >> 2)) & 0x0f0f0f0f0f0f0f0f + X = (X | (X >> 4)) & 0x00ff00ff00ff00ff + X = (X | (X >> 8)) & 0x0000ffff0000ffff + X = (X | (X >> 16)) & 0x00000000ffffffff + return uint32(X) +} + +// Deinterleave the bits of X into 32-bit words containing the even and odd +// bitlevels of X, respectively. +func deinterleave(X uint64) (uint32, uint32) { + return squash(X), squash(X >> 1) +} diff --git a/vendor/github.com/mmcloughlin/geohash/geohash_test.go b/vendor/github.com/mmcloughlin/geohash/geohash_test.go new file mode 100644 index 00000000..dcc411f9 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/geohash_test.go @@ -0,0 +1,213 @@ +package geohash + +import ( + "testing" + "testing/quick" +) + +func TestInterleaving(t *testing.T) { + cases := []struct { + x, y uint32 + X uint64 + }{ + {0x00000000, 0x00000000, 0x0000000000000000}, + {0xffffffff, 0x00000000, 0x5555555555555555}, + {0x789e22e9, 0x8ed4182e, 0x95e8e37406845ce9}, + {0xb96346bb, 0xf8a80f02, 0xefc19c8510be454d}, + {0xa1dfc6c2, 0x01c886f9, 0x4403f1d5d03cfa86}, + {0xfb59e296, 0xad2c6c02, 0xdde719e17ca4411c}, + {0x94e0bbf2, 0xb520e8b2, 0xcb325c00edc5df0c}, + {0x1638ca5f, 0x5e16a514, 0x23bc0768d8661375}, + {0xe15bbbf7, 0x0f6bf376, 0x54ab39cfef4f7f3d}, + {0x06a476a7, 0x94f35ec7, 0x8234ee1a37bce43f}, + } + for _, c := range cases { + res := interleave(c.x, c.y) + if c.X != res { + t.Errorf("incorrect interleave result") + } + + x, y := deinterleave(c.X) + if c.x != x || c.y != y { + t.Errorf("incorrect deinterleave result") + } + } +} + +func TestBase32Decode(t *testing.T) { + x := base32encoding.Decode("ezs42") + if 0xdfe082 != x { + t.Errorf("incorrect base64 decoding") + } +} + +func TestBase32Encode(t *testing.T) { + s := base32encoding.Encode(0xdfe082) + if "0000000ezs42" != s { + t.Errorf("incorrect base64 encoding") + } +} + +func TestBoxRound(t *testing.T) { + f := func() bool { + b := RandomBox() + lat, lng := b.Round() + return b.Contains(lat, lng) + } + if err := quick.Check(f, nil); err != nil { + t.Error(err) + } +} + +func TestBoxCenter(t *testing.T) { + b := Box{ + MinLat: 1, + MaxLat: 2, + MinLng: 3, + MaxLng: 4, + } + lat, lng := b.Center() + if 1.5 != lat || 3.5 != lng { + t.Errorf("incorrect box center") + } +} + +func TestBoxContains(t *testing.T) { + b := Box{ + MinLat: 1, + MaxLat: 2, + MinLng: 3, + MaxLng: 4, + } + cases := []struct { + lat, lng float64 + expect bool + }{ + {1.5, 3.5, true}, + {0.5, 3.5, false}, + {7.0, 3.5, false}, + {1.5, 1.5, false}, + {1.5, 9.5, false}, + {1, 3, true}, + {1, 4, true}, + {2, 3, true}, + {2, 4, true}, + } + for _, c := range cases { + if c.expect != b.Contains(c.lat, c.lng) { + t.Errorf("contains %f,%f should be %t", c.lat, c.lng, c.expect) + } + } +} + +func TestWikipediaExample(t *testing.T) { + h := EncodeWithPrecision(42.6, -5.6, 5) + if "ezs42" != h { + t.Errorf("incorrect encoding") + } +} + +func TestLeadingZero(t *testing.T) { + h := EncodeWithPrecision(-74.761330, -140.309714, 6) + if 6 != len(h) { + t.Errorf("incorrect geohash length") + } + if "0fsnxn" != h { + t.Errorf("incorrect encoding") + } +} + +func TestNeighbors(t *testing.T) { + for _, c := range neighborsTestCases { + neighbors := Neighbors(c.hashStr) + for i, neighbor := range neighbors { + expected := c.hashStrNeighbors[i] + if neighbor != expected { + t.Errorf("actual: %v \n expected: %v\n", neighbors, c.hashStrNeighbors) + break + } + } + } +} + +func TestNeighborsInt(t *testing.T) { + cases := []struct { + hash uint64 + neighbors []uint64 + }{ + { + hash: 6456360425798343065, + neighbors: []uint64{ + 6456360425798343068, + 6456360425798343070, + 6456360425798343067, + 6456360425798343066, + 6456360425798343064, + 6456360425798343058, + 6456360425798343059, + 6456360425798343062, + }, + }, + } + + for _, c := range cases { + neighbors := NeighborsInt(c.hash) + for i, neighbor := range neighbors { + expected := c.neighbors[i] + if neighbor != c.neighbors[i] { + t.Errorf("neighbor: %v does not match expected: %v", neighbor, expected) + } + } + } +} + +func TestNeighborsIntWithPrecision(t *testing.T) { + for _, c := range neighborsTestCases { + neighbors := NeighborsIntWithPrecision(c.hashInt, c.hashIntBitDepth) + for i, neighbor := range neighbors { + expected := c.hashIntNeighbors[i] + if neighbor != expected { + t.Errorf("actual: %v \n expected: %v\n", neighbors, c.hashIntNeighbors) + break + } + } + } +} + +func TestNeighbor(t *testing.T) { + c := neighborsTestCases[0] + neighbor := Neighbor(c.hashStr, North) + expected := c.hashStrNeighbors[North] + if neighbor != expected { + t.Errorf("actual: %v \n expected: %v\n", neighbor, expected) + } +} + +func TestNeighborInt(t *testing.T) { + cases := []struct { + hash uint64 + neighborEast uint64 + }{ + { + hash: 6456360425798343065, + neighborEast: 6456360425798343067, + }, + } + + for _, c := range cases { + neighbor := NeighborInt(c.hash, East) + expected := c.neighborEast + if neighbor != expected { + t.Errorf("actual: %v \n expected: %v\n", neighbor, expected) + } + } +} + +func TestNeighborIntWithPrecision(t *testing.T) { + c := neighborsTestCases[0] + neighbor := NeighborIntWithPrecision(c.hashInt, c.hashIntBitDepth, South) + expected := c.hashIntNeighbors[South] + if neighbor != expected { + t.Errorf("actual: %v \n expected: %v\n", neighbor, expected) + } +} diff --git a/vendor/github.com/mmcloughlin/geohash/geohash_x86.go b/vendor/github.com/mmcloughlin/geohash/geohash_x86.go new file mode 100644 index 00000000..68cfb18d --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/geohash_x86.go @@ -0,0 +1,24 @@ +// +build amd64,go1.6 + +package geohash + +// useAsm flag determines whether the assembly version of EncodeInt will be +// used. By Default we fall back to encodeInt. +var useAsm bool + +// cpuid executes the CPUID instruction to obtain processor identification and +// feature information. +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) + +// hasBMI2 returns whether the CPU supports Bit Manipulation Instruction Set +// 2. +func hasBMI2() bool { + _, ebx, _, _ := cpuid(7, 0) + return ebx&(1<<8) != 0 +} + +// init determines whether to use assembly version by performing CPU feature +// check. +func init() { + useAsm = hasBMI2() +} diff --git a/vendor/github.com/mmcloughlin/geohash/neighbors_testcases_test.go b/vendor/github.com/mmcloughlin/geohash/neighbors_testcases_test.go new file mode 100644 index 00000000..18ad2045 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/neighbors_testcases_test.go @@ -0,0 +1,1084 @@ +package geohash + +// // Test cases generated with: https://github.com/sunng87/node-geohash +// var geohash = require('ngeohash'); +// var fs = require('fs'); +// +// const num_trials = 1000; +// const seededRand = getSeededRand(30); +// +// console.log("package geohash"); +// console.log("") +// console.log(getFileContentsAsComments()) +// console.log(`var neighborsTestCases = []struct { +// \tlat float64 +// \tlng float64 +// \thashInt uint64 +// \thashIntBitDepth uint +// \thashIntNeighbors []uint64 +// \thashStr string +// \thashStrNeighbors []string +// }{`) +// for (let i = 0; i < num_trials; i++) { +// // -90 --> 90 +// let lat = (-90 + 180 * seededRand()) +// // -180 --> 180 +// let lng = (-180 + 360 * seededRand()) +// +// // 1 --> 9. Use minimum precision of 4 because results are more likely to +// // differ by small amounts between libraries when precision is low. +// let strPrecision = Math.floor(seededRand() *6) + 4 +// // 32 --> 52. ngeohash library specifies that bitdepth must be even. Also, +// // use minimum bitdepth of 32 because results are more likely to differ by +// // small amounts betweeen libraries when bitdepth is low. +// let intBitdepth = 2 * Math.floor(seededRand() * 16) + 20 +// let hashStr = geohash.encode(lat, lng, strPrecision) +// let strNeighbors = geohash.neighbors(hashStr) +// let hashInt = geohash.encode_int(lat, lng, intBitdepth) +// let intNeighbors = geohash.neighbors_int(hashInt, intBitdepth) +// console.log(`\t{${lat}, ${lng}, ${hashInt}, ${intBitdepth}, []uint64{${spaceArray(intNeighbors)}}, "${hashStr}", []string{${spaceArray(quoteArray(strNeighbors))}}},`) +// } +// +// console.log("}") +// +// function spaceArray(arr) { +// return arr.join(", "); +// } +// +// function quoteArray(arr) { +// return arr.map(function(item) { +// return `"${item}"`; +// }) +// }; +// +// function getFileContentsAsComments() { +// let filename = process.argv[1]; +// let contents = fs.readFileSync(`${filename}`).toString(); +// return contents.split("\n").map(function(line) { +// return line === "" ? `//` : `//\t ${line}`; +// }).join("\n") +// } +// +// // Native JS Math.Rand() does not support seeds. +// // Solution from: http://stackoverflow.com/questions/521295/javascript-random-seeds +// function getSeededRand(seed) { +// return function randWithSeed() { +// var x; +// do { +// x = Math.sin(seed++) * 10000; +// x = x - Math.floor(x); +// } while(x < 0.15 || x > 0.9); +// return (x-0.15) * 1 / 0.75; +// } +// } +// +var neighborsTestCases = []struct { + lat float64 + lng float64 + hashInt uint64 + hashIntBitDepth uint + hashIntNeighbors []uint64 + hashStr string + hashStrNeighbors []string +}{ + {38.102177131717326, 47.30244928800673, 225496739236146, 48, []uint64{225496739236147, 225496739236153, 225496739236152, 225496739236141, 225496739236135, 225496739236133, 225496739236144, 225496739236145}, "tnc8", []string{"tnc9", "tncc", "tncb", "tn9z", "tn9x", "tn9r", "tnc2", "tnc3"}}, + {-84.40679076238303, -150.49652695585974, 37983511289, 42, []uint64{37983511292, 37983511294, 37983511291, 37983511290, 37983511288, 37983511282, 37983511283, 37983511286}, "08uzz65", []string{"08uzz67", "08uzz6k", "08uzz6h", "08uzz3u", "08uzz3g", "08uzz3f", "08uzz64", "08uzz66"}}, + {59.59486869840839, 124.57000495819375, 1071213305513192, 50, []uint64{1071213305513193, 1071213305513195, 1071213305513194, 1071213305513151, 1071213305513149, 1071213305513143, 1071213305513186, 1071213305513187}, "yf8e", []string{"yf8s", "yf8u", "yf8g", "yf8f", "yf8d", "yf86", "yf87", "yf8k"}}, + {49.494588537621894, -34.37435360075324, 7880340, 24, []uint64{7880341, 7880343, 7880342, 7880339, 7880337, 7880251, 7880254, 7880255}, "g0z99f", []string{"g0z99g", "g0z9d5", "g0z9d4", "g0z9d1", "g0z99c", "g0z999", "g0z99d", "g0z99e"}}, + {-55.97797590061964, -119.55820567079354, 227065487, 32, []uint64{227065498, 227065520, 227065509, 227065508, 227065486, 227065484, 227065485, 227065496}, "1q4c", []string{"1q4f", "1q54", "1q51", "1q50", "1q4b", "1q48", "1q49", "1q4d"}}, + {-8.405008588393699, 58.825189559545834, 165227046, 28, []uint64{165227047, 165227053, 165227052, 165227049, 165227043, 165227041, 165227044, 165227045}, "mq9b4tgj", []string{"mq9b4tgn", "mq9b4tgq", "mq9b4tgm", "mq9b4tgk", "mq9b4tgh", "mq9b4tfu", "mq9b4tfv", "mq9b4tfy"}}, + {88.50935467993986, 142.70395971351536, 68035274899, 36, []uint64{68035274902, 68035274908, 68035274905, 68035274904, 68035274898, 68035274896, 68035274897, 68035274900}, "zptr", []string{"zpv2", "zpv8", "zptx", "zptw", "zptq", "zptn", "zptp", "zpv0"}}, + {-63.508327702526, -114.33844750357093, 3498042510531, 46, []uint64{3498042510534, 3498042510540, 3498042510537, 3498042510536, 3498042510530, 3498042510528, 3498042510529, 3498042510532}, "1kwwwn", []string{"1kwwwp", "1kwwwr", "1kwwwq", "1kwwwm", "1kwwwj", "1kwwtv", "1kwwty", "1kwwtz"}}, + {47.56634570096503, -179.10615929867345, 1311091, 22, []uint64{1311094, 1311100, 1311097, 1311096, 1311090, 1311088, 1311089, 1311092}, "b02ws74", []string{"b02ws76", "b02ws77", "b02ws75", "b02ws6g", "b02ws6f", "b02ws6c", "b02ws71", "b02ws73"}}, + {-58.69652953829791, 5.94975241238717, 2169874, 22, []uint64{2169875, 2169881, 2169880, 2169869, 2169863, 2169861, 2169872, 2169873}, "hjs4", []string{"hjs5", "hjs7", "hjs6", "hjs3", "hjs1", "hjec", "hjef", "hjeg"}}, + {62.29437008447712, 167.19020396002452, 1079796916293, 40, []uint64{1079796916304, 1079796916306, 1079796916295, 1079796916294, 1079796916292, 1079796910830, 1079796910831, 1079796910842}, "zenfk", []string{"zenfs", "zenft", "zenfm", "zenfj", "zenfh", "zenf5", "zenf7", "zenfe"}}, + {-67.4878766090078, -52.545619486947544, 661997510875, 42, []uint64{661997510878, 661997510900, 661997510897, 661997510896, 661997510874, 661997510872, 661997510873, 661997510876}, "4u48h61qw", []string{"4u48h61qy", "4u48h61qz", "4u48h61qx", "4u48h61qr", "4u48h61qq", "4u48h61qm", "4u48h61qt", "4u48h61qv"}}, + {84.02820277644787, -89.13415586229529, 7857441423, 34, []uint64{7857441434, 7857441456, 7857441445, 7857441444, 7857441422, 7857441420, 7857441421, 7857441432}, "fnbw", []string{"fnbx", "fnbz", "fnby", "fnbv", "fnbt", "fnbm", "fnbq", "fnbr"}}, + {22.115366690253722, 169.17808485520072, 247288666, 28, []uint64{247288667, 247288689, 247288688, 247288677, 247288655, 247288653, 247288664, 247288665}, "xgbm", []string{"xgbq", "xgbw", "xgbt", "xgbs", "xgbk", "xgbh", "xgbj", "xgbn"}}, + {-64.1107719585998, -123.40147964993957, 3453538197459, 46, []uint64{3453538197462, 3453538197468, 3453538197465, 3453538197464, 3453538197458, 3453538197456, 3453538197457, 3453538197460}, "1k85rbvz9", []string{"1k85rbvzc", "1k85rbvzf", "1k85rbvzd", "1k85rbvz6", "1k85rbvz3", "1k85rbvz2", "1k85rbvz8", "1k85rbvzb"}}, + {29.319510036497363, 23.999611907755025, 812084, 20, []uint64{812085, 812087, 812086, 812083, 812081, 812059, 812062, 812063}, "st1nf17yx", []string{"st1nf17yz", "st1nf1knb", "st1nf1kn8", "st1nf1kn2", "st1nf17yr", "st1nf17yq", "st1nf17yw", "st1nf17yy"}}, + {-23.744072839341243, 23.145191659375996, 603458, 20, []uint64{603459, 603465, 603464, 603421, 603415, 603413, 603456, 603457}, "keb2yt", []string{"keb2yw", "keb2yy", "keb2yv", "keb2yu", "keb2ys", "keb2yk", "keb2ym", "keb2yq"}}, + {66.93283514525683, -82.39668110286583, 7614543365, 34, []uint64{7614543376, 7614543378, 7614543367, 7614543366, 7614543364, 7614543022, 7614543023, 7614543034}, "f5vkv0b", []string{"f5vkv10", "f5vkv11", "f5vkv0c", "f5vkv09", "f5vkv08", "f5vkubx", "f5vkubz", "f5vkucp"}}, + {-89.46066340905963, 33.795894448820036, 136840457, 28, []uint64{136840460, 136840462, 136840459, 136840458, 136840456, 136840450, 136840451, 136840454}, "hb051", []string{"hb053", "hb056", "hb054", "hb04f", "hb04c", "hb04b", "hb050", "hb052"}}, + {-62.14089232434708, 149.21830107957067, 11312451, 24, []uint64{11312454, 11312460, 11312457, 11312456, 11312450, 11312448, 11312449, 11312452}, "pkfn7x97", []string{"pkfn7x9k", "pkfn7x9s", "pkfn7x9e", "pkfn7x9d", "pkfn7x96", "pkfn7x94", "pkfn7x95", "pkfn7x9h"}}, + {38.846649603450714, 68.60773013407015, 3474302423, 32, []uint64{3474305154, 3474305160, 3474302429, 3474302428, 3474302422, 3474302420, 3474302421, 3474305152}, "twbucpyd5", []string{"twbucpyd7", "twbucpydk", "twbucpydh", "twbucpy9u", "twbucpy9g", "twbucpy9f", "twbucpyd4", "twbucpyd6"}}, + {66.31493937979394, -61.634686504723504, 30991816876, 36, []uint64{30991816877, 30991816879, 30991816878, 30991816875, 30991816873, 30991816867, 30991816870, 30991816871}, "feu1m2qb", []string{"feu1m2qc", "feu1m2r1", "feu1m2r0", "feu1m2pp", "feu1m2nz", "feu1m2nx", "feu1m2q8", "feu1m2q9"}}, + {62.17464367370121, -35.222183359757764, 2036907611, 32, []uint64{2036907614, 2036907636, 2036907633, 2036907632, 2036907610, 2036907608, 2036907609, 2036907612}, "g5ncwqxc", []string{"g5ncwqxf", "g5ncww84", "g5ncww81", "g5ncww80", "g5ncwqxb", "g5ncwqx8", "g5ncwqx9", "g5ncwqxd"}}, + {33.85362173636531, -5.510853574262029, 7491033287, 34, []uint64{7491033298, 7491033304, 7491033293, 7491033292, 7491033286, 7491033284, 7491033285, 7491033296}, "eyh0", []string{"eyh1", "eyh3", "eyh2", "evur", "evup", "evgz", "ey5b", "ey5c"}}, + {-76.7430027310329, 130.3811709248912, 670285, 20, []uint64{670296, 670298, 670287, 670286, 670284, 670278, 670279, 670290}, "nfkeqvket", []string{"nfkeqvkev", "nfkeqvkey", "nfkeqvkew", "nfkeqvkeq", "nfkeqvkem", "nfkeqvkek", "nfkeqvkes", "nfkeqvkeu"}}, + {-2.5028993330452067, -136.33666821510997, 1571349, 24, []uint64{1571392, 1571394, 1571351, 1571350, 1571348, 1571006, 1571007, 1571050}, "2zx1", []string{"2zx4", "2zx6", "2zx3", "2zx2", "2zx0", "2zwb", "2zwc", "2zwf"}}, + {-6.841904111002805, 179.15594431037607, 12867657953, 34, []uint64{12867657956, 12867657958, 12867657955, 12867657954, 12867657952, 12867657930, 12867657931, 12867657934}, "ryz3h", []string{"ryz3k", "ryz3m", "ryz3j", "ryz2v", "ryz2u", "ryz2g", "ryz35", "ryz37"}}, + {-61.89305779154529, -14.613614254587475, 203269514097601, 50, []uint64{203269514097604, 203269514097606, 203269514097603, 203269514097602, 203269514097600, 203269514097514, 203269514097515, 203269514097518}, "5svxgkw", []string{"5svxgky", "5svxgkz", "5svxgkx", "5svxgkr", "5svxgkq", "5svxgkm", "5svxgkt", "5svxgkv"}}, + {-34.738202911627006, 61.943145404336974, 672699378611435, 50, []uint64{672699378611438, 672699378611780, 672699378611777, 672699378611776, 672699378611434, 672699378611432, 672699378611433, 672699378611436}, "m3u43s", []string{"m3u43t", "m3u43v", "m3u43u", "m3u43g", "m3u43e", "m3u437", "m3u43k", "m3u43m"}}, + {54.80593024511472, -145.59725284202796, 5557845170, 34, []uint64{5557845171, 5557845177, 5557845176, 5557845165, 5557845159, 5557845157, 5557845168, 5557845169}, "bc8ryc", []string{"bc8ryf", "bc8rz4", "bc8rz1", "bc8rz0", "bc8ryb", "bc8ry8", "bc8ry9", "bc8ryd"}}, + {-80.55816045963729, 133.9595482001896, 2670285, 22, []uint64{2670296, 2670298, 2670287, 2670286, 2670284, 2670278, 2670279, 2670290}, "ncxm8qf6e", []string{"ncxm8qf6g", "ncxm8qf6u", "ncxm8qf6s", "ncxm8qf6k", "ncxm8qf67", "ncxm8qf66", "ncxm8qf6d", "ncxm8qf6f"}}, + {56.54986732012185, -112.91228231554851, 367273, 20, []uint64{367276, 367278, 367275, 367274, 367272, 367266, 367267, 367270}, "c6p9wwef", []string{"c6p9wweg", "c6p9wws5", "c6p9wws4", "c6p9wws1", "c6p9wwec", "c6p9wwe9", "c6p9wwed", "c6p9wwee"}}, + {21.941635568968195, -43.55818614293824, 27614228, 26, []uint64{27614229, 27614231, 27614230, 27614227, 27614225, 27612859, 27612862, 27612863}, "e5chbf3", []string{"e5chbf9", "e5chbfd", "e5chbf6", "e5chbf4", "e5chbf1", "e5chbf0", "e5chbf2", "e5chbf8"}}, + {-74.61999428045237, -163.64704401380732, 1727419771, 38, []uint64{1727419774, 1727419860, 1727419857, 1727419856, 1727419770, 1727419768, 1727419769, 1727419772}, "06ex", []string{"06g8", "06gb", "06ez", "06ey", "06ew", "06eq", "06er", "06g2"}}, + {37.032458544839756, 67.88047731807455, 13895831964, 34, []uint64{13895831965, 13895831967, 13895831966, 13895831963, 13895831961, 13895831955, 13895831958, 13895831959}, "tw86", []string{"tw87", "tw8e", "tw8d", "tw89", "tw83", "tw81", "tw84", "tw85"}}, + {0.974887747928733, -110.97513523494126, 79469840821, 38, []uint64{79469840864, 79469840866, 79469840823, 79469840822, 79469840820, 79469840798, 79469840799, 79469840842}, "981jd9", []string{"981jdd", "981jdf", "981jdc", "981jdb", "981jd8", "981jd2", "981jd3", "981jd6"}}, + {-81.86259246683767, 110.26881886774208, 674978401, 30, []uint64{674978404, 674978406, 674978403, 674978402, 674978400, 674978378, 674978379, 674978382}, "n3qqm1r7c", []string{"n3qqm1rk1", "n3qqm1rk4", "n3qqm1r7f", "n3qqm1r7d", "n3qqm1r79", "n3qqm1r78", "n3qqm1r7b", "n3qqm1rk0"}}, + {-3.988280761783244, 4.729295689383434, 40078883423, 36, []uint64{40078883594, 40078883616, 40078883445, 40078883444, 40078883422, 40078883420, 40078883421, 40078883592}, "kp7379gy8", []string{"kp7379gyb", "kp7379gyc", "kp7379gy9", "kp7379gy3", "kp7379gy2", "kp7379gwr", "kp7379gwx", "kp7379gwz"}}, + {14.632969516373123, -141.3000690030749, 70888711, 28, []uint64{70888722, 70888728, 70888717, 70888716, 70888710, 70888708, 70888709, 70888720}, "8fee0xu", []string{"8fee28h", "8fee28j", "8fee0xv", "8fee0xt", "8fee0xs", "8fee0xe", "8fee0xg", "8fee285"}}, + {-88.05541944786091, -140.27151428471552, 10823, 20, []uint64{10834, 10840, 10829, 10828, 10822, 10820, 10821, 10832}, "0bk70", []string{"0bk72", "0bk73", "0bk71", "0bk6c", "0bk6b", "0bk4z", "0bk5p", "0bk5r"}}, + {81.9383751473506, 73.97070277144667, 3656752, 22, []uint64{3656753, 3656755, 3656754, 3656743, 3656741, 3656719, 3656730, 3656731}, "vwsd5h", []string{"vwsd5j", "vwsd5m", "vwsd5k", "vwsd57", "vwsd55", "vwsd4g", "vwsd4u", "vwsd4v"}}, + {-17.628189319744706, 44.22372673591599, 647441045976, 40, []uint64{647441045977, 647441045979, 647441045978, 647441045967, 647441045965, 647441045959, 647441045970, 647441045971}, "kuz7wqfsr", []string{"kuz7wqfsx", "kuz7wqfu8", "kuz7wqfu2", "kuz7wqfu0", "kuz7wqfsp", "kuz7wqfsn", "kuz7wqfsq", "kuz7wqfsw"}}, + {83.84825145681043, 35.998189481149865, 226064680, 28, []uint64{226064681, 226064683, 226064682, 226064511, 226064509, 226064503, 226064674, 226064675}, "uyct50", []string{"uyct51", "uyct53", "uyct52", "uycsgr", "uycsgp", "uycsfz", "uyct4b", "uyct4c"}}, + {46.7530996670248, -138.1023788768216, 86666077, 28, []uint64{86666760, 86666762, 86666079, 86666078, 86666076, 86666070, 86666071, 86666754}, "bbmccr", []string{"bbmf12", "bbmf18", "bbmccx", "bbmccw", "bbmccq", "bbmccn", "bbmccp", "bbmf10"}}, + {-30.710841958149096, -161.80359332555963, 72459, 20, []uint64{72462, 72484, 72481, 72480, 72458, 72456, 72457, 72460}, "26scq16", []string{"26scq1d", "26scq1e", "26scq17", "26scq15", "26scq14", "26scq11", "26scq13", "26scq19"}}, + {3.6635149672511034, 75.25082964501777, 54277984671, 36, []uint64{54277984714, 54277984736, 54277984693, 54277984692, 54277984670, 54277984668, 54277984669, 54277984712}, "t8tsb6g", []string{"t8tsb75", "t8tsb7h", "t8tsb6u", "t8tsb6s", "t8tsb6e", "t8tsb6d", "t8tsb6f", "t8tsb74"}}, + {3.7284690926062467, 21.30448667606106, 3157702, 22, []uint64{3157703, 3157709, 3157708, 3157705, 3157699, 3157697, 3157700, 3157701}, "s2xjhy9ry", []string{"s2xjhyc2n", "s2xjhyc2p", "s2xjhy9rz", "s2xjhy9rx", "s2xjhy9rw", "s2xjhy9rt", "s2xjhy9rv", "s2xjhyc2j"}}, + {72.69049450586317, -18.89437864901265, 33853890583, 36, []uint64{33853890626, 33853890632, 33853890589, 33853890588, 33853890582, 33853890580, 33853890581, 33853890624}, "gsftd0", []string{"gsftd1", "gsftd3", "gsftd2", "gsft6r", "gsft6p", "gsft3z", "gsft9b", "gsft9c"}}, + {27.52347020510932, 91.63300047189114, 244909810845, 38, []uint64{244909810888, 244909810890, 244909810847, 244909810846, 244909810844, 244909810838, 244909810839, 244909810882}, "whcht4mpg", []string{"whcht4t05", "whcht4t0h", "whcht4mpu", "whcht4mps", "whcht4mpe", "whcht4mpd", "whcht4mpf", "whcht4t04"}}, + {63.551314185714546, -32.957174008945, 498761, 20, []uint64{498764, 498766, 498763, 498762, 498760, 498754, 498755, 498758}, "g729", []string{"g72d", "g72f", "g72c", "g72b", "g728", "g722", "g723", "g726"}}, + {21.0013549237774, 79.46412152593257, 3339380, 22, []uint64{3339381, 3339383, 3339382, 3339379, 3339377, 3339355, 3339358, 3339359}, "tg8x2", []string{"tg8x8", "tg8x9", "tg8x3", "tg8x1", "tg8x0", "tg8rp", "tg8rr", "tg8rx"}}, + {-20.015762758936035, -79.4380276275042, 13680385, 26, []uint64{13680388, 13680390, 13680387, 13680386, 13680384, 13680042, 13680043, 13680046}, "6hrw0k4", []string{"6hrw0k6", "6hrw0k7", "6hrw0k5", "6hrw07g", "6hrw07f", "6hrw07c", "6hrw0k1", "6hrw0k3"}}, + {-34.11238559719641, 58.251660253998125, 10503139033735, 44, []uint64{10503139033746, 10503139033752, 10503139033741, 10503139033740, 10503139033734, 10503139033732, 10503139033733, 10503139033744}, "m3cmvw18f", []string{"m3cmvw194", "m3cmvw195", "m3cmvw18g", "m3cmvw18e", "m3cmvw18d", "m3cmvw189", "m3cmvw18c", "m3cmvw191"}}, + {-46.165062209489406, 167.2707236262504, 736046791, 30, []uint64{736046802, 736046808, 736046797, 736046796, 736046790, 736046788, 736046789, 736046800}, "pxycq", []string{"pxycw", "pxycx", "pxycr", "pxycp", "pxycn", "pxycj", "pxycm", "pxyct"}}, + {-65.98946359846741, -38.05480536731193, 189593948535, 40, []uint64{189593949218, 189593949224, 189593948541, 189593948540, 189593948534, 189593948532, 189593948533, 189593949216}, "5hkbw", []string{"5hkby", "5hkbz", "5hkbx", "5hkbr", "5hkbq", "5hkbm", "5hkbt", "5hkbv"}}, + {86.79791773631587, -84.93448240576254, 492037603, 30, []uint64{492037606, 492037612, 492037609, 492037608, 492037602, 492037600, 492037601, 492037604}, "fp7tg30de", []string{"fp7tg30dg", "fp7tg30du", "fp7tg30ds", "fp7tg30dk", "fp7tg30d7", "fp7tg30d6", "fp7tg30dd", "fp7tg30df"}}, + {-81.910093084658, 126.40265334779588, 42672032, 26, []uint64{42672033, 42672035, 42672034, 42671863, 42671861, 42671839, 42672010, 42672011}, "nc3yh2ex", []string{"nc3yh2g8", "nc3yh2gb", "nc3yh2ez", "nc3yh2ey", "nc3yh2ew", "nc3yh2eq", "nc3yh2er", "nc3yh2g2"}}, + {-12.226239652332893, -129.3528407332924, 477380976, 32, []uint64{477380977, 477380979, 477380978, 477380967, 477380965, 477380943, 477380954, 477380955}, "3ju42", []string{"3ju48", "3ju49", "3ju43", "3ju41", "3ju40", "3jgfp", "3jgfr", "3jgfx"}}, + {24.317227940977304, 54.588116830098414, 3507705630136, 42, []uint64{3507705630137, 3507705630139, 3507705630138, 3507705630127, 3507705630125, 3507705630119, 3507705630130, 3507705630131}, "thqf64v", []string{"thqf65j", "thqf65n", "thqf64y", "thqf64w", "thqf64t", "thqf64s", "thqf64u", "thqf65h"}}, + {-84.69911952788243, -12.716738921400975, 725709588479, 42, []uint64{725709591210, 725709592576, 725709589845, 725709589844, 725709588478, 725709588476, 725709588477, 725709591208}, "58yynsg", []string{"58yynt5", "58yynth", "58yynsu", "58yynss", "58yynse", "58yynsd", "58yynsf", "58yynt4"}}, + {-89.7166446246556, -145.44658387068193, 11005190246683, 50, []uint64{11005190246686, 11005190246708, 11005190246705, 11005190246704, 11005190246682, 11005190246680, 11005190246681, 11005190246684}, "0b09", []string{"0b0d", "0b0f", "0b0c", "0b0b", "0b08", "0b02", "0b03", "0b06"}}, + {-15.264406662841793, -29.261227847338915, 267423606927610, 50, []uint64{267423606927611, 267423606927953, 267423606927952, 267423606927941, 267423606927599, 267423606927597, 267423606927608, 267423606927609}, "7m71nj", []string{"7m71nn", "7m71nq", "7m71nm", "7m71nk", "7m71nh", "7m71ju", "7m71jv", "7m71jy"}}, + {-41.85494586288405, -97.514111846569, 111552368, 30, []uint64{111552369, 111552371, 111552370, 111552359, 111552357, 111552335, 111552346, 111552347}, "3bd9vh83", []string{"3bd9vh86", "3bd9vh8d", "3bd9vh89", "3bd9vh88", "3bd9vh82", "3bd9vh80", "3bd9vh81", "3bd9vh84"}}, + {20.094692247424973, 138.97180261848052, 1002218658320, 40, []uint64{1002218658321, 1002218658323, 1002218658322, 1002218658311, 1002218658309, 1002218657967, 1002218657978, 1002218657979}, "x5df6", []string{"x5dfd", "x5dfe", "x5df7", "x5df5", "x5df4", "x5df1", "x5df3", "x5df9"}}, + {-73.50991241167867, -146.165269384539, 58693, 22, []uint64{58704, 58706, 58695, 58694, 58692, 53230, 53231, 53242}, "0fbjc", []string{"0fbn1", "0fbn4", "0fbjf", "0fbjd", "0fbj9", "0fbj8", "0fbjb", "0fbn0"}}, + {9.44069270305043, -122.52559801493771, 320262220734600, 50, []uint64{320262220734601, 320262220734603, 320262220734602, 320262220723679, 320262220723677, 320262220723671, 320262220734594, 320262220734595}, "938vey", []string{"938vez", "938vsp", "938vsn", "938vsj", "938vev", "938vet", "938vew", "938vex"}}, + {-23.0550658232969, 145.00980089924997, 12448440037, 34, []uint64{12448440048, 12448440050, 12448440039, 12448440038, 12448440036, 12448440014, 12448440015, 12448440026}, "r5zh", []string{"r5zj", "r5zm", "r5zk", "r5z7", "r5z5", "r5yg", "r5yu", "r5yv"}}, + {-45.348485054215416, 26.441852031683084, 148837270520702, 48, []uint64{148837270520703, 148837270520789, 148837270520788, 148837270520785, 148837270520699, 148837270520697, 148837270520700, 148837270520701}, "hxfy18w3", []string{"hxfy18w6", "hxfy18wd", "hxfy18w9", "hxfy18w8", "hxfy18w2", "hxfy18w0", "hxfy18w1", "hxfy18w4"}}, + {-66.76683457169565, 20.59498537710897, 2225703014, 32, []uint64{2225703015, 2225703021, 2225703020, 2225703017, 2225703011, 2225703009, 2225703012, 2225703013}, "hknshtky", []string{"hknshtkz", "hknshtmp", "hknshtmn", "hknshtmj", "hknshtkv", "hknshtkt", "hknshtkw", "hknshtkx"}}, + {-16.16326133194525, -19.427026647201274, 16719579890, 36, []uint64{16719579891, 16719579897, 16719579896, 16719579885, 16719579879, 16719579877, 16719579888, 16719579889}, "7t4hjctd", []string{"7t4hjcte", "7t4hjctg", "7t4hjctf", "7t4hjctc", "7t4hjct9", "7t4hjct3", "7t4hjct6", "7t4hjct7"}}, + {-65.32300432978082, -137.95315944316098, 27912794, 30, []uint64{27912795, 27912817, 27912816, 27912805, 27912783, 27912781, 27912792, 27912793}, "0umuku39f", []string{"0umuku3d4", "0umuku3d5", "0umuku39g", "0umuku39e", "0umuku39d", "0umuku399", "0umuku39c", "0umuku3d1"}}, + {-64.68029673045385, 37.89060235893703, 591960982914548, 50, []uint64{591960982914549, 591960982914551, 591960982914550, 591960982914547, 591960982914545, 591960982914523, 591960982914526, 591960982914527}, "hudbn1r", []string{"hudbn1x", "hudbn38", "hudbn32", "hudbn30", "hudbn1p", "hudbn1n", "hudbn1q", "hudbn1w"}}, + {69.09057132259841, -81.18517234531464, 31184847448, 36, []uint64{31184847449, 31184847451, 31184847450, 31184847439, 31184847437, 31184847431, 31184847442, 31184847443}, "fhq309d", []string{"fhq309f", "fhq309g", "fhq309e", "fhq3097", "fhq3096", "fhq3093", "fhq3099", "fhq309c"}}, + {-27.399090180202627, 6.478258853720007, 159847992144485, 48, []uint64{159847992144496, 159847992144498, 159847992144487, 159847992144486, 159847992144484, 159847992144462, 159847992144463, 159847992144474}, "k5hs5kjk", []string{"k5hs5kjm", "k5hs5kjt", "k5hs5kjs", "k5hs5kje", "k5hs5kj7", "k5hs5kj5", "k5hs5kjh", "k5hs5kjj"}}, + {-12.234802699400475, -61.55054136153193, 228397810, 30, []uint64{228397811, 228397817, 228397816, 228397805, 228397799, 228397797, 228397808, 228397809}, "6tu4rks", []string{"6tu4rku", "6tu4rkv", "6tu4rkt", "6tu4rkm", "6tu4rkk", "6tu4rk7", "6tu4rke", "6tu4rkg"}}, + {-58.75892570702126, -107.14018382382346, 986421652649, 44, []uint64{986421652652, 986421652654, 986421652651, 986421652650, 986421652648, 986421652642, 986421652643, 986421652646}, "1tec9zqb", []string{"1tec9zqc", "1tec9zr1", "1tec9zr0", "1tec9zpp", "1tec9znz", "1tec9znx", "1tec9zq8", "1tec9zq9"}}, + {-89.48250913666561, -171.150263821939, 173507482827, 48, []uint64{173507482830, 173507482852, 173507482849, 173507482848, 173507482826, 173507482824, 173507482825, 173507482828}, "00n6cq", []string{"00n6cr", "00n6cx", "00n6cw", "00n6ct", "00n6cm", "00n6cj", "00n6cn", "00n6cp"}}, + {48.17317478073528, -142.74417355346668, 338310, 20, []uint64{338311, 338317, 338316, 338313, 338307, 338305, 338308, 338309}, "bbd6", []string{"bbd7", "bbde", "bbdd", "bbd9", "bbd3", "bbd1", "bbd4", "bbd5"}}, + {-77.64211391398567, -61.695192473736824, 603529156207, 42, []uint64{603529156218, 603529156304, 603529156293, 603529156292, 603529156206, 603529156204, 603529156205, 603529156216}, "4dhnk1", []string{"4dhnk4", "4dhnk6", "4dhnk3", "4dhnk2", "4dhnk0", "4dhn7b", "4dhn7c", "4dhn7f"}}, + {-42.12315587051853, -7.4604465333978, 960034, 22, []uint64{960035, 960041, 960040, 959357, 959351, 959349, 960032, 960033}, "7bd8q5", []string{"7bd8qh", "7bd8qk", "7bd8q7", "7bd8q6", "7bd8q4", "7bd8mf", "7bd8mg", "7bd8mu"}}, + {86.71264389826683, 84.84244464850053, 15385968668733, 44, []uint64{15385968668776, 15385968668778, 15385968668735, 15385968668734, 15385968668732, 15385968668726, 15385968668727, 15385968668770}, "vzkm69s3", []string{"vzkm69s6", "vzkm69sd", "vzkm69s9", "vzkm69s8", "vzkm69s2", "vzkm69s0", "vzkm69s1", "vzkm69s4"}}, + {33.83301922125975, 140.9235295986291, 16295117963900, 44, []uint64{16295117963901, 16295117963903, 16295117963902, 16295117963899, 16295117963897, 16295117963891, 16295117963894, 16295117963895}, "xnh0qz", []string{"xnh0wb", "xnh0x0", "xnh0rp", "xnh0rn", "xnh0qy", "xnh0qw", "xnh0qx", "xnh0w8"}}, + {36.94742773101461, -157.11329378408846, 19533915884300, 46, []uint64{19533915884301, 19533915884303, 19533915884302, 19533915884299, 19533915884297, 19533915884291, 19533915884294, 19533915884295}, "8w860y", []string{"8w860z", "8w861p", "8w861n", "8w861j", "8w860v", "8w860t", "8w860w", "8w860x"}}, + {28.255422256974256, 161.96478362206835, 1048007249680202, 50, []uint64{1048007249680203, 1048007249680225, 1048007249680224, 1048007249680181, 1048007249680159, 1048007249680157, 1048007249680200, 1048007249680201}, "xt50t", []string{"xt50v", "xt50y", "xt50w", "xt50q", "xt50m", "xt50k", "xt50s", "xt50u"}}, + {-85.88696600754338, 135.44968651182717, 184792901739172, 48, []uint64{184792901739173, 184792901739175, 184792901739174, 184792901739171, 184792901739169, 184792901739147, 184792901739150, 184792901739151}, "p08r6hz", []string{"p08r6jp", "p08r6m0", "p08r6kb", "p08r6k8", "p08r6hx", "p08r6hw", "p08r6hy", "p08r6jn"}}, + {-85.14834281400545, 159.7256204978912, 696685, 20, []uint64{696696, 696698, 696687, 696686, 696684, 696678, 696679, 696690}, "p8cedehwu", []string{"p8cedehxh", "p8cedehxj", "p8cedehwv", "p8cedehwt", "p8cedehws", "p8cedehwe", "p8cedehwg", "p8cedehx5"}}, + {87.99783332293737, 42.43372717281818, 3538499, 22, []uint64{3538502, 3538508, 3538505, 3538504, 3538498, 3538496, 3538497, 3538500}, "uzwhte", []string{"uzwhts", "uzwhtu", "uzwhtg", "uzwhtf", "uzwhtd", "uzwht6", "uzwht7", "uzwhtk"}}, + {33.129521581489826, 20.26060727982258, 3304925652, 32, []uint64{3304925653, 3304925655, 3304925654, 3304925651, 3304925649, 3304925563, 3304925566, 3304925567}, "smyk", []string{"smym", "smyt", "smys", "smye", "smy7", "smy5", "smyh", "smyj"}}, + {-58.33277394268953, -116.17714543640614, 13904777841, 38, []uint64{13904777844, 13904777846, 13904777843, 13904777842, 13904777840, 13904777818, 13904777819, 13904777822}, "1mtkhkf6u", []string{"1mtkhkf7h", "1mtkhkf7j", "1mtkhkf6v", "1mtkhkf6t", "1mtkhkf6s", "1mtkhkf6e", "1mtkhkf6g", "1mtkhkf75"}}, + {6.941227041010279, 13.68403665383812, 3311678749727, 42, []uint64{3311678749770, 3311678749792, 3311678749749, 3311678749748, 3311678749726, 3311678749724, 3311678749725, 3311678749768}, "s31x", []string{"s338", "s33b", "s31z", "s31y", "s31w", "s31q", "s31r", "s332"}}, + {-84.60948571302288, -9.966061785627971, 731615796026, 42, []uint64{731615796027, 731615796113, 731615796112, 731615796101, 731615796015, 731615796013, 731615796024, 731615796025}, "5bby", []string{"5bbz", "5bcp", "5bcn", "5bcj", "5bbv", "5bbt", "5bbw", "5bbx"}}, + {-81.61436909675649, 24.722821861185366, 2185746033, 32, []uint64{2185746036, 2185746038, 2185746035, 2185746034, 2185746032, 2185746010, 2185746011, 2185746014}, "h93xd", []string{"h93xf", "h93xg", "h93xe", "h93x7", "h93x6", "h93x3", "h93x9", "h93xc"}}, + {70.78491725728236, -45.63381305040093, 32636135022762, 46, []uint64{32636135022763, 32636135023105, 32636135023104, 32636134848341, 32636134847999, 32636134847997, 32636135022760, 32636135022761}, "fuxd9", []string{"fuxdc", "fuxdf", "fuxdd", "fuxd6", "fuxd3", "fuxd2", "fuxd8", "fuxdb"}}, + {55.32779975319863, 111.82681391769438, 1035066483247, 40, []uint64{1035066483258, 1035066483344, 1035066483333, 1035066483332, 1035066483246, 1035066483244, 1035066483245, 1035066483256}, "y3zd", []string{"y3ze", "y3zg", "y3zf", "y3zc", "y3z9", "y3z3", "y3z6", "y3z7"}}, + {-30.000635492739093, 96.22816241474357, 725779, 20, []uint64{725782, 725788, 725785, 725784, 725778, 725776, 725777, 725780}, "q4sm", []string{"q4sq", "q4sw", "q4st", "q4ss", "q4sk", "q4sh", "q4sj", "q4sn"}}, + {-32.11928156402427, 87.24269581097178, 10446980645, 34, []uint64{10446980656, 10446980658, 10446980647, 10446980646, 10446980644, 10446980622, 10446980623, 10446980634}, "mfq132", []string{"mfq133", "mfq139", "mfq138", "mfq11x", "mfq11r", "mfq11p", "mfq130", "mfq131"}}, + {-72.95460069196997, -21.56694761523977, 11610414442, 36, []uint64{11610414443, 11610414529, 11610414528, 11610414485, 11610414399, 11610414397, 11610414440, 11610414441}, "5e08vpp2", []string{"5e08vpp3", "5e08vpp9", "5e08vpp8", "5e08vnzx", "5e08vnzr", "5e08vnzp", "5e08vpp0", "5e08vpp1"}}, + {-88.36406175063166, 67.38785390964765, 587046948806, 40, []uint64{587046948807, 587046948813, 587046948812, 587046948809, 587046948803, 587046948801, 587046948804, 587046948805}, "j2rcm3y", []string{"j2rcm6n", "j2rcm6p", "j2rcm3z", "j2rcm3x", "j2rcm3w", "j2rcm3t", "j2rcm3v", "j2rcm6j"}}, + {-87.78578329912852, 34.18284969474189, 140144881449, 38, []uint64{140144881452, 140144881454, 140144881451, 140144881450, 140144881448, 140144881442, 140144881443, 140144881446}, "hb2k9g54w", []string{"hb2k9g54y", "hb2k9g54z", "hb2k9g54x", "hb2k9g54r", "hb2k9g54q", "hb2k9g54m", "hb2k9g54t", "hb2k9g54v"}}, + {27.04266384824585, -162.79076760230237, 18885104139701, 46, []uint64{18885104139744, 18885104139746, 18885104139703, 18885104139702, 18885104139700, 18885104139678, 18885104139679, 18885104139722}, "8ku1z", []string{"8ku4p", "8ku60", "8ku3b", "8ku38", "8ku1x", "8ku1w", "8ku1y", "8ku4n"}}, + {-39.86281193001196, 103.74411000139662, 2962731132, 32, []uint64{2962731133, 2962731135, 2962731134, 2962731131, 2962731129, 2962731123, 2962731126, 2962731127}, "q2cv0z", []string{"q2cv2b", "q2cv30", "q2cv1p", "q2cv1n", "q2cv0y", "q2cv0w", "q2cv0x", "q2cv28"}}, + {47.91108136904222, -19.60120032937266, 128024650, 28, []uint64{128024651, 128024673, 128024672, 128024629, 128024607, 128024605, 128024648, 128024649}, "g8d09cy", []string{"g8d09fn", "g8d09fp", "g8d09cz", "g8d09cx", "g8d09cw", "g8d09ct", "g8d09cv", "g8d09fj"}}, + {45.026902235435045, -25.336764143023203, 33160011379865, 46, []uint64{33160011379868, 33160011379870, 33160011379867, 33160011379866, 33160011379864, 33160011379858, 33160011379859, 33160011379862}, "g2jbpky", []string{"g2jbpmn", "g2jbpmp", "g2jbpkz", "g2jbpkx", "g2jbpkw", "g2jbpkt", "g2jbpkv", "g2jbpmj"}}, + {-33.34354621679813, 150.26389021641808, 203997584416624, 48, []uint64{203997584416625, 203997584416627, 203997584416626, 203997584416615, 203997584416613, 203997584416591, 203997584416602, 203997584416603}, "r64f", []string{"r64g", "r655", "r654", "r651", "r64c", "r649", "r64d", "r64e"}}, + {32.6076211638283, 170.1521196764661, 16027679400, 34, []uint64{16027679401, 16027679403, 16027679402, 16027676671, 16027676669, 16027676663, 16027679394, 16027679395}, "xvbcxbhcp", []string{"xvbcxbhcr", "xvbcxbj12", "xvbcxbj10", "xvbcxbj0b", "xvbcxbhbz", "xvbcxbhby", "xvbcxbhcn", "xvbcxbhcq"}}, + {-42.98221673541411, 140.84748273465084, 197720962353, 38, []uint64{197720962356, 197720962358, 197720962355, 197720962354, 197720962352, 197720962330, 197720962331, 197720962334}, "r0k5m", []string{"r0k5t", "r0k5w", "r0k5q", "r0k5n", "r0k5j", "r0k5h", "r0k5k", "r0k5s"}}, + {26.56710643167024, 8.567032553663012, 3292090986, 32, []uint64{3292090987, 3292091073, 3292091072, 3292091029, 3292090943, 3292090941, 3292090984, 3292090985}, "shwp4uqkd", []string{"shwp4uqkf", "shwp4uqkg", "shwp4uqke", "shwp4uqk7", "shwp4uqk6", "shwp4uqk3", "shwp4uqk9", "shwp4uqkc"}}, + {10.710820283187772, -50.83653645632876, 1619945, 22, []uint64{1619948, 1619950, 1619947, 1619946, 1619944, 1619938, 1619939, 1619942}, "dcgu", []string{"dcgv", "dcuj", "dcuh", "dcu5", "dcgg", "dcge", "dcgs", "dcgt"}}, + {71.875996495668, -143.48096219985746, 95205193749032, 48, []uint64{95205193749033, 95205193749035, 95205193749034, 95205193746303, 95205193746301, 95205193746295, 95205193749026, 95205193749027}, "bucbzh8k5", []string{"bucbzh8k7", "bucbzh8kk", "bucbzh8kh", "bucbzh87u", "bucbzh87g", "bucbzh87f", "bucbzh8k4", "bucbzh8k6"}}, + {-76.1888058608165, 61.52222520050418, 37812685420554, 46, []uint64{37812685420555, 37812685420577, 37812685420576, 37812685417845, 37812685417823, 37812685417821, 37812685420552, 37812685420553}, "j67wxfr", []string{"j67wxfx", "j67y848", "j67y842", "j67y840", "j67wxfp", "j67wxfn", "j67wxfq", "j67wxfw"}}, + {-58.82048854322056, -167.3842604449892, 5049276, 28, []uint64{5049277, 5049279, 5049278, 5049275, 5049273, 5049267, 5049270, 5049271}, "0m8cr", []string{"0m8cx", "0m918", "0m912", "0m910", "0m8cp", "0m8cn", "0m8cq", "0m8cw"}}, + {71.53854064088955, -116.15676646851352, 25481426439467, 46, []uint64{25481426439470, 25481426439556, 25481426439553, 25481426439552, 25481426439466, 25481426439464, 25481426439465, 25481426439468}, "cktquz1", []string{"cktquz3", "cktquz6", "cktquz4", "cktquyf", "cktquyc", "cktquyb", "cktquz0", "cktquz2"}}, + {21.248795590567156, 45.351616545693076, 211108436, 28, []uint64{211108437, 211108439, 211108438, 211108435, 211108433, 211108091, 211108094, 211108095}, "t5b2bh", []string{"t5b2bj", "t5b2bm", "t5b2bk", "t5b2b7", "t5b2b5", "t5b0zg", "t5b0zu", "t5b0zv"}}, + {-86.94519872172896, 77.68090614085668, 9058843, 24, []uint64{9058846, 9058868, 9058865, 9058864, 9058842, 9058840, 9058841, 9058844}, "j8x1rs", []string{"j8x1rt", "j8x1rv", "j8x1ru", "j8x1rg", "j8x1re", "j8x1r7", "j8x1rk", "j8x1rm"}}, + {-42.43554460875748, 141.58566216600593, 3163632625588, 42, []uint64{3163632625589, 3163632625591, 3163632625590, 3163632625587, 3163632625585, 3163632625563, 3163632625566, 3163632625567}, "r0kwtfg", []string{"r0kwtg5", "r0kwtgh", "r0kwtfu", "r0kwtfs", "r0kwtfe", "r0kwtfd", "r0kwtff", "r0kwtg4"}}, + {61.14189877908211, -50.1023976902361, 31059311536, 36, []uint64{31059311537, 31059311539, 31059311538, 31059311527, 31059311525, 31059311503, 31059311514, 31059311515}, "ffu7g", []string{"ffuk5", "ffukh", "ffu7u", "ffu7s", "ffu7e", "ffu7d", "ffu7f", "ffuk4"}}, + {-59.82827877874661, -164.04762125045818, 80639681, 32, []uint64{80639684, 80639686, 80639683, 80639682, 80639680, 80639594, 80639595, 80639598}, "0m77", []string{"0m7k", "0m7s", "0m7e", "0m7d", "0m76", "0m74", "0m75", "0m7h"}}, + {-18.71089004463647, 8.230612873274367, 155401064, 28, []uint64{155401065, 155401067, 155401066, 155401023, 155401021, 155401015, 155401058, 155401059}, "khtve3c35", []string{"khtve3c37", "khtve3c3k", "khtve3c3h", "khtve3c2u", "khtve3c2g", "khtve3c2f", "khtve3c34", "khtve3c36"}}, + {-74.87956409656908, 3.3638469085854013, 135370370, 28, []uint64{135370371, 135370377, 135370376, 135369693, 135369687, 135369685, 135370368, 135370369}, "h4dq", []string{"h4dr", "h4dx", "h4dw", "h4dt", "h4dm", "h4dj", "h4dn", "h4dp"}}, + {88.79364489066938, 19.226569051214, 876393, 20, []uint64{876396, 876398, 876395, 876394, 876392, 876386, 876387, 876390}, "urv9j", []string{"urv9m", "urv9q", "urv9n", "urv8y", "urv8v", "urv8u", "urv9h", "urv9k"}}, + {24.199798078683656, -177.2693214370811, 1168683637082, 42, []uint64{1168683637083, 1168683637105, 1168683637104, 1168683637093, 1168683637071, 1168683637069, 1168683637080, 1168683637081}, "8h3cw", []string{"8h3cy", "8h3cz", "8h3cx", "8h3cr", "8h3cq", "8h3cm", "8h3ct", "8h3cv"}}, + {-87.44051244991715, 69.84411510745122, 9045964, 24, []uint64{9045965, 9045967, 9045966, 9045963, 9045961, 9045955, 9045958, 9045959}, "j83wt3fyj", []string{"j83wt3fym", "j83wt3fyq", "j83wt3fyn", "j83wt3fvy", "j83wt3fvv", "j83wt3fvu", "j83wt3fyh", "j83wt3fyk"}}, + {14.050048151692849, 51.086919965535344, 3295581, 22, []uint64{3296264, 3296266, 3295583, 3295582, 3295580, 3295574, 3295575, 3296258}, "t4krft", []string{"t4krfw", "t4krfy", "t4krfv", "t4krfu", "t4krfs", "t4krfk", "t4krfm", "t4krfq"}}, + {87.60476164587453, 152.53445151200867, 16642155, 24, []uint64{16642158, 16642244, 16642241, 16642240, 16642154, 16642152, 16642153, 16642156}, "zrs6r5bn", []string{"zrs6r5bp", "zrs6r5br", "zrs6r5bq", "zrs6r5bm", "zrs6r5bj", "zrs6qgzv", "zrs6qgzy", "zrs6qgzz"}}, + {32.773529059908455, 167.23810568405315, 1000290940, 30, []uint64{1000290941, 1000290943, 1000290942, 1000290939, 1000290937, 1000290931, 1000290934, 1000290935}, "xtyf", []string{"xtyg", "xtz5", "xtz4", "xtz1", "xtyc", "xty9", "xtyd", "xtye"}}, + {87.0806061772455, -78.34597568816389, 517963510580931, 50, []uint64{517963510580934, 517963510580940, 517963510580937, 517963510580936, 517963510580930, 517963510580928, 517963510580929, 517963510580932}, "fr2r3hw1q", []string{"fr2r3hw1w", "fr2r3hw1x", "fr2r3hw1r", "fr2r3hw1p", "fr2r3hw1n", "fr2r3hw1j", "fr2r3hw1m", "fr2r3hw1t"}}, + {64.44881101683131, 143.51199436617026, 274247702512106, 48, []uint64{274247702512107, 274247702512449, 274247702512448, 274247702512405, 274247702512063, 274247702512061, 274247702512104, 274247702512105}, "z5qn", []string{"z5qp", "z5qr", "z5qq", "z5qm", "z5qj", "z5mv", "z5my", "z5mz"}}, + {-16.60778323073464, 66.53003009950041, 2570893, 22, []uint64{2570904, 2570906, 2570895, 2570894, 2570892, 2570886, 2570887, 2570898}, "mmp39btsw", []string{"mmp39btsy", "mmp39btsz", "mmp39btsx", "mmp39btsr", "mmp39btsq", "mmp39btsm", "mmp39btst", "mmp39btsv"}}, + {43.25607210134331, -124.39371793839382, 83197175400, 38, []uint64{83197175401, 83197175403, 83197175402, 83197175359, 83197175357, 83197175351, 83197175394, 83197175395}, "9pxw1", []string{"9pxw3", "9pxw6", "9pxw4", "9pxtf", "9pxtc", "9pxtb", "9pxw0", "9pxw2"}}, + {50.12427818883589, -53.402175730909235, 1877828, 22, []uint64{1877829, 1877831, 1877830, 1877827, 1877825, 1877483, 1877486, 1877487}, "fbfj0uc", []string{"fbfj0v1", "fbfj0v4", "fbfj0uf", "fbfj0ud", "fbfj0u9", "fbfj0u8", "fbfj0ub", "fbfj0v0"}}, + {59.96609345666366, 43.63607330841475, 14550024236535, 44, []uint64{14550024239266, 14550024239272, 14550024236541, 14550024236540, 14550024236534, 14550024236532, 14550024236533, 14550024239264}, "ufxj0uq", []string{"ufxj0uw", "ufxj0ux", "ufxj0ur", "ufxj0up", "ufxj0un", "ufxj0uj", "ufxj0um", "ufxj0ut"}}, + {18.773846751322708, 26.873946808307664, 51198238, 26, []uint64{51198239, 51198261, 51198260, 51198257, 51198235, 51198233, 51198236, 51198237}, "se74g9", []string{"se74gd", "se74gf", "se74gc", "se74gb", "se74g8", "se74g2", "se74g3", "se74g6"}}, + {-7.265240409993581, 53.05741281900552, 43211459607615, 46, []uint64{43211459607658, 43211459607744, 43211459607701, 43211459607700, 43211459607614, 43211459607612, 43211459607613, 43211459607656}, "mntwx", []string{"mntwz", "mntyb", "mnty8", "mnty2", "mntwr", "mntwq", "mntww", "mntwy"}}, + {-73.81234586502251, -177.1314978316077, 4669478, 30, []uint64{4669479, 4669485, 4669484, 4669481, 4669475, 4669473, 4669476, 4669477}, "04fh16", []string{"04fh17", "04fh1e", "04fh1d", "04fh19", "04fh13", "04fh11", "04fh14", "04fh15"}}, + {80.20950209355215, 0.30832074244969476, 3659583591536, 42, []uint64{3659583591537, 3659583591539, 3659583591538, 3659583591527, 3659583591525, 3659583591503, 3659583591514, 3659583591515}, "un20r", []string{"un20x", "un228", "un222", "un220", "un20p", "un20n", "un20q", "un20w"}}, + {-71.72369560279185, 172.34102743965695, 2882131446, 32, []uint64{2882131447, 2882131453, 2882131452, 2882131449, 2882131443, 2882131441, 2882131444, 2882131445}, "pg4xcx", []string{"pg6818", "pg681b", "pg4xcz", "pg4xcy", "pg4xcw", "pg4xcq", "pg4xcr", "pg6812"}}, + {13.953589120268589, -22.782946556355455, 29051099549432, 46, []uint64{29051099549433, 29051099549435, 29051099549434, 29051099549423, 29051099549421, 29051099549415, 29051099549426, 29051099549427}, "e6rz", []string{"e6xb", "ed80", "ed2p", "ed2n", "e6ry", "e6rw", "e6rx", "e6x8"}}, + {-83.9902921762914, 117.47753781307256, 697038854153, 40, []uint64{697038854156, 697038854158, 697038854155, 697038854154, 697038854152, 697038854146, 697038854147, 697038854150}, "n95d1", []string{"n95d3", "n95d6", "n95d4", "n959f", "n959c", "n959b", "n95d0", "n95d2"}}, + {-63.220018630614504, 98.16140172723681, 44080700265, 36, []uint64{44080700268, 44080700270, 44080700267, 44080700266, 44080700264, 44080700258, 44080700259, 44080700262}, "nhvb", []string{"nhvc", "nhy1", "nhy0", "nhwp", "nhtz", "nhtx", "nhv8", "nhv9"}}, + {14.94227053239591, 46.58079943363009, 863608416945, 40, []uint64{863608416948, 863608416950, 863608416947, 863608416946, 863608416944, 863608416922, 863608416923, 863608416926}, "t49j5", []string{"t49j7", "t49jk", "t49jh", "t49hu", "t49hg", "t49hf", "t49j4", "t49j6"}}, + {31.131142913567587, 147.9519684525876, 970017, 20, []uint64{970020, 970022, 970019, 970018, 970016, 969994, 969995, 969998}, "xm91ner2", []string{"xm91ner3", "xm91ner9", "xm91ner8", "xm91nepx", "xm91nepr", "xm91nepp", "xm91ner0", "xm91ner1"}}, + {41.489713766699424, 40.57990385740413, 818778, 20, []uint64{818779, 818801, 818800, 818789, 818767, 818765, 818776, 818777}, "szku52", []string{"szku53", "szku59", "szku58", "szkggx", "szkggr", "szkggp", "szku50", "szku51"}}, + {65.59559125357191, -98.59341643367952, 98598523825, 38, []uint64{98598523828, 98598523830, 98598523827, 98598523826, 98598523824, 98598523802, 98598523803, 98598523806}, "cg9vhmq6", []string{"cg9vhmq7", "cg9vhmqe", "cg9vhmqd", "cg9vhmq9", "cg9vhmq3", "cg9vhmq1", "cg9vhmq4", "cg9vhmq5"}}, + {-89.6677716097256, -13.626766086264979, 2829113257, 34, []uint64{2829113260, 2829113262, 2829113259, 2829113258, 2829113256, 2829113250, 2829113251, 2829113254}, "58n3cumr3", []string{"58n3cumr9", "58n3cumrd", "58n3cumr6", "58n3cumr4", "58n3cumr1", "58n3cumr0", "58n3cumr2", "58n3cumr8"}}, + {54.1839519645182, -113.4616938000836, 382170453496, 40, []uint64{382170453497, 382170453499, 382170453498, 382170453487, 382170453485, 382170453479, 382170453490, 382170453491}, "c3xk4", []string{"c3xk6", "c3xk7", "c3xk5", "c3x7g", "c3x7f", "c3x7c", "c3xk1", "c3xk3"}}, + {74.78456586175889, 55.65090517234054, 236686988843, 38, []uint64{236686988846, 236686988932, 236686988929, 236686988928, 236686988842, 236686988840, 236686988841, 236686988844}, "vjr96q5g", []string{"vjr96q5u", "vjr96qhh", "vjr96qh5", "vjr96qh4", "vjr96q5f", "vjr96q5d", "vjr96q5e", "vjr96q5s"}}, + {-82.10563287249533, -123.65177106464398, 9416331694, 38, []uint64{9416331695, 9416332037, 9416332036, 9416332033, 9416331691, 9416331689, 9416331692, 9416331693}, "132hf", []string{"132j4", "132j5", "132hg", "132he", "132hd", "132h9", "132hc", "132j1"}}, + {80.48808963269403, 129.6181982154376, 1063610343537, 40, []uint64{1063610343540, 1063610343542, 1063610343539, 1063610343538, 1063610343536, 1063610343514, 1063610343515, 1063610343518}, "yyk1vs", []string{"yyk1vt", "yyk1vv", "yyk1vu", "yyk1vg", "yyk1ve", "yyk1v7", "yyk1vk", "yyk1vm"}}, + {-81.78722113219555, -172.73100715409964, 6932363409, 42, []uint64{6932363412, 6932363414, 6932363411, 6932363410, 6932363408, 6932363322, 6932363323, 6932363326}, "01mntrj", []string{"01mntrm", "01mntrq", "01mntrn", "01mntqy", "01mntqv", "01mntqu", "01mntrh", "01mntrk"}}, + {-59.850167175860406, -74.41046732116956, 2470008974, 34, []uint64{2470008975, 2470008997, 2470008996, 2470008993, 2470008971, 2470008969, 2470008972, 2470008973}, "4m75d8xg", []string{"4m75d8xu", "4m75db8h", "4m75db85", "4m75db84", "4m75d8xf", "4m75d8xd", "4m75d8xe", "4m75d8xs"}}, + {38.96453497085895, 162.46992027791566, 62684755, 26, []uint64{62684758, 62684764, 62684761, 62684760, 62684754, 62684752, 62684753, 62684756}, "xwgt9j6bz", []string{"xwgt9j6cp", "xwgt9j710", "xwgt9j70b", "xwgt9j708", "xwgt9j6bx", "xwgt9j6bw", "xwgt9j6by", "xwgt9j6cn"}}, + {-26.470508481084835, -153.54252384847496, 80948433, 30, []uint64{80948436, 80948438, 80948435, 80948434, 80948432, 80948346, 80948347, 80948350}, "2e6c6j", []string{"2e6c6n", "2e6c6q", "2e6c6m", "2e6c6k", "2e6c6h", "2e6c3u", "2e6c3v", "2e6c3y"}}, + {-27.25746859688661, -164.3749750078423, 4890927128275, 46, []uint64{4890927128278, 4890927128284, 4890927128281, 4890927128280, 4890927128274, 4890927128272, 4890927128273, 4890927128276}, "275hg", []string{"275j5", "275jh", "275hu", "275hs", "275he", "275hd", "275hf", "275j4"}}, + {-83.4303570829943, -176.68965702719288, 307029879, 38, []uint64{307032610, 307032616, 307029885, 307029884, 307029878, 307029876, 307029877, 307032608}, "014m77f", []string{"014m7k4", "014m7k5", "014m77g", "014m77e", "014m77d", "014m779", "014m77c", "014m7k1"}}, + {-1.4986271668894915, 37.29155932026333, 636908637, 30, []uint64{636908808, 636908810, 636908639, 636908638, 636908636, 636908630, 636908631, 636908802}, "kzdx2x4", []string{"kzdx2x6", "kzdx2x7", "kzdx2x5", "kzdx2wg", "kzdx2wf", "kzdx2wc", "kzdx2x1", "kzdx2x3"}}, + {-11.432023306966585, -40.44429823482642, 4148753806966, 44, []uint64{4148753806967, 4148753806973, 4148753806972, 4148753806969, 4148753806963, 4148753806961, 4148753806964, 4148753806965}, "7jgnzwv7", []string{"7jgnzwvk", "7jgnzwvs", "7jgnzwve", "7jgnzwvd", "7jgnzwv6", "7jgnzwv4", "7jgnzwv5", "7jgnzwvh"}}, + {-49.59472248062957, -41.80438287120705, 741965, 22, []uint64{741976, 741978, 741967, 741966, 741964, 741958, 741959, 741970}, "5p4mb", []string{"5p4q0", "5p4q1", "5p4mc", "5p4m9", "5p4m8", "5p4jx", "5p4jz", "5p4np"}}, + {-59.443837640370475, -179.0765901156119, 4586955841, 38, []uint64{4586955844, 4586955846, 4586955843, 4586955842, 4586955840, 4586950378, 4586950379, 4586950382}, "0j2tv48", []string{"0j2tv4b", "0j2tv4c", "0j2tv49", "0j2tv43", "0j2tv42", "0j2tufr", "0j2tufx", "0j2tufz"}}, + {-51.77437430009013, 104.3189600748592, 711288551880, 40, []uint64{711288551881, 711288551883, 711288551882, 711288551839, 711288551837, 711288551831, 711288551874, 711288551875}, "nqf1m", []string{"nqf1t", "nqf1w", "nqf1q", "nqf1n", "nqf1j", "nqf1h", "nqf1k", "nqf1s"}}, + {-24.795915887021707, 59.64670333993851, 165190268424, 38, []uint64{165190268425, 165190268427, 165190268426, 165190267743, 165190267741, 165190267735, 165190268418, 165190268419}, "m7d6", []string{"m7d7", "m7de", "m7dd", "m7d9", "m7d3", "m7d1", "m7d4", "m7d5"}}, + {-20.83657312914147, 38.88755328863044, 10103711213, 34, []uint64{10103711224, 10103711226, 10103711215, 10103711214, 10103711212, 10103711206, 10103711207, 10103711218}, "ku79kyv4", []string{"ku79kyv5", "ku79kyv7", "ku79kyv6", "ku79kyv3", "ku79kyv1", "ku79kyuc", "ku79kyuf", "ku79kyug"}}, + {23.038875318219652, -30.29406545977693, 455220876, 30, []uint64{455220877, 455220879, 455220878, 455220875, 455220873, 455220867, 455220870, 455220871}, "ek47ndhe1", []string{"ek47ndhe3", "ek47ndhe6", "ek47ndhe4", "ek47ndhdf", "ek47ndhdc", "ek47ndhdb", "ek47ndhe0", "ek47ndhe2"}}, + {-72.76036703793216, -20.286149711057107, 11340552, 26, []uint64{11340553, 11340555, 11340554, 11340383, 11340381, 11340375, 11340546, 11340547}, "5e1d", []string{"5e1e", "5e1g", "5e1f", "5e1c", "5e19", "5e13", "5e16", "5e17"}}, + {-85.48791720875306, -162.29121385799954, 3029757680, 40, []uint64{3029757681, 3029757683, 3029757682, 3029757671, 3029757669, 3029757647, 3029757658, 3029757659}, "02u9dv", []string{"02u9dy", "02u9en", "02u9ej", "02u9eh", "02u9du", "02u9ds", "02u9dt", "02u9dw"}}, + {41.40168550692033, 121.76976460873266, 15903414730049, 44, []uint64{15903414730052, 15903414730054, 15903414730051, 15903414730050, 15903414730048, 15903414728682, 15903414728683, 15903414728686}, "wxqe", []string{"wxqs", "wxqu", "wxqg", "wxqf", "wxqd", "wxq6", "wxq7", "wxqk"}}, + {29.10802533073729, -117.96321347387857, 20153434, 26, []uint64{20153435, 20153457, 20153456, 20153445, 20153423, 20153421, 20153432, 20153433}, "9mhje", []string{"9mhjg", "9mhju", "9mhjs", "9mhjk", "9mhj7", "9mhj6", "9mhjd", "9mhjf"}}, + {43.75735152933339, 36.42103746137579, 53643761364, 36, []uint64{53643761365, 53643761367, 53643761366, 53643761363, 53643761361, 53643761275, 53643761278, 53643761279}, "szcbu", []string{"szcch", "szccj", "szcbv", "szcbt", "szcbs", "szcbe", "szcbg", "szcc5"}}, + {-4.318266513815615, 80.80096629081527, 10978871113515, 44, []uint64{10978871113518, 10978871113604, 10978871113601, 10978871113600, 10978871113514, 10978871113512, 10978871113513, 10978871113516}, "mz1rqtv", []string{"mz1rqwj", "mz1rqwn", "mz1rqty", "mz1rqtw", "mz1rqtt", "mz1rqts", "mz1rqtu", "mz1rqwh"}}, + {-49.016908927675104, 83.62811827572659, 632463647070019, 50, []uint64{632463647070022, 632463647070028, 632463647070025, 632463647070024, 632463647070018, 632463647070016, 632463647070017, 632463647070020}, "jz73ph8", []string{"jz73phb", "jz73phc", "jz73ph9", "jz73ph3", "jz73ph2", "jz73nur", "jz73nux", "jz73nuz"}}, + {21.154934649224728, 37.62193871484487, 13144065106, 34, []uint64{13144065107, 13144065113, 13144065112, 13144065101, 13144065095, 13144065093, 13144065104, 13144065105}, "sgfb255k", []string{"sgfb255m", "sgfb255t", "sgfb255s", "sgfb255e", "sgfb2557", "sgfb2555", "sgfb255h", "sgfb255j"}}, + {-88.21164905458863, 43.56642414943781, 8769481532, 34, []uint64{8769481533, 8769481535, 8769481534, 8769481531, 8769481529, 8769481523, 8769481526, 8769481527}, "hbqfpms1", []string{"hbqfpms4", "hbqfpms6", "hbqfpms3", "hbqfpms2", "hbqfpms0", "hbqfpmeb", "hbqfpmec", "hbqfpmef"}}, + {61.57554614152468, 14.987483712437097, 858588, 20, []uint64{858589, 858591, 858590, 858587, 858585, 858579, 858582, 858583}, "u6fwm13", []string{"u6fwm19", "u6fwm1d", "u6fwm16", "u6fwm14", "u6fwm11", "u6fwm10", "u6fwm12", "u6fwm18"}}, + {17.25931902356386, 13.582548810343695, 208049156471, 38, []uint64{208049157154, 208049157160, 208049156477, 208049156476, 208049156470, 208049156468, 208049156469, 208049157152}, "s71d", []string{"s71e", "s71g", "s71f", "s71c", "s719", "s713", "s716", "s717"}}, + {34.2199230066617, -151.20071140833898, 4661358, 24, []uint64{4661359, 4661445, 4661444, 4661441, 4661355, 4661353, 4661356, 4661357}, "8wh6xm", []string{"8wh6xq", "8wh6xw", "8wh6xt", "8wh6xs", "8wh6xk", "8wh6xh", "8wh6xj", "8wh6xn"}}, + {-89.20049466432465, 171.8779039705696, 46876407745127, 46, []uint64{46876407745138, 46876407745144, 46876407745133, 46876407745132, 46876407745126, 46876407745124, 46876407745125, 46876407745136}, "pb4h", []string{"pb4j", "pb4m", "pb4k", "pb47", "pb45", "pb1g", "pb1u", "pb1v"}}, + {-78.90854773411411, -71.74578421533806, 566263183432, 42, []uint64{566263183433, 566263183435, 566263183434, 566263183391, 566263183389, 566263183383, 566263183426, 566263183427}, "43uzp", []string{"43uzr", "43vp2", "43vp0", "43vnb", "43uyz", "43uyy", "43uzn", "43uzq"}}, + {-7.784516635925684, 59.01185507402988, 10326782, 24, []uint64{10326783, 10328149, 10328148, 10328145, 10326779, 10326777, 10326780, 10326781}, "mq9g", []string{"mq9u", "mqdh", "mqd5", "mqd4", "mq9f", "mq9d", "mq9e", "mq9s"}}, + {25.595018530060756, -23.200296186871128, 478194416436587, 50, []uint64{478194416436590, 478194416436676, 478194416436673, 478194416436672, 478194416436586, 478194416436584, 478194416436585, 478194416436588}, "ekx9856jc", []string{"ekx9856n1", "ekx9856n4", "ekx9856jf", "ekx9856jd", "ekx9856j9", "ekx9856j8", "ekx9856jb", "ekx9856n0"}}, + {25.74317809007333, 32.96845382338506, 3325717330, 32, []uint64{3325717331, 3325717337, 3325717336, 3325717325, 3325717319, 3325717317, 3325717328, 3325717329}, "ssx6", []string{"ssx7", "ssxe", "ssxd", "ssx9", "ssx3", "ssx1", "ssx4", "ssx5"}}, + {9.309183637073147, 177.93012195685878, 1033429628775763, 50, []uint64{1033429628775766, 1033429628775772, 1033429628775769, 1033429628775768, 1033429628775762, 1033429628775760, 1033429628775761, 1033429628775764}, "xcwsbys", []string{"xcwsbyu", "xcwsbyv", "xcwsbyt", "xcwsbym", "xcwsbyk", "xcwsby7", "xcwsbye", "xcwsbyg"}}, + {59.844023980353086, -73.53305692045251, 29781549, 26, []uint64{29781560, 29781562, 29781551, 29781550, 29781548, 29781542, 29781543, 29781554}, "f6es", []string{"f6et", "f6ev", "f6eu", "f6eg", "f6ee", "f6e7", "f6ek", "f6em"}}, + {57.013911618618295, -3.294332241959637, 518578789, 30, []uint64{518578800, 518578802, 518578791, 518578790, 518578788, 518578766, 518578767, 518578778}, "gfjsm5", []string{"gfjsmh", "gfjsmk", "gfjsm7", "gfjsm6", "gfjsm4", "gfjskf", "gfjskg", "gfjsku"}}, + {-25.75149309000699, -149.60257465881296, 5208893992, 36, []uint64{5208893993, 5208893995, 5208893994, 5208883071, 5208883069, 5208883063, 5208893986, 5208893987}, "2emte8nd", []string{"2emte8ne", "2emte8ng", "2emte8nf", "2emte8nc", "2emte8n9", "2emte8n3", "2emte8n6", "2emte8n7"}}, + {-70.90641473676078, -93.17866259199218, 12482795, 28, []uint64{12482798, 12483140, 12483137, 12483136, 12482794, 12482792, 12482793, 12482796}, "1gmsx", []string{"1gmsz", "1gmub", "1gmu8", "1gmu2", "1gmsr", "1gmsq", "1gmsw", "1gmsy"}}, + {72.22856725062593, -91.65408036281588, 24834973, 26, []uint64{24835016, 24835018, 24834975, 24834974, 24834972, 24834966, 24834967, 24835010}, "cuyf", []string{"cuyg", "cuz5", "cuz4", "cuz1", "cuyc", "cuy9", "cuyd", "cuye"}}, + {-61.56472724972991, -63.613137907806944, 164428581591, 40, []uint64{164428581762, 164428581768, 164428581597, 164428581596, 164428581590, 164428581588, 164428581589, 164428581760}, "4t4cb2qr6", []string{"4t4cb2qrd", "4t4cb2qre", "4t4cb2qr7", "4t4cb2qr5", "4t4cb2qr4", "4t4cb2qr1", "4t4cb2qr3", "4t4cb2qr9"}}, + {14.1577433716011, 110.44686887061107, 248182614019827, 48, []uint64{248182614019830, 248182614019836, 248182614019833, 248182614019832, 248182614019826, 248182614019824, 248182614019825, 248182614019828}, "w6w8932", []string{"w6w8938", "w6w8939", "w6w8933", "w6w8931", "w6w8930", "w6w891p", "w6w891r", "w6w891x"}}, + {46.41777738709061, -46.41725197364579, 480979628, 30, []uint64{480979629, 480979631, 480979630, 480979627, 480979625, 480979619, 480979622, 480979623}, "fbqbpdpgp", []string{"fbqbpdpgr", "fbqbpf052", "fbqbpf050", "fbqbpf04b", "fbqbpdpfz", "fbqbpdpfy", "fbqbpdpgn", "fbqbpdpgq"}}, + {22.938786659055026, -70.16381655560689, 432319749970, 40, []uint64{432319749971, 432319749977, 432319749976, 432319749965, 432319749959, 432319749957, 432319749968, 432319749969}, "dkn4", []string{"dkn5", "dkn7", "dkn6", "dkn3", "dkn1", "dkjc", "dkjf", "dkjg"}}, + {-60.17487518119742, 16.86998302739812, 2281617209693, 42, []uint64{2281617210376, 2281617210378, 2281617209695, 2281617209694, 2281617209692, 2281617209686, 2281617209687, 2281617210370}, "hm7cxvkr", []string{"hm7cxvs2", "hm7cxvs8", "hm7cxvkx", "hm7cxvkw", "hm7cxvkq", "hm7cxvkn", "hm7cxvkp", "hm7cxvs0"}}, + {50.32963741067215, 90.09123249311233, 62936332, 26, []uint64{62936333, 62936335, 62936334, 62936331, 62936329, 62936323, 62936326, 62936327}, "y0bn644rm", []string{"y0bn644rt", "y0bn644rw", "y0bn644rq", "y0bn644rn", "y0bn644rj", "y0bn644rh", "y0bn644rk", "y0bn644rs"}}, + {16.614631477263174, -6.0594447328476235, 28890161178, 36, []uint64{28890161179, 28890161201, 28890161200, 28890161189, 28890161167, 28890161165, 28890161176, 28890161177}, "efgww0eeh", []string{"efgww0eek", "efgww0eem", "efgww0eej", "efgww0edv", "efgww0edu", "efgww0edg", "efgww0ee5", "efgww0ee7"}}, + {-44.95768291244166, 29.34211246007908, 9576610, 24, []uint64{9576611, 9576617, 9576616, 8877565, 8877559, 8877557, 9576608, 9576609}, "k8hb5xwq8", []string{"k8hb5xwqb", "k8hb5xwqc", "k8hb5xwq9", "k8hb5xwq3", "k8hb5xwq2", "k8hb5xwnr", "k8hb5xwnx", "k8hb5xwnz"}}, + {-32.11136839287041, 169.42783711708034, 12289083, 24, []uint64{12289086, 12289172, 12289169, 12289168, 12289082, 12289080, 12289081, 12289084}, "rf23r6m", []string{"rf23r6t", "rf23r6w", "rf23r6q", "rf23r6n", "rf23r6j", "rf23r6h", "rf23r6k", "rf23r6s"}}, + {73.8926145724181, 19.38432300547717, 14286751318, 34, []uint64{14286751319, 14286751325, 14286751324, 14286751321, 14286751315, 14286751313, 14286751316, 14286751317}, "umju35epx", []string{"umju35epz", "umju35erb", "umju35er8", "umju35er2", "umju35epr", "umju35epq", "umju35epw", "umju35epy"}}, + {5.450738585408544, 90.72283733007498, 15038103595, 34, []uint64{15038103598, 15038103684, 15038103681, 15038103680, 15038103594, 15038103592, 15038103593, 15038103596}, "w0bx", []string{"w108", "w10b", "w0bz", "w0by", "w0bw", "w0bq", "w0br", "w102"}}, + {9.885070688163978, -83.72861123050097, 26513011237427, 46, []uint64{26513011237430, 26513011237436, 26513011237433, 26513011237432, 26513011237426, 26513011237424, 26513011237425, 26513011237428}, "d1u2", []string{"d1u3", "d1u9", "d1u8", "d1sx", "d1sr", "d1sp", "d1u0", "d1u1"}}, + {-29.49522162301583, 81.69872638115339, 10198018, 24, []uint64{10198019, 10198025, 10198024, 10197341, 10197335, 10197333, 10198016, 10198017}, "mff05ne", []string{"mff05ng", "mff05nu", "mff05ns", "mff05nk", "mff05n7", "mff05n6", "mff05nd", "mff05nf"}}, + {-17.45692824742582, 11.59409018594306, 653471688831018, 50, []uint64{653471688831019, 653471688831105, 653471688831104, 653471688828373, 653471688828287, 653471688828285, 653471688831016, 653471688831017}, "kkbhxy4", []string{"kkbhxy6", "kkbhxy7", "kkbhxy5", "kkbhxvg", "kkbhxvf", "kkbhxvc", "kkbhxy1", "kkbhxy3"}}, + {-3.8359146104048705, 133.35739554004977, 808912153934361, 50, []uint64{808912153934364, 808912153934366, 808912153934363, 808912153934362, 808912153934360, 808912153934354, 808912153934355, 808912153934358}, "qzqf4teyh", []string{"qzqf4teyk", "qzqf4teym", "qzqf4teyj", "qzqf4tevv", "qzqf4tevu", "qzqf4tevg", "qzqf4tey5", "qzqf4tey7"}}, + {-72.30765363079263, -119.85964626134955, 40090, 20, []uint64{40091, 40113, 40112, 40101, 40079, 40077, 40088, 40089}, "174u8", []string{"174ub", "174uc", "174u9", "174u3", "174u2", "174sr", "174sx", "174sz"}}, + {-25.826948183626897, 77.21488991036313, 667560743109, 40, []uint64{667560743120, 667560743122, 667560743111, 667560743110, 667560743108, 667560743022, 667560743023, 667560743034}, "meqvj", []string{"meqvm", "meqvq", "meqvn", "mequy", "mequv", "mequu", "meqvh", "meqvk"}}, + {73.31938723289932, -9.6263395496062, 519201, 20, []uint64{519204, 519206, 519203, 519202, 519200, 519178, 519179, 519182}, "gv11hgqk6", []string{"gv11hgqkd", "gv11hgqke", "gv11hgqk7", "gv11hgqk5", "gv11hgqk4", "gv11hgqk1", "gv11hgqk3", "gv11hgqk9"}}, + {5.809729026499554, 176.85426393302623, 4035302145224, 42, []uint64{4035302145225, 4035302145227, 4035302145226, 4035302145183, 4035302145181, 4035302145175, 4035302145218, 4035302145219}, "xcjc03t", []string{"xcjc03v", "xcjc03y", "xcjc03w", "xcjc03q", "xcjc03m", "xcjc03k", "xcjc03s", "xcjc03u"}}, + {-17.874551137261733, 134.4207391004311, 196221128224, 38, []uint64{196221128225, 196221128227, 196221128226, 196221127543, 196221127541, 196221127519, 196221128202, 196221128203}, "quzd6f", []string{"quzd6g", "quzd75", "quzd74", "quzd71", "quzd6c", "quzd69", "quzd6d", "quzd6e"}}, + {-67.10728097337415, -16.932142237666994, 188590, 20, []uint64{188591, 188933, 188932, 188929, 188587, 188585, 188588, 188589}, "5s5fnx", []string{"5s5fq8", "5s5fqb", "5s5fnz", "5s5fny", "5s5fnw", "5s5fnq", "5s5fnr", "5s5fq2"}}, + {-64.88314565600012, 168.42144161889155, 187031967766, 38, []uint64{187031967767, 187031967773, 187031967772, 187031967769, 187031967763, 187031967761, 187031967764, 187031967765}, "psrybs2u2", []string{"psrybs2u8", "psrybs2u9", "psrybs2u3", "psrybs2u1", "psrybs2u0", "psrybs2sp", "psrybs2sr", "psrybs2sx"}}, + {27.70462208949901, 3.2077453242963827, 52643972079, 36, []uint64{52643972090, 52643973456, 52643973445, 52643973444, 52643972078, 52643972076, 52643972077, 52643972088}, "shfm8grz", []string{"shfm8gxb", "shfm9580", "shfm952p", "shfm952n", "shfm8gry", "shfm8grw", "shfm8grx", "shfm8gx8"}}, + {-86.21243747585686, -99.71227884921245, 173253, 22, []uint64{173264, 173266, 173255, 173254, 173252, 173166, 173167, 173178}, "1b9jdc", []string{"1b9jdf", "1b9je4", "1b9je1", "1b9je0", "1b9jdb", "1b9jd8", "1b9jd9", "1b9jdd"}}, + {6.342568408148509, -67.06215916480868, 412108846, 30, []uint64{412108847, 412108933, 412108932, 412108929, 412108843, 412108841, 412108844, 412108845}, "d90k", []string{"d90m", "d90t", "d90s", "d90e", "d907", "d905", "d90h", "d90j"}}, + {34.31756255120854, 169.7929788219044, 16458544279147, 44, []uint64{16458544279150, 16458544279236, 16458544279233, 16458544279232, 16458544279146, 16458544279144, 16458544279145, 16458544279148}, "xy0epx", []string{"xy0er8", "xy0erb", "xy0epz", "xy0epy", "xy0epw", "xy0epq", "xy0epr", "xy0er2"}}, + {27.863295166374883, -67.05465515336253, 418134, 20, []uint64{418135, 418141, 418140, 418137, 418131, 418129, 418132, 418133}, "dsbqd", []string{"dsbqf", "dsbqg", "dsbqe", "dsbq7", "dsbq6", "dsbq3", "dsbq9", "dsbqc"}}, + {72.35201586683979, -166.78267771628452, 1453207908644, 42, []uint64{1453207908645, 1453207908647, 1453207908646, 1453207908643, 1453207908641, 1453207908619, 1453207908622, 1453207908623}, "bkc7sg291", []string{"bkc7sg293", "bkc7sg296", "bkc7sg294", "bkc7sg28f", "bkc7sg28c", "bkc7sg28b", "bkc7sg290", "bkc7sg292"}}, + {29.134609501710656, 66.2832201916608, 3437960285, 32, []uint64{3437960456, 3437960458, 3437960287, 3437960286, 3437960284, 3437960278, 3437960279, 3437960450}, "tmpjsrccq", []string{"tmpjsrccw", "tmpjsrccx", "tmpjsrccr", "tmpjsrccp", "tmpjsrccn", "tmpjsrccj", "tmpjsrccm", "tmpjsrcct"}}, + {-71.47747958618857, 4.4704490665462515, 8885777432372, 44, []uint64{8885777432373, 8885777432375, 8885777432374, 8885777432371, 8885777432369, 8885777432347, 8885777432350, 8885777432351}, "h571mez", []string{"h571msp", "h571mu0", "h571mgb", "h571mg8", "h571mex", "h571mew", "h571mey", "h571msn"}}, + {-57.41888338926947, 112.4469474129437, 725655879735786, 50, []uint64{725655879735787, 725655879736129, 725655879736128, 725655879736085, 725655879735743, 725655879735741, 725655879735784, 725655879735785}, "nmzcqg1", []string{"nmzcqg3", "nmzcqg6", "nmzcqg4", "nmzcqff", "nmzcqfc", "nmzcqfb", "nmzcqg0", "nmzcqg2"}}, + {27.662408339587273, 142.13106273350422, 64930322010723, 46, []uint64{64930322010726, 64930322010732, 64930322010729, 64930322010728, 64930322010722, 64930322010720, 64930322010721, 64930322010724}, "xhvj67b", []string{"xhvj6k0", "xhvj6k1", "xhvj67c", "xhvj679", "xhvj678", "xhvj65x", "xhvj65z", "xhvj6hp"}}, + {-55.486967906541395, -23.80852768215118, 12258914623, 36, []uint64{12258914666, 12258914752, 12258914709, 12258914708, 12258914622, 12258914620, 12258914621, 12258914664}, "5qph6", []string{"5qphd", "5qphe", "5qph7", "5qph5", "5qph4", "5qph1", "5qph3", "5qph9"}}, + {26.61452117061708, -124.93604025959212, 79934876, 28, []uint64{79934877, 79934879, 79934878, 79934875, 79934873, 79934867, 79934870, 79934871}, "9hxpmj0", []string{"9hxpmj2", "9hxpmj3", "9hxpmj1", "9hxpmhc", "9hxpmhb", "9hxpkuz", "9hxpkvp", "9hxpkvr"}}, + {-77.67499976309045, -179.0914305030601, 4324865852, 40, []uint64{4324865853, 4324865855, 4324865854, 4324865851, 4324865849, 4324865843, 4324865846, 4324865847}, "040whetw7", []string{"040whetwe", "040whetws", "040whetwk", "040whetwh", "040whetw5", "040whetw4", "040whetw6", "040whetwd"}}, + {75.0973305683001, 80.26193236419931, 14950156664, 34, []uint64{14950156665, 14950156667, 14950156666, 14950156655, 14950156653, 14950156647, 14950156658, 14950156659}, "vv35", []string{"vv3h", "vv3k", "vv37", "vv36", "vv34", "vv2f", "vv2g", "vv2u"}}, + {-66.11247346199525, 46.154985158143944, 9395756324, 34, []uint64{9395756325, 9395756327, 9395756326, 9395756323, 9395756321, 9395756299, 9395756302, 9395756303}, "jh0zf", []string{"jh2b4", "jh2b5", "jh0zg", "jh0ze", "jh0zd", "jh0z9", "jh0zc", "jh2b1"}}, + {-63.34196376264663, 177.57374323537806, 2862685, 22, []uint64{2862856, 2862858, 2862687, 2862686, 2862684, 2862678, 2862679, 2862850}, "puwr", []string{"puy2", "puy8", "puwx", "puww", "puwq", "puwn", "puwp", "puy0"}}, + {86.63544582921895, 152.3093095542281, 68154448880, 36, []uint64{68154448881, 68154448883, 68154448882, 68154448871, 68154448869, 68154448847, 68154448858, 68154448859}, "zrkkcgs", []string{"zrkkcgu", "zrkkcgv", "zrkkcgt", "zrkkcgm", "zrkkcgk", "zrkkcg7", "zrkkcge", "zrkkcgg"}}, + {61.760291416343534, 75.19682545444809, 919461605, 30, []uint64{919461616, 919461618, 919461607, 919461606, 919461604, 919461582, 919461583, 919461594}, "vdvrr5", []string{"vdvrrh", "vdvrrk", "vdvrr7", "vdvrr6", "vdvrr4", "vdvrqf", "vdvrqg", "vdvrqu"}}, + {53.78255527224974, -140.45642841712106, 339713, 20, []uint64{339716, 339718, 339715, 339714, 339712, 339370, 339371, 339374}, "bcs1g", []string{"bcs45", "bcs4h", "bcs1u", "bcs1s", "bcs1e", "bcs1d", "bcs1f", "bcs44"}}, + {39.30645084043499, 43.45621443026903, 53617869786, 36, []uint64{53617869787, 53617869809, 53617869808, 53617869797, 53617869775, 53617869773, 53617869784, 53617869785}, "syyzsg", []string{"syyzsu", "syyzth", "syyzt5", "syyzt4", "syyzsf", "syyzsd", "syyzse", "syyzss"}}, + {8.48326842175446, -50.58120501710803, 1620992, 22, []uint64{1620993, 1620995, 1620994, 1620311, 1620309, 1618943, 1619626, 1619627}, "dcs02br", []string{"dcs02bx", "dcs0308", "dcs0302", "dcs0300", "dcs02bp", "dcs02bn", "dcs02bq", "dcs02bw"}}, + {25.13198974027908, 94.32323682148126, 14946119, 24, []uint64{14946130, 14946136, 14946125, 14946124, 14946118, 14946116, 14946117, 14946128}, "wh7nf", []string{"wh7p4", "wh7p5", "wh7ng", "wh7ne", "wh7nd", "wh7n9", "wh7nc", "wh7p1"}}, + {84.26094171887962, -109.25827881455189, 6233457, 24, []uint64{6233460, 6233462, 6233459, 6233458, 6233456, 6233434, 6233435, 6233438}, "cwfr3g0x9", []string{"cwfr3g0xc", "cwfr3g0xf", "cwfr3g0xd", "cwfr3g0x6", "cwfr3g0x3", "cwfr3g0x2", "cwfr3g0x8", "cwfr3g0xb"}}, + {31.72844345847261, 178.4804261389654, 256597719005, 38, []uint64{256597762696, 256597762698, 256597719007, 256597719006, 256597719004, 256597718998, 256597718999, 256597762690}, "xvwumrvrx", []string{"xvwumrvrz", "xvwumrvxb", "xvwumrvx8", "xvwumrvx2", "xvwumrvrr", "xvwumrvrq", "xvwumrvrw", "xvwumrvry"}}, + {34.46661383166793, 74.51951443694998, 848410, 20, []uint64{848411, 848433, 848432, 848421, 848399, 848397, 848408, 848409}, "twhupdrq", []string{"twhupdrr", "twhupdrx", "twhupdrw", "twhupdrt", "twhupdrm", "twhupdrj", "twhupdrn", "twhupdrp"}}, + {7.8994896060467, -83.63523559545865, 414020106377, 40, []uint64{414020106380, 414020106382, 414020106379, 414020106378, 414020106376, 414020106370, 414020106371, 414020106374}, "d1ksby4", []string{"d1ksby6", "d1ksby7", "d1ksby5", "d1ksbvg", "d1ksbvf", "d1ksbvc", "d1ksby1", "d1ksby3"}}, + {-20.129183917102637, 55.88864316433319, 10733018403058, 44, []uint64{10733018403059, 10733018403065, 10733018403064, 10733018403053, 10733018403047, 10733018403045, 10733018403056, 10733018403057}, "mhrt", []string{"mhrw", "mhry", "mhrv", "mhru", "mhrs", "mhrk", "mhrm", "mhrq"}}, + {-73.30437010010064, 22.10091529789497, 8707325433, 34, []uint64{8707325436, 8707325438, 8707325435, 8707325434, 8707325432, 8707325426, 8707325427, 8707325430}, "h6zwy", []string{"h6zxn", "h6zxp", "h6zwz", "h6zwx", "h6zww", "h6zwt", "h6zwv", "h6zxj"}}, + {38.970237572979386, -52.88151284668129, 6790460, 24, []uint64{6790461, 6790463, 6790462, 6790459, 6790457, 6790451, 6790454, 6790455}, "dyfmswk", []string{"dyfmsws", "dyfmswt", "dyfmswm", "dyfmswj", "dyfmswh", "dyfmsw5", "dyfmsw7", "dyfmswe"}}, + {-71.33839873081888, 75.39886880401173, 2283952, 22, []uint64{2283953, 2283955, 2283954, 2283943, 2283941, 2283919, 2283930, 2283931}, "jemd5tp", []string{"jemd5tr", "jemd5v2", "jemd5v0", "jemd5ub", "jemd5sz", "jemd5sy", "jemd5tn", "jemd5tq"}}, + {-78.17916788489674, 37.67139080283232, 141234550268, 38, []uint64{141234550269, 141234550271, 141234550270, 141234550267, 141234550265, 141234550259, 141234550262, 141234550263}, "hf4g1p", []string{"hf4g30", "hf4g32", "hf4g1r", "hf4g1q", "hf4g1n", "hf4g0y", "hf4g0z", "hf4g2b"}}, + {-62.08553013509663, -1.0218132149311998, 49018450, 28, []uint64{49018451, 49018457, 49018456, 49018445, 49018439, 49018437, 49018448, 49018449}, "5uzq", []string{"5uzr", "5uzx", "5uzw", "5uzt", "5uzm", "5uzj", "5uzn", "5uzp"}}, + {41.17897895803617, 10.754419630800811, 808684, 20, []uint64{808685, 808687, 808686, 808683, 808681, 808675, 808678, 808679}, "sprdk8r", []string{"sprdk8x", "sprdkb8", "sprdkb2", "sprdkb0", "sprdk8p", "sprdk8n", "sprdk8q", "sprdk8w"}}, + {-37.630445815972045, 102.35339855859638, 47450905690, 36, []uint64{47450905691, 47450905713, 47450905712, 47450905701, 47450905679, 47450905677, 47450905688, 47450905689}, "q32ccje", []string{"q32ccjg", "q32ccju", "q32ccjs", "q32ccjk", "q32ccj7", "q32ccj6", "q32ccjd", "q32ccjf"}}, + {62.99316965114849, -141.60052493413968, 5758311453341, 44, []uint64{5758311453384, 5758311453386, 5758311453343, 5758311453342, 5758311453340, 5758311453334, 5758311453335, 5758311453378}, "bg5q3", []string{"bg5q9", "bg5qd", "bg5q6", "bg5q4", "bg5q1", "bg5q0", "bg5q2", "bg5q8"}}, + {71.60926877912425, 126.79106472741114, 1010069, 20, []uint64{1010112, 1010114, 1010071, 1010070, 1010068, 1009982, 1009983, 1010026}, "yudpmhn6j", []string{"yudpmhn6m", "yudpmhn6q", "yudpmhn6n", "yudpmhn3y", "yudpmhn3v", "yudpmhn3u", "yudpmhn6h", "yudpmhn6k"}}, + {53.91994105381309, -149.00984103728842, 5534454623, 34, []uint64{5534455306, 5534455328, 5534454645, 5534454644, 5534454622, 5534454620, 5534454621, 5534455304}, "b9w4", []string{"b9w5", "b9w7", "b9w6", "b9w3", "b9w1", "b9tc", "b9tf", "b9tg"}}, + {-72.67579659783314, 169.56417998776305, 180096098, 28, []uint64{180096099, 180096105, 180096104, 180096061, 180096055, 180096053, 180096096, 180096097}, "pg0d", []string{"pg0e", "pg0g", "pg0f", "pg0c", "pg09", "pg03", "pg06", "pg07"}}, + {-65.35303823447612, -170.14219564708765, 4599915857149, 48, []uint64{4599915857320, 4599915857322, 4599915857151, 4599915857150, 4599915857148, 4599915857142, 4599915857143, 4599915857314}, "0hrh0qf4z", []string{"0hrh0qf5p", "0hrh0qf70", "0hrh0qf6b", "0hrh0qf68", "0hrh0qf4x", "0hrh0qf4w", "0hrh0qf4y", "0hrh0qf5n"}}, + {41.072658477191, 146.78479197839624, 15582268, 24, []uint64{15582269, 15582271, 15582270, 15582267, 15582265, 15582259, 15582262, 15582263}, "xr23sjj3t", []string{"xr23sjj3v", "xr23sjj3y", "xr23sjj3w", "xr23sjj3q", "xr23sjj3m", "xr23sjj3k", "xr23sjj3s", "xr23sjj3u"}}, + {39.30860977542034, -154.03628090472193, 1165151, 22, []uint64{1167882, 1167904, 1165173, 1165172, 1165150, 1165148, 1165149, 1167880}, "8wfrwg", []string{"8wfrwu", "8wfrxh", "8wfrx5", "8wfrx4", "8wfrwf", "8wfrwd", "8wfrwe", "8wfrws"}}, + {81.6712274608144, 21.2403809927564, 917949029835, 40, []uint64{917949029838, 917949029860, 917949029857, 917949029856, 917949029834, 917949029832, 917949029833, 917949029836}, "uqx0e", []string{"uqx0g", "uqx0u", "uqx0s", "uqx0k", "uqx07", "uqx06", "uqx0d", "uqx0f"}}, + {79.75085776337801, 139.87431390499114, 1036467, 20, []uint64{1036470, 1036476, 1036473, 1036472, 1036466, 1036464, 1036465, 1036468}, "zn5mwyjq", []string{"zn5mwyjr", "zn5mwyjx", "zn5mwyjw", "zn5mwyjt", "zn5mwyjm", "zn5mwyjj", "zn5mwyjn", "zn5mwyjp"}}, + {56.673110491305124, 69.40099028649274, 235159483136, 38, []uint64{235159483137, 235159483139, 235159483138, 235159482967, 235159482965, 235159482623, 235159482794, 235159482795}, "vd167m02b", []string{"vd167m030", "vd167m031", "vd167m02c", "vd167m029", "vd167m028", "vd167m00x", "vd167m00z", "vd167m01p"}}, + {79.6513418177201, 135.5782508161501, 66323683, 26, []uint64{66323686, 66323692, 66323689, 66323688, 66323682, 66323680, 66323681, 66323684}, "zn0mjhj", []string{"zn0mjhm", "zn0mjhq", "zn0mjhn", "zn0mj5y", "zn0mj5v", "zn0mj5u", "zn0mjhh", "zn0mjhk"}}, + {-75.07770606398117, 51.253311516091316, 603377358905625, 50, []uint64{603377358905628, 603377358905630, 603377358905627, 603377358905626, 603377358905624, 603377358905618, 603377358905619, 603377358905622}, "j4skyk9", []string{"j4skykc", "j4skykf", "j4skykd", "j4skyk6", "j4skyk3", "j4skyk2", "j4skyk8", "j4skykb"}}, + {39.65431433475169, -51.69808540743543, 27860790044, 36, []uint64{27860790045, 27860790047, 27860790046, 27860790043, 27860790041, 27860790035, 27860790038, 27860790039}, "dz51xd", []string{"dz51xe", "dz51xg", "dz51xf", "dz51xc", "dz51x9", "dz51x3", "dz51x6", "dz51x7"}}, + {12.801787139964276, 175.4340247946384, 63254974735, 36, []uint64{63254974746, 63254974768, 63254974757, 63254974756, 63254974734, 63254974732, 63254974733, 63254974744}, "xfkbb47", []string{"xfkbb4e", "xfkbb4s", "xfkbb4k", "xfkbb4h", "xfkbb45", "xfkbb44", "xfkbb46", "xfkbb4d"}}, + {18.758548914251136, 174.67438823817065, 253286915700, 38, []uint64{253286915701, 253286915703, 253286915702, 253286915699, 253286915697, 253286915675, 253286915678, 253286915679}, "xgk4wyfh", []string{"xgk4wyfj", "xgk4wyfm", "xgk4wyfk", "xgk4wyf7", "xgk4wyf5", "xgk4wycg", "xgk4wycu", "xgk4wycv"}}, + {17.57668314580222, -162.55899361965203, 4527027122524, 44, []uint64{4527027122525, 4527027122527, 4527027122526, 4527027122523, 4527027122521, 4527027122515, 4527027122518, 4527027122519}, "87h7uz", []string{"87hkhb", "87hkj0", "87h7vp", "87h7vn", "87h7uy", "87h7uw", "87h7ux", "87hkh8"}}, + {-37.747590712766396, -28.397978719178354, 243985918048, 40, []uint64{243985918049, 243985918051, 243985918050, 243985918007, 243985918005, 243985917983, 243985918026, 243985918027}, "737c3", []string{"737c9", "737cd", "737c6", "737c4", "737c1", "737c0", "737c2", "737c8"}}, + {50.88363626257342, 163.42217142210575, 16802409968, 34, []uint64{16802409969, 16802409971, 16802409970, 16802409959, 16802409957, 16802409935, 16802409946, 16802409947}, "z9h1qz06", []string{"z9h1qz07", "z9h1qz0e", "z9h1qz0d", "z9h1qz09", "z9h1qz03", "z9h1qz01", "z9h1qz04", "z9h1qz05"}}, + {-5.481237088039052, -42.333374965412105, 269426331544671, 50, []uint64{269426331544842, 269426331544864, 269426331544693, 269426331544692, 269426331544670, 269426331544668, 269426331544669, 269426331544840}, "7p1budjv2", []string{"7p1budjv8", "7p1budjv9", "7p1budjv3", "7p1budjv1", "7p1budjv0", "7p1budjtp", "7p1budjtr", "7p1budjtx"}}, + {-79.05858673258626, 90.07587179127586, 43039068030, 36, []uint64{43039068031, 43039068117, 43039068116, 43039068113, 43039068027, 43039068025, 43039068028, 43039068029}, "n1bn1", []string{"n1bn3", "n1bn6", "n1bn4", "n1bjf", "n1bjc", "n1bjb", "n1bn0", "n1bn2"}}, + {-52.232312765686174, 123.9540895849932, 2878789179745, 42, []uint64{2878789179748, 2878789179750, 2878789179747, 2878789179746, 2878789179744, 2878789179722, 2878789179723, 2878789179726}, "ny8nue", []string{"ny8nus", "ny8nuu", "ny8nug", "ny8nuf", "ny8nud", "ny8nu6", "ny8nu7", "ny8nuk"}}, + {28.335377327806782, 169.64385280957504, 1001399830, 30, []uint64{1001399831, 1001399837, 1001399836, 1001399833, 1001399827, 1001399825, 1001399828, 1001399829}, "xv09", []string{"xv0d", "xv0f", "xv0c", "xv0b", "xv08", "xv02", "xv03", "xv06"}}, + {53.19318545711576, -156.317471057293, 1347961, 22, []uint64{1347964, 1347966, 1347963, 1347962, 1347960, 1347954, 1347955, 1347958}, "b92ydgt0", []string{"b92ydgt1", "b92ydgt3", "b92ydgt2", "b92ydgmr", "b92ydgmp", "b92ydgkz", "b92ydgsb", "b92ydgsc"}}, + {-50.949558987791534, 28.66053511662176, 145179152606, 38, []uint64{145179152607, 145179152629, 145179152628, 145179152625, 145179152603, 145179152601, 145179152604, 145179152605}, "hwuq", []string{"hwur", "hwux", "hwuw", "hwut", "hwum", "hwuj", "hwun", "hwup"}}, + {-0.2354991145839591, -153.22841681262014, 392119494, 32, []uint64{392119495, 392119501, 392119500, 392119497, 392119491, 392119489, 392119492, 392119493}, "2xgn9", []string{"2xgnc", "2xgnf", "2xgnd", "2xgn6", "2xgn3", "2xgn2", "2xgn8", "2xgnb"}}, + {-81.13071196903184, 119.98812701259158, 44653044220780, 46, []uint64{44653044220781, 44653044220783, 44653044220782, 44653044220779, 44653044220777, 44653044220771, 44653044220774, 44653044220775}, "n9t66q", []string{"n9t66r", "n9t66x", "n9t66w", "n9t66t", "n9t66m", "n9t66j", "n9t66n", "n9t66p"}}, + {-56.794493782523205, 114.9665171825909, 182895365017633, 48, []uint64{182895365017636, 182895365017638, 182895365017635, 182895365017634, 182895365017632, 182895365017610, 182895365017611, 182895365017614}, "ntcu", []string{"ntcv", "ntfj", "ntfh", "ntf5", "ntcg", "ntce", "ntcs", "ntct"}}, + {40.40790187273524, 178.02564821887063, 982681, 20, []uint64{982684, 982686, 982683, 982682, 982680, 982674, 982675, 982678}, "xzntg", []string{"xznw5", "xznwh", "xzntu", "xznts", "xznte", "xzntd", "xzntf", "xznw4"}}, + {48.69836126809241, 73.28416957284205, 915162281, 30, []uint64{915162284, 915162286, 915162283, 915162282, 915162280, 915162274, 915162275, 915162278}, "v8sj5", []string{"v8sj7", "v8sjk", "v8sjh", "v8shu", "v8shg", "v8shf", "v8sj4", "v8sj6"}}, + {89.32275185902108, 112.4244934184826, 16904901399364, 44, []uint64{16904901399365, 16904901399367, 16904901399366, 16904901399363, 16904901399361, 16904901399019, 16904901399022, 16904901399023}, "yrzunk9n8", []string{"yrzunk9nb", "yrzunk9nc", "yrzunk9n9", "yrzunk9n3", "yrzunk9n2", "yrzunk8yr", "yrzunk8yx", "yrzunk8yz"}}, + {-60.56225296955381, 133.74655324278865, 183529654449938, 48, []uint64{183529654449939, 183529654449945, 183529654449944, 183529654449933, 183529654449927, 183529654449925, 183529654449936, 183529654449937}, "nvpp7qz", []string{"nvpp7rp", "nvpp7x0", "nvpp7wb", "nvpp7w8", "nvpp7qx", "nvpp7qw", "nvpp7qy", "nvpp7rn"}}, + {-59.92705717968056, -94.86049648071639, 3904322, 26, []uint64{3904323, 3904329, 3904328, 3904285, 3904279, 3904277, 3904320, 3904321}, "1vke16s", []string{"1vke16u", "1vke16v", "1vke16t", "1vke16m", "1vke16k", "1vke167", "1vke16e", "1vke16g"}}, + {-72.03960850483872, 68.92951201106189, 37383046736, 36, []uint64{37383046737, 37383046739, 37383046738, 37383046727, 37383046725, 37383046383, 37383046394, 37383046395}, "je1n0", []string{"je1n2", "je1n3", "je1n1", "je1jc", "je1jb", "je0vz", "je0yp", "je0yr"}}, + {37.57755029312102, 149.27445663587423, 973201, 20, []uint64{973204, 973206, 973203, 973202, 973200, 973114, 973115, 973118}, "xqdjub", []string{"xqdjuc", "xqdjv1", "xqdjv0", "xqdjtp", "xqdjsz", "xqdjsx", "xqdju8", "xqdju9"}}, + {-11.995449387846747, -2.972265940363286, 276922112386676, 50, []uint64{276922112386677, 276922112386679, 276922112386678, 276922112386675, 276922112386673, 276922112386651, 276922112386654, 276922112386655}, "7vvgu279", []string{"7vvgu27d", "7vvgu27f", "7vvgu27c", "7vvgu27b", "7vvgu278", "7vvgu272", "7vvgu273", "7vvgu276"}}, + {-17.933635751265683, 105.79977671263623, 12411952121349, 44, []uint64{12411952121360, 12411952121362, 12411952121351, 12411952121350, 12411952121348, 12411952121006, 12411952121007, 12411952121018}, "qkg1zx", []string{"qkg4p8", "qkg4pb", "qkg1zz", "qkg1zy", "qkg1zw", "qkg1zq", "qkg1zr", "qkg4p2"}}, + {49.873535703431116, 40.100395689485566, 863053, 20, []uint64{863064, 863066, 863055, 863054, 863052, 863046, 863047, 863058}, "ubue8", []string{"ubueb", "ubuec", "ubue9", "ubue3", "ubue2", "ubu7r", "ubu7x", "ubu7z"}}, + {-65.04685144909308, -84.15603127238865, 148049, 20, []uint64{148052, 148054, 148051, 148050, 148048, 147706, 147707, 147710}, "4hkjuyx6", []string{"4hkjuyx7", "4hkjuyxe", "4hkjuyxd", "4hkjuyx9", "4hkjuyx3", "4hkjuyx1", "4hkjuyx4", "4hkjuyx5"}}, + {-31.728434633856516, 105.36924529803218, 727247, 20, []uint64{727258, 727280, 727269, 727268, 727246, 727244, 727245, 727256}, "q66gt", []string{"q66gv", "q66gy", "q66gw", "q66gq", "q66gm", "q66gk", "q66gs", "q66gu"}}, + {54.04937568890455, 128.64317719254177, 16297619315, 34, []uint64{16297619318, 16297619324, 16297619321, 16297619320, 16297619314, 16297619312, 16297619313, 16297619316}, "yce7rr", []string{"yce7x2", "yce7x8", "yce7rx", "yce7rw", "yce7rq", "yce7rn", "yce7rp", "yce7x0"}}, + {-80.86962799805497, -103.60613495536381, 179883042888, 42, []uint64{179883042889, 179883042891, 179883042890, 179883042847, 179883042845, 179883042839, 179883042882, 179883042883}, "19w7fq", []string{"19w7fr", "19w7fx", "19w7fw", "19w7ft", "19w7fm", "19w7fj", "19w7fn", "19w7fp"}}, + {14.162398727770778, 17.473705159616657, 12998193858, 34, []uint64{12998193859, 12998193865, 12998193864, 12998193821, 12998193815, 12998193813, 12998193856, 12998193857}, "s6s2td5", []string{"s6s2td7", "s6s2tdk", "s6s2tdh", "s6s2t9u", "s6s2t9g", "s6s2t9f", "s6s2td4", "s6s2td6"}}, + {32.35787367657757, -159.1463016065536, 18084488, 26, []uint64{18084489, 18084491, 18084490, 18081759, 18081757, 18081751, 18084482, 18084483}, "8myb4", []string{"8myb6", "8myb7", "8myb5", "8mwzg", "8mwzf", "8mwzc", "8myb1", "8myb3"}}, + {-32.32063698547426, 52.45574469551502, 642353208, 30, []uint64{642353209, 642353211, 642353210, 642353199, 642353197, 642353191, 642353202, 642353203}, "m4m21", []string{"m4m23", "m4m26", "m4m24", "m4jrf", "m4jrc", "m4jrb", "m4m20", "m4m22"}}, + {27.063974965189118, 98.53089079345227, 1003786962015236, 50, []uint64{1003786962015237, 1003786962015239, 1003786962015238, 1003786962015235, 1003786962015233, 1003786961665707, 1003786961665710, 1003786961665711}, "why1f", []string{"why44", "why45", "why1g", "why1e", "why1d", "why19", "why1c", "why41"}}, + {16.516027313831728, -69.11284966013407, 1639823321, 32, []uint64{1639823324, 1639823326, 1639823323, 1639823322, 1639823320, 1639823314, 1639823315, 1639823318}, "d6yvgq9", []string{"d6yvgqc", "d6yvgqf", "d6yvgqd", "d6yvgq6", "d6yvgq3", "d6yvgq2", "d6yvgq8", "d6yvgqb"}}, + {43.54279309313279, 177.32956760691013, 257670100205, 38, []uint64{257670100216, 257670100218, 257670100207, 257670100206, 257670100204, 257670100198, 257670100199, 257670100210}, "xzwpenx", []string{"xzwpenz", "xzwpeqb", "xzwpeq8", "xzwpeq2", "xzwpenr", "xzwpenq", "xzwpenw", "xzwpeny"}}, + {3.978418512182529, -13.106226725096377, 6961612, 24, []uint64{6961613, 6961615, 6961614, 6961611, 6961609, 6961603, 6961606, 6961607}, "e8wwtu0r", []string{"e8wwtu22", "e8wwtu28", "e8wwtu0x", "e8wwtu0w", "e8wwtu0q", "e8wwtu0n", "e8wwtu0p", "e8wwtu20"}}, + {39.318563038890716, -18.817117614518793, 477241994559, 40, []uint64{477241994602, 477241994688, 477241994645, 477241994644, 477241994558, 477241994556, 477241994557, 477241994600}, "ewfx", []string{"ex48", "ex4b", "ewfz", "ewfy", "ewfw", "ewfq", "ewfr", "ex42"}}, + {34.07140490452002, -176.15776579175144, 74124355274, 38, []uint64{74124355275, 74124355297, 74124355296, 74124355253, 74124355231, 74124355229, 74124355272, 74124355273}, "8n49z6t", []string{"8n49z6v", "8n49z6y", "8n49z6w", "8n49z6q", "8n49z6m", "8n49z6k", "8n49z6s", "8n49z6u"}}, + {74.28089062019717, -3.6835004517633934, 8515334217, 34, []uint64{8515334220, 8515334222, 8515334219, 8515334218, 8515334216, 8515334210, 8515334211, 8515334214}, "gvjqs4mv8", []string{"gvjqs4mvb", "gvjqs4mvc", "gvjqs4mv9", "gvjqs4mv3", "gvjqs4mv2", "gvjqs4mtr", "gvjqs4mtx", "gvjqs4mtz"}}, + {1.5398811870763751, -134.97174871346215, 309305090636, 40, []uint64{309305090637, 309305090639, 309305090638, 309305090635, 309305090633, 309305090627, 309305090630, 309305090631}, "9020b8kd", []string{"9020b8ke", "9020b8kg", "9020b8kf", "9020b8kc", "9020b8k9", "9020b8k3", "9020b8k6", "9020b8k7"}}, + {-55.754192028558464, 32.722356137863216, 9288509755019, 44, []uint64{9288509755022, 9288509755044, 9288509755041, 9288509755040, 9288509755018, 9288509755016, 9288509755017, 9288509755020}, "hwp6b", []string{"hwp70", "hwp71", "hwp6c", "hwp69", "hwp68", "hwp4x", "hwp4z", "hwp5p"}}, + {45.96721462525602, 124.45319454080891, 63571536, 26, []uint64{63571537, 63571539, 63571538, 63571527, 63571525, 63571183, 63571194, 63571195}, "yb0t8004", []string{"yb0t8005", "yb0t8007", "yb0t8006", "yb0t8003", "yb0t8001", "yb0mxbpc", "yb0mxbpf", "yb0mxbpg"}}, + {88.25630463956622, 160.7979748233338, 1045910, 20, []uint64{1045911, 1045917, 1045916, 1045913, 1045907, 1045905, 1045908, 1045909}, "zxdq54", []string{"zxdq55", "zxdq57", "zxdq56", "zxdq53", "zxdq51", "zxdq4c", "zxdq4f", "zxdq4g"}}, + {24.12949883806867, -171.3711304970202, 4684761832230, 44, []uint64{4684761832231, 4684761832237, 4684761832236, 4684761832233, 4684761832227, 4684761832225, 4684761832228, 4684761832229}, "8hq1k2ek", []string{"8hq1k2em", "8hq1k2et", "8hq1k2es", "8hq1k2ee", "8hq1k2e7", "8hq1k2e5", "8hq1k2eh", "8hq1k2ej"}}, + {-35.82162067106401, 96.05185235898534, 722706, 20, []uint64{722707, 722713, 722712, 722701, 722695, 722693, 722704, 722705}, "q1sk1w", []string{"q1sk1x", "q1sk1z", "q1sk1y", "q1sk1v", "q1sk1t", "q1sk1m", "q1sk1q", "q1sk1r"}}, + {54.50237545110576, -75.34047345815634, 121170962803, 38, []uint64{121170962806, 121170962812, 121170962809, 121170962808, 121170962802, 121170962800, 121170962801, 121170962804}, "f3dqj9fe", []string{"f3dqj9fs", "f3dqj9fu", "f3dqj9fg", "f3dqj9ff", "f3dqj9fd", "f3dqj9f6", "f3dqj9f7", "f3dqj9fk"}}, + {65.49217129037424, -69.37345599511173, 1867363, 22, []uint64{1867366, 1867372, 1867369, 1867368, 1867362, 1867360, 1867361, 1867364}, "f7wst67z9", []string{"f7wst67zc", "f7wst67zf", "f7wst67zd", "f7wst67z6", "f7wst67z3", "f7wst67z2", "f7wst67z8", "f7wst67zb"}}, + {-8.595969535323093, -163.0130204699817, 1487487058821, 44, []uint64{1487487058832, 1487487058834, 1487487058823, 1487487058822, 1487487058820, 1487487058734, 1487487058735, 1487487058746}, "2qkp4e1s", []string{"2qkp4e1t", "2qkp4e1v", "2qkp4e1u", "2qkp4e1g", "2qkp4e1e", "2qkp4e17", "2qkp4e1k", "2qkp4e1m"}}, + {-47.22898263471143, -39.34591559681576, 11911492, 26, []uint64{11911493, 11911495, 11911494, 11911491, 11911489, 11889643, 11889646, 11889647}, "5ps52dj", []string{"5ps52dm", "5ps52dq", "5ps52dn", "5ps529y", "5ps529v", "5ps529u", "5ps52dh", "5ps52dk"}}, + {89.28935823705979, 66.70732144801877, 244085105072653, 48, []uint64{244085105072664, 244085105072666, 244085105072655, 244085105072654, 244085105072652, 244085105072646, 244085105072647, 244085105072658}, "vrz7", []string{"vrzk", "vrzs", "vrze", "vrzd", "vrz6", "vrz4", "vrz5", "vrzh"}}, + {26.538929817674216, 19.23498602042673, 3300118006, 32, []uint64{3300118007, 3300118013, 3300118012, 3300118009, 3300118003, 3300118001, 3300118004, 3300118005}, "sktwvx", []string{"sktxj8", "sktxjb", "sktwvz", "sktwvy", "sktwvw", "sktwvq", "sktwvr", "sktxj2"}}, + {-61.356213197737816, 81.42296398244798, 149696215, 28, []uint64{149696386, 149696392, 149696221, 149696220, 149696214, 149696212, 149696213, 149696384}, "jv1fuy6q", []string{"jv1fuy6r", "jv1fuy6x", "jv1fuy6w", "jv1fuy6t", "jv1fuy6m", "jv1fuy6j", "jv1fuy6n", "jv1fuy6p"}}, + {15.90385880850954, -161.67735304468079, 4409853434, 34, []uint64{4409853435, 4409853777, 4409853776, 4409853765, 4409853423, 4409853421, 4409853432, 4409853433}, "86v42", []string{"86v48", "86v49", "86v43", "86v41", "86v40", "86ufp", "86ufr", "86ufx"}}, + {52.761879251906066, -49.74338921692106, 481911313, 30, []uint64{481911316, 481911318, 481911315, 481911314, 481911312, 481910970, 481910971, 481910974}, "fckshj", []string{"fckshn", "fckshq", "fckshm", "fckshk", "fckshh", "fcks5u", "fcks5v", "fcks5y"}}, + {-61.18124692913261, -106.31627411700902, 235551, 22, []uint64{235594, 235616, 235573, 235572, 235550, 235548, 235549, 235592}, "1th7uw", []string{"1th7ux", "1th7uz", "1th7uy", "1th7uv", "1th7ut", "1th7um", "1th7uq", "1th7ur"}}, + {86.31077640666626, 16.61732706624025, 235084800217711, 48, []uint64{235084800217722, 235084800217808, 235084800217797, 235084800217796, 235084800217710, 235084800217708, 235084800217709, 235084800217720}, "ur7g", []string{"ur7u", "urkh", "urk5", "urk4", "ur7f", "ur7d", "ur7e", "ur7s"}}, + {82.8108579308755, 74.81695360412047, 3656918, 22, []uint64{3656919, 3656925, 3656924, 3656921, 3656915, 3656913, 3656916, 3656917}, "vwtpn", []string{"vwtpq", "vwtpr", "vwtpp", "vwtnz", "vwtny", "vwtnv", "vwtpj", "vwtpm"}}, + {-55.04604752322484, 45.10761923692189, 37850739177, 36, []uint64{37850739180, 37850739182, 37850739179, 37850739178, 37850739176, 37850739170, 37850739171, 37850739174}, "jn0nf", []string{"jn0p4", "jn0p5", "jn0ng", "jn0ne", "jn0nd", "jn0n9", "jn0nc", "jn0p1"}}, + {-77.24613693362335, 166.441390733351, 2871797391, 32, []uint64{2871797402, 2871797424, 2871797413, 2871797412, 2871797390, 2871797388, 2871797389, 2871797400}, "pdq2e3", []string{"pdq2e6", "pdq2ed", "pdq2e9", "pdq2e8", "pdq2e2", "pdq2e0", "pdq2e1", "pdq2e4"}}, + {-2.0305764157965314, 84.12521562346956, 167623221, 28, []uint64{167623264, 167623266, 167623223, 167623222, 167623220, 167623198, 167623199, 167623242}, "mzeu6", []string{"mzeud", "mzeue", "mzeu7", "mzeu5", "mzeu4", "mzeu1", "mzeu3", "mzeu9"}}, + {28.050942976609804, -23.159879390588685, 7126996, 24, []uint64{7126997, 7126999, 7126998, 7126995, 7126993, 7126907, 7126910, 7126911}, "ekzx8fx2", []string{"ekzx8fx3", "ekzx8fx9", "ekzx8fx8", "ekzx8frx", "ekzx8frr", "ekzx8frp", "ekzx8fx0", "ekzx8fx1"}}, + {79.54026066078222, -117.62339473312022, 6136099, 24, []uint64{6136102, 6136108, 6136105, 6136104, 6136098, 6136096, 6136097, 6136100}, "cqhk", []string{"cqhm", "cqht", "cqhs", "cqhe", "cqh7", "cqh5", "cqhh", "cqhj"}}, + {74.1259051989473, 129.31687575078104, 1035136924, 30, []uint64{1035136925, 1035136927, 1035136926, 1035136923, 1035136921, 1035136915, 1035136918, 1035136919}, "yv5vw", []string{"yv5vy", "yv5vz", "yv5vx", "yv5vr", "yv5vq", "yv5vm", "yv5vt", "yv5vv"}}, + {39.94943051470182, 62.21744636103537, 3536929596568, 42, []uint64{3536929596569, 3536929596571, 3536929596570, 3536929596559, 3536929596557, 3536929596551, 3536929596562, 3536929596563}, "trh5rb9", []string{"trh5rbc", "trh5rbf", "trh5rbd", "trh5rb6", "trh5rb3", "trh5rb2", "trh5rb8", "trh5rbb"}}, + {-83.2810615375056, -150.51600133959437, 639543267, 36, []uint64{639543270, 639543276, 639543273, 639543272, 639543266, 639543264, 639543265, 639543268}, "09hyn", []string{"09hyq", "09hyr", "09hyp", "09hvz", "09hvy", "09hvv", "09hyj", "09hym"}}, + {5.2170394824352115, -112.2202747859701, 310724499, 30, []uint64{310724502, 310724508, 310724505, 310724504, 310724498, 310724496, 310724497, 310724500}, "98bj", []string{"98bn", "98bq", "98bm", "98bk", "98bh", "92zu", "92zv", "92zy"}}, + {-39.71390836640785, 53.044263181480346, 623484, 20, []uint64{623485, 623487, 623486, 623483, 623481, 623475, 623478, 623479}, "m0vwp4", []string{"m0vwp5", "m0vwp7", "m0vwp6", "m0vwp3", "m0vwp1", "m0vwnc", "m0vwnf", "m0vwng"}}, + {65.29894098583463, -51.21077725084615, 31099533158, 36, []uint64{31099533159, 31099533165, 31099533164, 31099533161, 31099533155, 31099533153, 31099533156, 31099533157}, "fgee", []string{"fges", "fgeu", "fgeg", "fgef", "fged", "fge6", "fge7", "fgek"}}, + {-67.59021334868157, -6.111576510831611, 184022623, 30, []uint64{184022794, 184022816, 184022645, 184022644, 184022622, 184022620, 184022621, 184022792}, "5ggxkz", []string{"5ggxsb", "5ggxt0", "5ggxmp", "5ggxmn", "5ggxky", "5ggxkw", "5ggxkx", "5ggxs8"}}, + {-63.24835966409955, 12.894926735723857, 2224425167, 32, []uint64{2224425178, 2224425200, 2224425189, 2224425188, 2224425166, 2224425164, 2224425165, 2224425176}, "hkc0", []string{"hkc1", "hkc3", "hkc2", "hk9r", "hk9p", "hk8z", "hkbb", "hkbc"}}, + {-28.93263516927255, 4.998972816509195, 2434716137, 32, []uint64{2434716140, 2434716142, 2434716139, 2434716138, 2434716136, 2434716130, 2434716131, 2434716134}, "k4ge", []string{"k4gs", "k4gu", "k4gg", "k4gf", "k4gd", "k4g6", "k4g7", "k4gk"}}, + {63.292504958211794, -7.819741671730327, 132926571530, 38, []uint64{132926571531, 132926571553, 132926571552, 132926570869, 132926570847, 132926570845, 132926571528, 132926571529}, "gg62", []string{"gg63", "gg69", "gg68", "gg4x", "gg4r", "gg4p", "gg60", "gg61"}}, + {12.516648525983314, 35.59185881383016, 214969303639280, 48, []uint64{214969303639281, 214969303639283, 214969303639282, 214969303639271, 214969303639269, 214969303639247, 214969303639258, 214969303639259}, "sf1r1", []string{"sf1r3", "sf1r6", "sf1r4", "sf1qf", "sf1qc", "sf1qb", "sf1r0", "sf1r2"}}, + {-58.34722988159774, -40.8377048674738, 195054476622664, 50, []uint64{195054476622665, 195054476622667, 195054476622666, 195054476622623, 195054476622621, 195054476622615, 195054476622658, 195054476622659}, "5jdund", []string{"5jdune", "5jdung", "5jdunf", "5jdunc", "5jdun9", "5jdun3", "5jdun6", "5jdun7"}}, + {-53.375826035349746, 15.87707065450374, 9181064708979, 44, []uint64{9181064708982, 9181064708988, 9181064708985, 9181064708984, 9181064708978, 9181064708976, 9181064708977, 9181064708980}, "hqe2371r6", []string{"hqe2371rd", "hqe2371re", "hqe2371r7", "hqe2371r5", "hqe2371r4", "hqe2371r1", "hqe2371r3", "hqe2371r9"}}, + {-30.51260803711193, -93.6856165631907, 29739340177, 38, []uint64{29739340180, 29739340182, 29739340179, 29739340178, 29739340176, 29739340090, 29739340091, 29739340094}, "3ft6kjk5", []string{"3ft6kjkh", "3ft6kjkk", "3ft6kjk7", "3ft6kjk6", "3ft6kjk4", "3ft6kj7f", "3ft6kj7g", "3ft6kj7u"}}, + {88.89583722635871, -15.464477998126, 547441889781, 40, []uint64{547441890464, 547441890466, 547441889783, 547441889782, 547441889780, 547441889758, 547441889759, 547441890442}, "gxv18n", []string{"gxv18p", "gxv18r", "gxv18q", "gxv18m", "gxv18j", "gxucxv", "gxucxy", "gxucxz"}}, + {-55.90329429723715, 139.246586694906, 743198520839, 40, []uint64{743198520850, 743198520856, 743198520845, 743198520844, 743198520838, 743198520836, 743198520837, 743198520848}, "pn51bxh7", []string{"pn51bxhk", "pn51bxhs", "pn51bxhe", "pn51bxhd", "pn51bxh6", "pn51bxh4", "pn51bxh5", "pn51bxhh"}}, + {-80.76815602436545, -23.658945771341706, 687541411, 32, []uint64{687541414, 687541420, 687541417, 687541416, 687541410, 687541408, 687541409, 687541412}, "53xht8s56", []string{"53xht8s5d", "53xht8s5e", "53xht8s57", "53xht8s55", "53xht8s54", "53xht8s51", "53xht8s53", "53xht8s59"}}, + {46.91107662793365, -123.990080550313, 369867213, 30, []uint64{369867224, 369867226, 369867215, 369867214, 369867212, 369867206, 369867207, 369867218}, "c0rffe", []string{"c0rffs", "c0rffu", "c0rffg", "c0rfff", "c0rffd", "c0rff6", "c0rff7", "c0rffk"}}, + {84.51393844238191, 102.10911878170737, 4123035556, 32, []uint64{4123035557, 4123035559, 4123035558, 4123035555, 4123035553, 4123035531, 4123035534, 4123035535}, "yr08", []string{"yr09", "yr0c", "yr0b", "yqbz", "yqbx", "yqbr", "yr02", "yr03"}}, + {37.4739258234913, -63.35795714726554, 1729739341, 32, []uint64{1729739352, 1729739354, 1729739343, 1729739342, 1729739340, 1729739334, 1729739335, 1729739346}, "dwdvnm", []string{"dwdvnq", "dwdvnw", "dwdvnt", "dwdvns", "dwdvnk", "dwdvnh", "dwdvnj", "dwdvnn"}}, + {-17.477039573292146, -161.42055396182695, 91105800602439, 50, []uint64{91105800602450, 91105800602456, 91105800602445, 91105800602444, 91105800602438, 91105800602436, 91105800602437, 91105800602448}, "2kvh", []string{"2kvj", "2kvm", "2kvk", "2kv7", "2kv5", "2kug", "2kuu", "2kuv"}}, + {28.645846760395216, 167.37632001325255, 262140199169190, 48, []uint64{262140199169191, 262140199169197, 262140199169196, 262140199169193, 262140199169187, 262140199169185, 262140199169188, 262140199169189}, "xtp4bwzd", []string{"xtp4bwze", "xtp4bwzg", "xtp4bwzf", "xtp4bwzc", "xtp4bwz9", "xtp4bwz3", "xtp4bwz6", "xtp4bwz7"}}, + {-12.295329115305336, -168.92317014414584, 5633822773168, 46, []uint64{5633822773169, 5633822773171, 5633822773170, 5633822773159, 5633822773157, 5633822773135, 5633822773146, 5633822773147}, "2jzfh19y", []string{"2jzfh19z", "2jzfh1dp", "2jzfh1dn", "2jzfh1dj", "2jzfh19v", "2jzfh19t", "2jzfh19w", "2jzfh19x"}}, + {-3.2729209771205348, -110.83517014968675, 2099013544, 34, []uint64{2099013545, 2099013547, 2099013546, 2099013375, 2099013373, 2099013367, 2099013538, 2099013539}, "3x3jmuh", []string{"3x3jmuk", "3x3jmum", "3x3jmuj", "3x3jmgv", "3x3jmgu", "3x3jmgg", "3x3jmu5", "3x3jmu7"}}, + {-72.24642905786459, 123.44959869148443, 43867856247, 36, []uint64{43867899938, 43867899944, 43867856253, 43867856252, 43867856246, 43867856244, 43867856245, 43867899936}, "nepucpv", []string{"nepv10j", "nepv10n", "nepucpy", "nepucpw", "nepucpt", "nepucps", "nepucpu", "nepv10h"}}, + {64.02255391926155, 17.298908849596046, 220090895, 28, []uint64{220090906, 220090928, 220090917, 220090916, 220090894, 220090892, 220090893, 220090904}, "u7kk1wu", []string{"u7kk1xh", "u7kk1xj", "u7kk1wv", "u7kk1wt", "u7kk1ws", "u7kk1we", "u7kk1wg", "u7kk1x5"}}, + {-72.17798857565504, 145.96544144366635, 710601848, 30, []uint64{710601849, 710601851, 710601850, 710601839, 710601837, 710601831, 710601842, 710601843}, "p5pv", []string{"p5py", "p70n", "p70j", "p70h", "p5pu", "p5ps", "p5pt", "p5pw"}}, + {37.499658643850125, 153.70264241367113, 3987943857, 32, []uint64{3987943860, 3987943862, 3987943859, 3987943858, 3987943856, 3987943834, 3987943835, 3987943838}, "xqtm3d", []string{"xqtm3e", "xqtm3g", "xqtm3f", "xqtm3c", "xqtm39", "xqtm33", "xqtm36", "xqtm37"}}, + {44.21672837618098, -117.19668125161843, 319309, 20, []uint64{319320, 319322, 319311, 319310, 319308, 319302, 319303, 319314}, "9rue", []string{"9rus", "9ruu", "9rug", "9ruf", "9rud", "9ru6", "9ru7", "9ruk"}}, + {-49.31155324325664, -6.862262636934247, 821098967700, 42, []uint64{821098967701, 821098967703, 821098967702, 821098967699, 821098967697, 821098967611, 821098967614, 821098967615}, "5z5p7z5", []string{"5z5p7z7", "5z5p7zk", "5z5p7zh", "5z5p7yu", "5z5p7yg", "5z5p7yf", "5z5p7z4", "5z5p7z6"}}, + {23.669454521390435, 38.30196415580576, 13011790, 24, []uint64{13011791, 13011813, 13011812, 13011809, 13011787, 13011785, 13011788, 13011789}, "su5nx", []string{"su5nz", "su5qb", "su5q8", "su5q2", "su5nr", "su5nq", "su5nw", "su5ny"}}, + {-31.29471896632458, -99.81525590522506, 115459420, 30, []uint64{115459421, 115459423, 115459422, 115459419, 115459417, 115459411, 115459414, 115459415}, "3f3jb", []string{"3f3n0", "3f3n1", "3f3jc", "3f3j9", "3f3j8", "3f2vx", "3f2vz", "3f2yp"}}, + {12.981035213218988, -111.48069493329967, 5155201837322, 44, []uint64{5155201837323, 5155201837345, 5155201837344, 5155201837173, 5155201837151, 5155201837149, 5155201837320, 5155201837321}, "9d29z5nh", []string{"9d29z5nj", "9d29z5nm", "9d29z5nk", "9d29z5n7", "9d29z5n5", "9d29z5jg", "9d29z5ju", "9d29z5jv"}}, + {-32.343945296030135, 84.63006595863408, 2611043837, 32, []uint64{2611218600, 2611218602, 2611043839, 2611043838, 2611043836, 2611043830, 2611043831, 2611218594}, "mfhp", []string{"mfk0", "mfk2", "mfhr", "mfhq", "mfhn", "mf5y", "mf5z", "mf7b"}}, + {-60.7604208503617, -123.02501271176152, 836033, 24, []uint64{836036, 836038, 836035, 836034, 836032, 835946, 835947, 835950}, "1m0w2", []string{"1m0w8", "1m0w9", "1m0w3", "1m0w1", "1m0w0", "1m0qp", "1m0qr", "1m0qx"}}, + {78.11859572235699, -138.4705428344314, 373519664139, 40, []uint64{373519664142, 373519664164, 373519664161, 373519664160, 373519664138, 373519664136, 373519664137, 373519664140}, "bvvs3j0", []string{"bvvs3j2", "bvvs3j3", "bvvs3j1", "bvvs3hc", "bvvs3hb", "bvvs2uz", "bvvs2vp", "bvvs2vr"}}, + {-84.01815565821016, -72.85580276587278, 564807601489, 42, []uint64{564807601492, 564807601494, 564807601491, 564807601490, 564807601488, 564807600122, 564807600123, 564807600126}, "43h4", []string{"43h5", "43h7", "43h6", "43h3", "43h1", "435c", "435f", "435g"}}, + {-42.18664566412917, 68.31441744184119, 165425481740, 38, []uint64{165425481741, 165425481743, 165425481742, 165425481739, 165425481737, 165425481731, 165425481734, 165425481735}, "m888", []string{"m889", "m88c", "m88b", "m82z", "m82x", "m82r", "m882", "m883"}}, + {-89.2201600571716, 64.34014759467388, 2292425029, 32, []uint64{2292425040, 2292425042, 2292425031, 2292425030, 2292425028, 2292419566, 2292419567, 2292419578}, "j2ju2jg", []string{"j2ju2n5", "j2ju2nh", "j2ju2ju", "j2ju2js", "j2ju2je", "j2ju2jd", "j2ju2jf", "j2ju2n4"}}, + {69.47284221306654, -129.49393294550828, 1581552726219, 42, []uint64{1581552726222, 1581552726244, 1581552726241, 1581552726240, 1581552726218, 1581552726216, 1581552726217, 1581552726220}, "ch7gjr1", []string{"ch7gjr3", "ch7gjr6", "ch7gjr4", "ch7gjqf", "ch7gjqc", "ch7gjqb", "ch7gjr0", "ch7gjr2"}}, + {-85.35385677265003, -75.9232703889138, 133572, 20, []uint64{133573, 133575, 133574, 133571, 133569, 133483, 133486, 133487}, "42f4", []string{"42f5", "42f7", "42f6", "42f3", "42f1", "42cc", "42cf", "42cg"}}, + {55.26709565607598, 109.07341772287327, 4139728572472, 42, []uint64{4139728572473, 4139728572475, 4139728572474, 4139728572463, 4139728572461, 4139728572455, 4139728572466, 4139728572467}, "y3vd6j0f4", []string{"y3vd6j0f6", "y3vd6j0f7", "y3vd6j0f5", "y3vd6j0cg", "y3vd6j0cf", "y3vd6j0cc", "y3vd6j0f1", "y3vd6j0f3"}}, + {-65.10944831746747, 173.09120321265073, 767718306400119, 50, []uint64{767718306402850, 767718306402856, 767718306400125, 767718306400124, 767718306400118, 767718306400116, 767718306400117, 767718306402848}, "pu7jdg1j", []string{"pu7jdg1n", "pu7jdg1q", "pu7jdg1m", "pu7jdg1k", "pu7jdg1h", "pu7jdg0u", "pu7jdg0v", "pu7jdg0y"}}, + {77.1510746438289, -98.87819303915603, 388412, 20, []uint64{388413, 388415, 388414, 388411, 388409, 388403, 388406, 388407}, "cv9w", []string{"cv9x", "cv9z", "cv9y", "cv9v", "cv9t", "cv9m", "cv9q", "cv9r"}}, + {-79.87604802890564, 80.45747224857041, 582322063, 30, []uint64{582322074, 582322096, 582322085, 582322084, 582322062, 582322060, 582322061, 582322072}, "jcc1wg", []string{"jcc1wu", "jcc1xh", "jcc1x5", "jcc1x4", "jcc1wf", "jcc1wd", "jcc1we", "jcc1ws"}}, + {72.64725302482839, 154.0900415564538, 69466344096445, 46, []uint64{69466344096488, 69466344096490, 69466344096447, 69466344096446, 69466344096444, 69466344096438, 69466344096439, 69466344096482}, "zkvt63hby", []string{"zkvt63hcn", "zkvt63hcp", "zkvt63hbz", "zkvt63hbx", "zkvt63hbw", "zkvt63hbt", "zkvt63hbv", "zkvt63hcj"}}, + {-12.696573822919177, 25.752508793055284, 157652848, 28, []uint64{157652849, 157652851, 157652850, 157652839, 157652837, 157652815, 157652826, 157652827}, "ktdrf0", []string{"ktdrf1", "ktdrf3", "ktdrf2", "ktdrdr", "ktdrdp", "ktdr9z", "ktdrcb", "ktdrcc"}}, + {-58.372965140702945, -46.427555730217136, 2615410580, 34, []uint64{2615410581, 2615410583, 2615410582, 2615410579, 2615410577, 2615410491, 2615410494, 2615410495}, "4vwgz", []string{"4vwup", "4vxh0", "4vx5b", "4vx58", "4vwgx", "4vwgw", "4vwgy", "4vwun"}}, + {-77.51605558010488, -39.46205993273182, 2754603012, 34, []uint64{2754603013, 2754603015, 2754603014, 2754603011, 2754603009, 2754601643, 2754601646, 2754601647}, "545zn08", []string{"545zn0b", "545zn0c", "545zn09", "545zn03", "545zn02", "545zjbr", "545zjbx", "545zjbz"}}, + {64.22843519374874, 168.8660595776746, 1031249, 20, []uint64{1031252, 1031254, 1031251, 1031250, 1031248, 1029882, 1029883, 1029886}, "zg2j6sktb", []string{"zg2j6skw0", "zg2j6skw1", "zg2j6sktc", "zg2j6skt9", "zg2j6skt8", "zg2j6skmx", "zg2j6skmz", "zg2j6skqp"}}, + {79.0463185897388, -113.59427102409609, 1534599, 22, []uint64{1534610, 1534616, 1534605, 1534604, 1534598, 1534596, 1534597, 1534608}, "cqp1xjgn", []string{"cqp1xjgp", "cqp1xjgr", "cqp1xjgq", "cqp1xjgm", "cqp1xjgj", "cqp1xjfv", "cqp1xjfy", "cqp1xjfz"}}, + {-30.438746385085324, -38.73377483035438, 245625729483, 40, []uint64{245625729486, 245625729508, 245625729505, 245625729504, 245625729482, 245625729480, 245625729481, 245625729484}, "74s6", []string{"74s7", "74se", "74sd", "74s9", "74s3", "74s1", "74s4", "74s5"}}, + {-15.531373811478275, 127.90520092149382, 12266764676, 34, []uint64{12266764677, 12266764679, 12266764678, 12266764675, 12266764673, 12266764587, 12266764590, 12266764591}, "qv4zws9", []string{"qv4zwsc", "qv4zwsf", "qv4zwsd", "qv4zws6", "qv4zws3", "qv4zws2", "qv4zws8", "qv4zwsb"}}, + {18.29654192059388, -134.0216673379764, 4801674, 24, []uint64{4801675, 4801697, 4801696, 4801013, 4800991, 4800989, 4801672, 4801673}, "9528n6", []string{"9528n7", "9528ne", "9528nd", "9528n9", "9528n3", "9528n1", "9528n4", "9528n5"}}, + {53.708011561160674, -144.45805706319516, 1356941, 22, []uint64{1356952, 1356954, 1356943, 1356942, 1356940, 1356934, 1356935, 1356946}, "bc938c", []string{"bc938f", "bc9394", "bc9391", "bc9390", "bc938b", "bc9388", "bc9389", "bc938d"}}, + {-31.18940127105452, -74.4964154815825, 54491394617963, 48, []uint64{54491394617966, 54491394618052, 54491394618049, 54491394618048, 54491394617962, 54491394617960, 54491394617961, 54491394617964}, "667n8f1ke", []string{"667n8f1kg", "667n8f1ku", "667n8f1ks", "667n8f1kk", "667n8f1k7", "667n8f1k6", "667n8f1kd", "667n8f1kf"}}, + {42.45727528343559, -46.17754962982144, 28580979516242, 46, []uint64{28580979516243, 28580979516249, 28580979516248, 28580979516237, 28580979516231, 28580979516229, 28580979516240, 28580979516241}, "dzx1t", []string{"dzx1v", "dzx1y", "dzx1w", "dzx1q", "dzx1m", "dzx1k", "dzx1s", "dzx1u"}}, + {-39.40071171114687, 93.71696578022966, 3025661121328, 42, []uint64{3025661121329, 3025661121331, 3025661121330, 3025661121319, 3025661121317, 3025661121295, 3025661121306, 3025661121307}, "q0fxue6d5", []string{"q0fxue6d7", "q0fxue6dk", "q0fxue6dh", "q0fxue69u", "q0fxue69g", "q0fxue69f", "q0fxue6d4", "q0fxue6d6"}}, + {24.91775436205353, -99.96265438679256, 21584079359143, 46, []uint64{21584079359154, 21584079359160, 21584079359149, 21584079359148, 21584079359142, 21584079359140, 21584079359141, 21584079359152}, "9u2vv21", []string{"9u2vv23", "9u2vv26", "9u2vv24", "9u2vtrf", "9u2vtrc", "9u2vtrb", "9u2vv20", "9u2vv22"}}, + {19.298949687858098, 45.544050830465494, 13507032121, 34, []uint64{13507032124, 13507032126, 13507032123, 13507032122, 13507032120, 13507032114, 13507032115, 13507032118}, "t52mu3k", []string{"t52mu3s", "t52mu3t", "t52mu3m", "t52mu3j", "t52mu3h", "t52mu35", "t52mu37", "t52mu3e"}}, + {33.96083004727551, -6.520514455653057, 116957999, 28, []uint64{116958010, 116958096, 116958085, 116958084, 116957998, 116957996, 116957997, 116958008}, "ey535w7u", []string{"ey535w7v", "ey535wkj", "ey535wkh", "ey535wk5", "ey535w7g", "ey535w7e", "ey535w7s", "ey535w7t"}}, + {-74.16697093367111, -90.33059232085361, 3228607887744, 46, []uint64{3228607887745, 3228607887747, 3228607887746, 3228607887575, 3228607887573, 3228607887487, 3228607887658, 3228607887659}, "1fzf", []string{"1fzg", "44b5", "44b4", "44b1", "1fzc", "1fz9", "1fzd", "1fze"}}, + {42.86423475567426, -120.84571384813171, 20896379209, 36, []uint64{20896379212, 20896379214, 20896379211, 20896379210, 20896379208, 20896379202, 20896379203, 20896379206}, "9rd5f", []string{"9rdh4", "9rdh5", "9rd5g", "9rd5e", "9rd5d", "9rd59", "9rd5c", "9rdh1"}}, + {-47.65776314608593, -144.91350799467182, 8592875467190, 48, []uint64{8592875467191, 8592875467197, 8592875467196, 8592875467193, 8592875467187, 8592875467185, 8592875467188, 8592875467189}, "0z8b", []string{"0z8c", "0z91", "0z90", "0z3p", "0z2z", "0z2x", "0z88", "0z89"}}, + {-75.90787004877347, 175.36248497996712, 188774723022945, 48, []uint64{188774723022948, 188774723022950, 188774723022947, 188774723022946, 188774723022944, 188774723022922, 188774723022923, 188774723022926}, "pfs8nmrhd", []string{"pfs8nmrhf", "pfs8nmrhg", "pfs8nmrhe", "pfs8nmrh7", "pfs8nmrh6", "pfs8nmrh3", "pfs8nmrh9", "pfs8nmrhc"}}, + {-22.830696296201495, -115.39127406958141, 1742710879, 34, []uint64{1742711050, 1742711072, 1742710901, 1742710900, 1742710878, 1742710876, 1742710877, 1742711048}, "37vyn5y9", []string{"37vyn5yd", "37vyn5yf", "37vyn5yc", "37vyn5yb", "37vyn5y8", "37vyn5y2", "37vyn5y3", "37vyn5y6"}}, + {33.37176654508221, 104.42608279595152, 15358809200, 34, []uint64{15358809201, 15358809203, 15358809202, 15358809191, 15358809189, 15358809167, 15358809178, 15358809179}, "wmfmb7", []string{"wmfmbk", "wmfmbs", "wmfmbe", "wmfmbd", "wmfmb6", "wmfmb4", "wmfmb5", "wmfmbh"}}, + {59.5252899324114, -65.79121003003092, 1885331, 22, []uint64{1885334, 1885340, 1885337, 1885336, 1885330, 1885328, 1885329, 1885332}, "fd94wuhrc", []string{"fd94wuk21", "fd94wuk24", "fd94wuhrf", "fd94wuhrd", "fd94wuhr9", "fd94wuhr8", "fd94wuhrb", "fd94wuk20"}}, + {28.75816455424136, -125.67375420086319, 1312707074946, 42, []uint64{1312707074947, 1312707074953, 1312707074952, 1312707074781, 1312707074775, 1312707074773, 1312707074944, 1312707074945}, "9jnes5r0", []string{"9jnes5r1", "9jnes5r3", "9jnes5r2", "9jnes5pr", "9jnes5pp", "9jnes5nz", "9jnes5qb", "9jnes5qc"}}, + {-18.35868490242865, -143.43492887604225, 23696704, 28, []uint64{23696705, 23696707, 23696706, 23696663, 23696661, 23674815, 23674858, 23674859}, "2udp81cu", []string{"2udp81cv", "2udp81fj", "2udp81fh", "2udp81f5", "2udp81cg", "2udp81ce", "2udp81cs", "2udp81ct"}}, + {-71.90162878790579, -17.1657420155243, 181598582, 30, []uint64{181598583, 181598589, 181598588, 181598585, 181598579, 181598577, 181598580, 181598581}, "5e5ycqs", []string{"5e5ycqu", "5e5ycqv", "5e5ycqt", "5e5ycqm", "5e5ycqk", "5e5ycq7", "5e5ycqe", "5e5ycqg"}}, + {-22.932991005596705, -14.980448714006343, 997668484, 32, []uint64{997668485, 997668487, 997668486, 997668483, 997668481, 997668395, 997668398, 997668399}, "7evme", []string{"7evmg", "7evmu", "7evms", "7evmk", "7evm7", "7evm6", "7evmd", "7evmf"}}, + {-45.59819184950902, 126.78222319403903, 11265126650, 34, []uint64{11265126651, 11265126993, 11265126992, 11265126981, 11265126639, 11265126637, 11265126648, 11265126649}, "nzfhsgpg", []string{"nzfhsgpu", "nzfht50h", "nzfht505", "nzfht504", "nzfhsgpf", "nzfhsgpd", "nzfhsgpe", "nzfhsgps"}}, + {-20.184031740005594, 65.88100306337583, 43067460667725, 46, []uint64{43067460667736, 43067460667738, 43067460667727, 43067460667726, 43067460667724, 43067460667718, 43067460667719, 43067460667730}, "mkqv5jt", []string{"mkqv5jv", "mkqv5jy", "mkqv5jw", "mkqv5jq", "mkqv5jm", "mkqv5jk", "mkqv5js", "mkqv5ju"}}, + {7.663020876701907, -159.7670851572475, 285537755234916, 50, []uint64{285537755234917, 285537755234919, 285537755234918, 285537755234915, 285537755234913, 285537755234891, 285537755234894, 285537755234895}, "83q7s7j", []string{"83q7s7m", "83q7s7q", "83q7s7n", "83q7s6y", "83q7s6v", "83q7s6u", "83q7s7h", "83q7s7k"}}, + {-67.89475354520255, 33.63260863273172, 2259135373920, 42, []uint64{2259135373921, 2259135373923, 2259135373922, 2259135373879, 2259135373877, 2259135373855, 2259135373898, 2259135373899}, "hezv", []string{"hezy", "hgbn", "hgbj", "hgbh", "hezu", "hezs", "hezt", "hezw"}}, + {14.562672667656429, -103.0898932256896, 1232435, 22, []uint64{1232438, 1232444, 1232441, 1232440, 1232434, 1232432, 1232433, 1232436}, "9dwdy5h", []string{"9dwdy5k", "9dwdy5m", "9dwdy5j", "9dwdy4v", "9dwdy4u", "9dwdy4g", "9dwdy55", "9dwdy57"}}, + {43.08026786199912, -34.51448233437259, 1836792369, 32, []uint64{1836792372, 1836792374, 1836792371, 1836792370, 1836792368, 1836792346, 1836792347, 1836792350}, "epxmn", []string{"epxmq", "epxmr", "epxmp", "epxkz", "epxky", "epxkv", "epxmj", "epxmm"}}, + {-53.750874433222634, 106.88963793768198, 10855744, 24, []uint64{10855745, 10855747, 10855746, 10855703, 10855701, 10850239, 10850282, 10850283}, "nqkn", []string{"nqkp", "nqkr", "nqkq", "nqkm", "nqkj", "nq7v", "nq7y", "nq7z"}}, + {40.421712039154954, -171.70125880208798, 4656657894, 34, []uint64{4656657895, 4656657901, 4656657900, 4656657897, 4656657891, 4656657889, 4656657892, 4656657893}, "8pjv", []string{"8pjy", "8pnn", "8pnj", "8pnh", "8pju", "8pjs", "8pjt", "8pjw"}}, + {87.9741653870442, 110.70651530736359, 16120203, 24, []uint64{16120206, 16120228, 16120225, 16120224, 16120202, 16120200, 16120201, 16120204}, "yrwsrpnn8", []string{"yrwsrpnnb", "yrwsrpnnc", "yrwsrpnn9", "yrwsrpnn3", "yrwsrpnn2", "yrwsrpjyr", "yrwsrpjyx", "yrwsrpjyz"}}, + {-61.79679193921038, 136.15737215147317, 2822184, 22, []uint64{2822185, 2822187, 2822186, 2819455, 2819453, 2819447, 2822178, 2822179}, "pj0b6q4z", []string{"pj0b6q6b", "pj0b6q70", "pj0b6q5p", "pj0b6q5n", "pj0b6q4y", "pj0b6q4w", "pj0b6q4x", "pj0b6q68"}}, + {-27.720838142791763, 128.6164103928313, 193048791531, 38, []uint64{193048791534, 193048791876, 193048791873, 193048791872, 193048791530, 193048791528, 193048791529, 193048791532}, "qg56", []string{"qg57", "qg5e", "qg5d", "qg59", "qg53", "qg51", "qg54", "qg55"}}, + {-70.93596496220562, -98.81080940907123, 771979, 24, []uint64{771982, 772004, 772001, 772000, 771978, 771976, 771977, 771980}, "1g3srw8", []string{"1g3srwb", "1g3srwc", "1g3srw9", "1g3srw3", "1g3srw2", "1g3srqr", "1g3srqx", "1g3srqz"}}, + {65.31687980676361, -102.44960634040763, 6138982606, 34, []uint64{6138982607, 6138982629, 6138982628, 6138982625, 6138982603, 6138982601, 6138982604, 6138982605}, "cex5s", []string{"cex5u", "cex5v", "cex5t", "cex5m", "cex5k", "cex57", "cex5e", "cex5g"}}, + {-69.64826342036395, 1.2293770916294307, 34713074367, 36, []uint64{34713074410, 34713096256, 34713096213, 34713096212, 34713074366, 34713074364, 34713074365, 34713074408}, "h58gg", []string{"h58u5", "h58uh", "h58gu", "h58gs", "h58ge", "h58gd", "h58gf", "h58u4"}}, + {-80.29436621474451, 19.484091921010986, 141785239777172, 48, []uint64{141785239777173, 141785239777175, 141785239777174, 141785239777171, 141785239777169, 141785239777083, 141785239777086, 141785239777087}, "h3tz5qgg", []string{"h3tz5qgu", "h3tz5quh", "h3tz5qu5", "h3tz5qu4", "h3tz5qgf", "h3tz5qgd", "h3tz5qge", "h3tz5qgs"}}, + {-59.935024970560335, 110.13021239862428, 43233736, 26, []uint64{43233737, 43233739, 43233738, 43233695, 43233693, 43233687, 43233730, 43233731}, "nmq7", []string{"nmqk", "nmqs", "nmqe", "nmqd", "nmq6", "nmq4", "nmq5", "nmqh"}}, + {-52.01188972788805, -118.52550811755646, 3570216, 26, []uint64{3570217, 3570219, 3570218, 3567487, 3567485, 3567479, 3570210, 3570211}, "1qg8ngs2g", []string{"1qg8ngs35", "1qg8ngs3h", "1qg8ngs2u", "1qg8ngs2s", "1qg8ngs2e", "1qg8ngs2d", "1qg8ngs2f", "1qg8ngs34"}}, + {3.336492572692805, -53.691237858322, 6460135, 24, []uint64{6460146, 6460152, 6460141, 6460140, 6460134, 6460132, 6460133, 6460144}, "db9ffp", []string{"db9g40", "db9g42", "db9ffr", "db9ffq", "db9ffn", "db9fcy", "db9fcz", "db9g1b"}}, + {72.63022780179563, -110.0600653802685, 101030738262, 38, []uint64{101030738263, 101030738269, 101030738268, 101030738265, 101030738259, 101030738257, 101030738260, 101030738261}, "csctptb", []string{"csctpw0", "csctpw1", "csctptc", "csctpt9", "csctpt8", "csctpmx", "csctpmz", "csctpqp"}}, + {20.31896882216097, -17.544123897270765, 439725, 20, []uint64{439736, 439738, 439727, 439726, 439724, 439718, 439719, 439730}, "eeee8", []string{"eeeeb", "eeeec", "eeee9", "eeee3", "eeee2", "eee7r", "eee7x", "eee7z"}}, + {-40.64651413014508, -54.57567493070384, 3315215, 24, []uint64{3315226, 3315248, 3315237, 3315236, 3315214, 3315212, 3315213, 3315224}, "6bc0y", []string{"6bc1n", "6bc1p", "6bc0z", "6bc0x", "6bc0w", "6bc0t", "6bc0v", "6bc1j"}}, + {37.30573052212003, 162.66241667163558, 16046236539, 34, []uint64{16046236542, 16046236628, 16046236625, 16046236624, 16046236538, 16046236536, 16046236537, 16046236540}, "xwesjr", []string{"xwesm2", "xwesm8", "xwesjx", "xwesjw", "xwesjq", "xwesjn", "xwesjp", "xwesm0"}}, + {77.55238630392705, 156.4056396210508, 4145030, 22, []uint64{4145031, 4145037, 4145036, 4145033, 4145027, 4145025, 4145028, 4145029}, "zmz1pjg", []string{"zmz1pn5", "zmz1pnh", "zmz1pju", "zmz1pjs", "zmz1pje", "zmz1pjd", "zmz1pjf", "zmz1pn4"}}, + {-65.30935569398571, -0.037403652851935476, 783264723, 32, []uint64{783264726, 783264732, 783264729, 783264728, 783264722, 783264720, 783264721, 783264724}, "5ururnuc", []string{"5ururnuf", "5ururnv4", "5ururnv1", "5ururnv0", "5ururnub", "5ururnu8", "5ururnu9", "5ururnud"}}, + {47.2081238439423, 157.081646053819, 267024303101, 38, []uint64{267024303784, 267024303786, 267024303103, 267024303102, 267024303100, 267024303094, 267024303095, 267024303778}, "z2rsw3zr7", []string{"z2rsw3zre", "z2rsw3zrs", "z2rsw3zrk", "z2rsw3zrh", "z2rsw3zr5", "z2rsw3zr4", "z2rsw3zr6", "z2rsw3zrd"}}, + {-88.54165911173914, -152.9253255787189, 33672, 22, []uint64{33673, 33675, 33674, 33503, 33501, 33495, 33666, 33667}, "0872217p", []string{"087221e0", "087221e2", "0872217r", "0872217q", "0872217n", "0872216y", "0872216z", "087221db"}}, + {6.229419271607171, -58.05612517189003, 432809993316821, 50, []uint64{432809993317504, 432809993317506, 432809993316823, 432809993316822, 432809993316820, 432809993316734, 432809993316735, 432809993317418}, "d9neqyhb", []string{"d9neqyhc", "d9neqyj1", "d9neqyj0", "d9neqvvp", "d9neqvuz", "d9neqvux", "d9neqyh8", "d9neqyh9"}}, + {-17.643603029369842, -25.717501422506757, 66756113081443, 48, []uint64{66756113081446, 66756113081452, 66756113081449, 66756113081448, 66756113081442, 66756113081440, 66756113081441, 66756113081444}, "7kve", []string{"7kvs", "7kvu", "7kvg", "7kvf", "7kvd", "7kv6", "7kv7", "7kvk"}}, + {16.783361657220667, 162.754666343797, 64630516691337, 46, []uint64{64630516691340, 64630516691342, 64630516691339, 64630516691338, 64630516691336, 64630516691330, 64630516691331, 64630516691334}, "xdgxr", []string{"xdgxx", "xdgz8", "xdgz2", "xdgz0", "xdgxp", "xdgxn", "xdgxq", "xdgxw"}}, + {12.41349481328507, 165.0218523652875, 1034149110319087, 50, []uint64{1034149110319098, 1034149110320464, 1034149110320453, 1034149110320452, 1034149110319086, 1034149110319084, 1034149110319085, 1034149110319096}, "xdjqe5v1", []string{"xdjqe5v4", "xdjqe5v6", "xdjqe5v3", "xdjqe5v2", "xdjqe5v0", "xdjqe5ub", "xdjqe5uc", "xdjqe5uf"}}, + {44.653669468374574, 141.85978231293848, 254971347069, 38, []uint64{254971347240, 254971347242, 254971347071, 254971347070, 254971347068, 254971347062, 254971347063, 254971347234}, "xpuyh0g", []string{"xpuyh15", "xpuyh1h", "xpuyh0u", "xpuyh0s", "xpuyh0e", "xpuyh0d", "xpuyh0f", "xpuyh14"}}, + {9.304654311548802, -79.61595554184169, 6630134830716, 44, []uint64{6630134830717, 6630134830719, 6630134830718, 6630134830715, 6630134830713, 6630134830707, 6630134830710, 6630134830711}, "d1xkumc", []string{"d1xkuq1", "d1xkuq4", "d1xkumf", "d1xkumd", "d1xkum9", "d1xkum8", "d1xkumb", "d1xkuq0"}}, + {-8.010293687235404, -119.3545499809552, 7946409044, 36, []uint64{7946409045, 7946409047, 7946409046, 7946409043, 7946409041, 7946387195, 7946387198, 7946387199}, "3qe4kjb", []string{"3qe4kn0", "3qe4kn1", "3qe4kjc", "3qe4kj9", "3qe4kj8", "3qe47vx", "3qe47vz", "3qe47yp"}}, + {-27.813499114505248, 172.92536398181983, 3150495617, 32, []uint64{3150495620, 3150495622, 3150495619, 3150495618, 3150495616, 3150495530, 3150495531, 3150495534}, "rg4cz08", []string{"rg4cz0b", "rg4cz0c", "rg4cz09", "rg4cz03", "rg4cz02", "rg4cybr", "rg4cybx", "rg4cybz"}}, + {-68.77361766675313, 29.663829444878502, 137846904, 28, []uint64{137846905, 137846907, 137846906, 137846895, 137846893, 137846887, 137846898, 137846899}, "hev0g00s", []string{"hev0g00t", "hev0g00v", "hev0g00u", "hev0g00g", "hev0g00e", "hev0g007", "hev0g00k", "hev0g00m"}}, + {76.42137778055621, -11.74635232327276, 556263570813377, 50, []uint64{556263570813380, 556263570813382, 556263570813379, 556263570813378, 556263570813376, 556263570813290, 556263570813291, 556263570813294}, "gtxd", []string{"gtxe", "gtxg", "gtxf", "gtxc", "gtx9", "gtx3", "gtx6", "gtx7"}}, + {-49.79668881188081, -90.70565975317731, 1068284911, 34, []uint64{1068284922, 1068372304, 1068372293, 1068372292, 1068284910, 1068284908, 1068284909, 1068284920}, "1zpkxyy", []string{"1zpkxzn", "1zpkxzp", "1zpkxyz", "1zpkxyx", "1zpkxyw", "1zpkxyt", "1zpkxyv", "1zpkxzj"}}, + {26.375936663811444, 165.65207965829177, 65476644913463, 46, []uint64{65476644913506, 65476644913512, 65476644913469, 65476644913468, 65476644913462, 65476644913460, 65476644913461, 65476644913504}, "xsty198", []string{"xsty19b", "xsty19c", "xsty199", "xsty193", "xsty192", "xsty13r", "xsty13x", "xsty13z"}}, + {78.63606097895536, -92.56863103978685, 389077, 20, []uint64{391808, 391810, 389079, 389078, 389076, 388990, 388991, 391722}, "cvypme", []string{"cvypms", "cvypmu", "cvypmg", "cvypmf", "cvypmd", "cvypm6", "cvypm7", "cvypmk"}}, + {-54.582659147068625, -100.71206965457532, 1066285759119, 44, []uint64{1066285759130, 1066285759152, 1066285759141, 1066285759140, 1066285759118, 1066285759116, 1066285759117, 1066285759128}, "1y23kpx8", []string{"1y23kpx9", "1y23kpxc", "1y23kpxb", "1y23kprz", "1y23kprx", "1y23kprr", "1y23kpx2", "1y23kpx3"}}, + {-47.645334251006716, 115.11776297190227, 701803004, 30, []uint64{701803005, 701803007, 701803006, 701803003, 701803001, 701802995, 701802998, 701802999}, "nx9bgw6jy", []string{"nx9bgw6nn", "nx9bgw6np", "nx9bgw6jz", "nx9bgw6jx", "nx9bgw6jw", "nx9bgw6jt", "nx9bgw6jv", "nx9bgw6nj"}}, + {-32.07307234073232, -33.80765894881915, 14990072, 26, []uint64{14990073, 14990075, 14990074, 14990063, 14990061, 14990055, 14990066, 14990067}, "74rcw9", []string{"74rcwd", "74rcwf", "74rcwc", "74rcwb", "74rcw8", "74rcw2", "74rcw3", "74rcw6"}}, + {-23.78897351221532, -81.85334286204306, 212443909701, 40, []uint64{212443909712, 212443909714, 212443909703, 212443909702, 212443909700, 212443909358, 212443909359, 212443909370}, "65vb9mk5", []string{"65vb9mkh", "65vb9mkk", "65vb9mk7", "65vb9mk6", "65vb9mk4", "65vb9m7f", "65vb9m7g", "65vb9m7u"}}, + {-24.18723057214811, -102.6168202716799, 112564, 20, []uint64{112565, 112567, 112566, 112563, 112561, 112539, 112542, 112543}, "3exn2u", []string{"3exn2v", "3exn3j", "3exn3h", "3exn35", "3exn2g", "3exn2e", "3exn2s", "3exn2t"}}, + {-65.16475442027149, -37.24384234420722, 12137112251358, 46, []uint64{12137112251359, 12137112251381, 12137112251380, 12137112251377, 12137112251355, 12137112251353, 12137112251356, 12137112251357}, "5hmt23", []string{"5hmt26", "5hmt2d", "5hmt29", "5hmt28", "5hmt22", "5hmt20", "5hmt21", "5hmt24"}}, + {30.431072538944136, -5.113836982520326, 465128621, 30, []uint64{465128632, 465128634, 465128623, 465128622, 465128620, 465128614, 465128615, 465128626}, "evkm5eu", []string{"evkm5sh", "evkm5sj", "evkm5ev", "evkm5et", "evkm5es", "evkm5ee", "evkm5eg", "evkm5s5"}}, + {55.71715393909835, 74.21022426414129, 14316965, 24, []uint64{14316976, 14316978, 14316967, 14316966, 14316964, 14316942, 14316943, 14316954}, "v9uubwypz", []string{"v9uubxn0p", "v9uubxn20", "v9uubwyrb", "v9uubwyr8", "v9uubwypx", "v9uubwypw", "v9uubwypy", "v9uubxn0n"}}, + {-24.490887042025875, -179.7406580845127, 283715, 22, []uint64{283718, 283724, 283721, 283720, 283714, 283712, 283713, 283716}, "258htvsf5", []string{"258htvsf7", "258htvsfk", "258htvsfh", "258htvscu", "258htvscg", "258htvscf", "258htvsf4", "258htvsf6"}}, + {21.15275497175753, 97.79040850153979, 15493693542900, 44, []uint64{15493693542901, 15493693542903, 15493693542902, 15493693542899, 15493693542897, 15493693542875, 15493693542878, 15493693542879}, "w5v8368z", []string{"w5v836bb", "w5v836c0", "w5v8369p", "w5v8369n", "w5v8368y", "w5v8368w", "w5v8368x", "w5v836b8"}}, + {-45.27406038623303, 150.7330747024098, 45579565, 26, []uint64{45579576, 45579578, 45579567, 45579566, 45579564, 45579558, 45579559, 45579570}, "prgnqn07r", []string{"prgnqn07x", "prgnqn0e8", "prgnqn0e2", "prgnqn0e0", "prgnqn07p", "prgnqn07n", "prgnqn07q", "prgnqn07w"}}, + {75.33668931259308, -129.73911171971122, 24779483961, 36, []uint64{24779483964, 24779483966, 24779483963, 24779483962, 24779483960, 24779483954, 24779483955, 24779483958}, "cj7sx", []string{"cj7sz", "cj7ub", "cj7u8", "cj7u2", "cj7sr", "cj7sq", "cj7sw", "cj7sy"}}, + {33.370133474767485, -13.699830427678535, 1898188137748, 42, []uint64{1898188137749, 1898188137751, 1898188137750, 1898188137747, 1898188137745, 1898188132283, 1898188132286, 1898188132287}, "etymb6b", []string{"etymb70", "etymb71", "etymb6c", "etymb69", "etymb68", "etymb4x", "etymb4z", "etymb5p"}}, + {28.42069326461933, -63.60803119302726, 418955, 20, []uint64{418958, 418980, 418977, 418976, 418954, 418952, 418953, 418956}, "dt4c8tf4", []string{"dt4c8tf5", "dt4c8tf7", "dt4c8tf6", "dt4c8tf3", "dt4c8tf1", "dt4c8tcc", "dt4c8tcf", "dt4c8tcg"}}, + {59.76735242175346, 5.6471145983668976, 14038597761, 34, []uint64{14038597764, 14038597766, 14038597763, 14038597762, 14038597760, 14038597674, 14038597675, 14038597678}, "u4sh0", []string{"u4sh2", "u4sh3", "u4sh1", "u4s5c", "u4s5b", "u4egz", "u4eup", "u4eur"}}, + {77.48504623572808, -128.1779938507243, 6204595742, 34, []uint64{6204595743, 6204595765, 6204595764, 6204595761, 6204595739, 6204595737, 6204595740, 6204595741}, "cjubg1", []string{"cjubg4", "cjubg6", "cjubg3", "cjubg2", "cjubg0", "cjubfb", "cjubfc", "cjubff"}}, + {32.72178203193471, 44.32303646329092, 854677545444, 40, []uint64{854677545445, 854677545447, 854677545446, 854677545443, 854677545441, 854677545419, 854677545422, 854677545423}, "svzd0sg41", []string{"svzd0sg43", "svzd0sg46", "svzd0sg44", "svzd0sg1f", "svzd0sg1c", "svzd0sg1b", "svzd0sg40", "svzd0sg42"}}, + {-71.90893297063303, -97.93079297053919, 772455, 24, []uint64{772466, 772472, 772461, 772460, 772454, 772452, 772453, 772464}, "1g4qg", []string{"1g4r5", "1g4rh", "1g4qu", "1g4qs", "1g4qe", "1g4qd", "1g4qf", "1g4r4"}}, + {-10.48590501306171, -85.62687502638437, 56954514689, 38, []uint64{56954514692, 56954514694, 56954514691, 56954514690, 56954514688, 56954513322, 56954513323, 56954513326}, "6n5h7e07", []string{"6n5h7e0k", "6n5h7e0s", "6n5h7e0e", "6n5h7e0d", "6n5h7e06", "6n5h7e04", "6n5h7e05", "6n5h7e0h"}}, + {-61.986276554031065, -90.32759115248336, 60415, 20, []uint64{61098, 148480, 147797, 147796, 60414, 60412, 60413, 61096}, "1uzz", []string{"1vpb", "4j00", "4hbp", "4hbn", "1uzy", "1uzw", "1uzx", "1vp8"}}, + {74.16612579209323, -120.20585089255474, 24907437680, 36, []uint64{24907437681, 24907437683, 24907437682, 24907437671, 24907437669, 24907437647, 24907437658, 24907437659}, "cm4t", []string{"cm4w", "cm4y", "cm4v", "cm4u", "cm4s", "cm4k", "cm4m", "cm4q"}}, + {64.7148338292609, 176.61561937315855, 4226974315, 32, []uint64{4226974318, 4226974404, 4226974401, 4226974400, 4226974314, 4226974312, 4226974313, 4226974316}, "zgt84uz", []string{"zgt84vp", "zgt85j0", "zgt85hb", "zgt85h8", "zgt84ux", "zgt84uw", "zgt84uy", "zgt84vn"}}, + {-22.00383509940002, -23.22204475272156, 63612667, 28, []uint64{63612670, 63614036, 63614033, 63614032, 63612666, 63612664, 63612665, 63612668}, "7kp6zd64", []string{"7kp6zd65", "7kp6zd67", "7kp6zd66", "7kp6zd63", "7kp6zd61", "7kp6zd3c", "7kp6zd3f", "7kp6zd3g"}}, + {-12.053462713316563, 107.34822593000717, 758979803, 30, []uint64{758979806, 758979828, 758979825, 758979824, 758979802, 758979800, 758979801, 758979804}, "qmu76v8x", []string{"qmu76vb8", "qmu76vbb", "qmu76v8z", "qmu76v8y", "qmu76v8w", "qmu76v8q", "qmu76v8r", "qmu76vb2"}}, + {80.29595470658387, -161.238472322686, 5885756893087, 44, []uint64{5885756893130, 5885756893152, 5885756893109, 5885756893108, 5885756893086, 5885756893084, 5885756893085, 5885756893128}, "bqm2fcm", []string{"bqm2fct", "bqm2fcw", "bqm2fcq", "bqm2fcn", "bqm2fcj", "bqm2fch", "bqm2fck", "bqm2fcs"}}, + {-74.31791969452752, 150.913435761875, 186496914157135, 48, []uint64{186496914157146, 186496914157168, 186496914157157, 186496914157156, 186496914157134, 186496914157132, 186496914157133, 186496914157144}, "p6g34ngf9", []string{"p6g34ngfc", "p6g34ngff", "p6g34ngfd", "p6g34ngf6", "p6g34ngf3", "p6g34ngf2", "p6g34ngf8", "p6g34ngfb"}}, + {64.85194724961184, 49.793177862418815, 58345053277, 36, []uint64{58345053448, 58345053450, 58345053279, 58345053278, 58345053276, 58345053270, 58345053271, 58345053442}, "v5e2vjfmc", []string{"v5e2vjfq1", "v5e2vjfq4", "v5e2vjfmf", "v5e2vjfmd", "v5e2vjfm9", "v5e2vjfm8", "v5e2vjfmb", "v5e2vjfq0"}}, + {-32.15909668151289, 69.79954084400379, 166457639777, 38, []uint64{166457639780, 166457639782, 166457639779, 166457639778, 166457639776, 166457639754, 166457639755, 166457639758}, "md39h3", []string{"md39h6", "md39hd", "md39h9", "md39h8", "md39h2", "md39h0", "md39h1", "md39h4"}}, + {-4.466159042218351, 80.84858517690736, 2617563, 22, []uint64{2617566, 2617588, 2617585, 2617584, 2617562, 2617560, 2617561, 2617564}, "mz1qx", []string{"mz1qz", "mz1wb", "mz1w8", "mz1w2", "mz1qr", "mz1qq", "mz1qw", "mz1qy"}}, + {-35.876493843767086, -146.75159305889974, 4844405, 26, []uint64{4845088, 4845090, 4844407, 4844406, 4844404, 4844382, 4844383, 4845066}, "29xeu", []string{"29xsh", "29xsj", "29xev", "29xet", "29xes", "29xee", "29xeg", "29xs5"}}, + {17.26347022028358, 89.74329385306919, 3503276248276, 42, []uint64{3503276248277, 3503276248279, 3503276248278, 3503276248275, 3503276248273, 3503276248187, 3503276248190, 3503276248191}, "tgpf4nt", []string{"tgpf4nv", "tgpf4ny", "tgpf4nw", "tgpf4nq", "tgpf4nm", "tgpf4nk", "tgpf4ns", "tgpf4nu"}}, + {50.24689696946007, 38.84097403771011, 57895721839717, 46, []uint64{57895721839728, 57895721839730, 57895721839719, 57895721839718, 57895721839716, 57895721839694, 57895721839695, 57895721839706}, "ubgtgg", []string{"ubgtgu", "ubgtuh", "ubgtu5", "ubgtu4", "ubgtgf", "ubgtgd", "ubgtge", "ubgtgs"}}, + {55.312077434995444, -16.6467695241154, 538559691555476, 50, []uint64{538559691555477, 538559691555479, 538559691555478, 538559691555475, 538559691555473, 538559691555387, 538559691555390, 538559691555391}, "g9u4tjq", []string{"g9u4tjw", "g9u4tjx", "g9u4tjr", "g9u4tjp", "g9u4tjn", "g9u4tjj", "g9u4tjm", "g9u4tjt"}}, + {-59.993227273240336, 141.1297740030277, 180766317, 28, []uint64{180766328, 180766330, 180766319, 180766318, 180766316, 180766310, 180766311, 180766322}, "pjk6", []string{"pjk7", "pjke", "pjkd", "pjk9", "pjk3", "pjk1", "pjk4", "pjk5"}}, + {-56.63111491153904, 157.0460287235328, 760855449608045, 50, []uint64{760855449608056, 760855449608058, 760855449608047, 760855449608046, 760855449608044, 760855449608038, 760855449608039, 760855449608050}, "pmztvdt", []string{"pmztvdv", "pmztvdy", "pmztvdw", "pmztvdq", "pmztvdm", "pmztvdk", "pmztvds", "pmztvdu"}}, + {-89.9412457117578, 174.69055590413336, 732829553470, 40, []uint64{732829553471, 732829553557, 732829553556, 732829553553, 732829553467, 732829553465, 732829553468, 732829553469}, "pbh0r4", []string{"pbh0r5", "pbh0r7", "pbh0r6", "pbh0r3", "pbh0r1", "pbh0qc", "pbh0qf", "pbh0qg"}}, + {4.403128788893795, 95.89167946233647, 986057517961860, 50, []uint64{986057517961861, 986057517961863, 986057517961862, 986057517961859, 986057517961857, 986057517961771, 986057517961774, 986057517961775}, "w0u1n1d4", []string{"w0u1n1d5", "w0u1n1d7", "w0u1n1d6", "w0u1n1d3", "w0u1n1d1", "w0u1n19c", "w0u1n19f", "w0u1n19g"}}, + {-26.41979096844444, 54.304194154799916, 41184489263, 36, []uint64{41184489274, 41184489360, 41184489349, 41184489348, 41184489262, 41184489260, 41184489261, 41184489272}, "m5q9ewrj", []string{"m5q9ewrn", "m5q9ewrq", "m5q9ewrm", "m5q9ewrk", "m5q9ewrh", "m5q9ewqu", "m5q9ewqv", "m5q9ewqy"}}, + {-11.04885023910903, 77.56804097659187, 683606065575, 40, []uint64{683606065586, 683606065592, 683606065581, 683606065580, 683606065574, 683606065572, 683606065573, 683606065584}, "mwp1jhe", []string{"mwp1jhg", "mwp1jhu", "mwp1jhs", "mwp1jhk", "mwp1jh7", "mwp1jh6", "mwp1jhd", "mwp1jhf"}}, + {-47.23516051273327, 174.41104057081975, 773786755536575, 50, []uint64{773786755536618, 773786755537984, 773786755537941, 773786755537940, 773786755536574, 773786755536572, 773786755536573, 773786755536616}, "pzs52c", []string{"pzs52f", "pzs534", "pzs531", "pzs530", "pzs52b", "pzs528", "pzs529", "pzs52d"}}, + {-18.921332569385413, 100.07271536492044, 47246374, 26, []uint64{47246375, 47246381, 47246380, 47246377, 47246371, 47246369, 47246372, 47246373}, "qhxhm5qx", []string{"qhxhm5w8", "qhxhm5wb", "qhxhm5qz", "qhxhm5qy", "qhxhm5qw", "qhxhm5qq", "qhxhm5qr", "qhxhm5w2"}}, + {-9.91026815676014, -119.58228450373281, 31717298038, 38, []uint64{31717298039, 31717298045, 31717298044, 31717298041, 31717298035, 31717298033, 31717298036, 31717298037}, "3q4zw", []string{"3q4zy", "3q4zz", "3q4zx", "3q4zr", "3q4zq", "3q4zm", "3q4zt", "3q4zv"}}, + {83.03491251111063, 159.10064878390403, 1121880440874107, 50, []uint64{1121880440874110, 1121880440874196, 1121880440874193, 1121880440874192, 1121880440874106, 1121880440874104, 1121880440874105, 1121880440874108}, "zwc0k", []string{"zwc0s", "zwc0t", "zwc0m", "zwc0j", "zwc0h", "zwc05", "zwc07", "zwc0e"}}, + {88.08804437611252, 89.90364304493414, 962000895358, 40, []uint64{962000895359, 962000895445, 962000895444, 962000895441, 962000895355, 962000895353, 962000895356, 962000895357}, "vzxvjgcy", []string{"vzxvjgcz", "vzxvjgfp", "vzxvjgfn", "vzxvjgfj", "vzxvjgcv", "vzxvjgct", "vzxvjgcw", "vzxvjgcx"}}, + {-4.842902212345507, 88.57007106137462, 10480043, 24, []uint64{10480046, 10480388, 10480385, 10480384, 10480042, 10480040, 10480041, 10480044}, "mznurq", []string{"mznurr", "mznurx", "mznurw", "mznurt", "mznurm", "mznurj", "mznurn", "mznurp"}}, + {47.305555229162565, -66.89698151039192, 489713874868, 40, []uint64{489713874869, 489713874871, 489713874870, 489713874867, 489713874865, 489713874843, 489713874846, 489713874847}, "f82mj", []string{"f82mm", "f82mq", "f82mn", "f82ky", "f82kv", "f82ku", "f82mh", "f82mk"}}, + {-55.884530493436614, -85.02762205858016, 2545506922672, 44, []uint64{2545506922673, 2545506922675, 2545506922674, 2545506922663, 2545506922661, 2545506922639, 2545506922650, 2545506922651}, "4n5d14sc", []string{"4n5d14sf", "4n5d14t4", "4n5d14t1", "4n5d14t0", "4n5d14sb", "4n5d14s8", "4n5d14s9", "4n5d14sd"}}, + {67.78294059992186, -121.02627132920315, 378923, 20, []uint64{378926, 379012, 379009, 379008, 378922, 378920, 378921, 378924}, "ck1ctgx25", []string{"ck1ctgx27", "ck1ctgx2k", "ck1ctgx2h", "ck1ctgrru", "ck1ctgrrg", "ck1ctgrrf", "ck1ctgx24", "ck1ctgx26"}}, + {81.35934566706419, -136.36139619981986, 96410298222792, 48, []uint64{96410298222793, 96410298222795, 96410298222794, 96410298222751, 96410298222749, 96410298222743, 96410298222786, 96410298222787}, "byrnc508", []string{"byrnc509", "byrnc50c", "byrnc50b", "byrnc4bz", "byrnc4bx", "byrnc4br", "byrnc502", "byrnc503"}}, + {1.651987858407665, 147.9830822291915, 3809670, 22, []uint64{3809671, 3809677, 3809676, 3809673, 3809667, 3809665, 3809668, 3809669}, "x231r", []string{"x231x", "x2338", "x2332", "x2330", "x231p", "x231n", "x231q", "x231w"}}, + {-72.4498663995073, -179.33098325523315, 1376529936605, 48, []uint64{1376529936776, 1376529936778, 1376529936607, 1376529936606, 1376529936604, 1376529936598, 1376529936599, 1376529936770}, "0507z4", []string{"0507z5", "0507z7", "0507z6", "0507z3", "0507z1", "0507yc", "0507yf", "0507yg"}}, + {-5.161974602902774, 167.92916124443582, 210463812535065, 48, []uint64{210463812535068, 210463812535070, 210463812535067, 210463812535066, 210463812535064, 210463812535058, 210463812535059, 210463812535062}, "rxp6tk6", []string{"rxp6tkd", "rxp6tke", "rxp6tk7", "rxp6tk5", "rxp6tk4", "rxp6tk1", "rxp6tk3", "rxp6tk9"}}, + {80.40123932083952, -3.383319119049702, 2091428, 22, []uint64{2091429, 2091431, 2091430, 2091427, 2091425, 2091403, 2091406, 2091407}, "gym97h87", []string{"gym97h8k", "gym97h8s", "gym97h8e", "gym97h8d", "gym97h86", "gym97h84", "gym97h85", "gym97h8h"}}, + {80.6563522729848, 127.99223376216833, 265810955528, 38, []uint64{265810955529, 265810955531, 265810955530, 265810955359, 265810955357, 265810955351, 265810955522, 265810955523}, "yy74be1", []string{"yy74be3", "yy74be6", "yy74be4", "yy74bdf", "yy74bdc", "yy74bdb", "yy74be0", "yy74be2"}}, + {46.54091284759852, 174.73659658132237, 4205061379, 32, []uint64{4205061382, 4205061388, 4205061385, 4205061384, 4205061378, 4205061376, 4205061377, 4205061380}, "zbk2b", []string{"zbk30", "zbk31", "zbk2c", "zbk29", "zbk28", "zbk0x", "zbk0z", "zbk1p"}}, + {-4.277587006014073, -170.26558658794966, 1471559910456, 44, []uint64{1471559910457, 1471559910459, 1471559910458, 1471559910447, 1471559910445, 1471559910439, 1471559910450, 1471559910451}, "2pnztt23", []string{"2pnztt26", "2pnztt2d", "2pnztt29", "2pnztt28", "2pnztt22", "2pnztt20", "2pnztt21", "2pnztt24"}}, + {-75.7819027575315, 133.97409900172352, 45004971611053, 46, []uint64{45004971611064, 45004971611066, 45004971611055, 45004971611054, 45004971611052, 45004971611046, 45004971611047, 45004971611058}, "nfx2bskfq", []string{"nfx2bskfw", "nfx2bskfx", "nfx2bskfr", "nfx2bskfp", "nfx2bskfn", "nfx2bskfj", "nfx2bskfm", "nfx2bskft"}}, + {-54.3842148038093, 98.76485603669425, 709412954002, 40, []uint64{709412954003, 709412954009, 709412954008, 709412953997, 709412953991, 709412953989, 709412954000, 709412954001}, "nnq4x7", []string{"nnq4xk", "nnq4xs", "nnq4xe", "nnq4xd", "nnq4x6", "nnq4x4", "nnq4x5", "nnq4xh"}}, + {-20.589167481899494, -27.33930560501176, 3974341, 24, []uint64{3974352, 3974354, 3974343, 3974342, 3974340, 3974254, 3974255, 3974266}, "7kkdcgu5", []string{"7kkdcguh", "7kkdcguk", "7kkdcgu7", "7kkdcgu6", "7kkdcgu4", "7kkdcggf", "7kkdcggg", "7kkdcggu"}}, + {31.688745123596163, -92.61593745311256, 1356742274946, 42, []uint64{1356742274947, 1356742274953, 1356742274952, 1356742274781, 1356742274775, 1356742274773, 1356742274944, 1356742274945}, "9vwhk", []string{"9vwhs", "9vwht", "9vwhm", "9vwhj", "9vwhh", "9vwh5", "9vwh7", "9vwhe"}}, + {-69.95576200120558, -98.62465116256499, 198369955, 32, []uint64{198369958, 198369964, 198369961, 198369960, 198369954, 198369952, 198369953, 198369956}, "1g9f5", []string{"1g9f7", "1g9fk", "1g9fh", "1g9cu", "1g9cg", "1g9cf", "1g9f4", "1g9f6"}}, + {-67.86630397605768, -103.48108590117772, 12047324, 28, []uint64{12047325, 12047327, 12047326, 12047323, 12047321, 12047315, 12047318, 12047319}, "1eymvjr65", []string{"1eymvjr67", "1eymvjr6k", "1eymvjr6h", "1eymvjr3u", "1eymvjr3g", "1eymvjr3f", "1eymvjr64", "1eymvjr66"}}, + {-10.443679089090438, 0.2841028790571727, 159991957234, 38, []uint64{159991957235, 159991957241, 159991957240, 159991957229, 159991957223, 159991957221, 159991957232, 159991957233}, "kn0hw6ych", []string{"kn0hw6yck", "kn0hw6ycm", "kn0hw6ycj", "kn0hw6ybv", "kn0hw6ybu", "kn0hw6ybg", "kn0hw6yc5", "kn0hw6yc7"}}, + {-85.82267513580155, -80.16100817784898, 8447998, 26, []uint64{8447999, 8449365, 8449364, 8449361, 8447995, 8447993, 8447996, 8447997}, "40wzzbkw9", []string{"40wzzbkwc", "40wzzbkwf", "40wzzbkwd", "40wzzbkw6", "40wzzbkw3", "40wzzbkw2", "40wzzbkw8", "40wzzbkwb"}}, + {0.3012119140330327, 54.06880178907886, 819843, 20, []uint64{819846, 819852, 819849, 819848, 819842, 819840, 819841, 819844}, "t0n3wqgdw", []string{"t0n3wqgdy", "t0n3wqgdz", "t0n3wqgdx", "t0n3wqgdr", "t0n3wqgdq", "t0n3wqgdm", "t0n3wqgdt", "t0n3wqgdv"}}, + {-39.67353799869306, -73.03760705547757, 214220196995922, 50, []uint64{214220196995923, 214220196995929, 214220196995928, 214220196995917, 214220196995911, 214220196995909, 214220196995920, 214220196995921}, "62un", []string{"62up", "62ur", "62uq", "62um", "62uj", "62gv", "62gy", "62gz"}}, + {63.35817512734502, 119.31531347287819, 4181462041128, 42, []uint64{4181462041129, 4181462041131, 4181462041130, 4181462030207, 4181462030205, 4181462030199, 4181462041122, 4181462041123}, "yekb7n4b", []string{"yekb7n4c", "yekb7n51", "yekb7n50", "yekb7jgp", "yekb7jfz", "yekb7jfx", "yekb7n48", "yekb7n49"}}, + {20.481904623069553, 116.43140487166238, 238393928, 28, []uint64{238393929, 238393931, 238393930, 238393887, 238393885, 238393879, 238393922, 238393923}, "wedu92w", []string{"wedu92y", "wedu92z", "wedu92x", "wedu92r", "wedu92q", "wedu92m", "wedu92t", "wedu92v"}}, + {82.20501993410289, 41.153309409390204, 226174788, 28, []uint64{226174789, 226174791, 226174790, 226174787, 226174785, 226174443, 226174446, 226174447}, "uyt78", []string{"uyt7b", "uyt7c", "uyt79", "uyt73", "uyt72", "uyt5r", "uyt5x", "uyt5z"}}, + {-31.27131449482112, -146.62760713737225, 5273995939143, 46, []uint64{5273995939154, 5273995939160, 5273995939149, 5273995939148, 5273995939142, 5273995939140, 5273995939141, 5273995939152}, "2drwp7jp3", []string{"2drwp7jp9", "2drwp7jpd", "2drwp7jp6", "2drwp7jp4", "2drwp7jp1", "2drwp7jp0", "2drwp7jp2", "2drwp7jp8"}}, + {10.39444907988944, 65.2255300267716, 3371989058, 32, []uint64{3371989059, 3371989065, 3371989064, 3371989021, 3371989015, 3371989013, 3371989056, 3371989057}, "t3y7hhrb1", []string{"t3y7hhrb3", "t3y7hhrb6", "t3y7hhrb4", "t3y7hhpzf", "t3y7hhpzc", "t3y7hhpzb", "t3y7hhrb0", "t3y7hhrb2"}}, + {29.345165651888223, -6.705232216219883, 29044031, 26, []uint64{29044074, 29044160, 29044117, 29044116, 29044030, 29044028, 29044029, 29044072}, "ev5nzqj7", []string{"ev5nzqjk", "ev5nzqjs", "ev5nzqje", "ev5nzqjd", "ev5nzqj6", "ev5nzqj4", "ev5nzqj5", "ev5nzqjh"}}, + {16.077253486204413, 165.85438847255136, 1010239754722, 40, []uint64{1010239754723, 1010239754729, 1010239754728, 1010239754685, 1010239754679, 1010239754677, 1010239754720, 1010239754721}, "xdvgqng2z", []string{"xdvgqng3p", "xdvgqng90", "xdvgqng8b", "xdvgqng88", "xdvgqng2x", "xdvgqng2w", "xdvgqng2y", "xdvgqng3n"}}, + {2.6637045530369505, 22.70021394543437, 794709, 20, []uint64{794880, 794882, 794711, 794710, 794708, 789246, 789247, 789418}, "s82phs", []string{"s82pht", "s82phv", "s82phu", "s82phg", "s82phe", "s82ph7", "s82phk", "s82phm"}}, + {-37.70860205060307, -127.22132137179142, 6396487, 26, []uint64{6396498, 6396504, 6396493, 6396492, 6396486, 6396484, 6396485, 6396496}, "31m93", []string{"31m99", "31m9d", "31m96", "31m94", "31m91", "31m90", "31m92", "31m98"}}, + {29.002817814820432, 176.82878724791226, 16416321297588, 44, []uint64{16416321297589, 16416321297591, 16416321297590, 16416321297587, 16416321297585, 16416321297563, 16416321297566, 16416321297567}, "xvjszzfc9", []string{"xvjszzfcc", "xvjszzfcf", "xvjszzfcd", "xvjszzfc6", "xvjszzfc3", "xvjszzfc2", "xvjszzfc8", "xvjszzfcb"}}, + {78.42321531787456, -117.61931067590194, 408835982623462, 50, []uint64{408835982623463, 408835982623469, 408835982623468, 408835982623465, 408835982623459, 408835982623457, 408835982623460, 408835982623461}, "cmuq", []string{"cmur", "cmux", "cmuw", "cmut", "cmum", "cmuj", "cmun", "cmup"}}, + {39.682869250231306, -146.80119152231055, 292521, 20, []uint64{292524, 292526, 292523, 292522, 292520, 292514, 292515, 292518}, "8xp9g", []string{"8xpd5", "8xpdh", "8xp9u", "8xp9s", "8xp9e", "8xp9d", "8xp9f", "8xpd4"}}, + {46.9859598170151, 13.261680416842864, 3416478, 22, []uint64{3416479, 3416501, 3416500, 3416497, 3416475, 3416473, 3416476, 3416477}, "u237mc8c", []string{"u237mc8f", "u237mc94", "u237mc91", "u237mc90", "u237mc8b", "u237mc88", "u237mc89", "u237mc8d"}}, + {88.4093843279552, 101.17796218310832, 269912645368157, 48, []uint64{269912645368840, 269912645368842, 269912645368159, 269912645368158, 269912645368156, 269912645368150, 269912645368151, 269912645368834}, "ypxy", []string{"ypxz", "yr8p", "yr8n", "yr8j", "ypxv", "ypxt", "ypxw", "ypxx"}}, + {40.003725004411535, 78.10316592408344, 222712636951, 38, []uint64{222712636994, 222712637000, 222712636957, 222712636956, 222712636950, 222712636948, 222712636949, 222712636992}, "txpe962yx", []string{"txpe962yz", "txpe963nb", "txpe963n8", "txpe963n2", "txpe962yr", "txpe962yq", "txpe962yw", "txpe962yy"}}, + {48.467568302952714, 5.524090894439723, 55862873964, 36, []uint64{55862873965, 55862873967, 55862873966, 55862873963, 55862873961, 55862873955, 55862873958, 55862873959}, "u0egtx", []string{"u0egv8", "u0egvb", "u0egtz", "u0egty", "u0egtw", "u0egtq", "u0egtr", "u0egv2"}}, + {32.13460971607128, -68.50727069969435, 6939431427476, 44, []uint64{6939431427477, 6939431427479, 6939431427478, 6939431427475, 6939431427473, 6939431427387, 6939431427390, 6939431427391}, "dmxqc1", []string{"dmxqc4", "dmxqc6", "dmxqc3", "dmxqc2", "dmxqc0", "dmxqbb", "dmxqbc", "dmxqbf"}}, + {22.811056395352352, 95.52338302962016, 3825909155, 32, []uint64{3825909158, 3825909164, 3825909161, 3825909160, 3825909154, 3825909152, 3825909153, 3825909156}, "wh5cv8wh0", []string{"wh5cv8wh2", "wh5cv8wh3", "wh5cv8wh1", "wh5cv8w5c", "wh5cv8w5b", "wh5cv8tgz", "wh5cv8tup", "wh5cv8tur"}}, + {44.04498822177993, 45.93084714750876, 215305417, 28, []uint64{215305420, 215305422, 215305419, 215305418, 215305416, 215305410, 215305411, 215305414}, "tpbd", []string{"tpbe", "tpbg", "tpbf", "tpbc", "tpb9", "tpb3", "tpb6", "tpb7"}}, + {-40.30537812135299, 76.7367226954666, 646918548, 30, []uint64{646918549, 646918551, 646918550, 646918547, 646918545, 646918459, 646918462, 646918463}, "m8yddntup", []string{"m8yddntur", "m8yddnwh2", "m8yddnwh0", "m8yddnw5b", "m8yddntgz", "m8yddntgy", "m8yddntun", "m8yddntuq"}}, + {7.1987065252906035, 136.47961810446577, 62350450471, 36, []uint64{62350450482, 62350450488, 62350450477, 62350450476, 62350450470, 62350450468, 62350450469, 62350450480}, "x130cwmr", []string{"x130cwt2", "x130cwt8", "x130cwmx", "x130cwmw", "x130cwmq", "x130cwmn", "x130cwmp", "x130cwt0"}}, + {-0.5648119292309417, 20.37632037489672, 659658634749341, 50, []uint64{659658634749384, 659658634749386, 659658634749343, 659658634749342, 659658634749340, 659658634749334, 659658634749335, 659658634749378}, "krykz9j", []string{"krykz9m", "krykz9q", "krykz9n", "krykz8y", "krykz8v", "krykz8u", "krykz9h", "krykz9k"}}, + {2.1912158132035984, -92.97295032406691, 4892585, 24, []uint64{4892588, 4892590, 4892587, 4892586, 4892584, 4892578, 4892579, 4892582}, "9bmukq", []string{"9bmukr", "9bmukx", "9bmukw", "9bmukt", "9bmukm", "9bmukj", "9bmukn", "9bmukp"}}, + {84.178468874401, -140.71612625196576, 5742573, 24, []uint64{5742584, 5742586, 5742575, 5742574, 5742572, 5742566, 5742567, 5742578}, "bygyv", []string{"bygzj", "bygzn", "bygyy", "bygyw", "bygyt", "bygys", "bygyu", "bygzh"}}, + {76.9501488947244, -127.07589282628032, 99269165617, 38, []uint64{99269165620, 99269165622, 99269165619, 99269165618, 99269165616, 99269165594, 99269165595, 99269165598}, "cjttu265", []string{"cjttu26h", "cjttu26k", "cjttu267", "cjttu266", "cjttu264", "cjttu23f", "cjttu23g", "cjttu23u"}}, + {-71.52495423762593, -21.765392545668902, 47573477280479, 48, []uint64{47573477280650, 47573477280672, 47573477280501, 47573477280500, 47573477280478, 47573477280476, 47573477280477, 47573477280648}, "5e290eq", []string{"5e290ew", "5e290ex", "5e290er", "5e290ep", "5e290en", "5e290ej", "5e290em", "5e290et"}}, + {52.82827381763491, 27.494915312025114, 56454826594, 36, []uint64{56454826595, 56454826601, 56454826600, 56454826557, 56454826551, 56454826549, 56454826592, 56454826593}, "u97s99j5", []string{"u97s99jh", "u97s99jk", "u97s99j7", "u97s99j6", "u97s99j4", "u97s99hf", "u97s99hg", "u97s99hu"}}, + {22.998049575347977, 5.513863954518428, 52624808379, 36, []uint64{52624808382, 52624808724, 52624808721, 52624808720, 52624808378, 52624808376, 52624808377, 52624808380}, "sh5fv6xj6", []string{"sh5fv6xjd", "sh5fv6xje", "sh5fv6xj7", "sh5fv6xj5", "sh5fv6xj4", "sh5fv6xj1", "sh5fv6xj3", "sh5fv6xj9"}}, + {20.941401375061105, -28.97522620379462, 7274458653868, 44, []uint64{7274458653869, 7274458653871, 7274458653870, 7274458653867, 7274458653865, 7274458653859, 7274458653862, 7274458653863}, "e7erh", []string{"e7erk", "e7erm", "e7erj", "e7eqv", "e7equ", "e7eqg", "e7er5", "e7er7"}}, + {-56.45676844303671, -128.88984140666435, 3424597055943, 46, []uint64{3424597055954, 3424597055960, 3424597055949, 3424597055948, 3424597055942, 3424597055940, 3424597055941, 3424597055952}, "1juqg4", []string{"1juqg5", "1juqg7", "1juqg6", "1juqg3", "1juqg1", "1juqfc", "1juqff", "1juqfg"}}, + {-81.00258475498413, 44.65575975799581, 2197483591, 32, []uint64{2197483602, 2197483608, 2197483597, 2197483596, 2197483590, 2197483588, 2197483589, 2197483600}, "hcxg0j", []string{"hcxg0n", "hcxg0q", "hcxg0m", "hcxg0k", "hcxg0h", "hcxepu", "hcxepv", "hcxepy"}}, + {-26.989015806117095, 32.813606585667, 162089439314369, 48, []uint64{162089439314372, 162089439314374, 162089439314371, 162089439314370, 162089439314368, 162089439314282, 162089439314283, 162089439314286}, "kepq6w", []string{"kepq6x", "kepq6z", "kepq6y", "kepq6v", "kepq6t", "kepq6m", "kepq6q", "kepq6r"}}, + {-30.246274755714694, -40.033320634276635, 981053412659, 42, []uint64{981053412662, 981053412668, 981053412665, 981053412664, 981053412658, 981053412656, 981053412657, 981053412660}, "74ee", []string{"74es", "74eu", "74eg", "74ef", "74ed", "74e6", "74e7", "74ek"}}, + {-70.48843231948558, 115.48215446670656, 718209262548005, 50, []uint64{718209262548016, 718209262548018, 718209262548007, 718209262548006, 718209262548004, 718209262547982, 718209262547983, 718209262547994}, "ne6ng", []string{"ne6p5", "ne6ph", "ne6nu", "ne6ns", "ne6ne", "ne6nd", "ne6nf", "ne6p4"}}, + {-29.27584792161361, 63.58801227380172, 10562964008829, 44, []uint64{10562964011560, 10562964011562, 10562964008831, 10562964008830, 10562964008828, 10562964008822, 10562964008823, 10562964011554}, "m6v1q", []string{"m6v1w", "m6v1x", "m6v1r", "m6v1p", "m6v1n", "m6v1j", "m6v1m", "m6v1t"}}, + {-88.47313534953719, -42.55133186891908, 2686073759, 34, []uint64{2686073802, 2686073824, 2686073781, 2686073780, 2686073758, 2686073756, 2686073757, 2686073800}, "5038", []string{"5039", "503c", "503b", "501z", "501x", "501r", "5032", "5033"}}, + {-68.5028646523424, 166.97803651780123, 754209923917778, 50, []uint64{754209923917779, 754209923917785, 754209923917784, 754209923917773, 754209923917767, 754209923917765, 754209923917776, 754209923917777}, "peydr9mt", []string{"peydr9mw", "peydr9my", "peydr9mv", "peydr9mu", "peydr9ms", "peydr9mk", "peydr9mm", "peydr9mq"}}, + {73.1062999168571, -118.35746130603366, 1553987427, 32, []uint64{1553987430, 1553987436, 1553987433, 1553987432, 1553987426, 1553987424, 1553987425, 1553987428}, "ckgzfswep", []string{"ckgzfswer", "ckgzfswg2", "ckgzfswg0", "ckgzfswfb", "ckgzfswdz", "ckgzfswdy", "ckgzfswen", "ckgzfsweq"}}, + {17.18517261970554, -0.9432872716861311, 118653691393150, 48, []uint64{118653691393151, 118653691393237, 118653691393236, 118653691393233, 118653691393147, 118653691393145, 118653691393148, 118653691393149}, "egp3f", []string{"egp64", "egp65", "egp3g", "egp3e", "egp3d", "egp39", "egp3c", "egp61"}}, + {15.959540279349312, -45.400183778258025, 109671170854184, 48, []uint64{109671170854185, 109671170854187, 109671170854186, 109671170854015, 109671170854013, 109671170854007, 109671170854178, 109671170854179}, "dfzdy", []string{"dfzen", "dfzep", "dfzdz", "dfzdx", "dfzdw", "dfzdt", "dfzdv", "dfzej"}}, + {-44.8496015911187, -156.65128446696326, 79173938003992, 50, []uint64{79173938003993, 79173938003995, 79173938003994, 79173938003983, 79173938003981, 79173938003975, 79173938003986, 79173938003987}, "2808g76", []string{"2808g7d", "2808g7e", "2808g77", "2808g75", "2808g74", "2808g71", "2808g73", "2808g79"}}, + {-5.539953333442099, 10.901875481475145, 10027173203, 34, []uint64{10027173206, 10027173212, 10027173209, 10027173208, 10027173202, 10027173200, 10027173201, 10027173204}, "kppb2p6x2", []string{"kppb2p6x8", "kppb2p6x9", "kppb2p6x3", "kppb2p6x1", "kppb2p6x0", "kppb2p6rp", "kppb2p6rr", "kppb2p6rx"}}, + {17.301061915808532, -155.92497852706583, 1101968, 22, []uint64{1101969, 1101971, 1101970, 1101959, 1101957, 1101871, 1101882, 1101883}, "8e147vdcu", []string{"8e147vdfh", "8e147vdfj", "8e147vdcv", "8e147vdct", "8e147vdcs", "8e147vdce", "8e147vdcg", "8e147vdf5"}}, + {6.439718639070634, 124.74256257974776, 3804335712, 32, []uint64{3804335713, 3804335715, 3804335714, 3804335671, 3804335669, 3804335647, 3804335690, 3804335691}, "wc0sws6", []string{"wc0swsd", "wc0swse", "wc0sws7", "wc0sws5", "wc0sws4", "wc0sws1", "wc0sws3", "wc0sws9"}}, + {66.24786713544745, -9.47344352901564, 8509979927197, 44, []uint64{8509979927240, 8509979927242, 8509979927199, 8509979927198, 8509979927196, 8509979927190, 8509979927191, 8509979927234}, "ggc2bk", []string{"ggc2bm", "ggc2bt", "ggc2bs", "ggc2be", "ggc2b7", "ggc2b5", "ggc2bh", "ggc2bj"}}, + {45.06357557859155, 154.82188954524463, 16295939, 24, []uint64{16295942, 16295948, 16295945, 16295944, 16295938, 16295936, 16295937, 16295940}, "z2n0759", []string{"z2n075c", "z2n075f", "z2n075d", "z2n0756", "z2n0753", "z2n0752", "z2n0758", "z2n075b"}}, + {-62.86126337524911, 165.59037386676937, 713582, 20, []uint64{713583, 713669, 713668, 713665, 713579, 713577, 713580, 713581}, "psvf2h", []string{"psvf2j", "psvf2m", "psvf2k", "psvf27", "psvf25", "psvdrg", "psvdru", "psvdrv"}}, + {-31.6670561532519, 173.1959557677037, 206220129875616, 48, []uint64{206220129875617, 206220129875619, 206220129875618, 206220129874935, 206220129874933, 206220129874911, 206220129875594, 206220129875595}, "rf75v5", []string{"rf75vh", "rf75vk", "rf75v7", "rf75v6", "rf75v4", "rf75uf", "rf75ug", "rf75uu"}}, + {70.34752138059412, -70.65722223679768, 128310542752424, 48, []uint64{128310542752425, 128310542752427, 128310542752426, 128310542749695, 128310542749693, 128310542749687, 128310542752418, 128310542752419}, "fktb0n", []string{"fktb0p", "fktb0r", "fktb0q", "fktb0m", "fktb0j", "fkt8pv", "fkt8py", "fkt8pz"}}, + {-76.63351390737807, -48.835340990364784, 2392621211, 34, []uint64{2392621214, 2392621236, 2392621233, 2392621232, 2392621210, 2392621208, 2392621209, 2392621212}, "4fmk09", []string{"4fmk0d", "4fmk0f", "4fmk0c", "4fmk0b", "4fmk08", "4fmk02", "4fmk03", "4fmk06"}}, + {-7.7654387504808255, -141.26108819054207, 96685, 20, []uint64{96696, 96698, 96687, 96686, 96684, 96678, 96679, 96690}, "2yee", []string{"2yes", "2yeu", "2yeg", "2yef", "2yed", "2ye6", "2ye7", "2yek"}}, + {-60.274700296955416, 26.929361720383895, 36052239302, 36, []uint64{36052239303, 36052239309, 36052239308, 36052239305, 36052239299, 36052239297, 36052239300, 36052239301}, "ht71h", []string{"ht71k", "ht71m", "ht71j", "ht70v", "ht70u", "ht70g", "ht715", "ht717"}}, + {-61.002387845052, -120.19398200852447, 14059667416985, 48, []uint64{14059667416988, 14059667416990, 14059667416987, 14059667416986, 14059667416984, 14059667416978, 14059667416979, 14059667416982}, "1m4s", []string{"1m4t", "1m4v", "1m4u", "1m4g", "1m4e", "1m47", "1m4k", "1m4m"}}, + {-7.1522893159417436, -163.38044234234258, 371242835451, 42, []uint64{371242835454, 371242835796, 371242835793, 371242835792, 371242835450, 371242835448, 371242835449, 371242835452}, "2qez61vyz", []string{"2qez61vzp", "2qez61yp0", "2qez61ynb", "2qez61yn8", "2qez61vyx", "2qez61vyw", "2qez61vyy", "2qez61vzn"}}, + {-35.94902936152357, -134.87800202591825, 1593427, 24, []uint64{1593430, 1593436, 1593433, 1593432, 1593426, 1593424, 1593425, 1593428}, "31856z8vd", []string{"31856z8vf", "31856z8vg", "31856z8ve", "31856z8v7", "31856z8v6", "31856z8v3", "31856z8v9", "31856z8vc"}}, + {-80.16648491896922, 65.56052278506104, 36769202059, 36, []uint64{36769202062, 36769202084, 36769202081, 36769202080, 36769202058, 36769202056, 36769202057, 36769202060}, "j3wxgy5sq", []string{"j3wxgy5sw", "j3wxgy5sx", "j3wxgy5sr", "j3wxgy5sp", "j3wxgy5sn", "j3wxgy5sj", "j3wxgy5sm", "j3wxgy5st"}}, + {-14.002871216012863, 76.9688025941723, 170156812019, 38, []uint64{170156812022, 170156812028, 170156812025, 170156812024, 170156812018, 170156812016, 170156812017, 170156812020}, "mtw8r6ygr", []string{"mtw8r6ygx", "mtw8r6z58", "mtw8r6z52", "mtw8r6z50", "mtw8r6ygp", "mtw8r6ygn", "mtw8r6ygq", "mtw8r6ygw"}}, + {88.53928765506134, 1.1303980259690434, 938191914439445, 50, []uint64{938191914439488, 938191914439490, 938191914439447, 938191914439446, 938191914439444, 938191914439102, 938191914439103, 938191914439146}, "up8z9", []string{"up8zc", "up8zf", "up8zd", "up8z6", "up8z3", "up8z2", "up8z8", "up8zb"}}, + {13.069619213914848, -64.93212902957748, 25959308, 26, []uint64{25959309, 25959311, 25959310, 25959307, 25959305, 25959299, 25959302, 25959303}, "dd3f67mb", []string{"dd3f67mc", "dd3f67q1", "dd3f67q0", "dd3f67np", "dd3f67jz", "dd3f67jx", "dd3f67m8", "dd3f67m9"}}, + {42.23845221391821, -73.87139720481355, 1668746, 22, []uint64{1668747, 1668769, 1668768, 1668085, 1668063, 1668061, 1668744, 1668745}, "dre2r122z", []string{"dre2r123p", "dre2r1290", "dre2r128b", "dre2r1288", "dre2r122x", "dre2r122w", "dre2r122y", "dre2r123n"}}, + {-89.30347627335868, -124.34374156061676, 2245023693424, 46, []uint64{2245023693425, 2245023693427, 2245023693426, 2245023693415, 2245023693413, 2245023693391, 2245023693402, 2245023693403}, "10pefqz9s", []string{"10pefqz9u", "10pefqz9v", "10pefqz9t", "10pefqz9m", "10pefqz9k", "10pefqz97", "10pefqz9e", "10pefqz9g"}}, + {-35.55406112967466, 111.10741016488464, 778369699724166, 50, []uint64{778369699724167, 778369699724173, 778369699724172, 778369699724169, 778369699724163, 778369699724161, 778369699724164, 778369699724165}, "q3xj8r", []string{"q3xjb2", "q3xjb8", "q3xj8x", "q3xj8w", "q3xj8q", "q3xj8n", "q3xj8p", "q3xjb0"}}, + {2.9709086409493324, 4.623912489158101, 211218535277650, 48, []uint64{211218535277651, 211218535277657, 211218535277656, 211218535277645, 211218535277639, 211218535277637, 211218535277648, 211218535277649}, "s0e2chz", []string{"s0e2cjp", "s0e2cm0", "s0e2ckb", "s0e2ck8", "s0e2chx", "s0e2chw", "s0e2chy", "s0e2cjn"}}, + {-46.26339883392211, 10.450075509259477, 34994358, 26, []uint64{34994359, 34994365, 34994364, 34994361, 34994355, 34994353, 34994356, 34994357}, "hpz2vf", []string{"hpz2vg", "hpz2y5", "hpz2y4", "hpz2y1", "hpz2vc", "hpz2v9", "hpz2vd", "hpz2ve"}}, + {74.45548527581559, -25.765040952712297, 8184796, 24, []uint64{8184797, 8184799, 8184798, 8184795, 8184793, 8184787, 8184790, 8184791}, "gmjxtdnqx", []string{"gmjxtdnqz", "gmjxtdnwb", "gmjxtdnw8", "gmjxtdnw2", "gmjxtdnqr", "gmjxtdnqq", "gmjxtdnqw", "gmjxtdnqy"}}, + {-89.4242746529344, -78.2423714066972, 142944271443788, 50, []uint64{142944271443789, 142944271443791, 142944271443790, 142944271443787, 142944271443785, 142944271443779, 142944271443782, 142944271443783}, "420778c9u", []string{"420778cdh", "420778cdj", "420778c9v", "420778c9t", "420778c9s", "420778c9e", "420778c9g", "420778cd5"}}, + {17.491261761810165, 136.8989987406785, 3821725, 22, []uint64{3821768, 3821770, 3821727, 3821726, 3821724, 3821718, 3821719, 3821762}, "x517e0nvc", []string{"x517e0ny1", "x517e0ny4", "x517e0nvf", "x517e0nvd", "x517e0nv9", "x517e0nv8", "x517e0nvb", "x517e0ny0"}}, + {-4.9353545674384804, 40.74819464233585, 166992619744159, 48, []uint64{166992619744202, 166992619744224, 166992619744181, 166992619744180, 166992619744158, 166992619744156, 166992619744157, 166992619744200}, "kzhg", []string{"kzhu", "kzjh", "kzj5", "kzj4", "kzhf", "kzhd", "kzhe", "kzhs"}}, + {-44.662773192845634, -146.79460767499404, 304781284, 32, []uint64{304781285, 304781287, 304781286, 304781283, 304781281, 304781259, 304781262, 304781263}, "28p9gt7", []string{"28p9gte", "28p9gts", "28p9gtk", "28p9gth", "28p9gt5", "28p9gt4", "28p9gt6", "28p9gtd"}}, + {-25.001762349216747, -64.43770074506754, 841223, 22, []uint64{841234, 841240, 841229, 841228, 841222, 841220, 841221, 841232}, "6ed1", []string{"6ed4", "6ed6", "6ed3", "6ed2", "6ed0", "6e9b", "6e9c", "6e9f"}}, + {78.77409137361974, 149.2739928522933, 1063387674, 30, []uint64{1063387675, 1063387697, 1063387696, 1063387685, 1063387663, 1063387661, 1063387672, 1063387673}, "zq40h", []string{"zq40k", "zq40m", "zq40j", "zmfpv", "zmfpu", "zmfpg", "zq405", "zq407"}}, + {-4.51140757848043, -100.96411777910544, 2081099, 24, []uint64{2081102, 2081124, 2081121, 2081120, 2081098, 2081096, 2081097, 2081100}, "3z0nq", []string{"3z0nw", "3z0nx", "3z0nr", "3z0np", "3z0nn", "3z0nj", "3z0nm", "3z0nt"}}, + {34.78227577556389, -85.38523401624114, 105952089, 28, []uint64{105952092, 105952094, 105952091, 105952090, 105952088, 105952082, 105952083, 105952086}, "dn5mc5", []string{"dn5mch", "dn5mck", "dn5mc7", "dn5mc6", "dn5mc4", "dn5mbf", "dn5mbg", "dn5mbu"}}, + {-36.16447405301733, -20.214041523460793, 978503936, 32, []uint64{978503937, 978503939, 978503938, 978503767, 978503765, 978502399, 978502570, 978502571}, "799dk", []string{"799ds", "799dt", "799dm", "799dj", "799dh", "799d5", "799d7", "799de"}}, + {-66.05564061626501, -138.28241657250328, 7312330292768, 48, []uint64{7312330292769, 7312330292771, 7312330292770, 7312330292087, 7312330292085, 7312330292063, 7312330292746, 7312330292747}, "0um8jq", []string{"0um8jr", "0um8jx", "0um8jw", "0um8jt", "0um8jm", "0um8jj", "0um8jn", "0um8jp"}}, + {77.81535948486999, -115.88607581783617, 380780, 20, []uint64{380781, 380783, 380782, 380779, 380777, 380771, 380774, 380775}, "cmvd", []string{"cmve", "cmvg", "cmvf", "cmvc", "cmv9", "cmv3", "cmv6", "cmv7"}}, + {45.08026301728387, -107.4039766828646, 99001210474315, 48, []uint64{99001210474318, 99001210474340, 99001210474337, 99001210474336, 99001210474314, 99001210474312, 99001210474313, 99001210474316}, "c8587", []string{"c858e", "c858s", "c858k", "c858h", "c8585", "c8584", "c8586", "c858d"}}, + {71.75811360697844, -57.977579561295, 123980295, 28, []uint64{123980306, 123980312, 123980301, 123980300, 123980294, 123980292, 123980293, 123980304}, "fsyb0xn", []string{"fsyb0xq", "fsyb0xr", "fsyb0xp", "fsyb0wz", "fsyb0wy", "fsyb0wv", "fsyb0xj", "fsyb0xm"}}, + {26.625851649427204, 156.04814282004372, 254183137187, 38, []uint64{254183137190, 254183137196, 254183137193, 254183137192, 254183137186, 254183137184, 254183137185, 254183137188}, "xkwzq", []string{"xkwzw", "xkwzx", "xkwzr", "xkwzp", "xkwzn", "xkwzj", "xkwzm", "xkwzt"}}, + {-82.3375120839919, -103.37496905779699, 683134, 24, []uint64{683135, 683221, 683220, 683217, 683131, 683129, 683132, 683133}, "19q7", []string{"19qk", "19qs", "19qe", "19qd", "19q6", "19q4", "19q5", "19qh"}}, + {-16.358272750745527, 106.95794571493752, 758649214, 30, []uint64{758649215, 758649301, 758649300, 758649297, 758649211, 758649209, 758649212, 758649213}, "qmh4cyh6", []string{"qmh4cyh7", "qmh4cyhe", "qmh4cyhd", "qmh4cyh9", "qmh4cyh3", "qmh4cyh1", "qmh4cyh4", "qmh4cyh5"}}, + {-52.98283921818074, -115.07857256411808, 235706410761, 42, []uint64{235706410764, 235706410766, 235706410763, 235706410762, 235706410760, 235706410754, 235706410755, 235706410758}, "1qw4t6f2d", []string{"1qw4t6f2f", "1qw4t6f2g", "1qw4t6f2e", "1qw4t6f27", "1qw4t6f26", "1qw4t6f23", "1qw4t6f29", "1qw4t6f2c"}}, + {-59.288405930798035, 159.68500524610863, 713852, 20, []uint64{713853, 713855, 713854, 713851, 713849, 713843, 713846, 713847}, "pt3w9", []string{"pt3wc", "pt3wf", "pt3wd", "pt3w6", "pt3w3", "pt3w2", "pt3w8", "pt3wb"}}, + {-16.35732318468945, 2.121197574713733, 2429105, 22, []uint64{2429108, 2429110, 2429107, 2429106, 2429104, 2429082, 2429083, 2429086}, "kj1d", []string{"kj1e", "kj1g", "kj1f", "kj1c", "kj19", "kj13", "kj16", "kj17"}}, + {68.98158810980385, -157.46978763362858, 90193942, 28, []uint64{90193943, 90193949, 90193948, 90193945, 90193939, 90193937, 90193940, 90193941}, "bs202ttyz", []string{"bs202ttzp", "bs202twp0", "bs202twnb", "bs202twn8", "bs202ttyx", "bs202ttyw", "bs202ttyy", "bs202ttzn"}}, + {-0.5080615022743586, -22.6609295506496, 260042247, 30, []uint64{260042258, 260042264, 260042253, 260042252, 260042246, 260042244, 260042245, 260042256}, "7rzvh7", []string{"7rzvhk", "7rzvhs", "7rzvhe", "7rzvhd", "7rzvh6", "7rzvh4", "7rzvh5", "7rzvhh"}}, + {-76.54543983744225, -167.8118686551461, 1595592, 28, []uint64{1595593, 1595595, 1595594, 1595551, 1595549, 1595543, 1595586, 1595587}, "062st", []string{"062sv", "062sy", "062sw", "062sq", "062sm", "062sk", "062ss", "062su"}}, + {52.23594060366304, 92.51997921278235, 1031978142922, 40, []uint64{1031978142923, 1031978142945, 1031978142944, 1031978142901, 1031978142879, 1031978142877, 1031978142920, 1031978142921}, "y13c1", []string{"y13c3", "y13c6", "y13c4", "y13bf", "y13bc", "y13bb", "y13c0", "y13c2"}}, + {31.99639310062048, -160.61960746886325, 74037338192, 38, []uint64{74037338193, 74037338195, 74037338194, 74037338183, 74037338181, 74037332719, 74037332730, 74037332731}, "8mty10", []string{"8mty11", "8mty13", "8mty12", "8mtvcr", "8mtvcp", "8mtvbz", "8mty0b", "8mty0c"}}, + {-52.397310622730856, 106.49376118130749, 2778439628, 32, []uint64{2778439629, 2778439631, 2778439630, 2778439627, 2778439625, 2778439619, 2778439622, 2778439623}, "nqetzm67s", []string{"nqetzm67u", "nqetzm67v", "nqetzm67t", "nqetzm67m", "nqetzm67k", "nqetzm677", "nqetzm67e", "nqetzm67g"}}, + {-70.77406376274303, 8.323794865835197, 555790999413, 40, []uint64{555791010336, 555791010338, 555790999415, 555790999414, 555790999412, 555790999390, 555790999391, 555791010314}, "h5mvm7v", []string{"h5mvmkj", "h5mvmkn", "h5mvm7y", "h5mvm7w", "h5mvm7t", "h5mvm7s", "h5mvm7u", "h5mvmkh"}}, + {-88.97932924178895, -124.49291084473953, 140341286789, 42, []uint64{140341286800, 140341286802, 140341286791, 140341286790, 140341286788, 140341286702, 140341286703, 140341286714}, "10pmz1g1", []string{"10pmz1g4", "10pmz1g6", "10pmz1g3", "10pmz1g2", "10pmz1g0", "10pmz1fb", "10pmz1fc", "10pmz1ff"}}, + {6.360544035182102, 145.98593527113553, 3807976, 22, []uint64{3807977, 3807979, 3807978, 3807935, 3807933, 3807927, 3807970, 3807971}, "x1pu1vzsy", []string{"x1pu1vztn", "x1pu1vztp", "x1pu1vzsz", "x1pu1vzsx", "x1pu1vzsw", "x1pu1vzst", "x1pu1vzsv", "x1pu1vztj"}}, + {-78.04899715158376, -86.37694913358426, 9080464204155, 46, []uint64{9080464204158, 9080464204244, 9080464204241, 9080464204240, 9080464204154, 9080464204152, 9080464204153, 9080464204156}, "444ef", []string{"444s4", "444s5", "444eg", "444ee", "444ed", "444e9", "444ec", "444s1"}}, + {9.61478172581701, 85.06821194122313, 871633975374, 40, []uint64{871633975375, 871633975397, 871633975396, 871633975393, 871633975371, 871633975369, 871633975372, 871633975373}, "tcsq", []string{"tcsr", "tcsx", "tcsw", "tcst", "tcsm", "tcsj", "tcsn", "tcsp"}}, + {70.89955083231325, -136.71035750779265, 381409307342190, 50, []uint64{381409307342191, 381409307342277, 381409307342276, 381409307342273, 381409307342187, 381409307342185, 381409307342188, 381409307342189}, "buwg34fe", []string{"buwg34fs", "buwg34fu", "buwg34fg", "buwg34ff", "buwg34fd", "buwg34f6", "buwg34f7", "buwg34fk"}}, + {-67.51135316700675, -114.23604935232096, 40927, 20, []uint64{51850, 51872, 40949, 40948, 40926, 40924, 40925, 51848}, "17yzbmzvg", []string{"17yzbmzy5", "17yzbmzyh", "17yzbmzvu", "17yzbmzvs", "17yzbmzve", "17yzbmzvd", "17yzbmzvf", "17yzbmzy4"}}, + {-9.404920581997445, 131.04207708657486, 49298055159, 36, []uint64{49298066082, 49298066088, 49298055165, 49298055164, 49298055158, 49298055156, 49298055157, 49298066080}, "qym4mz", []string{"qym4tb", "qym4w0", "qym4qp", "qym4qn", "qym4my", "qym4mw", "qym4mx", "qym4t8"}}, + {69.03318246031995, -114.14968793079606, 407586381379315, 50, []uint64{407586381379318, 407586381379324, 407586381379321, 407586381379320, 407586381379314, 407586381379312, 407586381379313, 407586381379316}, "ckqbdr", []string{"ckqbf2", "ckqbf8", "ckqbdx", "ckqbdw", "ckqbdq", "ckqbdn", "ckqbdp", "ckqbf0"}}, + {-20.492713890649625, 164.5875420277007, 778853, 20, []uint64{778864, 778866, 778855, 778854, 778852, 778830, 778831, 778842}, "rsm53m2", []string{"rsm53m8", "rsm53m9", "rsm53m3", "rsm53m1", "rsm53m0", "rsm53jp", "rsm53jr", "rsm53jx"}}, + {25.483916555334872, 94.11881140357582, 3826822646, 32, []uint64{3826822647, 3826822653, 3826822652, 3826822649, 3826822643, 3826822641, 3826822644, 3826822645}, "whdbvxp", []string{"whdbvxr", "whdbvz2", "whdbvz0", "whdbvyb", "whdbvwz", "whdbvwy", "whdbvxn", "whdbvxq"}}, + {-71.76596677718157, 59.664925220247795, 151498477983967, 48, []uint64{151498477984138, 151498477984160, 151498477983989, 151498477983988, 151498477983966, 151498477983964, 151498477983965, 151498477984136}, "j74rtxq", []string{"j74rtxw", "j74rtxx", "j74rtxr", "j74rtxp", "j74rtxn", "j74rtxj", "j74rtxm", "j74rtxt"}}, + {32.56536980558303, 13.804037568232161, 865705620913186, 50, []uint64{865705620913187, 865705620913193, 865705620913192, 865705620912509, 865705620912503, 865705620912501, 865705620913184, 865705620913185}, "smcc6", []string{"smccd", "smcce", "smcc7", "smcc5", "smcc4", "smcc1", "smcc3", "smcc9"}}, + {88.7078520766081, -26.328285075025633, 8255012, 24, []uint64{8255013, 8255015, 8255014, 8255011, 8255009, 8254987, 8254990, 8254991}, "grv28u", []string{"grv28v", "grv29j", "grv29h", "grv295", "grv28g", "grv28e", "grv28s", "grv28t"}}, + {-52.911668556931545, 95.14321873943845, 2836477203670, 42, []uint64{2836477203671, 2836477203677, 2836477203676, 2836477203673, 2836477203667, 2836477203665, 2836477203668, 2836477203669}, "nnedv", []string{"nneej", "nneen", "nnedy", "nnedw", "nnedt", "nneds", "nnedu", "nneeh"}}, + {-53.92830503603909, -136.2708666107501, 2062626073, 36, []uint64{2062626076, 2062626078, 2062626075, 2062626074, 2062626072, 2062626066, 2062626067, 2062626070}, "0yrj", []string{"0yrn", "0yrq", "0yrm", "0yrk", "0yrh", "0yqu", "0yqv", "0yqy"}}, + {-56.555857901636045, -116.14232123977854, 222761976754, 42, []uint64{222761976755, 222761976761, 222761976760, 222761976749, 222761976743, 222761976741, 222761976752, 222761976753}, "1mvqm07d", []string{"1mvqm07e", "1mvqm07g", "1mvqm07f", "1mvqm07c", "1mvqm079", "1mvqm073", "1mvqm076", "1mvqm077"}}, + {-86.0075191418, -136.82998579725972, 11164, 20, []uint64{11165, 11167, 11166, 11163, 11161, 11155, 11158, 11159}, "0bwwwq", []string{"0bwwwr", "0bwwwx", "0bwwww", "0bwwwt", "0bwwwm", "0bwwwj", "0bwwwn", "0bwwwp"}}, + {44.90333690015541, 79.19396348585724, 57128917417111, 46, []uint64{57128917417154, 57128917417160, 57128917417117, 57128917417116, 57128917417110, 57128917417108, 57128917417109, 57128917417152}, "tzbr6n7kc", []string{"tzbr6n7m1", "tzbr6n7m4", "tzbr6n7kf", "tzbr6n7kd", "tzbr6n7k9", "tzbr6n7k8", "tzbr6n7kb", "tzbr6n7m0"}}, + {36.44428215228254, -137.06781386065995, 76964712117, 38, []uint64{76964712160, 76964712162, 76964712119, 76964712118, 76964712116, 76964712094, 76964712095, 76964712138}, "8yqx", []string{"8yw8", "8ywb", "8yqz", "8yqy", "8yqw", "8yqq", "8yqr", "8yw2"}}, + {-89.27144036942627, -71.86586879706009, 136866328, 30, []uint64{136866329, 136866331, 136866330, 136866319, 136866317, 136866311, 136866322, 136866323}, "42huh", []string{"42huk", "42hum", "42huj", "42hgv", "42hgu", "42hgg", "42hu5", "42hu7"}}, + {45.98510287722456, -100.55626747361384, 398044599259952, 50, []uint64{398044599259953, 398044599259955, 398044599259954, 398044599259943, 398044599259941, 398044599259919, 398044599259930, 398044599259931}, "cb0mxg34", []string{"cb0mxg35", "cb0mxg37", "cb0mxg36", "cb0mxg33", "cb0mxg31", "cb0mxg2c", "cb0mxg2f", "cb0mxg2g"}}, + {4.282082128833267, 15.497010603547096, 52946214437405, 46, []uint64{52946214437448, 52946214437450, 52946214437407, 52946214437406, 52946214437404, 52946214437398, 52946214437399, 52946214437442}, "s2g0", []string{"s2g1", "s2g3", "s2g2", "s2er", "s2ep", "s2dz", "s2fb", "s2fc"}}, + {36.89882058263174, -47.16459898248286, 445472794303, 40, []uint64{445472794346, 445472795712, 445472795669, 445472795668, 445472794302, 445472794300, 445472794301, 445472794344}, "dyw3ytp", []string{"dyw3ytr", "dyw3yv2", "dyw3yv0", "dyw3yub", "dyw3ysz", "dyw3ysy", "dyw3ytn", "dyw3ytq"}}, + {-52.63339266340245, -132.32974445843138, 13712067, 28, []uint64{13712070, 13712076, 13712073, 13712072, 13712066, 13712064, 13712065, 13712068}, "1n9usf2km", []string{"1n9usf2kt", "1n9usf2kw", "1n9usf2kq", "1n9usf2kn", "1n9usf2kj", "1n9usf2kh", "1n9usf2kk", "1n9usf2ks"}}, + {87.51099293013249, -58.9683701217582, 2004358931, 32, []uint64{2004358934, 2004358940, 2004358937, 2004358936, 2004358930, 2004358928, 2004358929, 2004358932}, "fxw1f4u", []string{"fxw1f5h", "fxw1f5j", "fxw1f4v", "fxw1f4t", "fxw1f4s", "fxw1f4e", "fxw1f4g", "fxw1f55"}}, + {-49.97742152905266, -97.29950436335639, 258621, 22, []uint64{258664, 258666, 258623, 258622, 258620, 258614, 258615, 258658}, "1z4g9v", []string{"1z4g9y", "1z4gdn", "1z4gdj", "1z4gdh", "1z4g9u", "1z4g9s", "1z4g9t", "1z4g9w"}}, + {64.26201192067674, 17.904405992128858, 57696034671180, 46, []uint64{57696034671181, 57696034671183, 57696034671182, 57696034671179, 57696034671177, 57696034671171, 57696034671174, 57696034671175}, "u7ktx6", []string{"u7ktx7", "u7ktxe", "u7ktxd", "u7ktx9", "u7ktx3", "u7ktx1", "u7ktx4", "u7ktx5"}}, + {84.61596930376254, 92.96433984691976, 65842264572, 36, []uint64{65842264573, 65842264575, 65842264574, 65842264571, 65842264569, 65842264563, 65842264566, 65842264567}, "yp4177", []string{"yp417k", "yp417s", "yp417e", "yp417d", "yp4176", "yp4174", "yp4175", "yp417h"}}, + {-0.7337647678432546, 74.16253405591121, 2675236784, 32, []uint64{2675236785, 2675236787, 2675236786, 2675236775, 2675236773, 2675236751, 2675236762, 2675236763}, "mxue", []string{"mxus", "mxuu", "mxug", "mxuf", "mxud", "mxu6", "mxu7", "mxuk"}}, + {-61.84747547261941, 2.804881974632707, 554740411, 30, []uint64{554740414, 554827796, 554827793, 554827792, 554740410, 554740408, 554740409, 554740412}, "hj1bpv", []string{"hj1bpy", "hj400n", "hj400j", "hj400h", "hj1bpu", "hj1bps", "hj1bpt", "hj1bpw"}}, + {-18.331446884563775, -33.60891483808882, 266379698583468, 50, []uint64{266379698583469, 266379698583471, 266379698583470, 266379698583467, 266379698583465, 266379698583459, 266379698583462, 266379698583463}, "7k8p", []string{"7kb0", "7kb2", "7k8r", "7k8q", "7k8n", "7hxy", "7hxz", "7hzb"}}, + {-65.73282637103694, 138.83280922503036, 189170937470690, 48, []uint64{189170937470691, 189170937470697, 189170937470696, 189170937470653, 189170937470647, 189170937470645, 189170937470688, 189170937470689}, "ph6dp", []string{"ph6dr", "ph6f2", "ph6f0", "ph6cb", "ph69z", "ph69y", "ph6dn", "ph6dq"}}, + {-89.84696921879367, 89.65338356833672, 609862075004151, 50, []uint64{609862075004322, 609862075004328, 609862075004157, 609862075004156, 609862075004150, 609862075004148, 609862075004149, 609862075004320}, "jbpbb5ge7", []string{"jbpbb5gee", "jbpbb5ges", "jbpbb5gek", "jbpbb5geh", "jbpbb5ge5", "jbpbb5ge4", "jbpbb5ge6", "jbpbb5ged"}}, + {76.62702684582109, 142.09046076441882, 4235351501, 32, []uint64{4235351512, 4235351514, 4235351503, 4235351502, 4235351500, 4235351494, 4235351495, 4235351506}, "zjt5cme", []string{"zjt5cmg", "zjt5cmu", "zjt5cms", "zjt5cmk", "zjt5cm7", "zjt5cm6", "zjt5cmd", "zjt5cmf"}}, + {9.905963105906267, 122.88249192467265, 949979719, 30, []uint64{949979730, 949979736, 949979725, 949979724, 949979718, 949979716, 949979717, 949979728}, "w9z2k726", []string{"w9z2k727", "w9z2k72e", "w9z2k72d", "w9z2k729", "w9z2k723", "w9z2k721", "w9z2k724", "w9z2k725"}}, + {9.660175888915546, -15.934797062975122, 1785843163, 32, []uint64{1785843166, 1785843188, 1785843185, 1785843184, 1785843162, 1785843160, 1785843161, 1785843164}, "e9sw", []string{"e9sx", "e9sz", "e9sy", "e9sv", "e9st", "e9sm", "e9sq", "e9sr"}}, + {87.18758878849621, 147.4279916661617, 66536072, 26, []uint64{66536073, 66536075, 66536074, 66525151, 66525149, 66525143, 66536066, 66536067}, "zr8b4b", []string{"zr8b4c", "zr8b51", "zr8b50", "zr2zgp", "zr2zfz", "zr2zfx", "zr8b48", "zr8b49"}}, + {55.74578325062612, 22.09516610816354, 57449056439609, 46, []uint64{57449056439612, 57449056439614, 57449056439611, 57449056439610, 57449056439608, 57449056439602, 57449056439603, 57449056439606}, "u3ztnu1n", []string{"u3ztnu1p", "u3ztnu1r", "u3ztnu1q", "u3ztnu1m", "u3ztnu1j", "u3ztnu0v", "u3ztnu0y", "u3ztnu0z"}}, + {66.43284981916077, 10.885605309857056, 56235785849, 36, []uint64{56235785852, 56235785854, 56235785851, 56235785850, 56235785848, 56235785842, 56235785843, 56235785846}, "u5z9zt", []string{"u5z9zw", "u5z9zy", "u5z9zv", "u5z9zu", "u5z9zs", "u5z9zk", "u5z9zm", "u5z9zq"}}, + {-87.95000444873585, -116.60049806089955, 2267481, 26, []uint64{2267484, 2267486, 2267483, 2267482, 2267480, 2267474, 2267475, 2267478}, "12m5dtnn", []string{"12m5dtnp", "12m5dtnr", "12m5dtnq", "12m5dtnm", "12m5dtnj", "12m5dtjv", "12m5dtjy", "12m5dtjz"}}, + {-28.699596980673967, -9.147935971152066, 999763708, 32, []uint64{999763709, 999763711, 999763710, 999763707, 999763705, 999763699, 999763702, 999763703}, "7fckxz6st", []string{"7fckxz6sv", "7fckxz6sy", "7fckxz6sw", "7fckxz6sq", "7fckxz6sm", "7fckxz6sk", "7fckxz6ss", "7fckxz6su"}}, + {72.64570544435992, 135.14134687914338, 270671339751, 38, []uint64{270671339762, 270671339768, 270671339757, 270671339756, 270671339750, 270671339748, 270671339749, 270671339760}, "zhbj70wz", []string{"zhbj70yb", "zhbj70z0", "zhbj70xp", "zhbj70xn", "zhbj70wy", "zhbj70ww", "zhbj70wx", "zhbj70y8"}}, + {-8.333968713428476, -123.5746389279957, 121088, 20, []uint64{121089, 121091, 121090, 120919, 120917, 119551, 119722, 119723}, "3q80efze", []string{"3q80efzs", "3q80efzu", "3q80efzg", "3q80efzf", "3q80efzd", "3q80efz6", "3q80efz7", "3q80efzk"}}, + {-75.1408847447019, 169.2011167316814, 754559587716015, 50, []uint64{754559587716026, 754559587721488, 754559587721477, 754559587721476, 754559587716014, 754559587716012, 754559587716013, 754559587716024}, "pf8kd3", []string{"pf8kd6", "pf8kdd", "pf8kd9", "pf8kd8", "pf8kd2", "pf8kd0", "pf8kd1", "pf8kd4"}}, + {68.78052144360845, -13.379227889920003, 33866365506, 36, []uint64{33866365507, 33866365513, 33866365512, 33866365469, 33866365463, 33866365461, 33866365504, 33866365505}, "gsnr", []string{"gsq2", "gsq8", "gsnx", "gsnw", "gsnq", "gsnn", "gsnp", "gsq0"}}, + {-74.89415750665648, 152.03201402979903, 11387821975, 34, []uint64{11387822018, 11387822024, 11387821981, 11387821980, 11387821974, 11387821972, 11387821973, 11387822016}, "p6sjgtf", []string{"p6sjgw4", "p6sjgw5", "p6sjgtg", "p6sjgte", "p6sjgtd", "p6sjgt9", "p6sjgtc", "p6sjgw1"}}, + {-11.305319367798802, -77.63503914076136, 216415, 20, []uint64{219146, 219168, 216437, 216436, 216414, 216412, 216413, 219144}, "6mbz9m", []string{"6mbz9q", "6mbz9w", "6mbz9t", "6mbz9s", "6mbz9k", "6mbz9h", "6mbz9j", "6mbz9n"}}, + {-9.546942663451773, -102.9423424486522, 34276873251210, 48, []uint64{34276873251211, 34276873251233, 34276873251232, 34276873251061, 34276873251039, 34276873251037, 34276873251208, 34276873251209}, "3wqc9", []string{"3wqcc", "3wqcf", "3wqcd", "3wqc6", "3wqc3", "3wqc2", "3wqc8", "3wqcb"}}, + {-84.1589249215358, 129.38990292456583, 43721491916, 36, []uint64{43721491917, 43721491919, 43721491918, 43721491915, 43721491913, 43721491907, 43721491910, 43721491911}, "nch10r6fe", []string{"nch10r6fg", "nch10r6fu", "nch10r6fs", "nch10r6fk", "nch10r6f7", "nch10r6f6", "nch10r6fd", "nch10r6ff"}}, + {-29.951769854320453, 121.8366809092986, 187931073, 28, []uint64{187931076, 187931078, 187931075, 187931074, 187931072, 187930986, 187930987, 187930990}, "qdwts7q", []string{"qdwts7w", "qdwts7x", "qdwts7r", "qdwts7p", "qdwts7n", "qdwts7j", "qdwts7m", "qdwts7t"}}, + {-64.5901646950515, 84.72636767034419, 156886811787048, 48, []uint64{156886811787049, 156886811787051, 156886811787050, 156886811786879, 156886811786877, 156886811786871, 156886811787042, 156886811787043}, "jus0x", []string{"jus0z", "jus2b", "jus28", "jus22", "jus0r", "jus0q", "jus0w", "jus0y"}}, + {65.12444451799092, -32.46139465615852, 133938183766838, 48, []uint64{133938183766839, 133938183766845, 133938183766844, 133938183766841, 133938183766835, 133938183766833, 133938183766836, 133938183766837}, "g78fmrd3", []string{"g78fmrd6", "g78fmrdd", "g78fmrd9", "g78fmrd8", "g78fmrd2", "g78fmrd0", "g78fmrd1", "g78fmrd4"}}, + {-46.76911997864954, -52.666966274748134, 2674284437, 34, []uint64{2674284480, 2674284482, 2674284439, 2674284438, 2674284436, 2674284350, 2674284351, 2674284394}, "4zdtctcn4", []string{"4zdtctcn6", "4zdtctcn7", "4zdtctcn5", "4zdtctcjg", "4zdtctcjf", "4zdtctcjc", "4zdtctcn1", "4zdtctcn3"}}, + {24.249350212106947, -74.58568715194997, 421736414, 30, []uint64{421736415, 421736437, 421736436, 421736433, 421736411, 421736409, 421736412, 421736413}, "dk6cyy2q", []string{"dk6cyy2r", "dk6cyy2x", "dk6cyy2w", "dk6cyy2t", "dk6cyy2m", "dk6cyy2j", "dk6cyy2n", "dk6cyy2p"}}, + {-64.65948590049811, -161.79583197418833, 5035954603, 38, []uint64{5035954606, 5035954948, 5035954945, 5035954944, 5035954602, 5035954600, 5035954601, 5035954604}, "0ksbn", []string{"0ksbq", "0ksbr", "0ksbp", "0kkzz", "0kkzy", "0kkzv", "0ksbj", "0ksbm"}}, + {14.21376557990152, -39.784427353108214, 28890698205899, 46, []uint64{28890698205902, 28890698205924, 28890698205921, 28890698205920, 28890698205898, 28890698205896, 28890698205897, 28890698205900}, "e4e8yetc5", []string{"e4e8yetc7", "e4e8yetck", "e4e8yetch", "e4e8yetbu", "e4e8yetbg", "e4e8yetbf", "e4e8yetc4", "e4e8yetc6"}}, + {37.93950059005873, 153.2295979586779, 996966350, 30, []uint64{996966351, 996966373, 996966372, 996966369, 996966347, 996966345, 996966348, 996966349}, "xqszyfdms", []string{"xqszyfdmu", "xqszyfdmv", "xqszyfdmt", "xqszyfdmm", "xqszyfdmk", "xqszyfdm7", "xqszyfdme", "xqszyfdmg"}}, + {63.307150621225446, 24.028775601516827, 56713291411, 36, []uint64{56713291414, 56713291420, 56713291417, 56713291416, 56713291410, 56713291408, 56713291409, 56713291412}, "ue30", []string{"ue31", "ue33", "ue32", "ue1r", "ue1p", "ue0z", "ue2b", "ue2c"}}, + {-23.846674803222413, 50.343230086698895, 643278956, 30, []uint64{643278957, 643278959, 643278958, 643278955, 643278953, 643278947, 643278950, 643278951}, "m5gb3dfg", []string{"m5gb3dfu", "m5gb3dgh", "m5gb3dg5", "m5gb3dg4", "m5gb3dff", "m5gb3dfd", "m5gb3dfe", "m5gb3dfs"}}, + {16.736991829624458, -11.97308294463437, 7197289413, 34, []uint64{7197289424, 7197289426, 7197289415, 7197289414, 7197289412, 7197289326, 7197289327, 7197289338}, "edzrpwckn", []string{"edzrpwckq", "edzrpwckr", "edzrpwckp", "edzrpwc7z", "edzrpwc7y", "edzrpwc7v", "edzrpwckj", "edzrpwckm"}}, + {76.22320765251425, 37.527162012818735, 14418080682, 34, []uint64{14418080683, 14418082049, 14418082048, 14418081877, 14418080511, 14418080509, 14418080680, 14418080681}, "uvd9tun", []string{"uvd9tuq", "uvd9tur", "uvd9tup", "uvd9tgz", "uvd9tgy", "uvd9tgv", "uvd9tuj", "uvd9tum"}}, + {-26.40133030932339, 149.69971718540182, 3044111, 22, []uint64{3044122, 3044144, 3044133, 3044132, 3044110, 3044108, 3044109, 3044120}, "r763y", []string{"r766n", "r766p", "r763z", "r763x", "r763w", "r763t", "r763v", "r766j"}}, + {-54.996847518137656, 51.95091055193913, 591953552, 30, []uint64{591953553, 591953555, 591953554, 591953543, 591953541, 591953455, 591953466, 591953467}, "jnhznhj", []string{"jnhznhm", "jnhznhq", "jnhznhn", "jnhzn5y", "jnhzn5v", "jnhzn5u", "jnhznhh", "jnhznhk"}}, + {-70.03135227999883, 63.47783150112082, 148118112172, 38, []uint64{148118112173, 148118112175, 148118112174, 148118112171, 148118112169, 148118112163, 148118112166, 148118112167}, "j7t1s7", []string{"j7t1sk", "j7t1ss", "j7t1se", "j7t1sd", "j7t1s6", "j7t1s4", "j7t1s5", "j7t1sh"}}, + {-59.40056793022086, 138.79717555025127, 722694790, 30, []uint64{722694791, 722694797, 722694796, 722694793, 722694787, 722694785, 722694788, 722694789}, "pj6wn6m", []string{"pj6wn6t", "pj6wn6w", "pj6wn6q", "pj6wn6n", "pj6wn6j", "pj6wn6h", "pj6wn6k", "pj6wn6s"}}, + {-65.9170521141059, 159.98021385192988, 712811, 20, []uint64{712814, 712900, 712897, 712896, 712810, 712808, 712809, 712812}, "ps3c02nj3", []string{"ps3c02nj9", "ps3c02njd", "ps3c02nj6", "ps3c02nj4", "ps3c02nj1", "ps3c02nj0", "ps3c02nj2", "ps3c02nj8"}}, + {39.1997554674017, -58.87412956904156, 113513167594892, 48, []uint64{113513167594893, 113513167594895, 113513167594894, 113513167594891, 113513167594889, 113513167594883, 113513167594886, 113513167594887}, "dwyph21", []string{"dwyph23", "dwyph26", "dwyph24", "dwynurf", "dwynurc", "dwynurb", "dwyph20", "dwyph22"}}, + {-66.44389028756996, 122.77105896774447, 174241290, 28, []uint64{174241291, 174241313, 174241312, 174240629, 174240607, 174240605, 174241288, 174241289}, "nspq1", []string{"nspq3", "nspq6", "nspq4", "nspmf", "nspmc", "nspmb", "nspq0", "nspq2"}}, + {-4.7141804937528065, 34.78397687879624, 41714445800183, 46, []uint64{41714445800354, 41714445800360, 41714445800189, 41714445800188, 41714445800182, 41714445800180, 41714445800181, 41714445800352}, "kz0tpt", []string{"kz0tpw", "kz0tpy", "kz0tpv", "kz0tpu", "kz0tps", "kz0tpk", "kz0tpm", "kz0tpq"}}, + {60.73067540410557, -151.98837166440353, 91391432537073, 48, []uint64{91391432537076, 91391432537078, 91391432537075, 91391432537074, 91391432537072, 91391432537050, 91391432537051, 91391432537054}, "bdgcmrtmy", []string{"bdgcmrtqn", "bdgcmrtqp", "bdgcmrtmz", "bdgcmrtmx", "bdgcmrtmw", "bdgcmrtmt", "bdgcmrtmv", "bdgcmrtqj"}}, + {-44.621130966057535, -121.11390205976204, 102807738, 30, []uint64{102807739, 102808081, 102808080, 102808069, 102807727, 102807725, 102807736, 102807737}, "321f5uzx", []string{"321f5vp8", "321f5vpb", "321f5uzz", "321f5uzy", "321f5uzw", "321f5uzq", "321f5uzr", "321f5vp2"}}, + {69.77164766475107, 118.41463875677437, 16914939533497, 44, []uint64{16914939533500, 16914939533502, 16914939533499, 16914939533498, 16914939533496, 16914939533490, 16914939533491, 16914939533494}, "yskhy", []string{"yskjn", "yskjp", "yskhz", "yskhx", "yskhw", "yskht", "yskhv", "yskjj"}}, + {-74.22044094440571, 42.129767883277935, 2209791779, 32, []uint64{2209791782, 2209791788, 2209791785, 2209791784, 2209791778, 2209791776, 2209791777, 2209791780}, "hfvcy8tf", []string{"hfvcy8tg", "hfvcy8w5", "hfvcy8w4", "hfvcy8w1", "hfvcy8tc", "hfvcy8t9", "hfvcy8td", "hfvcy8te"}}, + {-63.645252313741366, 139.10732936664135, 11549472574, 34, []uint64{11549472575, 11549472661, 11549472660, 11549472657, 11549472571, 11549472569, 11549472572, 11549472573}, "phdvvm", []string{"phdvvq", "phdvvw", "phdvvt", "phdvvs", "phdvvk", "phdvvh", "phdvvj", "phdvvn"}}, + {15.138255420300979, -157.89315369579708, 1129293960627, 42, []uint64{1129293960630, 1129293960636, 1129293960633, 1129293960632, 1129293960626, 1129293960624, 1129293960625, 1129293960628}, "86xwp5cd", []string{"86xwp5ce", "86xwp5cg", "86xwp5cf", "86xwp5cc", "86xwp5c9", "86xwp5c3", "86xwp5c6", "86xwp5c7"}}, + {-12.577460875219003, -160.727011026931, 351701872, 32, []uint64{351701873, 351701875, 351701874, 351701863, 351701861, 351701839, 351701850, 351701851}, "2mv8qw", []string{"2mv8qx", "2mv8qz", "2mv8qy", "2mv8qv", "2mv8qt", "2mv8qm", "2mv8qq", "2mv8qr"}}, + {2.221144786293735, 169.54367489885772, 983654800, 30, []uint64{983654801, 983654803, 983654802, 983654791, 983654789, 983654703, 983654714, 983654715}, "xb2s", []string{"xb2t", "xb2v", "xb2u", "xb2g", "xb2e", "xb27", "xb2k", "xb2m"}}, + {-34.52112347125512, 123.01162532513263, 2994634391, 32, []uint64{2994634434, 2994634440, 2994634397, 2994634396, 2994634390, 2994634388, 2994634389, 2994634432}, "q9z7", []string{"q9zk", "q9zs", "q9ze", "q9zd", "q9z6", "q9z4", "q9z5", "q9zh"}}, + {72.28269904988701, 19.036866881258902, 58470413165675, 46, []uint64{58470413165678, 58470413165764, 58470413165761, 58470413165760, 58470413165674, 58470413165672, 58470413165673, 58470413165676}, "ukve1nwj", []string{"ukve1nwn", "ukve1nwq", "ukve1nwm", "ukve1nwk", "ukve1nwh", "ukve1ntu", "ukve1ntv", "ukve1nty"}}, + {13.00977785015128, 44.83578379772368, 13447249036409, 44, []uint64{13447249036412, 13447249036414, 13447249036411, 13447249036410, 13447249036408, 13447249036402, 13447249036403, 13447249036406}, "sfrfh22", []string{"sfrfh28", "sfrfh29", "sfrfh23", "sfrfh21", "sfrfh20", "sfrfh0p", "sfrfh0r", "sfrfh0x"}}, + {-0.2250900326325791, -69.58919700980186, 14493173224, 36, []uint64{14493173225, 14493173227, 14493173226, 14493173183, 14493173181, 14493173175, 14493173218, 14493173219}, "6ryw", []string{"6ryx", "6ryz", "6ryy", "6ryv", "6ryt", "6rym", "6ryq", "6ryr"}}, + {62.19527466534055, -20.055410084125498, 8271855305, 34, []uint64{8271855308, 8271855310, 8271855307, 8271855306, 8271855304, 8271855298, 8271855299, 8271855302}, "ge19zdk", []string{"ge19zds", "ge19zdt", "ge19zdm", "ge19zdj", "ge19zdh", "ge19zd5", "ge19zd7", "ge19zde"}}, + {-39.61328858271008, -144.66570884981775, 311905345, 32, []uint64{311905348, 311905350, 311905347, 311905346, 311905344, 311903978, 311903979, 311903982}, "2bcnsh9", []string{"2bcnshc", "2bcnshf", "2bcnshd", "2bcnsh6", "2bcnsh3", "2bcnsh2", "2bcnsh8", "2bcnshb"}}, + {15.82524006231688, -23.952358063554158, 116260103508687, 48, []uint64{116260103508698, 116260103508720, 116260103508709, 116260103508708, 116260103508686, 116260103508684, 116260103508685, 116260103508696}, "e6yf", []string{"e6yg", "e6z5", "e6z4", "e6z1", "e6yc", "e6y9", "e6yd", "e6ye"}}, + {21.497141563362675, -99.45696494203003, 1242520, 22, []uint64{1242521, 1242523, 1242522, 1242511, 1242509, 1242503, 1242514, 1242515}, "9gc62c3", []string{"9gc62c9", "9gc62cd", "9gc62c6", "9gc62c4", "9gc62c1", "9gc62c0", "9gc62c2", "9gc62c8"}}, + {-84.55961711473356, 122.95542103308253, 664566, 20, []uint64{664567, 664573, 664572, 664569, 664563, 664561, 664564, 664565}, "n8zqvy", []string{"n8zqvz", "n8zqyp", "n8zqyn", "n8zqyj", "n8zqvv", "n8zqvt", "n8zqvw", "n8zqvx"}}, + {-30.86554266273742, 157.4307796061912, 12171947, 24, []uint64{12171950, 12259332, 12259329, 12259328, 12171946, 12171944, 12171945, 12171948}, "r6xbq", []string{"r6xbw", "r6xbx", "r6xbr", "r6xbp", "r6xbn", "r6xbj", "r6xbm", "r6xbt"}}, + {3.475822044914821, -140.84973670647014, 292933356816562, 50, []uint64{292933356816563, 292933356816569, 292933356816568, 292933356816557, 292933356816551, 292933356816549, 292933356816560, 292933356816561}, "8begfbu", []string{"8begfch", "8begfcj", "8begfbv", "8begfbt", "8begfbs", "8begfbe", "8begfbg", "8begfc5"}}, + {-17.692383868139586, -58.73794591981279, 14217582, 26, []uint64{14217583, 14217669, 14217668, 14217665, 14217579, 14217577, 14217580, 14217581}, "6sy5r7hq6", []string{"6sy5r7hqd", "6sy5r7hqe", "6sy5r7hq7", "6sy5r7hq5", "6sy5r7hq4", "6sy5r7hq1", "6sy5r7hq3", "6sy5r7hq9"}}, + {-56.77909910833114, 89.68761924884166, 38385899505, 36, []uint64{38385899508, 38385899510, 38385899507, 38385899506, 38385899504, 38385899482, 38385899483, 38385899486}, "jvzubzs", []string{"jvzubzu", "jvzubzv", "jvzubzt", "jvzubzm", "jvzubzk", "jvzubz7", "jvzubze", "jvzubzg"}}, + {28.727723476171377, 122.34581178484223, 3958504756468, 42, []uint64{3958504756469, 3958504756471, 3958504756470, 3958504756467, 3958504756465, 3958504756443, 3958504756446, 3958504756447}, "wtp52", []string{"wtp58", "wtp59", "wtp53", "wtp51", "wtp50", "wtngp", "wtngr", "wtngx"}}, + {8.82135712839407, -50.95197451027343, 1698315736554, 42, []uint64{1698315736555, 1698315736897, 1698315736896, 1698315736853, 1698315736511, 1698315736509, 1698315736552, 1698315736553}, "dcef", []string{"dceg", "dcs5", "dcs4", "dcs1", "dcec", "dce9", "dced", "dcee"}}, + {-37.01325054623885, 76.8116785109014, 10612214003136, 44, []uint64{10612214003137, 10612214003139, 10612214003138, 10612214003095, 10612214003093, 10612214003007, 10612214003050, 10612214003051}, "m9qt7vuw1", []string{"m9qt7vuw3", "m9qt7vuw6", "m9qt7vuw4", "m9qt7vutf", "m9qt7vutc", "m9qt7vutb", "m9qt7vuw0", "m9qt7vuw2"}}, + {9.809802787625813, -3.1204464148613624, 117596023156229, 48, []uint64{117596023156240, 117596023156242, 117596023156231, 117596023156230, 117596023156228, 117596023155886, 117596023155887, 117596023155898}, "ectzb", []string{"ecvb0", "ecvb1", "ectzc", "ectz9", "ectz8", "ectxx", "ectxz", "ecv8p"}}, + {54.884618981945096, -113.71785508864559, 97848085214347, 48, []uint64{97848085214350, 97848085214372, 97848085214369, 97848085214368, 97848085214346, 97848085214344, 97848085214345, 97848085214348}, "c3z0h", []string{"c3z0k", "c3z0m", "c3z0j", "c3xpv", "c3xpu", "c3xpg", "c3z05", "c3z07"}}, + {48.61369275995821, 154.09559898712178, 4272655209790, 42, []uint64{4272655209791, 4272655209877, 4272655209876, 4272655209873, 4272655209787, 4272655209785, 4272655209788, 4272655209789}, "z2tsd9bgr", []string{"z2tsd9bgx", "z2tsd9c58", "z2tsd9c52", "z2tsd9c50", "z2tsd9bgp", "z2tsd9bgn", "z2tsd9bgq", "z2tsd9bgw"}}, + {71.2331680591742, 162.3372301515774, 69848224063950, 46, []uint64{69848224063951, 69848224063973, 69848224063972, 69848224063969, 69848224063947, 69848224063945, 69848224063948, 69848224063949}, "zsemnp", []string{"zsemq0", "zsemq2", "zsemnr", "zsemnq", "zsemnn", "zsemjy", "zsemjz", "zsemmb"}}, + {20.756969094421933, 93.68759605754167, 247775635387561, 48, []uint64{247775635387564, 247775635387566, 247775635387563, 247775635387562, 247775635387560, 247775635387554, 247775635387555, 247775635387558}, "w5dw5f", []string{"w5dw5g", "w5dwh5", "w5dwh4", "w5dwh1", "w5dw5c", "w5dw59", "w5dw5d", "w5dw5e"}}, + {75.48222284056828, -86.94008919675252, 7621913, 24, []uint64{7621916, 7621918, 7621915, 7621914, 7621912, 7621906, 7621907, 7621910}, "fj6jmt", []string{"fj6jmw", "fj6jmy", "fj6jmv", "fj6jmu", "fj6jms", "fj6jmk", "fj6jmm", "fj6jmq"}}, + {-72.52902522298973, 126.24225461850801, 175837567159, 38, []uint64{175837567202, 175837567208, 175837567165, 175837567164, 175837567158, 175837567156, 175837567157, 175837567200}, "ng1g2s", []string{"ng1g2t", "ng1g2v", "ng1g2u", "ng1g2g", "ng1g2e", "ng1g27", "ng1g2k", "ng1g2m"}}, + {-1.3224191399294796, -173.05827836785465, 5897703109254, 46, []uint64{5897703109255, 5897703109261, 5897703109260, 5897703109257, 5897703109251, 5897703109249, 5897703109252, 5897703109253}, "2pubm", []string{"2pubt", "2pubw", "2pubq", "2pubn", "2pubj", "2pubh", "2pubk", "2pubs"}}, + {45.9838979438282, 108.21073983210954, 15770046, 24, []uint64{15770047, 15770389, 15770388, 15770385, 15770043, 15770041, 15770044, 15770045}, "y2hvw7he", []string{"y2hvw7hs", "y2hvw7hu", "y2hvw7hg", "y2hvw7hf", "y2hvw7hd", "y2hvw7h6", "y2hvw7h7", "y2hvw7hk"}}, + {77.17992117074027, -55.86632626323264, 31894995755, 36, []uint64{31894995758, 31894995844, 31894995841, 31894995840, 31894995754, 31894995752, 31894995753, 31894995756}, "fv8r0", []string{"fv8r2", "fv8r3", "fv8r1", "fv8qc", "fv8qb", "fv8nz", "fv8pp", "fv8pr"}}, + {-14.581920880533303, -96.81015974917682, 8270219333, 36, []uint64{8270219344, 8270219346, 8270219335, 8270219334, 8270219332, 8270217966, 8270217967, 8270217978}, "3v7jj1", []string{"3v7jj4", "3v7jj6", "3v7jj3", "3v7jj2", "3v7jj0", "3v7jhb", "3v7jhc", "3v7jhf"}}, + {-43.76725520670879, -104.29334911156911, 449088883694, 42, []uint64{449088883695, 449088889157, 449088889156, 449088889153, 449088883691, 449088883689, 449088883692, 449088883693}, "38jz", []string{"38mb", "38q0", "38np", "38nn", "38jy", "38jw", "38jx", "38m8"}}, + {4.531243137811529, -158.4578160928795, 67887984, 28, []uint64{67887985, 67887987, 67887986, 67887975, 67887973, 67887951, 67887962, 67887963}, "82z3f", []string{"82z64", "82z65", "82z3g", "82z3e", "82z3d", "82z39", "82z3c", "82z61"}}, + {-1.9673689292685594, -52.63993027215476, 228760, 20, []uint64{228761, 228763, 228762, 228751, 228749, 228743, 228754, 228755}, "6zdsf1ug0", []string{"6zdsf1ug2", "6zdsf1ug3", "6zdsf1ug1", "6zdsf1ufc", "6zdsf1ufb", "6zdsf1udz", "6zdsf1uep", "6zdsf1uer"}}, + {32.93547646810475, 131.2417065853224, 15136371, 24, []uint64{15136374, 15136380, 15136377, 15136376, 15136370, 15136368, 15136369, 15136372}, "wvv767xr1", []string{"wvv767xr3", "wvv767xr6", "wvv767xr4", "wvv767xqf", "wvv767xqc", "wvv767xqb", "wvv767xr0", "wvv767xr2"}}, + {-8.9923861327552, -92.413531967788, 518985, 22, []uint64{518988, 518990, 518987, 518986, 518984, 518978, 518979, 518982}, "3yqk", []string{"3yqm", "3yqt", "3yqs", "3yqe", "3yq7", "3yq5", "3yqh", "3yqj"}}, + {15.685144807852325, -13.910229313652962, 7027730, 24, []uint64{7027731, 7027737, 7027736, 7027725, 7027719, 7027717, 7027728, 7027729}, "edy1", []string{"edy4", "edy6", "edy3", "edy2", "edy0", "edvb", "edvc", "edvf"}}, + {62.77726895869273, -53.8636372522451, 509135777014407, 50, []uint64{509135777014418, 509135777014424, 509135777014413, 509135777014412, 509135777014406, 509135777014404, 509135777014405, 509135777014416}, "fg1tnk", []string{"fg1tnm", "fg1tnt", "fg1tns", "fg1tne", "fg1tn7", "fg1tn5", "fg1tnh", "fg1tnj"}}, + {-40.49551235462422, 99.75853188091423, 739192720, 30, []uint64{739192721, 739192723, 739192722, 739192711, 739192709, 739192623, 739192634, 739192635}, "q0ycwh1bw", []string{"q0ycwh1by", "q0ycwh1bz", "q0ycwh1bx", "q0ycwh1br", "q0ycwh1bq", "q0ycwh1bm", "q0ycwh1bt", "q0ycwh1bv"}}, + {-66.23312842240557, 107.00191499007633, 45253195121826, 46, []uint64{45253195121827, 45253195121833, 45253195121832, 45253195121149, 45253195121143, 45253195121141, 45253195121824, 45253195121825}, "nkhp4ysk", []string{"nkhp4ysm", "nkhp4yst", "nkhp4yss", "nkhp4yse", "nkhp4ys7", "nkhp4ys5", "nkhp4ysh", "nkhp4ysj"}}, + {-0.0867318293749122, -175.7265599705279, 367174748921, 42, []uint64{367174748924, 367174748926, 367174748923, 367174748922, 367174748920, 367174748914, 367174748915, 367174748918}, "2pgp90", []string{"2pgp91", "2pgp93", "2pgp92", "2pgp3r", "2pgp3p", "2pgp2z", "2pgp8b", "2pgp8c"}}, + {1.119142938754507, 121.41039416773128, 59286924, 26, []uint64{59286925, 59286927, 59286926, 59286923, 59286921, 59286915, 59286918, 59286919}, "w8nq6g", []string{"w8nq6u", "w8nq7h", "w8nq75", "w8nq74", "w8nq6f", "w8nq6d", "w8nq6e", "w8nq6s"}}, + {-27.16859915331589, 170.63588256653748, 787533022, 30, []uint64{787533023, 787533045, 787533044, 787533041, 787533019, 787533017, 787533020, 787533021}, "rg1m6yj72", []string{"rg1m6yj78", "rg1m6yj79", "rg1m6yj73", "rg1m6yj71", "rg1m6yj70", "rg1m6yj5p", "rg1m6yj5r", "rg1m6yj5x"}}, + {74.5165185136866, -44.08016585907899, 546489318881337, 50, []uint64{546489318881340, 546489318881342, 546489318881339, 546489318881338, 546489318881336, 546489318881330, 546489318881331, 546489318881334}, "gj0xuvmf", []string{"gj0xuvmg", "gj0xuvq5", "gj0xuvq4", "gj0xuvq1", "gj0xuvmc", "gj0xuvm9", "gj0xuvmd", "gj0xuvme"}}, + {1.8017563141474966, -178.86468387400964, 67128847, 28, []uint64{67128858, 67128880, 67128869, 67128868, 67128846, 67128844, 67128845, 67128856}, "802f1zfxy", []string{"802f3b48n", "802f3b48p", "802f1zfxz", "802f1zfxx", "802f1zfxw", "802f1zfxt", "802f1zfxv", "802f3b48j"}}, + {8.055847713352705, -108.43548817650299, 4986434670, 34, []uint64{4986434671, 4986434757, 4986434756, 4986434753, 4986434667, 4986434665, 4986434668, 4986434669}, "996vu6", []string{"996vu7", "996vue", "996vud", "996vu9", "996vu3", "996vu1", "996vu4", "996vu5"}}, + {-54.03762344796269, -19.42924666017646, 206932378714152, 50, []uint64{206932378714153, 206932378714155, 206932378714154, 206932378670463, 206932378670461, 206932378670455, 206932378714146, 206932378714147}, "5w6htfu01", []string{"5w6htfu03", "5w6htfu06", "5w6htfu04", "5w6htfspf", "5w6htfspc", "5w6htfspb", "5w6htfu00", "5w6htfu02"}}, + {-11.952211955445833, -158.10974482318852, 88072320, 30, []uint64{88072321, 88072323, 88072322, 88061399, 88061397, 88061311, 88072234, 88072235}, "2mzs", []string{"2mzt", "2mzv", "2mzu", "2mzg", "2mze", "2mz7", "2mzk", "2mzm"}}, + {-84.65730312318192, -35.086878104077186, 691439896116, 42, []uint64{691439896117, 691439896119, 691439896118, 691439896115, 691439896113, 691439896091, 691439896094, 691439896095}, "50zn3sde", []string{"50zn3sds", "50zn3sdu", "50zn3sdg", "50zn3sdf", "50zn3sdd", "50zn3sd6", "50zn3sd7", "50zn3sdk"}}, + {-4.0869685567995475, -176.3168839448481, 5717389271, 36, []uint64{5717392002, 5717392008, 5717389277, 5717389276, 5717389270, 5717389268, 5717389269, 5717392000}, "2p68ezczw", []string{"2p68ezczy", "2p68ezczz", "2p68ezczx", "2p68ezczr", "2p68ezczq", "2p68ezczm", "2p68ezczt", "2p68ezczv"}}, + {-10.790908365830546, 153.35431354583125, 49711378, 26, []uint64{49711379, 49711385, 49711384, 49711373, 49711367, 49711365, 49711376, 49711377}, "rqj49et", []string{"rqj49ev", "rqj49ey", "rqj49ew", "rqj49eq", "rqj49em", "rqj49ek", "rqj49es", "rqj49eu"}}, + {-43.75800720126426, 94.52592864178587, 47256544170, 36, []uint64{47256544171, 47256545537, 47256545536, 47256545365, 47256543999, 47256543997, 47256544168, 47256544169}, "q05pnf", []string{"q05png", "q05pp5", "q05pp4", "q05pp1", "q05pnc", "q05pn9", "q05pnd", "q05pne"}}, + {67.53129730297951, 159.8223177191976, 17046315446, 34, []uint64{17046315447, 17046315453, 17046315452, 17046315449, 17046315443, 17046315441, 17046315444, 17046315445}, "zs18", []string{"zs19", "zs1c", "zs1b", "zecz", "zecx", "zecr", "zs12", "zs13"}}, + {66.76420149369187, -179.3428049692302, 1397239900709, 42, []uint64{1397239900720, 1397239900722, 1397239900711, 1397239900710, 1397239900708, 1397239900686, 1397239900687, 1397239900698}, "b5b7y", []string{"b5bkn", "b5bkp", "b5b7z", "b5b7x", "b5b7w", "b5b7t", "b5b7v", "b5bkj"}}, + {-46.62044990409049, 66.46240934668458, 148878928, 28, []uint64{148878929, 148878931, 148878930, 148878919, 148878917, 148878575, 148878586, 148878587}, "jrxqb3h2", []string{"jrxqb3h3", "jrxqb3h9", "jrxqb3h8", "jrxqb2ux", "jrxqb2ur", "jrxqb2up", "jrxqb3h0", "jrxqb3h1"}}, + {80.33029936951061, -109.82467719393026, 104482285228414, 48, []uint64{104482285228415, 104482285228501, 104482285228500, 104482285228497, 104482285228411, 104482285228409, 104482285228412, 104482285228413}, "cw3buzsjg", []string{"cw3buzsn5", "cw3buzsnh", "cw3buzsju", "cw3buzsjs", "cw3buzsje", "cw3buzsjd", "cw3buzsjf", "cw3buzsn4"}}, + {-11.455611052166205, 24.696078470122274, 39411478, 26, []uint64{39411479, 39411485, 39411484, 39411481, 39411475, 39411473, 39411476, 39411477}, "ktcwc", []string{"ktcx1", "ktcx4", "ktcwf", "ktcwd", "ktcw9", "ktcw8", "ktcwb", "ktcx0"}}, + {42.931699850040786, -54.07557546906175, 6967657339, 34, []uint64{6967657342, 6967657428, 6967657425, 6967657424, 6967657338, 6967657336, 6967657337, 6967657340}, "dz9s1", []string{"dz9s3", "dz9s6", "dz9s4", "dz9ef", "dz9ec", "dz9eb", "dz9s0", "dz9s2"}}, + {26.71749328021542, -170.36435231321957, 279455, 20, []uint64{279498, 279520, 279477, 279476, 279454, 279452, 279453, 279496}, "8hwzgrb", []string{"8hyb520", "8hyb521", "8hwzgrc", "8hwzgr9", "8hwzgr8", "8hwzgpx", "8hwzgpz", "8hyb50p"}}, + {-5.921830721577862, 31.485163157311007, 9911657, 24, []uint64{9911660, 9911662, 9911659, 9911658, 9911656, 9911650, 9911651, 9911654}, "kwyq", []string{"kwyr", "kwyx", "kwyw", "kwyt", "kwym", "kwyj", "kwyn", "kwyp"}}, + {48.589238423650386, -41.28983746111044, 132051073136857, 48, []uint64{132051073136860, 132051073136862, 132051073136859, 132051073136858, 132051073136856, 132051073136850, 132051073136851, 132051073136854}, "g0ds", []string{"g0dt", "g0dv", "g0du", "g0dg", "g0de", "g0d7", "g0dk", "g0dm"}}, + {9.175963486544788, 133.2772206460986, 15232041441, 34, []uint64{15232041444, 15232041446, 15232041443, 15232041442, 15232041440, 15232041418, 15232041419, 15232041422}, "wcwu0", []string{"wcwu2", "wcwu3", "wcwu1", "wcwgc", "wcwgb", "wcwez", "wcwsp", "wcwsr"}}, + {-30.70148984750267, 45.41199139680248, 2567975323, 32, []uint64{2567975326, 2567975348, 2567975345, 2567975344, 2567975322, 2567975320, 2567975321, 2567975324}, "m48336un", []string{"m48336up", "m48336ur", "m48336uq", "m48336um", "m48336uj", "m48336gv", "m48336gy", "m48336gz"}}, + {11.147924657911062, -153.20477653224953, 285067044601, 40, []uint64{285067044604, 285067044606, 285067044603, 285067044602, 285067044600, 285067044594, 285067044595, 285067044598}, "89gp3", []string{"89gp9", "89gpd", "89gp6", "89gp4", "89gp1", "89gp0", "89gp2", "89gp8"}}, + {70.12586639646906, -20.304883966848138, 2165185178123, 42, []uint64{2165185178126, 2165185178148, 2165185178145, 2165185178144, 2165185178122, 2165185178120, 2165185178121, 2165185178124}, "gs3wc", []string{"gs3x1", "gs3x4", "gs3wf", "gs3wd", "gs3w9", "gs3w8", "gs3wb", "gs3x0"}}, + {-6.2309867313306455, -57.060427828284446, 14482610, 26, []uint64{14482611, 14482617, 14482616, 14482605, 14482599, 14482597, 14482608, 14482609}, "6wzk", []string{"6wzm", "6wzt", "6wzs", "6wze", "6wz7", "6wz5", "6wzh", "6wzj"}}, + {26.286115865281317, 149.55440329646808, 62022874, 26, []uint64{62022875, 62022897, 62022896, 62022885, 62022863, 62022861, 62022872, 62022873}, "xkdme1npw", []string{"xkdme1npy", "xkdme1npz", "xkdme1npx", "xkdme1npr", "xkdme1npq", "xkdme1npm", "xkdme1npt", "xkdme1npv"}}, + {-12.339936959993807, 55.67470953505833, 688270065045915, 50, []uint64{688270065045918, 688270065045940, 688270065045937, 688270065045936, 688270065045914, 688270065045912, 688270065045913, 688270065045916}, "mjz9", []string{"mjzd", "mjzf", "mjzc", "mjzb", "mjz8", "mjz2", "mjz3", "mjz6"}}, + {37.80592302404692, -158.47490085102618, 76671352358205, 48, []uint64{76671352358248, 76671352358250, 76671352358207, 76671352358206, 76671352358204, 76671352358198, 76671352358199, 76671352358242}, "8qxr", []string{"8qz2", "8qz8", "8qxx", "8qxw", "8qxq", "8qxn", "8qxp", "8qz0"}}, + {-35.61695030840929, -141.25448762460292, 308964, 22, []uint64{308965, 308967, 308966, 308963, 308961, 308939, 308942, 308943}, "2cet", []string{"2cew", "2cey", "2cev", "2ceu", "2ces", "2cek", "2cem", "2ceq"}}, + {70.54689744529605, 134.02494799270062, 271279558572991, 48, []uint64{271279558573034, 271279558594880, 271279558594837, 271279558594836, 271279558572990, 271279558572988, 271279558572989, 271279558573032}, "yux33", []string{"yux39", "yux3d", "yux36", "yux34", "yux31", "yux30", "yux32", "yux38"}}, + {-75.04769161948934, 152.62058306016843, 11387945151, 34, []uint64{11387945194, 11387945536, 11387945493, 11387945492, 11387945150, 11387945148, 11387945149, 11387945192}, "p6st", []string{"p6sw", "p6sy", "p6sv", "p6su", "p6ss", "p6sk", "p6sm", "p6sq"}}, + {3.906750288500916, 67.39173014240623, 53884129091, 36, []uint64{53884129094, 53884129100, 53884129097, 53884129096, 53884129090, 53884129088, 53884129089, 53884129092}, "t2xy", []string{"t2xz", "t88p", "t88n", "t88j", "t2xv", "t2xt", "t2xw", "t2xx"}}, + {8.233112703786901, 69.03932965241253, 54298704148, 36, []uint64{54298704149, 54298704151, 54298704150, 54298704147, 54298704145, 54298702779, 54298702782, 54298702783}, "t93ng4b", []string{"t93ng50", "t93ng51", "t93ng4c", "t93ng49", "t93ng48", "t93nffx", "t93nffz", "t93nfgp"}}, + {-66.54800350313599, -124.76175724400673, 52264267491, 40, []uint64{52264267494, 52264267500, 52264267497, 52264267496, 52264267490, 52264267488, 52264267489, 52264267492}, "1hpm2vr3b", []string{"1hpm2vr60", "1hpm2vr61", "1hpm2vr3c", "1hpm2vr39", "1hpm2vr38", "1hpm2vr1x", "1hpm2vr1z", "1hpm2vr4p"}}, + {-11.324561770248692, 74.66864171202175, 696941562905906, 50, []uint64{696941562905907, 696941562905913, 696941562905912, 696941562905901, 696941562905895, 696941562905893, 696941562905904, 696941562905905}, "mtvpe", []string{"mtvpg", "mtvpu", "mtvps", "mtvpk", "mtvp7", "mtvp6", "mtvpd", "mtvpf"}}, + {-83.49906512712187, -125.21785383806855, 34458, 20, []uint64{34459, 34481, 34480, 34469, 34447, 34445, 34456, 34457}, "11nuyx7", []string{"11nuyxe", "11nuyxs", "11nuyxk", "11nuyxh", "11nuyx5", "11nuyx4", "11nuyx6", "11nuyxd"}}, + {-20.214544276474058, 25.770459227613173, 164983418266605, 48, []uint64{164983418266616, 164983418266618, 164983418266607, 164983418266606, 164983418266604, 164983418266598, 164983418266599, 164983418266610}, "ks6m42j3x", []string{"ks6m42j3z", "ks6m42j9b", "ks6m42j98", "ks6m42j92", "ks6m42j3r", "ks6m42j3q", "ks6m42j3w", "ks6m42j3y"}}, + {6.987028596799064, -22.23733585869195, 467315862788184, 50, []uint64{467315862788185, 467315862788187, 467315862788186, 467315862788175, 467315862788173, 467315862788167, 467315862788178, 467315862788179}, "e90p", []string{"e920", "e922", "e90r", "e90q", "e90n", "e3py", "e3pz", "e3rb"}}, + {56.33409782922536, -160.53146653840668, 21400207, 26, []uint64{21400218, 21400240, 21400229, 21400228, 21400206, 21400204, 21400205, 21400216}, "b6jb", []string{"b6jc", "b6n1", "b6n0", "b3yp", "b3vz", "b3vx", "b6j8", "b6j9"}}, + {9.199611225216358, 1.6687535047530844, 845851512461071, 50, []uint64{845851512461082, 845851512461104, 845851512461093, 845851512461092, 845851512461070, 845851512461068, 845851512461069, 845851512461080}, "s19hm", []string{"s19ht", "s19hw", "s19hq", "s19hn", "s19hj", "s19hh", "s19hk", "s19hs"}}, + {-56.71405158319976, 85.53188627318195, 9593867382, 34, []uint64{9593867383, 9593867389, 9593867388, 9593867385, 9593867379, 9593867377, 9593867380, 9593867381}, "jvuv67d", []string{"jvuv67f", "jvuv67g", "jvuv67e", "jvuv677", "jvuv676", "jvuv673", "jvuv679", "jvuv67c"}}, + {-24.685740557721147, 101.99112698112731, 46613328, 26, []uint64{46613329, 46613331, 46613330, 46613319, 46613317, 46612975, 46612986, 46612987}, "q78e8f5e", []string{"q78e8f5s", "q78e8f5u", "q78e8f5g", "q78e8f5f", "q78e8f5d", "q78e8f56", "q78e8f57", "q78e8f5k"}}, + {47.12925356513006, 4.831304300576448, 57190881681750, 46, []uint64{57190881681751, 57190881681757, 57190881681756, 57190881681753, 57190881681747, 57190881681745, 57190881681748, 57190881681749}, "u07kjgw5c", []string{"u07kjgwh1", "u07kjgwh4", "u07kjgw5f", "u07kjgw5d", "u07kjgw59", "u07kjgw58", "u07kjgw5b", "u07kjgwh0"}}, + {-31.011013831972377, 87.74923098823638, 637655, 20, []uint64{637826, 637832, 637661, 637660, 637654, 637652, 637653, 637824}, "mfqrs", []string{"mfqru", "mfqrv", "mfqrt", "mfqrm", "mfqrk", "mfqr7", "mfqre", "mfqrg"}}, + {10.874646033975296, -17.927068179880735, 435699, 20, []uint64{435702, 435708, 435705, 435704, 435698, 435696, 435697, 435700}, "e9gmb59v", []string{"e9gmb59y", "e9gmb5dn", "e9gmb5dj", "e9gmb5dh", "e9gmb59u", "e9gmb59s", "e9gmb59t", "e9gmb59w"}}, + {-19.532136049034307, -2.965460417246504, 67321419794, 38, []uint64{67321419795, 67321419801, 67321419800, 67321419789, 67321419783, 67321419781, 67321419792, 67321419793}, "7utbus29", []string{"7utbus2d", "7utbus2f", "7utbus2c", "7utbus2b", "7utbus28", "7utbus22", "7utbus23", "7utbus26"}}, + {-34.78830215195193, 177.12977870641043, 12848103372795, 44, []uint64{12848103372798, 12848103374164, 12848103374161, 12848103374160, 12848103372794, 12848103372792, 12848103372793, 12848103372796}, "rcvf", []string{"rcvg", "rcy5", "rcy4", "rcy1", "rcvc", "rcv9", "rcvd", "rcve"}}, + {-83.53052133906749, 128.29793189722113, 178993023139253, 48, []uint64{178993023139296, 178993023139298, 178993023139255, 178993023139254, 178993023139252, 178993023139230, 178993023139231, 178993023139274}, "nc5hz", []string{"nc5jp", "nc5m0", "nc5kb", "nc5k8", "nc5hx", "nc5hw", "nc5hy", "nc5jn"}}, + {42.475023579230765, -112.64507646166021, 81767366, 28, []uint64{81767367, 81767373, 81767372, 81767369, 81767363, 81767361, 81767364, 81767365}, "9rxcssq", []string{"9rxcssw", "9rxcssx", "9rxcssr", "9rxcssp", "9rxcssn", "9rxcssj", "9rxcssm", "9rxcsst"}}, + {-55.44770300669916, 140.82646789113642, 181539011, 28, []uint64{181539014, 181539020, 181539017, 181539016, 181539010, 181539008, 181539009, 181539012}, "pnhh", []string{"pnhj", "pnhm", "pnhk", "pnh7", "pnh5", "pn5g", "pn5u", "pn5v"}}, + {25.75856140362157, -126.05352436500834, 81840941351, 38, []uint64{81840941362, 81840941368, 81840941357, 81840941356, 81840941350, 81840941348, 81840941349, 81840941360}, "9hw6e", []string{"9hw6g", "9hw6u", "9hw6s", "9hw6k", "9hw67", "9hw66", "9hw6d", "9hw6f"}}, + {-80.38554340515111, -34.03850871417674, 679208154, 32, []uint64{679208155, 679208177, 679208176, 679208165, 679208143, 679208141, 679208152, 679208153}, "51xy", []string{"51xz", "538p", "538n", "538j", "51xv", "51xt", "51xw", "51xx"}}, + {-65.06980248124455, 38.26956686197079, 9246828891909, 44, []uint64{9246828891920, 9246828891922, 9246828891911, 9246828891910, 9246828891908, 9246828891566, 9246828891567, 9246828891578}, "hu7jyf", []string{"hu7jyg", "hu7jz5", "hu7jz4", "hu7jz1", "hu7jyc", "hu7jy9", "hu7jyd", "hu7jye"}}, + {-58.377230681158835, -165.59316101914374, 81289193, 32, []uint64{81289196, 81289198, 81289195, 81289194, 81289192, 81289186, 81289187, 81289190}, "0md5zudx", []string{"0md5zuf8", "0md5zufb", "0md5zudz", "0md5zudy", "0md5zudw", "0md5zudq", "0md5zudr", "0md5zuf2"}}, + {70.39199505312718, -133.09474356577266, 96543293, 28, []uint64{96543336, 96543338, 96543295, 96543294, 96543292, 96543286, 96543287, 96543330}, "ch927q", []string{"ch927r", "ch927x", "ch927w", "ch927t", "ch927m", "ch927j", "ch927n", "ch927p"}}, + {-31.1139169498856, -70.2746662624413, 52089943, 28, []uint64{52090114, 52090120, 52089949, 52089948, 52089942, 52089940, 52089941, 52090112}, "66qnbzgs", []string{"66qnbzgt", "66qnbzgv", "66qnbzgu", "66qnbzgg", "66qnbzge", "66qnbzg7", "66qnbzgk", "66qnbzgm"}}, + {61.76053419423988, -78.71509678097209, 7622444272, 34, []uint64{7622444273, 7622444275, 7622444274, 7622444263, 7622444261, 7622444239, 7622444250, 7622444251}, "f6bp", []string{"f700", "f702", "f6br", "f6bq", "f6bn", "f4zy", "f4zz", "f5pb"}}, + {74.85245998969185, -7.037777410761919, 34037562255, 36, []uint64{34037562266, 34037562288, 34037562277, 34037562276, 34037562254, 34037562252, 34037562253, 34037562264}, "gv6cz", []string{"gv6fp", "gv740", "gv71b", "gv718", "gv6cx", "gv6cw", "gv6cy", "gv6fn"}}, + {79.64553839947621, 165.63100359850796, 1122115005721040, 50, []uint64{1122115005721041, 1122115005721043, 1122115005721042, 1122115005721031, 1122115005721029, 1122115005720943, 1122115005720954, 1122115005720955}, "zwjv150bf", []string{"zwjv150c4", "zwjv150c5", "zwjv150bg", "zwjv150be", "zwjv150bd", "zwjv150b9", "zwjv150bc", "zwjv150c1"}}, + {-15.493468773434884, -98.66860547033139, 33034194410, 38, []uint64{33034194411, 33034194753, 33034194752, 33034194709, 33034194367, 33034194365, 33034194408, 33034194409}, "3v1zfex8n", []string{"3v1zfex8q", "3v1zfex8r", "3v1zfex8p", "3v1zferxz", "3v1zferxy", "3v1zferxv", "3v1zfex8j", "3v1zfex8m"}}, + {-9.993034775703563, -132.1053314533783, 475732, 22, []uint64{475733, 475735, 475734, 475731, 475729, 475387, 475390, 475391}, "3n4p1u", []string{"3n4p1v", "3n4p4j", "3n4p4h", "3n4p45", "3n4p1g", "3n4p1e", "3n4p1s", "3n4p1t"}}, + {9.452049522427828, 12.830212958331686, 207043816167, 38, []uint64{207043816178, 207043816184, 207043816173, 207043816172, 207043816166, 207043816164, 207043816165, 207043816176}, "s39jg", []string{"s39n5", "s39nh", "s39ju", "s39js", "s39je", "s39jd", "s39jf", "s39n4"}}, + {79.33081749817939, -111.24281477244222, 1632152884959, 42, []uint64{1632152885130, 1632152885152, 1632152884981, 1632152884980, 1632152884958, 1632152884956, 1632152884957, 1632152885128}, "cw0gk9er", []string{"cw0gk9g2", "cw0gk9g8", "cw0gk9ex", "cw0gk9ew", "cw0gk9eq", "cw0gk9en", "cw0gk9ep", "cw0gk9g0"}}, + {21.03097636331222, 106.034056906763, 970050005420, 40, []uint64{970050005421, 970050005423, 970050005422, 970050005419, 970050005417, 970050005411, 970050005414, 970050005415}, "w7ers", []string{"w7eru", "w7erv", "w7ert", "w7erm", "w7erk", "w7er7", "w7ere", "w7erg"}}, + {87.23795095886453, 130.20035336897126, 4259568201925, 42, []uint64{4259568201936, 4259568201938, 4259568201927, 4259568201926, 4259568201924, 4259568201838, 4259568201839, 4259568201850}, "yzs86", []string{"yzs8d", "yzs8e", "yzs87", "yzs85", "yzs84", "yzs81", "yzs83", "yzs89"}}, + {60.33250362482613, 98.53546341805486, 64753050987, 36, []uint64{64753050990, 64753051076, 64753051073, 64753051072, 64753050986, 64753050984, 64753050985, 64753050988}, "y4wp", []string{"y4y0", "y4y2", "y4wr", "y4wq", "y4wn", "y4ty", "y4tz", "y4vb"}}, + {-4.403025638726845, -130.26905674228328, 32209703689954, 48, []uint64{32209703689955, 32209703689961, 32209703689960, 32209703689917, 32209703689911, 32209703689909, 32209703689952, 32209703689953}, "3p5qgwk", []string{"3p5qgws", "3p5qgwt", "3p5qgwm", "3p5qgwj", "3p5qgwh", "3p5qgw5", "3p5qgw7", "3p5qgwe"}}, + {50.01872899358568, 70.7396544770454, 914835753, 30, []uint64{914835756, 914835758, 914835755, 914835754, 914835752, 914835746, 914835747, 914835750}, "v8fk99xh1", []string{"v8fk99xh3", "v8fk99xh6", "v8fk99xh4", "v8fk99x5f", "v8fk99x5c", "v8fk99x5b", "v8fk99xh0", "v8fk99xh2"}}, + {-76.75438254192704, -79.36072160041658, 556716820, 32, []uint64{556716821, 556716823, 556716822, 556716819, 556716817, 556716475, 556716478, 556716479}, "44re65733", []string{"44re65739", "44re6573d", "44re65736", "44re65734", "44re65731", "44re65730", "44re65732", "44re65738"}}, + {-20.96819912856155, -50.78612494061602, 937287575350, 42, []uint64{937287575351, 937287575357, 937287575356, 937287575353, 937287575347, 937287575345, 937287575348, 937287575349}, "6u7bsqf", []string{"6u7bsr4", "6u7bsr5", "6u7bsqg", "6u7bsqe", "6u7bsqd", "6u7bsq9", "6u7bsqc", "6u7bsr1"}}, + {69.11318734777433, -18.11273846385305, 8662432012699, 44, []uint64{8662432012702, 8662432012724, 8662432012721, 8662432012720, 8662432012698, 8662432012696, 8662432012697, 8662432012700}, "gs71", []string{"gs74", "gs76", "gs73", "gs72", "gs70", "gs6b", "gs6c", "gs6f"}}, + {-10.662275293667335, 131.37331659975462, 50477113212273, 46, []uint64{50477113212276, 50477113212278, 50477113212275, 50477113212274, 50477113212272, 50477113212250, 50477113212251, 50477113212254}, "qyj7m", []string{"qyj7t", "qyj7w", "qyj7q", "qyj7n", "qyj7j", "qyj7h", "qyj7k", "qyj7s"}}, + {-75.18405814940343, 158.263252028235, 2870051212, 32, []uint64{2870051213, 2870051215, 2870051214, 2870051211, 2870051209, 2870051203, 2870051206, 2870051207}, "pd8s335", []string{"pd8s337", "pd8s33k", "pd8s33h", "pd8s32u", "pd8s32g", "pd8s32f", "pd8s334", "pd8s336"}}, + {-31.407670830900315, 1.442023394272809, 594033, 20, []uint64{594036, 594038, 594035, 594034, 594032, 594010, 594011, 594014}, "k43j2f", []string{"k43j2g", "k43j35", "k43j34", "k43j31", "k43j2c", "k43j29", "k43j2d", "k43j2e"}}, + {25.69226704510038, -137.37636636814568, 289670, 20, []uint64{289671, 289677, 289676, 289673, 289667, 289665, 289668, 289669}, "8uw6", []string{"8uw7", "8uwe", "8uwd", "8uw9", "8uw3", "8uw1", "8uw4", "8uw5"}}, + {26.97954855544957, -127.82899617642397, 5114449753, 34, []uint64{5114449756, 5114449758, 5114449755, 5114449754, 5114449752, 5114449746, 5114449747, 5114449750}, "9hv17pmz", []string{"9hv17ptb", "9hv17pw0", "9hv17pqp", "9hv17pqn", "9hv17pmy", "9hv17pmw", "9hv17pmx", "9hv17pt8"}}, + {86.49139336825465, 77.77029888890684, 14642976, 24, []uint64{14642977, 14642979, 14642978, 14642807, 14642805, 14642783, 14642954, 14642955}, "vxrk", []string{"vxrm", "vxrt", "vxrs", "vxre", "vxr7", "vxr5", "vxrh", "vxrj"}}, + {-45.298830886837095, -67.38699266093316, 172988608945203, 50, []uint64{172988608945206, 172988608945212, 172988608945209, 172988608945208, 172988608945202, 172988608945200, 172988608945201, 172988608945204}, "4xbn69", []string{"4xbn6d", "4xbn6f", "4xbn6c", "4xbn6b", "4xbn68", "4xbn62", "4xbn63", "4xbn66"}}, + {-47.69045460395864, 177.82838839298893, 720770, 20, []uint64{720771, 720777, 720776, 720605, 720599, 720597, 720768, 720769}, "pzw2ww4w", []string{"pzw2ww4x", "pzw2ww4z", "pzw2ww4y", "pzw2ww4v", "pzw2ww4t", "pzw2ww4m", "pzw2ww4q", "pzw2ww4r"}}, + {-67.73390960211691, -135.15302850940498, 4397717543220, 48, []uint64{4397717543221, 4397717543223, 4397717543222, 4397717543219, 4397717543217, 4397717543195, 4397717543198, 4397717543199}, "0gzyst2t", []string{"0gzyst2w", "0gzyst2y", "0gzyst2v", "0gzyst2u", "0gzyst2s", "0gzyst2k", "0gzyst2m", "0gzyst2q"}}, + {43.26760973845376, 39.76379786178586, 858748119472, 40, []uint64{858748119473, 858748119475, 858748119474, 858748119463, 858748119461, 858748119439, 858748119450, 858748119451}, "szsq0ue", []string{"szsq0ug", "szsq0uu", "szsq0us", "szsq0uk", "szsq0u7", "szsq0u6", "szsq0ud", "szsq0uf"}}, + {87.65806925816287, -47.024451329009025, 1965617, 22, []uint64{1965620, 1965622, 1965619, 1965618, 1965616, 1965594, 1965595, 1965598}, "fzwd9", []string{"fzwdc", "fzwdf", "fzwdd", "fzwd6", "fzwd3", "fzwd2", "fzwd8", "fzwdb"}}, + {21.178492844395805, -141.35894640759216, 18218139079, 36, []uint64{18218139090, 18218139096, 18218139085, 18218139084, 18218139078, 18218139076, 18218139077, 18218139088}, "8gg2rr3t", []string{"8gg2rr3w", "8gg2rr3y", "8gg2rr3v", "8gg2rr3u", "8gg2rr3s", "8gg2rr3k", "8gg2rr3m", "8gg2rr3q"}}, + {81.73687708991929, 34.8077035388296, 59254062278012, 46, []uint64{59254062278013, 59254062278015, 59254062278014, 59254062278011, 59254062278009, 59254062278003, 59254062278006, 59254062278007}, "uy8bbpdp", []string{"uy8bbpf0", "uy8bbpf2", "uy8bbpdr", "uy8bbpdq", "uy8bbpdn", "uy8bbp9y", "uy8bbp9z", "uy8bbpcb"}}, + {31.21436575273401, -32.95067620044574, 28525144, 26, []uint64{28525145, 28525147, 28525146, 28525135, 28525133, 28525127, 28525138, 28525139}, "em89d4qh", []string{"em89d4qj", "em89d4qm", "em89d4qk", "em89d4q7", "em89d4q5", "em89d4mg", "em89d4mu", "em89d4mv"}}, + {7.79252023689331, 148.3328863644856, 3999095745587, 42, []uint64{3999095745590, 3999095745596, 3999095745593, 3999095745592, 3999095745586, 3999095745584, 3999095745585, 3999095745588}, "x33kr6sd", []string{"x33kr6se", "x33kr6sg", "x33kr6sf", "x33kr6sc", "x33kr6s9", "x33kr6s3", "x33kr6s6", "x33kr6s7"}}, + {83.657213085884, 51.572517179563874, 906061, 20, []uint64{906072, 906074, 906063, 906062, 906060, 906054, 906055, 906066}, "vnuevt", []string{"vnuevw", "vnuevy", "vnuevv", "vnuevu", "vnuevs", "vnuevk", "vnuevm", "vnuevq"}}, + {55.466355756274424, -99.63850387913408, 399506862087132, 50, []uint64{399506862087133, 399506862087135, 399506862087134, 399506862087131, 399506862087129, 399506862087123, 399506862087126, 399506862087127}, "ccc5s", []string{"ccc5u", "ccc5v", "ccc5t", "ccc5m", "ccc5k", "ccc57", "ccc5e", "ccc5g"}}, + {-14.91630208207063, 134.14306358318572, 49105306810, 36, []uint64{49105306811, 49105307153, 49105307152, 49105307141, 49105306799, 49105306797, 49105306808, 49105306809}, "qvr7", []string{"qvrk", "qvrs", "qvre", "qvrd", "qvr6", "qvr4", "qvr5", "qvrh"}}, + {-36.242920013479306, -49.577400352747674, 14002263303897, 46, []uint64{14002263303900, 14002263303902, 14002263303899, 14002263303898, 14002263303896, 14002263303890, 14002263303891, 14002263303894}, "6cs9zf4", []string{"6cs9zf6", "6cs9zf7", "6cs9zf5", "6cs9zcg", "6cs9zcf", "6cs9zcc", "6cs9zf1", "6cs9zf3"}}, + {-83.86540966146276, -2.6086203476297953, 188708876520332, 50, []uint64{188708876520333, 188708876520335, 188708876520334, 188708876520331, 188708876520329, 188708876520323, 188708876520326, 188708876520327}, "5cn4usu2w", []string{"5cn4usu2y", "5cn4usu2z", "5cn4usu2x", "5cn4usu2r", "5cn4usu2q", "5cn4usu2m", "5cn4usu2t", "5cn4usu2v"}}, + {24.730702771434153, -123.25593103846768, 1253705, 22, []uint64{1253708, 1253710, 1253707, 1253706, 1253704, 1253698, 1253699, 1253702}, "9k2kenpf", []string{"9k2kenpg", "9k2keq05", "9k2keq04", "9k2keq01", "9k2kenpc", "9k2kenp9", "9k2kenpd", "9k2kenpe"}}, + {-0.9625416591880054, -134.32797721138922, 125970581858, 40, []uint64{125970581859, 125970581865, 125970581864, 125970581821, 125970581815, 125970581813, 125970581856, 125970581857}, "3pb6x2c", []string{"3pb6x31", "3pb6x34", "3pb6x2f", "3pb6x2d", "3pb6x29", "3pb6x28", "3pb6x2b", "3pb6x30"}}, + {-13.599334614205873, 45.05582948230585, 42967141323553, 46, []uint64{42967141323556, 42967141323558, 42967141323555, 42967141323554, 42967141323552, 42967141323530, 42967141323531, 42967141323534}, "mj849k2", []string{"mj849k8", "mj849k9", "mj849k3", "mj849k1", "mj849k0", "mj849hp", "mj849hr", "mj849hx"}}, + {-49.57290428724809, 32.47910835049697, 9305876945031, 44, []uint64{9305876945042, 9305876945048, 9305876945037, 9305876945036, 9305876945030, 9305876945028, 9305876945029, 9305876945040}, "hxpjgpd8f", []string{"hxpjgpd94", "hxpjgpd95", "hxpjgpd8g", "hxpjgpd8e", "hxpjgpd8d", "hxpjgpd89", "hxpjgpd8c", "hxpjgpd91"}}, + {-48.32092491736694, 20.415088724519592, 8987754705, 34, []uint64{8987754708, 8987754710, 8987754707, 8987754706, 8987754704, 8987754618, 8987754619, 8987754622}, "hrqt0e", []string{"hrqt0s", "hrqt0u", "hrqt0g", "hrqt0f", "hrqt0d", "hrqt06", "hrqt07", "hrqt0k"}}, + {-42.369153552514035, 23.771854447375517, 642216627226435, 50, []uint64{642216627226438, 642216627226444, 642216627226441, 642216627226440, 642216627226434, 642216627226432, 642216627226433, 642216627226436}, "k82yuyyju", []string{"k82yuyynh", "k82yuyynj", "k82yuyyjv", "k82yuyyjt", "k82yuyyjs", "k82yuyyje", "k82yuyyjg", "k82yuyyn5"}}, + {54.32750455633504, 84.91108813975006, 3587150, 22, []uint64{3587151, 3587173, 3587172, 3587169, 3587147, 3587145, 3587148, 3587149}, "vcsmh4n", []string{"vcsmh4q", "vcsmh4r", "vcsmh4p", "vcsmh1z", "vcsmh1y", "vcsmh1v", "vcsmh4j", "vcsmh4m"}}, + {-57.814456501815584, 111.34216713148635, 691983917, 30, []uint64{691983928, 691983930, 691983919, 691983918, 691983916, 691983910, 691983911, 691983922}, "nmxpjehy", []string{"nmxpjehz", "nmxpjejp", "nmxpjejn", "nmxpjejj", "nmxpjehv", "nmxpjeht", "nmxpjehw", "nmxpjehx"}}, + {46.460967057486414, -155.0921658133957, 5375627, 24, []uint64{5375630, 5375652, 5375649, 5375648, 5375626, 5375624, 5375625, 5375628}, "b838q", []string{"b838w", "b838x", "b838r", "b838p", "b838n", "b838j", "b838m", "b838t"}}, + {26.638798633017814, -98.61140937998425, 5399843456866, 44, []uint64{5399843456867, 5399843456873, 5399843456872, 5399843456829, 5399843456823, 5399843456821, 5399843456864, 5399843456865}, "9u9zs13", []string{"9u9zs19", "9u9zs1d", "9u9zs16", "9u9zs14", "9u9zs11", "9u9zs10", "9u9zs12", "9u9zs18"}}, + {38.56433102270239, 1.9008547615958378, 807271, 20, []uint64{807282, 807288, 807277, 807276, 807270, 807268, 807269, 807280}, "snc77k2", []string{"snc77k8", "snc77k9", "snc77k3", "snc77k1", "snc77k0", "snc77hp", "snc77hr", "snc77hx"}}, + {15.69818401926021, -36.57424126951082, 451889833954, 40, []uint64{451889833955, 451889833961, 451889833960, 451889833917, 451889833911, 451889833909, 451889833952, 451889833953}, "e4vcr9z2w", []string{"e4vcr9z2y", "e4vcr9z2z", "e4vcr9z2x", "e4vcr9z2r", "e4vcr9z2q", "e4vcr9z2m", "e4vcr9z2t", "e4vcr9z2v"}}, + {36.54094743853784, -131.19500412448542, 20199293, 26, []uint64{20210216, 20210218, 20199295, 20199294, 20199292, 20199286, 20199287, 20210210}, "9n6xys4dq", []string{"9n6xys4dw", "9n6xys4dx", "9n6xys4dr", "9n6xys4dp", "9n6xys4dn", "9n6xys4dj", "9n6xys4dm", "9n6xys4dt"}}, + {52.88411136562354, -157.33010297323926, 5653527252619, 44, []uint64{5653527252622, 5653527252644, 5653527252641, 5653527252640, 5653527252618, 5653527252616, 5653527252617, 5653527252620}, "b92hgg", []string{"b92hgu", "b92huh", "b92hu5", "b92hu4", "b92hgf", "b92hgd", "b92hge", "b92hgs"}}, + {22.27861216734162, -175.58552415738814, 1123086859888, 42, []uint64{1123086859889, 1123086859891, 1123086859890, 1123086859879, 1123086859877, 1123086859855, 1123086859866, 1123086859867}, "85gnsr", []string{"85gnu2", "85gnu8", "85gnsx", "85gnsw", "85gnsq", "85gnsn", "85gnsp", "85gnu0"}}, + {25.821066696866183, 91.10629317245912, 61221002290, 36, []uint64{61221002291, 61221002297, 61221002296, 61221002285, 61221002279, 61221002277, 61221002288, 61221002289}, "wh8fchtd", []string{"wh8fchte", "wh8fchtg", "wh8fchtf", "wh8fchtc", "wh8fcht9", "wh8fcht3", "wh8fcht6", "wh8fcht7"}}, + {-19.71435110384482, 75.89188974296849, 169818967971, 38, []uint64{169818967974, 169818967980, 169818967977, 169818967976, 169818967970, 169818967968, 169818967969, 169818967972}, "msmzygng9", []string{"msmzygngc", "msmzygngf", "msmzygngd", "msmzygng6", "msmzygng3", "msmzygng2", "msmzygng8", "msmzygngb"}}, + {3.352856994664748, -86.29228198248893, 25191264, 26, []uint64{25191265, 25191267, 25191266, 25191223, 25191221, 25191199, 25191242, 25191243}, "d0deh", []string{"d0dek", "d0dem", "d0dej", "d0ddv", "d0ddu", "d0ddg", "d0de5", "d0de7"}}, + {-64.0863696787128, -144.41484584257705, 107676, 22, []uint64{107677, 107679, 107678, 107675, 107673, 107667, 107670, 107671}, "0u973v2m", []string{"0u973v2q", "0u973v2w", "0u973v2t", "0u973v2s", "0u973v2k", "0u973v2h", "0u973v2j", "0u973v2n"}}, + {-14.83067988208495, -19.562316255352954, 65324390, 28, []uint64{65324391, 65324397, 65324396, 65324393, 65324387, 65324385, 65324388, 65324389}, "7t65du5", []string{"7t65du7", "7t65duk", "7t65duh", "7t65dgu", "7t65dgg", "7t65dgf", "7t65du4", "7t65du6"}}, + {60.281532174762106, -73.26948696186446, 119127766, 28, []uint64{119127767, 119127773, 119127772, 119127769, 119127763, 119127761, 119127764, 119127765}, "f6eyuty", []string{"f6eyuwn", "f6eyuwp", "f6eyutz", "f6eyutx", "f6eyutw", "f6eyutt", "f6eyutv", "f6eyuwj"}}, + {34.37153826589929, 81.08439991698833, 849965, 20, []uint64{849976, 849978, 849967, 849966, 849964, 849958, 849959, 849970}, "ty1et15ue", []string{"ty1et15ug", "ty1et15uu", "ty1et15us", "ty1et15uk", "ty1et15u7", "ty1et15u6", "ty1et15ud", "ty1et15uf"}}, + {-29.5802575750131, -173.71484276710544, 1181517430990, 44, []uint64{1181517430991, 1181517431013, 1181517431012, 1181517431009, 1181517430987, 1181517430985, 1181517430988, 1181517430989}, "24srx", []string{"24srz", "24sxb", "24sx8", "24sx2", "24srr", "24srq", "24srw", "24sry"}}, + {64.84346432966413, -38.29034452460476, 534098871970179, 50, []uint64{534098871970182, 534098871970188, 534098871970185, 534098871970184, 534098871970178, 534098871970176, 534098871970177, 534098871970180}, "g5sbb", []string{"g5sc0", "g5sc1", "g5sbc", "g5sb9", "g5sb8", "g5s8x", "g5s8z", "g5s9p"}}, + {46.13550850188767, 41.997852398024406, 3533956083, 32, []uint64{3533956086, 3533956092, 3533956089, 3533956088, 3533956082, 3533956080, 3533956081, 3533956084}, "ubjy7w", []string{"ubjy7x", "ubjy7z", "ubjy7y", "ubjy7v", "ubjy7t", "ubjy7m", "ubjy7q", "ubjy7r"}}, + {10.743950411619139, -34.72056425688788, 1795241999816, 42, []uint64{1795241999817, 1795241999819, 1795241999818, 1795241999775, 1795241999773, 1795241999767, 1795241999810, 1795241999811}, "e1zm1", []string{"e1zm3", "e1zm6", "e1zm4", "e1zkf", "e1zkc", "e1zkb", "e1zm0", "e1zm2"}}, + {-43.66175335447769, 110.42380704497918, 47422718928, 36, []uint64{47422718929, 47422718931, 47422718930, 47422718919, 47422718917, 47422718831, 47422718842, 47422718843}, "q2nx8g8", []string{"q2nx8gb", "q2nx8gc", "q2nx8g9", "q2nx8g3", "q2nx8g2", "q2nx8er", "q2nx8ex", "q2nx8ez"}}, + {-63.05216727754305, 86.31052131252363, 9577227284, 34, []uint64{9577227285, 9577227287, 9577227286, 9577227283, 9577227281, 9577221819, 9577221822, 9577221823}, "juv3k19q", []string{"juv3k19r", "juv3k19x", "juv3k19w", "juv3k19t", "juv3k19m", "juv3k19j", "juv3k19n", "juv3k19p"}}, + {81.86529738310492, -162.58227617910234, 359403287, 30, []uint64{359403330, 359403336, 359403293, 359403292, 359403286, 359403284, 359403285, 359403328}, "bqs3sr55y", []string{"bqs3sr5hn", "bqs3sr5hp", "bqs3sr55z", "bqs3sr55x", "bqs3sr55w", "bqs3sr55t", "bqs3sr55v", "bqs3sr5hj"}}, + {47.585888864872686, -85.01516950072255, 7344068, 24, []uint64{7344069, 7344071, 7344070, 7344067, 7344065, 7343979, 7343982, 7343983}, "f07w", []string{"f07x", "f07z", "f07y", "f07v", "f07t", "f07m", "f07q", "f07r"}}, + {-69.62258130669943, 99.4462119419477, 42328957, 26, []uint64{42329640, 42329642, 42328959, 42328958, 42328956, 42328950, 42328951, 42329634}, "n5weyvwe", []string{"n5weyvws", "n5weyvwu", "n5weyvwg", "n5weyvwf", "n5weyvwd", "n5weyvw6", "n5weyvw7", "n5weyvwk"}}, + {87.63243923486152, 173.93161531891383, 268283082, 28, []uint64{268283083, 268283105, 268283104, 268283061, 268283039, 268283037, 268283080, 268283081}, "zzedtbvp", []string{"zzedtcj0", "zzedtcj2", "zzedtbvr", "zzedtbvq", "zzedtbvn", "zzedtbuy", "zzedtbuz", "zzedtchb"}}, + {70.6758454155206, 51.11638280254556, 57721226, 26, []uint64{57721227, 57721249, 57721248, 57721077, 57721055, 57721053, 57721224, 57721225}, "vhs654j", []string{"vhs654m", "vhs654q", "vhs654n", "vhs651y", "vhs651v", "vhs651u", "vhs654h", "vhs654k"}}, + {55.33405317727011, 96.2553177569207, 264382869981869, 48, []uint64{264382869981880, 264382869981882, 264382869981871, 264382869981870, 264382869981868, 264382869981862, 264382869981863, 264382869981874}, "y1u6y", []string{"y1u7n", "y1u7p", "y1u6z", "y1u6x", "y1u6w", "y1u6t", "y1u6v", "y1u7j"}}, + {83.43981263575552, 17.912962178641436, 3501363, 22, []uint64{3501366, 3501372, 3501369, 3501368, 3501362, 3501360, 3501361, 3501364}, "uqudx", []string{"uqudz", "uqufb", "uquf8", "uquf2", "uqudr", "uqudq", "uqudw", "uqudy"}}, + {-83.3962538548949, -139.26801800476096, 49509099589, 42, []uint64{49509099600, 49509099602, 49509099591, 49509099590, 49509099588, 49509098222, 49509098223, 49509098234}, "0chvwfhj", []string{"0chvwfhn", "0chvwfhq", "0chvwfhm", "0chvwfhk", "0chvwfhh", "0chvwf5u", "0chvwf5v", "0chvwf5y"}}, + {3.300617730041253, 164.36748578137485, 61391796, 26, []uint64{61391797, 61391799, 61391798, 61391795, 61391793, 61391771, 61391774, 61391775}, "x8sfu2b", []string{"x8sfu30", "x8sfu31", "x8sfu2c", "x8sfu29", "x8sfu28", "x8sfu0x", "x8sfu0z", "x8sfu1p"}}, + {63.57937653787667, 87.80211891295767, 60451920460307, 46, []uint64{60451920460310, 60451920460316, 60451920460313, 60451920460312, 60451920460306, 60451920460304, 60451920460305, 60451920460308}, "vgq3t", []string{"vgq3v", "vgq3y", "vgq3w", "vgq3q", "vgq3m", "vgq3k", "vgq3s", "vgq3u"}}, + {32.5914249360394, -13.894826642383151, 115851582, 28, []uint64{115851583, 115851669, 115851668, 115851665, 115851579, 115851577, 115851580, 115851581}, "ety17", []string{"ety1e", "ety1s", "ety1k", "ety1h", "ety15", "ety14", "ety16", "ety1d"}}, + {5.488101681461558, -78.26461434178054, 25318859, 26, []uint64{25318862, 25318884, 25318881, 25318880, 25318858, 25318856, 25318857, 25318860}, "d2br5", []string{"d2br7", "d2brk", "d2brh", "d2bqu", "d2bqg", "d2bqf", "d2br4", "d2br6"}}, + {-73.0319900465256, -77.00321413281199, 144989660155, 40, []uint64{144989660158, 144989661524, 144989661521, 144989661520, 144989660154, 144989660152, 144989660153, 144989660156}, "4710", []string{"4711", "4713", "4712", "46cr", "46cp", "46bz", "470b", "470c"}}, + {5.198934946587528, 31.309519852100493, 3337001921391, 42, []uint64{3337001921402, 3337001921488, 3337001921477, 3337001921476, 3337001921390, 3337001921388, 3337001921389, 3337001921400}, "s8ym86qv", []string{"s8ym86qy", "s8ym86rn", "s8ym86rj", "s8ym86rh", "s8ym86qu", "s8ym86qs", "s8ym86qt", "s8ym86qw"}}, + {-34.67015531301149, -65.10494762932647, 3378199681, 34, []uint64{3378199684, 3378199686, 3378199683, 3378199682, 3378199680, 3378199594, 3378199595, 3378199598}, "69cd", []string{"69ce", "69cg", "69cf", "69cc", "69c9", "69c3", "69c6", "69c7"}}, + {-7.328675064432915, -38.309543723473325, 64167440, 28, []uint64{64167441, 64167443, 64167442, 64167431, 64167429, 64167087, 64167098, 64167099}, "7nsy21z", []string{"7nsy24p", "7nsy260", "7nsy23b", "7nsy238", "7nsy21x", "7nsy21w", "7nsy21y", "7nsy24n"}}, + {-49.00581497242092, 102.19662036088994, 695281212, 30, []uint64{695281213, 695281215, 695281214, 695281211, 695281209, 695281203, 695281206, 695281207}, "nr29jwc23", []string{"nr29jwc29", "nr29jwc2d", "nr29jwc26", "nr29jwc24", "nr29jwc21", "nr29jwc20", "nr29jwc22", "nr29jwc28"}}, + {25.052794124552747, -71.74662488355534, 6596078, 24, []uint64{6596079, 6596421, 6596420, 6596417, 6596075, 6596073, 6596076, 6596077}, "dkkyx", []string{"dkkyz", "dkmnb", "dkmn8", "dkmn2", "dkkyr", "dkkyq", "dkkyw", "dkkyy"}}, + {-16.672503030218664, -17.672758021182744, 4280531459670, 44, []uint64{4280531459671, 4280531459677, 4280531459676, 4280531459673, 4280531459667, 4280531459665, 4280531459668, 4280531459669}, "7t53jug5", []string{"7t53jugh", "7t53jugk", "7t53jug7", "7t53jug6", "7t53jug4", "7t53juff", "7t53jufg", "7t53jufu"}}, + {86.49059390822367, 32.242525274748886, 947463923583031, 50, []uint64{947463923583074, 947463923583080, 947463923583037, 947463923583036, 947463923583030, 947463923583028, 947463923583029, 947463923583072}, "uxquj9", []string{"uxqujd", "uxqujf", "uxqujc", "uxqujb", "uxquj8", "uxquj2", "uxquj3", "uxquj6"}}, + {56.87195796066953, 120.6607958736713, 4177054046836, 42, []uint64{4177054046837, 4177054046839, 4177054046838, 4177054046835, 4177054046833, 4177054046811, 4177054046814, 4177054046815}, "ydjg9", []string{"ydjgc", "ydjgf", "ydjgd", "ydjg6", "ydjg3", "ydjg2", "ydjg8", "ydjgb"}}, + {14.10330952849472, 110.51689062090009, 62045644865273, 46, []uint64{62045644865276, 62045644865278, 62045644865275, 62045644865274, 62045644865272, 62045644865266, 62045644865267, 62045644865270}, "w6w84z7v", []string{"w6w84z7y", "w6w84zkn", "w6w84zkj", "w6w84zkh", "w6w84z7u", "w6w84z7s", "w6w84z7t", "w6w84z7w"}}, + {59.21504780301257, 94.48566808202304, 258881078640, 38, []uint64{258881078641, 258881078643, 258881078642, 258881078631, 258881078629, 258881078607, 258881078618, 258881078619}, "y4e0y5f2", []string{"y4e0y5f3", "y4e0y5f9", "y4e0y5f8", "y4e0y5dx", "y4e0y5dr", "y4e0y5dp", "y4e0y5f0", "y4e0y5f1"}}, + {10.197745356475934, 67.66952024406055, 54312315535, 36, []uint64{54312315546, 54312315568, 54312315557, 54312315556, 54312315534, 54312315532, 54312315533, 54312315544}, "t9b45b7", []string{"t9b45be", "t9b45bs", "t9b45bk", "t9b45bh", "t9b45b5", "t9b45b4", "t9b45b6", "t9b45bd"}}, + {-33.6589756787871, -97.3429269179469, 7391036082, 36, []uint64{7391036083, 7391036089, 7391036088, 7391036077, 7391036071, 7391036069, 7391036080, 7391036081}, "3f4b8bt43", []string{"3f4b8bt49", "3f4b8bt4d", "3f4b8bt46", "3f4b8bt44", "3f4b8bt41", "3f4b8bt40", "3f4b8bt42", "3f4b8bt48"}}, + {81.65560699364869, -172.35952768012066, 357337898, 30, []uint64{357337899, 357337985, 357337984, 357337813, 357337727, 357337725, 357337896, 357337897}, "bnt2tbgw", []string{"bnt2tbgx", "bnt2tbgz", "bnt2tbgy", "bnt2tbgv", "bnt2tbgt", "bnt2tbgm", "bnt2tbgq", "bnt2tbgr"}}, + {-63.81935491480908, 146.34515949332854, 11580742984, 34, []uint64{11580742985, 11580742987, 11580742986, 11580742943, 11580742941, 11580742935, 11580742978, 11580742979}, "pk8hfn", []string{"pk8hfp", "pk8hfr", "pk8hfq", "pk8hfm", "pk8hfj", "pk8hcv", "pk8hcy", "pk8hcz"}}, + {47.778914617578266, 80.82080809778563, 240275494927001, 48, []uint64{240275494927004, 240275494927006, 240275494927003, 240275494927002, 240275494927000, 240275494926994, 240275494926995, 240275494926998}, "vb3rz1g", []string{"vb3rz45", "vb3rz4h", "vb3rz1u", "vb3rz1s", "vb3rz1e", "vb3rz1d", "vb3rz1f", "vb3rz44"}}, + {24.313553493506333, -59.39264821444522, 107114001, 28, []uint64{107114004, 107114006, 107114003, 107114002, 107114000, 107113658, 107113659, 107113662}, "dsmf", []string{"dsmg", "dsq5", "dsq4", "dsq1", "dsmc", "dsm9", "dsmd", "dsme"}}, + {-39.645219912796165, 155.8718515070068, 3099452283, 32, []uint64{3099452286, 3099452372, 3099452369, 3099452368, 3099452282, 3099452280, 3099452281, 3099452284}, "r2yy6", []string{"r2yyd", "r2yye", "r2yy7", "r2yy5", "r2yy4", "r2yy1", "r2yy3", "r2yy9"}}, + {-54.89929442026187, 45.53419377058887, 147855300, 28, []uint64{147855301, 147855303, 147855302, 147855299, 147855297, 147855211, 147855214, 147855215}, "jn0rsju", []string{"jn0rsnh", "jn0rsnj", "jn0rsjv", "jn0rsjt", "jn0rsjs", "jn0rsje", "jn0rsjg", "jn0rsn5"}}, + {28.220443073227216, 105.41124478183337, 15721910398045, 44, []uint64{15721910398216, 15721910398218, 15721910398047, 15721910398046, 15721910398044, 15721910398038, 15721910398039, 15721910398210}, "wm4bw9q", []string{"wm4bw9w", "wm4bw9x", "wm4bw9r", "wm4bw9p", "wm4bw9n", "wm4bw9j", "wm4bw9m", "wm4bw9t"}}, + {45.598068766325014, -179.13448584865546, 85902817524, 38, []uint64{85902817525, 85902817527, 85902817526, 85902817523, 85902817521, 85902817499, 85902817502, 85902817503}, "b00e", []string{"b00s", "b00u", "b00g", "b00f", "b00d", "b006", "b007", "b00k"}}, + {73.17649219696614, 35.18828642544395, 944514925305308, 50, []uint64{944514925305309, 944514925305311, 944514925305310, 944514925305307, 944514925305305, 944514925305299, 944514925305302, 944514925305303}, "uv1029r", []string{"uv1029x", "uv102c8", "uv102c2", "uv102c0", "uv1029p", "uv1029n", "uv1029q", "uv1029w"}}, + {-28.67370788172571, 85.03951626521302, 684809970171651, 50, []uint64{684809970171654, 684809970171660, 684809970171657, 684809970171656, 684809970171650, 684809970171648, 684809970171649, 684809970171652}, "mfukzh5gs", []string{"mfukzh5gu", "mfukzh5gv", "mfukzh5gt", "mfukzh5gm", "mfukzh5gk", "mfukzh5g7", "mfukzh5ge", "mfukzh5gg"}}, + {3.715766551962588, 93.55088014349167, 60156348039, 36, []uint64{60156348050, 60156348056, 60156348045, 60156348044, 60156348038, 60156348036, 60156348037, 60156348048}, "w0dt0u3tv", []string{"w0dt0u3wj", "w0dt0u3wn", "w0dt0u3ty", "w0dt0u3tw", "w0dt0u3tt", "w0dt0u3ts", "w0dt0u3tu", "w0dt0u3wh"}}, + {55.21010913186183, -179.09417740552453, 21563736859, 36, []uint64{21563736862, 21563736884, 21563736881, 21563736880, 21563736858, 21563736856, 21563736857, 21563736860}, "b1bdhde", []string{"b1bdhdg", "b1bdhdu", "b1bdhds", "b1bdhdk", "b1bdhd7", "b1bdhd6", "b1bdhdd", "b1bdhdf"}}, + {19.04449715546798, 32.1041995596606, 839358034300, 40, []uint64{839358034301, 839358034303, 839358034302, 839358034299, 839358034297, 839358034291, 839358034294, 839358034295}, "sequ", []string{"seqv", "serj", "serh", "ser5", "seqg", "seqe", "seqs", "seqt"}}, + {37.098367916736606, 29.207169540430186, 815887, 20, []uint64{815898, 815920, 815909, 815908, 815886, 815884, 815885, 815896}, "swsg0", []string{"swsg2", "swsg3", "swsg1", "swsfc", "swsfb", "swsdz", "swsep", "swser"}}, + {-33.36716032122785, -12.56831159919966, 248156305, 30, []uint64{248156308, 248156310, 248156307, 248156306, 248156304, 248156218, 248156219, 248156222}, "7dp44j8n1", []string{"7dp44j8n3", "7dp44j8n6", "7dp44j8n4", "7dp44j8jf", "7dp44j8jc", "7dp44j8jb", "7dp44j8n0", "7dp44j8n2"}}, + {59.731897687510354, -64.1459826081991, 7542909, 24, []uint64{7543080, 7543082, 7542911, 7542910, 7542908, 7542902, 7542903, 7543074}, "fdd7u3f7d", []string{"fdd7u3f7f", "fdd7u3f7g", "fdd7u3f7e", "fdd7u3f77", "fdd7u3f76", "fdd7u3f73", "fdd7u3f79", "fdd7u3f7c"}}, + {-41.156022964965814, 31.526288125722175, 38331638, 26, []uint64{38331639, 38331645, 38331644, 38331641, 38331635, 38331633, 38331636, 38331637}, "k8wmv7", []string{"k8wmvk", "k8wmvs", "k8wmve", "k8wmvd", "k8wmv6", "k8wmv4", "k8wmv5", "k8wmvh"}}, + {25.945832772871654, 170.85858450602973, 16394559942201, 44, []uint64{16394559942204, 16394559942206, 16394559942203, 16394559942202, 16394559942200, 16394559942194, 16394559942195, 16394559942198}, "xu97x", []string{"xu97z", "xu9eb", "xu9e8", "xu9e2", "xu97r", "xu97q", "xu97w", "xu97y"}}, + {-61.24923385040893, 89.28334556473419, 2397732519, 32, []uint64{2397732530, 2397732536, 2397732525, 2397732524, 2397732518, 2397732516, 2397732517, 2397732528}, "jvp7x9yjk", []string{"jvp7x9yjs", "jvp7x9yjt", "jvp7x9yjm", "jvp7x9yjj", "jvp7x9yjh", "jvp7x9yj5", "jvp7x9yj7", "jvp7x9yje"}}, + {-6.265619566926034, -75.49232265172759, 56218161, 28, []uint64{56218164, 56218166, 56218163, 56218162, 56218160, 56218138, 56218139, 56218142}, "6qfk6", []string{"6qfkd", "6qfke", "6qfk7", "6qfk5", "6qfk4", "6qfk1", "6qfk3", "6qfk9"}}, + {11.21532013561955, -20.380117021588376, 28546257019, 36, []uint64{28546257022, 28546257108, 28546257105, 28546257104, 28546257018, 28546257016, 28546257017, 28546257020}, "e9cxb1xtu", []string{"e9cxb1xwh", "e9cxb1xwj", "e9cxb1xtv", "e9cxb1xtt", "e9cxb1xts", "e9cxb1xte", "e9cxb1xtg", "e9cxb1xw5"}}, + {68.59682488304679, -176.35307180607197, 23100223647311, 46, []uint64{23100223647322, 23100223647344, 23100223647333, 23100223647332, 23100223647310, 23100223647308, 23100223647309, 23100223647320}, "bh4w4", []string{"bh4w6", "bh4w7", "bh4w5", "bh4tg", "bh4tf", "bh4tc", "bh4w1", "bh4w3"}}, + {-38.74563187835156, -85.27804862165067, 197799, 20, []uint64{197810, 197816, 197805, 197804, 197798, 197796, 197797, 197808}, "6157e6", []string{"6157e7", "6157ee", "6157ed", "6157e9", "6157e3", "6157e1", "6157e4", "6157e5"}}, + {72.33961091688252, 135.47638802567963, 277164812176082, 48, []uint64{277164812176083, 277164812176089, 277164812176088, 277164812176077, 277164812176071, 277164812176069, 277164812176080, 277164812176081}, "zhb7dc4", []string{"zhb7dc6", "zhb7dc7", "zhb7dc5", "zhb7dbg", "zhb7dbf", "zhb7dbc", "zhb7dc1", "zhb7dc3"}}, + {-80.78444711581687, -171.80060940477415, 7400, 22, []uint64{7401, 7403, 7402, 7359, 7357, 7351, 7394, 7395}, "01tu6td", []string{"01tu6tf", "01tu6tg", "01tu6te", "01tu6t7", "01tu6t6", "01tu6t3", "01tu6t9", "01tu6tc"}}, + {-23.063003462258166, -142.48730074806372, 21330356586, 38, []uint64{21330356587, 21330356673, 21330356672, 21330356629, 21330356543, 21330356541, 21330356584, 21330356585}, "2gfsv9e", []string{"2gfsv9g", "2gfsv9u", "2gfsv9s", "2gfsv9k", "2gfsv97", "2gfsv96", "2gfsv9d", "2gfsv9f"}}, + {33.64450093291815, 15.35800109061529, 54114414426319, 46, []uint64{54114414426330, 54114414426352, 54114414426341, 54114414426340, 54114414426318, 54114414426316, 54114414426317, 54114414426328}, "smfzmkz", []string{"smfzmmp", "smfzmt0", "smfzmsb", "smfzms8", "smfzmkx", "smfzmkw", "smfzmky", "smfzmmn"}}, + {-77.05705069232499, 80.97803398178075, 599258456726, 40, []uint64{599258456727, 599258456733, 599258456732, 599258456729, 599258456723, 599258456721, 599258456724, 599258456725}, "jf39dsn", []string{"jf39dsq", "jf39dsr", "jf39dsp", "jf39dez", "jf39dey", "jf39dev", "jf39dsj", "jf39dsm"}}, + {-49.594116011518054, 149.188128584472, 47769797487854, 46, []uint64{47769797487855, 47769797488197, 47769797488196, 47769797488193, 47769797487851, 47769797487849, 47769797487852, 47769797487853}, "pr4jf", []string{"pr4n4", "pr4n5", "pr4jg", "pr4je", "pr4jd", "pr4j9", "pr4jc", "pr4n1"}}, + {21.26540388395732, -104.47781115031103, 81027915023, 38, []uint64{81027915034, 81027915056, 81027915045, 81027915044, 81027915022, 81027915020, 81027915021, 81027915032}, "9ev8yx1xf", []string{"9ev8yx384", "9ev8yx385", "9ev8yx1xg", "9ev8yx1xe", "9ev8yx1xd", "9ev8yx1x9", "9ev8yx1xc", "9ev8yx381"}}, + {46.93970800066745, -1.440928311087191, 32930506856, 36, []uint64{32930506857, 32930506859, 32930506858, 32930506815, 32930506813, 32930506807, 32930506850, 32930506851}, "gbqgp1nex", []string{"gbqgp1nez", "gbqgp1ngb", "gbqgp1ng8", "gbqgp1ng2", "gbqgp1ner", "gbqgp1neq", "gbqgp1new", "gbqgp1ney"}}, + {-59.568826964328764, -133.47979770219536, 50289, 20, []uint64{50292, 50294, 50291, 50290, 50288, 50266, 50267, 50270}, "1j3j4", []string{"1j3j6", "1j3j7", "1j3j5", "1j3hg", "1j3hf", "1j3hc", "1j3j1", "1j3j3"}}, + {-54.87545930818305, 100.44597064156551, 173193179, 28, []uint64{173193182, 173193204, 173193201, 173193200, 173193178, 173193176, 173193177, 173193180}, "nnprv", []string{"nnr2j", "nnr2n", "nnpry", "nnprw", "nnprt", "nnprs", "nnpru", "nnr2h"}}, + {74.56732516677584, -87.99575362936774, 476258, 20, []uint64{476259, 476265, 476264, 476221, 476215, 476213, 476256, 476257}, "fj32jw", []string{"fj32jx", "fj32jz", "fj32jy", "fj32jv", "fj32jt", "fj32jm", "fj32jq", "fj32jr"}}, + {58.5503307627223, -159.02862791491498, 22451641971365, 46, []uint64{22451641971376, 22451641971378, 22451641971367, 22451641971366, 22451641971364, 22451641971342, 22451641971343, 22451641971354}, "b6qvj4ybk", []string{"b6qvj4ybs", "b6qvj4ybt", "b6qvj4ybm", "b6qvj4ybj", "b6qvj4ybh", "b6qvj4yb5", "b6qvj4yb7", "b6qvj4ybe"}}, + {-29.45579736633227, 83.04917700018268, 10443294646, 34, []uint64{10443294647, 10443294653, 10443294652, 10443294649, 10443294643, 10443294641, 10443294644, 10443294645}, "mfg0", []string{"mfg1", "mfg3", "mfg2", "mfer", "mfep", "mfdz", "mffb", "mffc"}}, + {-70.15857882340788, -99.71992857262376, 3249173831731, 46, []uint64{3249173831734, 3249173831740, 3249173831737, 3249173831736, 3249173831730, 3249173831728, 3249173831729, 3249173831732}, "1g90f", []string{"1g914", "1g915", "1g90g", "1g90e", "1g90d", "1g909", "1g90c", "1g911"}}, + {-44.70117667625891, 25.67742406384059, 10035245975439, 44, []uint64{10035245975450, 10035245975472, 10035245975461, 10035245975460, 10035245975438, 10035245975436, 10035245975437, 10035245975448}, "k8438q", []string{"k8438r", "k8438x", "k8438w", "k8438t", "k8438m", "k8438j", "k8438n", "k8438p"}}, + {-65.77712691590568, -58.16796955437168, 625447, 22, []uint64{625458, 625464, 625453, 625452, 625446, 625444, 625445, 625456}, "4sq9u3e", []string{"4sq9u3g", "4sq9u3u", "4sq9u3s", "4sq9u3k", "4sq9u37", "4sq9u36", "4sq9u3d", "4sq9u3f"}}, + {49.65442080878711, 148.46688015048858, 1042657495, 30, []uint64{1042657666, 1042657672, 1042657501, 1042657500, 1042657494, 1042657492, 1042657493, 1042657664}, "z2cd6rq3", []string{"z2cd6rq6", "z2cd6rqd", "z2cd6rq9", "z2cd6rq8", "z2cd6rq2", "z2cd6rq0", "z2cd6rq1", "z2cd6rq4"}}, + {-34.957599263812874, -142.7984440913424, 324016898252, 42, []uint64{324016898253, 324016898255, 324016898254, 324016898251, 324016898249, 324016898243, 324016898246, 324016898247}, "2cf3ns1m5", []string{"2cf3ns1m7", "2cf3ns1mk", "2cf3ns1mh", "2cf3ns1ku", "2cf3ns1kg", "2cf3ns1kf", "2cf3ns1m4", "2cf3ns1m6"}}, + {-38.99496814637678, 66.47026889630794, 40085889, 26, []uint64{40085892, 40085894, 40085891, 40085890, 40085888, 40085802, 40085803, 40085806}, "m3p60", []string{"m3p62", "m3p63", "m3p61", "m3p3c", "m3p3b", "m3p1z", "m3p4p", "m3p4r"}}, + {12.964901972294669, -66.37255765398731, 435487513279138, 50, []uint64{435487513279139, 435487513279145, 435487513279144, 435487513276413, 435487513276407, 435487513276405, 435487513279136, 435487513279137}, "dd2cc8h", []string{"dd2cc8k", "dd2cc8m", "dd2cc8j", "dd2c9xv", "dd2c9xu", "dd2c9xg", "dd2cc85", "dd2cc87"}}, + {40.46427307248814, -41.84151903001475, 7162186, 24, []uint64{7162187, 7162209, 7162208, 7162165, 7162143, 7162141, 7162184, 7162185}, "ep4npy", []string{"ep4npz", "ep4q0p", "ep4q0n", "ep4q0j", "ep4npv", "ep4npt", "ep4npw", "ep4npx"}}, + {-2.2909377476899095, 165.1655824111076, 12546671, 24, []uint64{12546682, 12546768, 12546757, 12546756, 12546670, 12546668, 12546669, 12546680}, "rxt6yqvym", []string{"rxt6yqvyt", "rxt6yqvyw", "rxt6yqvyq", "rxt6yqvyn", "rxt6yqvyj", "rxt6yqvyh", "rxt6yqvyk", "rxt6yqvys"}}, + {-84.23244185325166, 20.999244030379458, 2214638041042, 42, []uint64{2214638041043, 2214638041049, 2214638041048, 2214638041037, 2214638041031, 2214638041029, 2214638041040, 2214638041041}, "h3nb", []string{"h3nc", "h3p1", "h3p0", "h2zp", "h2yz", "h2yx", "h3n8", "h3n9"}}, + {83.03209384036018, -44.80498012973112, 131154065, 28, []uint64{131154068, 131154070, 131154067, 131154066, 131154064, 131153978, 131153979, 131153982}, "gnb0k7", []string{"gnb0kk", "gnb0ks", "gnb0ke", "gnb0kd", "gnb0k6", "gnb0k4", "gnb0k5", "gnb0kh"}}, + {-26.42166801271378, 126.70810473992606, 12355571124292, 44, []uint64{12355571124293, 12355571124295, 12355571124294, 12355571124291, 12355571124289, 12355571122923, 12355571122926, 12355571122927}, "qg61e", []string{"qg61g", "qg61u", "qg61s", "qg61k", "qg617", "qg616", "qg61d", "qg61f"}}, + {-36.23163611404016, 50.520799771809806, 39938807, 26, []uint64{39938978, 39938984, 39938813, 39938812, 39938806, 39938804, 39938805, 39938976}, "m1ecv", []string{"m1efj", "m1efn", "m1ecy", "m1ecw", "m1ect", "m1ecs", "m1ecu", "m1efh"}}, + {40.126411881969034, 149.2196776419296, 62333966, 26, []uint64{62333967, 62333989, 62333988, 62333985, 62333963, 62333961, 62333964, 62333965}, "xr4h78", []string{"xr4h79", "xr4h7c", "xr4h7b", "xr4h5z", "xr4h5x", "xr4h5r", "xr4h72", "xr4h73"}}, + {26.2673472016686, 91.02934880484827, 239147452, 28, []uint64{239147453, 239147455, 239147454, 239147451, 239147449, 239147443, 239147446, 239147447}, "wh8trmv", []string{"wh8trqj", "wh8trqn", "wh8trmy", "wh8trmw", "wh8trmt", "wh8trms", "wh8trmu", "wh8trqh"}}, + {-74.85076864644361, 66.47162208723603, 605751476720433, 50, []uint64{605751476720436, 605751476720438, 605751476720435, 605751476720434, 605751476720432, 605751476720410, 605751476720411, 605751476720414}, "j6xq0", []string{"j6xq2", "j6xq3", "j6xq1", "j6xmc", "j6xmb", "j6xjz", "j6xnp", "j6xnr"}}, + {40.894175794150215, 4.971392445702804, 54235256373334, 46, []uint64{54235256373335, 54235256373341, 54235256373340, 54235256373337, 54235256373331, 54235256373329, 54235256373332, 54235256373333}, "sp789", []string{"sp78c", "sp78f", "sp78d", "sp786", "sp783", "sp782", "sp788", "sp78b"}}, + {-70.97605717820989, 51.069160337909096, 9441716292457, 44, []uint64{9441716292460, 9441716292462, 9441716292459, 9441716292458, 9441716292456, 9441716292450, 9441716292451, 9441716292454}, "j5kk4p5", []string{"j5kk4p7", "j5kk4pk", "j5kk4ph", "j5kk4nu", "j5kk4ng", "j5kk4nf", "j5kk4p4", "j5kk4p6"}}, + {64.84065524701145, -44.795496764592826, 8141157597, 34, []uint64{8141157768, 8141157770, 8141157599, 8141157598, 8141157596, 8141157590, 8141157591, 8141157762}, "g580u", []string{"g581h", "g581j", "g580v", "g580t", "g580s", "g580e", "g580g", "g5815"}}, + {-3.929449178336654, 32.7731516195563, 2601556092006, 42, []uint64{2601556092007, 2601556092013, 2601556092012, 2601556092009, 2601556092003, 2601556092001, 2601556092004, 2601556092005}, "kxr39u8t", []string{"kxr39u8w", "kxr39u8y", "kxr39u8v", "kxr39u8u", "kxr39u8s", "kxr39u8k", "kxr39u8m", "kxr39u8q"}}, + {48.807226382763474, -22.825335128756706, 1978093, 22, []uint64{1978104, 1978106, 1978095, 1978094, 1978092, 1978086, 1978087, 1978098}, "g2xv8", []string{"g2xvb", "g2xvc", "g2xv9", "g2xv3", "g2xv2", "g2xtr", "g2xtx", "g2xtz"}}, + {28.385183807025882, -96.14925269331434, 5163673, 24, []uint64{5163676, 5163678, 5163675, 5163674, 5163672, 5163666, 5163667, 5163670}, "9v59k", []string{"9v59s", "9v59t", "9v59m", "9v59j", "9v59h", "9v595", "9v597", "9v59e"}}, + {-60.596139484638115, 137.43307079403894, 722532066, 30, []uint64{722532067, 722532073, 722532072, 722532029, 722532023, 722532021, 722532064, 722532065}, "pj1xr2g9y", []string{"pj1xr2gdn", "pj1xr2gdp", "pj1xr2g9z", "pj1xr2g9x", "pj1xr2g9w", "pj1xr2g9t", "pj1xr2g9v", "pj1xr2gdj"}}, + {-75.44052495886808, -59.75986766559072, 9227028, 26, []uint64{9227029, 9227031, 9227030, 9227027, 9227025, 9226683, 9226686, 9226687}, "4dtdb", []string{"4dte0", "4dte1", "4dtdc", "4dtd9", "4dtd8", "4dt6x", "4dt6z", "4dt7p"}}, + {-45.582366354443366, -142.67328492055822, 32216, 20, []uint64{32217, 32219, 32218, 32207, 32205, 32199, 32210, 32211}, "0zfs9murr", []string{"0zfs9murx", "0zfs9mux8", "0zfs9mux2", "0zfs9mux0", "0zfs9murp", "0zfs9murn", "0zfs9murq", "0zfs9murw"}}, + {-4.325271087596775, -110.33935750240926, 32783644, 28, []uint64{32783645, 32783647, 32783646, 32783643, 32783641, 32783635, 32783638, 32783639}, "3x1x", []string{"3x38", "3x3b", "3x1z", "3x1y", "3x1w", "3x1q", "3x1r", "3x32"}}, + {-39.12785247835563, -28.82573991958634, 232617, 20, []uint64{232620, 232622, 232619, 232618, 232616, 232610, 232611, 232614}, "73592hc", []string{"73592j1", "73592j4", "73592hf", "73592hd", "73592h9", "73592h8", "73592hb", "73592j0"}}, + {-15.657190197904129, -139.40704436730994, 24569838055, 38, []uint64{24569838066, 24569838072, 24569838061, 24569838060, 24569838054, 24569838052, 24569838053, 24569838064}, "2vhygtw", []string{"2vhygty", "2vhygtz", "2vhygtx", "2vhygtr", "2vhygtq", "2vhygtm", "2vhygtt", "2vhygtv"}}, + {-71.3734397192602, -55.50582104874776, 146505, 20, []uint64{146508, 146510, 146507, 146506, 146504, 146498, 146499, 146502}, "4g29byvg", []string{"4g29byvu", "4g29byyh", "4g29byy5", "4g29byy4", "4g29byvf", "4g29byvd", "4g29byve", "4g29byvs"}}, + {-25.248746525554466, -86.3488195638929, 847753664054, 42, []uint64{847753664055, 847753664061, 847753664060, 847753664057, 847753664051, 847753664049, 847753664052, 847753664053}, "65d875d", []string{"65d875f", "65d875g", "65d875e", "65d8757", "65d8756", "65d8753", "65d8759", "65d875c"}}, + {-29.283376971376136, 86.87620531495486, 167196253065, 38, []uint64{167196253068, 167196253070, 167196253067, 167196253066, 167196253064, 167196253058, 167196253059, 167196253062}, "mfvc", []string{"mfvf", "mfy4", "mfy1", "mfy0", "mfvb", "mfv8", "mfv9", "mfvd"}}, + {38.303625409593224, -179.91658897942398, 18108503, 26, []uint64{18108674, 18108680, 18108509, 18108508, 18108502, 18108500, 18108501, 18108672}, "8nb1cuuw", []string{"8nb1cuux", "8nb1cuuz", "8nb1cuuy", "8nb1cuuv", "8nb1cuut", "8nb1cuum", "8nb1cuuq", "8nb1cuur"}}, + {-44.335584643209586, -62.80470286397029, 214923946484, 40, []uint64{214923946485, 214923946487, 214923946486, 214923946483, 214923946481, 214923946459, 214923946462, 214923946463}, "6857fbg", []string{"6857fc5", "6857fch", "6857fbu", "6857fbs", "6857fbe", "6857fbd", "6857fbf", "6857fc4"}}, + {-38.06984019081574, 121.13684999506222, 766271644965, 40, []uint64{766271644976, 766271644978, 766271644967, 766271644966, 766271644964, 766271644942, 766271644943, 766271644954}, "q9np", []string{"q9q0", "q9q2", "q9nr", "q9nq", "q9nn", "q9jy", "q9jz", "q9mb"}}, + {-32.36884735169588, 167.28247678064508, 205789049713616, 48, []uint64{205789049713617, 205789049713619, 205789049713618, 205789049713607, 205789049713605, 205789049713519, 205789049713530, 205789049713531}, "rdnzye7mu", []string{"rdnzye7qh", "rdnzye7qj", "rdnzye7mv", "rdnzye7mt", "rdnzye7ms", "rdnzye7me", "rdnzye7mg", "rdnzye7q5"}}, + {64.61801576099242, 153.3052682049747, 1023605, 20, []uint64{1023776, 1023778, 1023607, 1023606, 1023604, 1023582, 1023583, 1023754}, "z7mp8", []string{"z7mpb", "z7mpc", "z7mp9", "z7mp3", "z7mp2", "z7kzr", "z7kzx", "z7kzz"}}, + {-77.92493160863023, 0.7877864442416467, 135272527, 28, []uint64{135272538, 135272560, 135272549, 135272548, 135272526, 135272524, 135272525, 135272536}, "h40s", []string{"h40t", "h40v", "h40u", "h40g", "h40e", "h407", "h40k", "h40m"}}, + {20.632327016777708, -133.14351124997484, 80621691120168, 48, []uint64{80621691120169, 80621691120171, 80621691120170, 80621688323967, 80621688323965, 80621688323959, 80621691120162, 80621691120163}, "959m6hp", []string{"959m6hr", "959m6k2", "959m6k0", "959m67b", "959m65z", "959m65y", "959m6hn", "959m6hq"}}, + {-64.97379465925042, -138.48111405600503, 29270285337108, 50, []uint64{29270285337109, 29270285337111, 29270285337110, 29270285337107, 29270285337105, 29270285336763, 29270285336766, 29270285336767}, "0umw", []string{"0umx", "0umz", "0umy", "0umv", "0umt", "0umm", "0umq", "0umr"}}, + {42.11195699854579, 23.01016901980617, 13059446, 24, []uint64{13059447, 13059453, 13059452, 13059449, 13059443, 13059441, 13059444, 13059445}, "sx2re", []string{"sx2rg", "sx2ru", "sx2rs", "sx2rk", "sx2r7", "sx2r6", "sx2rd", "sx2rf"}}, + {-29.861446273604095, 64.06737579451871, 10314777341, 34, []uint64{10314777512, 10314777514, 10314777343, 10314777342, 10314777340, 10314777334, 10314777335, 10314777506}, "m6tw1gukq", []string{"m6tw1gukw", "m6tw1gukx", "m6tw1gukr", "m6tw1gukp", "m6tw1gukn", "m6tw1gukj", "m6tw1gukm", "m6tw1gukt"}}, + {-87.69089192175306, -112.82757853771909, 2387667851768, 46, []uint64{2387667851769, 2387667851771, 2387667851770, 2387667851759, 2387667851757, 2387667851751, 2387667851762, 2387667851763}, "12rv0s37", []string{"12rv0s3k", "12rv0s3s", "12rv0s3e", "12rv0s3d", "12rv0s36", "12rv0s34", "12rv0s35", "12rv0s3h"}}, + {-50.106431110412814, -152.7106633935473, 477805, 24, []uint64{477816, 477818, 477807, 477806, 477804, 477798, 477799, 477810}, "0x56uy", []string{"0x56uz", "0x56vp", "0x56vn", "0x56vj", "0x56uv", "0x56ut", "0x56uw", "0x56ux"}}, + {23.296676053345422, -122.24893853376852, 1253569, 22, []uint64{1253572, 1253574, 1253571, 1253570, 1253568, 1253482, 1253483, 1253486}, "9k1hd1j", []string{"9k1hd1m", "9k1hd1q", "9k1hd1n", "9k1hd0y", "9k1hd0v", "9k1hd0u", "9k1hd1h", "9k1hd1k"}}, + {-73.86437974660657, -147.740187761985, 3395568, 28, []uint64{3395569, 3395571, 3395570, 3395559, 3395557, 3395535, 3395546, 3395547}, "0dygy16uu", []string{"0dygy16vh", "0dygy16vj", "0dygy16uv", "0dygy16ut", "0dygy16us", "0dygy16ue", "0dygy16ug", "0dygy16v5"}}, + {-66.40366729270318, 147.61832706205314, 740914026246, 40, []uint64{740914026247, 740914026253, 740914026252, 740914026249, 740914026243, 740914026241, 740914026244, 740914026245}, "pk0ypp", []string{"pk0yr0", "pk0yr2", "pk0ypr", "pk0ypq", "pk0ypn", "pk0yny", "pk0ynz", "pk0yqb"}}, + {35.22730913622944, 163.05141413857928, 15666859, 24, []uint64{15666862, 15672324, 15672321, 15672320, 15666858, 15666856, 15666857, 15666860}, "xw7b", []string{"xw7c", "xwk1", "xwk0", "xwhp", "xw5z", "xw5x", "xw78", "xw79"}}, + {-61.95097122110701, 11.061562826042064, 34668506, 26, []uint64{34668507, 34668529, 34668528, 34668517, 34668495, 34668493, 34668504, 34668505}, "hhzzednv", []string{"hhzzedny", "hhzzedpn", "hhzzedpj", "hhzzedph", "hhzzednu", "hhzzedns", "hhzzednt", "hhzzednw"}}, + {0.13090881292009726, -9.701057303056615, 7147625311, 34, []uint64{7147625994, 7147626016, 7147625333, 7147625332, 7147625310, 7147625308, 7147625309, 7147625992}, "eb10e", []string{"eb10g", "eb10u", "eb10s", "eb10k", "eb107", "eb106", "eb10d", "eb10f"}}, + {62.860461415097234, -134.28199509903789, 1533409260393, 42, []uint64{1533409260396, 1533409260398, 1533409260395, 1533409260394, 1533409260392, 1533409260386, 1533409260387, 1533409260390}, "c50t8", []string{"c50tb", "c50tc", "c50t9", "c50t3", "c50t2", "c50mr", "c50mx", "c50mz"}}, + {77.86728994970326, 91.74426892092742, 262355025328, 38, []uint64{262355025329, 262355025331, 262355025330, 262355025319, 262355025317, 262355025295, 262355025306, 262355025307}, "yjc4z", []string{"yjc5p", "yjc70", "yjc6b", "yjc68", "yjc4x", "yjc4w", "yjc4y", "yjc5n"}}, + {17.042102255421923, -137.38398441115532, 18228600709, 36, []uint64{18228600720, 18228600722, 18228600711, 18228600710, 18228600708, 18228600622, 18228600623, 18228600634}, "8gn2", []string{"8gn3", "8gn9", "8gn8", "8fyx", "8fyr", "8fyp", "8gn0", "8gn1"}}, + {54.60666636672977, 104.58489233785076, 1010194874, 30, []uint64{1010194875, 1010195217, 1010195216, 1010195205, 1010194863, 1010194861, 1010194872, 1010194873}, "y3dqe", []string{"y3dqg", "y3dqu", "y3dqs", "y3dqk", "y3dq7", "y3dq6", "y3dqd", "y3dqf"}}, + {14.92478119986481, -71.61424929075291, 26225964213, 36, []uint64{26225964256, 26225964258, 26225964215, 26225964214, 26225964212, 26225964190, 26225964191, 26225964234}, "d6thfku", []string{"d6thfmh", "d6thfmj", "d6thfkv", "d6thfkt", "d6thfks", "d6thfke", "d6thfkg", "d6thfm5"}}, + {-19.250288895578706, -142.29033341095783, 1552869055079, 44, []uint64{1552869055090, 1552869055096, 1552869055085, 1552869055084, 1552869055078, 1552869055076, 1552869055077, 1552869055088}, "2udf6pe6f", []string{"2udf6pe74", "2udf6pe75", "2udf6pe6g", "2udf6pe6e", "2udf6pe6d", "2udf6pe69", "2udf6pe6c", "2udf6pe71"}}, + {46.21055133588379, -122.51430322248962, 95034232891, 38, []uint64{95034232894, 95034232980, 95034232977, 95034232976, 95034232890, 95034232888, 95034232889, 95034232892}, "c20yuh7", []string{"c20yuhe", "c20yuhs", "c20yuhk", "c20yuhh", "c20yuh5", "c20yuh4", "c20yuh6", "c20yuhd"}}, + {-43.37286014500569, 25.855700363143114, 598211, 20, []uint64{598214, 598220, 598217, 598216, 598210, 598208, 598209, 598212}, "k863", []string{"k866", "k86d", "k869", "k868", "k862", "k860", "k861", "k864"}}, + {-84.20486200085725, -125.7812499149004, 34440, 20, []uint64{34441, 34443, 34442, 33759, 33757, 33751, 34434, 34435}, "11n8cyb", []string{"11n8cz0", "11n8cz1", "11n8cyc", "11n8cy9", "11n8cy8", "11n8cwx", "11n8cwz", "11n8cxp"}}, + {29.637253040185897, -60.770104319555685, 1759100688677, 42, []uint64{1759100688688, 1759100688690, 1759100688679, 1759100688678, 1759100688676, 1759100688654, 1759100688655, 1759100688666}, "dtkb9", []string{"dtkbc", "dtkbf", "dtkbd", "dtkb6", "dtkb3", "dtkb2", "dtkb8", "dtkbb"}}, + {65.29549046090688, -143.1210178420879, 92188718633970, 48, []uint64{92188718633971, 92188718633977, 92188718633976, 92188718633965, 92188718633959, 92188718633957, 92188718633968, 92188718633969}, "bgd5", []string{"bgdh", "bgdk", "bgd7", "bgd6", "bgd4", "bg9f", "bg9g", "bg9u"}}, + {36.86022651236635, 82.21019970875932, 55725778731, 36, []uint64{55725778734, 55725778820, 55725778817, 55725778816, 55725778730, 55725778728, 55725778729, 55725778732}, "tyd3ww", []string{"tyd3wx", "tyd3wz", "tyd3wy", "tyd3wv", "tyd3wt", "tyd3wm", "tyd3wq", "tyd3wr"}}, + {41.525807283382164, -169.08674340014113, 1192902096428, 42, []uint64{1192902096429, 1192902096431, 1192902096430, 1192902096427, 1192902096425, 1192902096419, 1192902096422, 1192902096423}, "8pru0rdc", []string{"8pru0rdf", "8pru0re4", "8pru0re1", "8pru0re0", "8pru0rdb", "8pru0rd8", "8pru0rd9", "8pru0rdd"}}, + {14.299568374539362, 122.5313059315231, 3812235548, 32, []uint64{3812235549, 3812235551, 3812235550, 3812235547, 3812235545, 3812235539, 3812235542, 3812235543}, "wdx1k70", []string{"wdx1k72", "wdx1k73", "wdx1k71", "wdx1k6c", "wdx1k6b", "wdx1k4z", "wdx1k5p", "wdx1k5r"}}, + {58.54990850252216, -80.55284406219653, 118675875, 28, []uint64{118675878, 118675884, 118675881, 118675880, 118675874, 118675872, 118675873, 118675876}, "f4qtn", []string{"f4qtq", "f4qtr", "f4qtp", "f4qsz", "f4qsy", "f4qsv", "f4qtj", "f4qtm"}}, + {-25.753329489467433, -5.567783612306812, 263420133046133, 50, []uint64{263420133089824, 263420133089826, 263420133046135, 263420133046134, 263420133046132, 263420133046110, 263420133046111, 263420133089802}, "7gkj3", []string{"7gkj9", "7gkjd", "7gkj6", "7gkj4", "7gkj1", "7gkj0", "7gkj2", "7gkj8"}}, + {5.36141398048494, -24.884986802382628, 7197665405476, 44, []uint64{7197665405477, 7197665405479, 7197665405478, 7197665405475, 7197665405473, 7197665405451, 7197665405454, 7197665405455}, "e2yq", []string{"e2yr", "e2yx", "e2yw", "e2yt", "e2ym", "e2yj", "e2yn", "e2yp"}}, + {88.02565547115955, -117.40303546003997, 394027330, 30, []uint64{394027331, 394027337, 394027336, 394027293, 394027287, 394027285, 394027328, 394027329}, "crssb2", []string{"crssb3", "crssb9", "crssb8", "crss8x", "crss8r", "crss8p", "crssb0", "crssb1"}}, + {-22.239628219191232, 105.72129145741928, 12115797979, 34, []uint64{12115797982, 12115798004, 12115798001, 12115798000, 12115797978, 12115797976, 12115797977, 12115797980}, "qk51m", []string{"qk51t", "qk51w", "qk51q", "qk51n", "qk51j", "qk51h", "qk51k", "qk51s"}}, + {-62.030014727439266, 29.57291122188323, 36028351484, 36, []uint64{36028351485, 36028351487, 36028351486, 36028351483, 36028351481, 36028351475, 36028351478, 36028351479}, "hsvp0gy2f", []string{"hsvp0gy34", "hsvp0gy35", "hsvp0gy2g", "hsvp0gy2e", "hsvp0gy2d", "hsvp0gy29", "hsvp0gy2c", "hsvp0gy31"}}, + {61.50405763665185, 53.40508427104214, 3644309448, 32, []uint64{3644309449, 3644309451, 3644309450, 3644309407, 3644309405, 3644309399, 3644309442, 3644309443}, "v4vvzk2rh", []string{"v4vvzk2rk", "v4vvzk2rm", "v4vvzk2rj", "v4vvzk2qv", "v4vvzk2qu", "v4vvzk2qg", "v4vvzk2r5", "v4vvzk2r7"}}, + {-82.48982630686078, 81.61441058339551, 568516, 20, []uint64{568517, 568519, 568518, 568515, 568513, 568427, 568430, 568431}, "jc649pj", []string{"jc649pm", "jc649pq", "jc649pn", "jc649ny", "jc649nv", "jc649nu", "jc649ph", "jc649pk"}}, + {25.611734580019387, 93.43123868264956, 244914820151, 38, []uint64{244914820194, 244914820200, 244914820157, 244914820156, 244914820150, 244914820148, 244914820149, 244914820192}, "whd3wn", []string{"whd3wp", "whd3wr", "whd3wq", "whd3wm", "whd3wj", "whd3tv", "whd3ty", "whd3tz"}}, + {72.00649532771786, 94.30923153046751, 16775628720040, 44, []uint64{16775628720041, 16775628720043, 16775628720042, 16775628719871, 16775628719869, 16775628719863, 16775628720034, 16775628720035}, "yhg1dh3uh", []string{"yhg1dh3uk", "yhg1dh3um", "yhg1dh3uj", "yhg1dh3gv", "yhg1dh3gu", "yhg1dh3gg", "yhg1dh3u5", "yhg1dh3u7"}}, + {-37.49721157969907, 153.13585305016022, 3176458723091, 42, []uint64{3176458723094, 3176458723100, 3176458723097, 3176458723096, 3176458723090, 3176458723088, 3176458723089, 3176458723092}, "r3kfsty4", []string{"r3kfsty5", "r3kfsty7", "r3kfsty6", "r3kfsty3", "r3kfsty1", "r3kfstvc", "r3kfstvf", "r3kfstvg"}}, + {-73.05966803026968, 125.3206766837393, 2747401143, 32, []uint64{2747401186, 2747401192, 2747401149, 2747401148, 2747401142, 2747401140, 2747401141, 2747401184}, "ng107", []string{"ng10e", "ng10s", "ng10k", "ng10h", "ng105", "ng104", "ng106", "ng10d"}}, + {69.74376932127052, 42.33494593758951, 879312, 20, []uint64{879313, 879315, 879314, 879303, 879301, 879215, 879226, 879227}, "uuqhg27q", []string{"uuqhg27r", "uuqhg27x", "uuqhg27w", "uuqhg27t", "uuqhg27m", "uuqhg27j", "uuqhg27n", "uuqhg27p"}}, + {49.66343107974717, 98.03740564570762, 66029544462119, 46, []uint64{66029544462130, 66029544462136, 66029544462125, 66029544462124, 66029544462118, 66029544462116, 66029544462117, 66029544462128}, "y0vdw", []string{"y0vdy", "y0vdz", "y0vdx", "y0vdr", "y0vdq", "y0vdm", "y0vdt", "y0vdv"}}, + {-65.30065027074306, -64.12028523848856, 638397482, 32, []uint64{638397483, 638397569, 638397568, 638396885, 638396799, 638396797, 638397480, 638397481}, "4s6ksbm", []string{"4s6ksbt", "4s6ksbw", "4s6ksbq", "4s6ksbn", "4s6ksbj", "4s6ksbh", "4s6ksbk", "4s6ksbs"}}, + {83.68007710143866, -121.57100127503509, 1607894995831, 42, []uint64{1607894996514, 1607894996520, 1607894995837, 1607894995836, 1607894995830, 1607894995828, 1607894995829, 1607894996512}, "cqcs196x", []string{"cqcs19d8", "cqcs19db", "cqcs196z", "cqcs196y", "cqcs196w", "cqcs196q", "cqcs196r", "cqcs19d2"}}, + {9.324322346714311, 66.63194018267677, 13487628315, 34, []uint64{13487628318, 13487628340, 13487628337, 13487628336, 13487628314, 13487628312, 13487628313, 13487628316}, "t3xmh1ry", []string{"t3xmh1rz", "t3xmh32p", "t3xmh32n", "t3xmh32j", "t3xmh1rv", "t3xmh1rt", "t3xmh1rw", "t3xmh1rx"}}, + {-51.6799281581425, 73.89687166357177, 38441410397, 36, []uint64{38441585160, 38441585162, 38441410399, 38441410398, 38441410396, 38441410390, 38441410391, 38441585154}, "jwu9cxf", []string{"jwud184", "jwud185", "jwu9cxg", "jwu9cxe", "jwu9cxd", "jwu9cx9", "jwu9cxc", "jwud181"}}, + {-51.34148797538364, -33.78290517491405, 2965247, 24, []uint64{2965418, 2987264, 2987093, 2987092, 2965246, 2965244, 2965245, 2965416}, "5nzgzm841", []string{"5nzgzm843", "5nzgzm846", "5nzgzm844", "5nzgzm81f", "5nzgzm81c", "5nzgzm81b", "5nzgzm840", "5nzgzm842"}}, + {44.06187874110765, -112.78240945993457, 81784398, 28, []uint64{81784399, 81784421, 81784420, 81784417, 81784395, 81784393, 81784396, 81784397}, "9rzf9t4", []string{"9rzf9t6", "9rzf9t7", "9rzf9t5", "9rzf9sg", "9rzf9sf", "9rzf9sc", "9rzf9t1", "9rzf9t3"}}, + {-20.76688840339193, -160.44763068351313, 86617935, 30, []uint64{86617946, 86617968, 86617957, 86617956, 86617934, 86617932, 86617933, 86617944}, "2kmcu", []string{"2kmfh", "2kmfj", "2kmcv", "2kmct", "2kmcs", "2kmce", "2kmcg", "2kmf5"}}, + {-49.07050862184042, -95.82295421164599, 271566401533, 42, []uint64{271566404264, 271566404266, 271566401535, 271566401534, 271566401532, 271566401526, 271566401527, 271566404258}, "1z7bg6z", []string{"1z7bg7p", "1z7bge0", "1z7bgdb", "1z7bgd8", "1z7bg6x", "1z7bg6w", "1z7bg6y", "1z7bg7n"}}, + {-3.743449832632919, -132.46514872839907, 7858966322, 36, []uint64{7858966323, 7858966329, 7858966328, 7858966317, 7858966311, 7858966309, 7858966320, 7858966321}, "3p3f9wt", []string{"3p3f9wv", "3p3f9wy", "3p3f9ww", "3p3f9wq", "3p3f9wm", "3p3f9wk", "3p3f9ws", "3p3f9wu"}}, + {-36.89612953612232, -45.693658992124284, 218770708170, 40, []uint64{218770708171, 218770708193, 218770708192, 218770708149, 218770708127, 218770708125, 218770708168, 218770708169}, "6crw05qb", []string{"6crw05qc", "6crw05r1", "6crw05r0", "6crw05pp", "6crw05nz", "6crw05nx", "6crw05q8", "6crw05q9"}}, + {-55.10115473612677, 144.274845408916, 11899683488654, 44, []uint64{11899683488655, 11899683488677, 11899683488676, 11899683488673, 11899683488651, 11899683488649, 11899683488652, 11899683488653}, "pnnwe11s", []string{"pnnwe11t", "pnnwe11v", "pnnwe11u", "pnnwe11g", "pnnwe11e", "pnnwe117", "pnnwe11k", "pnnwe11m"}}, + {-9.316157166118515, -72.73506635322701, 3686273288891, 44, []uint64{3686273288894, 3686273294356, 3686273294353, 3686273294352, 3686273288890, 3686273288888, 3686273288889, 3686273288892}, "6qk70b", []string{"6qk70c", "6qk711", "6qk710", "6qk6cp", "6qk6bz", "6qk6bx", "6qk708", "6qk709"}}, + {-24.563017049527843, 135.62943287787493, 3109104392, 32, []uint64{3109104393, 3109104395, 3109104394, 3109104223, 3109104221, 3109104215, 3109104386, 3109104387}, "r58k", []string{"r58m", "r58t", "r58s", "r58e", "r587", "r585", "r58h", "r58j"}}, + {54.62961493735202, 173.47357201125124, 4309691284129, 42, []uint64{4309691284132, 4309691284134, 4309691284131, 4309691284130, 4309691284128, 4309691284106, 4309691284107, 4309691284110}, "zceq", []string{"zcer", "zcex", "zcew", "zcet", "zcem", "zcej", "zcen", "zcep"}}, + {-50.43419480070588, 105.24206652666908, 173837091, 28, []uint64{173837094, 173837100, 173837097, 173837096, 173837090, 173837088, 173837089, 173837092}, "nr4c", []string{"nr4f", "nr54", "nr51", "nr50", "nr4b", "nr48", "nr49", "nr4d"}}, + {-33.279500595177524, -29.197395304037883, 235684, 20, []uint64{235685, 235687, 235686, 235683, 235681, 235659, 235662, 235663}, "7654", []string{"7655", "7657", "7656", "7653", "7651", "764c", "764f", "764g"}}, + {40.797591857211955, -4.662101377209183, 120143883997, 38, []uint64{120143884168, 120143884170, 120143883999, 120143883998, 120143883996, 120143883990, 120143883991, 120143884162}, "ezk8jfv", []string{"ezk8jgj", "ezk8jgn", "ezk8jfy", "ezk8jfw", "ezk8jft", "ezk8jfs", "ezk8jfu", "ezk8jgh"}}, + {33.05334090239192, -88.50126187945715, 27581214698715, 46, []uint64{27581214698718, 27581214698740, 27581214698737, 27581214698736, 27581214698714, 27581214698712, 27581214698713, 27581214698716}, "djch4", []string{"djch6", "djch7", "djch5", "djc5g", "djc5f", "djc5c", "djch1", "djch3"}}, + {20.383409198679146, 39.330971851421054, 13459084786623, 44, []uint64{13459084786666, 13459085136192, 13459085136149, 13459085136148, 13459084786622, 13459084786620, 13459084786621, 13459084786664}, "sgegyyxv", []string{"sgegyyxy", "sgegzn8n", "sgegzn8j", "sgegzn8h", "sgegyyxu", "sgegyyxs", "sgegyyxt", "sgegyyxw"}}, + {73.00097301011556, -17.952435637882445, 554692580724351, 50, []uint64{554692580724522, 554692580724608, 554692580724437, 554692580724436, 554692580724350, 554692580724348, 554692580724349, 554692580724520}, "gsgp", []string{"gt50", "gt52", "gsgr", "gsgq", "gsgn", "gsfy", "gsfz", "gt4b"}}, + {-81.22208040652913, 25.54617505459464, 136675804, 28, []uint64{136675805, 136675807, 136675806, 136675803, 136675801, 136675795, 136675798, 136675799}, "h9d1vmf", []string{"h9d1vq4", "h9d1vq5", "h9d1vmg", "h9d1vme", "h9d1vmd", "h9d1vm9", "h9d1vmc", "h9d1vq1"}}, + {80.3841324805253, -58.43853365509132, 124961712, 28, []uint64{124961713, 124961715, 124961714, 124961703, 124961701, 124961679, 124961690, 124961691}, "fwq3q1qre", []string{"fwq3q1qrg", "fwq3q1qru", "fwq3q1qrs", "fwq3q1qrk", "fwq3q1qr7", "fwq3q1qr6", "fwq3q1qrd", "fwq3q1qrf"}}, + {-73.65656874027627, 149.62236278294586, 11382601178, 34, []uint64{11382601179, 11382601201, 11382601200, 11382601189, 11382601167, 11382601165, 11382601176, 11382601177}, "p6fkuxpx", []string{"p6fkuxr8", "p6fkuxrb", "p6fkuxpz", "p6fkuxpy", "p6fkuxpw", "p6fkuxpq", "p6fkuxpr", "p6fkuxr2"}}, + {52.414648303587455, 33.14520640106639, 14460821733011, 44, []uint64{14460821733014, 14460821733020, 14460821733017, 14460821733016, 14460821733010, 14460821733008, 14460821733009, 14460821733012}, "u9rd", []string{"u9re", "u9rg", "u9rf", "u9rc", "u9r9", "u9r3", "u9r6", "u9r7"}}, + {-37.92656138868187, -175.97391928025172, 1068194, 24, []uint64{1068195, 1068201, 1068200, 1067517, 1067511, 1067509, 1068192, 1068193}, "216b5xety", []string{"216b5xewn", "216b5xewp", "216b5xetz", "216b5xetx", "216b5xetw", "216b5xett", "216b5xetv", "216b5xewj"}}, + {33.18252916235359, -166.74636603699764, 4619810438, 34, []uint64{4619810439, 4619810445, 4619810444, 4619810441, 4619810435, 4619810433, 4619810436, 4619810437}, "8mckv8", []string{"8mckv9", "8mckvc", "8mckvb", "8mcktz", "8mcktx", "8mcktr", "8mckv2", "8mckv3"}}, + {-27.557832018916088, 64.11871661079928, 2521268, 22, []uint64{2521269, 2521271, 2521270, 2521267, 2521265, 2521243, 2521246, 2521247}, "m7je5", []string{"m7je7", "m7jek", "m7jeh", "m7jdu", "m7jdg", "m7jdf", "m7je4", "m7je6"}}, + {19.528460201458074, -107.34510729594331, 19742562, 26, []uint64{19742563, 19742569, 19742568, 19742525, 19742519, 19742517, 19742560, 19742561}, "9e7x", []string{"9ee8", "9eeb", "9e7z", "9e7y", "9e7w", "9e7q", "9e7r", "9ee2"}}, + {-50.93236322858138, -33.952295646769926, 759161738, 32, []uint64{759161739, 759161761, 759161760, 759161589, 759161567, 759161565, 759161736, 759161737}, "5nzy72h", []string{"5nzy72k", "5nzy72m", "5nzy72j", "5nzy5rv", "5nzy5ru", "5nzy5rg", "5nzy725", "5nzy727"}}, + {14.024979007139322, -105.96198327434831, 1260772652, 32, []uint64{1260772653, 1260772655, 1260772654, 1260772651, 1260772649, 1260772643, 1260772646, 1260772647}, "9dkx", []string{"9ds8", "9dsb", "9dkz", "9dky", "9dkw", "9dkq", "9dkr", "9ds2"}}, + {-5.100735735555645, -23.70637630362762, 272306124484643, 50, []uint64{272306124484646, 272306124484652, 272306124484649, 272306124484648, 272306124484642, 272306124484640, 272306124484641, 272306124484644}, "7rp4ux3w", []string{"7rp4ux3x", "7rp4ux3z", "7rp4ux3y", "7rp4ux3v", "7rp4ux3t", "7rp4ux3m", "7rp4ux3q", "7rp4ux3r"}}, + {-62.17718178356881, -22.393368254460853, 3020099, 24, []uint64{3020102, 3020108, 3020105, 3020104, 3020098, 3020096, 3020097, 3020100}, "5sbn62vxs", []string{"5sbn62vxu", "5sbn62vxv", "5sbn62vxt", "5sbn62vxm", "5sbn62vxk", "5sbn62vx7", "5sbn62vxe", "5sbn62vxg"}}, + {69.15351319474576, 96.38407230665325, 4000036, 22, []uint64{4000037, 4000039, 4000038, 4000035, 4000033, 4000011, 4000014, 4000015}, "yhk93m08r", []string{"yhk93m08x", "yhk93m0b8", "yhk93m0b2", "yhk93m0b0", "yhk93m08p", "yhk93m08n", "yhk93m08q", "yhk93m08w"}}, + {-51.79740376851987, 15.507942481170176, 2189188, 22, []uint64{2189189, 2189191, 2189190, 2189187, 2189185, 2189099, 2189102, 2189103}, "hqg12", []string{"hqg18", "hqg19", "hqg13", "hqg11", "hqg10", "hqfcp", "hqfcr", "hqfcx"}}, + {86.634499437263, 111.589214346066, 264069834515, 38, []uint64{264069834518, 264069834524, 264069834521, 264069834520, 264069834514, 264069834512, 264069834513, 264069834516}, "yrrkg72f", []string{"yrrkg72g", "yrrkg735", "yrrkg734", "yrrkg731", "yrrkg72c", "yrrkg729", "yrrkg72d", "yrrkg72e"}}, + {81.52898530845414, -16.257530566683272, 33330684, 26, []uint64{33330685, 33330687, 33330686, 33330683, 33330681, 33330675, 33330678, 33330679}, "gwkry1c", []string{"gwkry41", "gwkry44", "gwkry1f", "gwkry1d", "gwkry19", "gwkry18", "gwkry1b", "gwkry40"}}, + {46.94884318798722, 44.52450630557723, 220917129, 28, []uint64{220917132, 220917134, 220917131, 220917130, 220917128, 220917122, 220917123, 220917126}, "ubre", []string{"ubrs", "ubru", "ubrg", "ubrf", "ubrd", "ubr6", "ubr7", "ubrk"}}, + {75.94935370309395, 172.71326347716968, 4175400, 22, []uint64{4175401, 4175403, 4175402, 4174719, 4174717, 4174711, 4175394, 4175395}, "zvdb44jv", []string{"zvdb44jy", "zvdb44nn", "zvdb44nj", "zvdb44nh", "zvdb44ju", "zvdb44js", "zvdb44jt", "zvdb44jw"}}, + {-15.642553120154389, 31.34440390113741, 2523492488, 32, []uint64{2523492489, 2523492491, 2523492490, 2523489759, 2523489757, 2523489751, 2523492482, 2523492483}, "ktnr1227", []string{"ktnr122k", "ktnr122s", "ktnr122e", "ktnr122d", "ktnr1226", "ktnr1224", "ktnr1225", "ktnr122h"}}, + {-86.0694066422875, -67.18683424095798, 571558807, 32, []uint64{571558850, 571558856, 571558813, 571558812, 571558806, 571558804, 571558805, 571558848}, "488nr", []string{"488nx", "488q8", "488q2", "488q0", "488np", "488nn", "488nq", "488nw"}}, + {27.54442899377318, -35.68221183627614, 7093644, 24, []uint64{7093645, 7093647, 7093646, 7093643, 7093641, 7093635, 7093638, 7093639}, "ehyssn31", []string{"ehyssn34", "ehyssn36", "ehyssn33", "ehyssn32", "ehyssn30", "ehyssn2b", "ehyssn2c", "ehyssn2f"}}, + {50.554304800374666, -54.057459830684834, 31499108713987, 46, []uint64{31499108713990, 31499108713996, 31499108713993, 31499108713992, 31499108713986, 31499108713984, 31499108713985, 31499108713988}, "fbcx9", []string{"fbcxc", "fbcxf", "fbcxd", "fbcx6", "fbcx3", "fbcx2", "fbcx8", "fbcxb"}}, + {-64.25486433313927, 7.243427654437227, 138618007, 28, []uint64{138618050, 138618056, 138618013, 138618012, 138618006, 138618004, 138618005, 138618048}, "hht4k", []string{"hht4s", "hht4t", "hht4m", "hht4j", "hht4h", "hht45", "hht47", "hht4e"}}, + {-70.72176856856095, 156.83193023793865, 729868954749, 40, []uint64{729868954920, 729868954922, 729868954751, 729868954750, 729868954748, 729868954742, 729868954743, 729868954914}, "p7rt8v3x", []string{"p7rt8v98", "p7rt8v9b", "p7rt8v3z", "p7rt8v3y", "p7rt8v3w", "p7rt8v3q", "p7rt8v3r", "p7rt8v92"}}, + {-78.7512915780244, -166.79872378331493, 882645, 28, []uint64{1581696, 1581698, 882647, 882646, 882644, 882558, 882559, 1581610}, "03cru", []string{"0612h", "0612j", "03crv", "03crt", "03crs", "03cre", "03crg", "06125"}}, + {-25.58807928771421, -142.53833294834476, 324466, 22, []uint64{324467, 324473, 324472, 324461, 324455, 324453, 324464, 324465}, "2g6w", []string{"2g6x", "2g6z", "2g6y", "2g6v", "2g6t", "2g6m", "2g6q", "2g6r"}}, +} diff --git a/vendor/github.com/mmcloughlin/geohash/stubs.s b/vendor/github.com/mmcloughlin/geohash/stubs.s new file mode 100644 index 00000000..97c046c9 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/stubs.s @@ -0,0 +1,7 @@ +// +build !amd64 !go1.6 + +// Define NOSPLIT ourselves since "textflag.h" is missing in old Go versions. +#define NOSPLIT 4 + +TEXT ·EncodeInt(SB), NOSPLIT, $0 + JMP ·encodeInt(SB) diff --git a/vendor/github.com/mmcloughlin/geohash/testcases_test.go b/vendor/github.com/mmcloughlin/geohash/testcases_test.go new file mode 100644 index 00000000..571dec84 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/testcases_test.go @@ -0,0 +1,4124 @@ +package geohash + +// # Test cases generated with https://github.com/hkwi/python-geohash +// import sys +// import random +// import geohash +// +// PLACES = 9 +// +// trials_bits = int(sys.argv[1]) +// trials = 1 << trials_bits +// +// print 'package geohash' +// print +// print ''.join(['//\t' + line for line in open(__file__, 'r')]) +// print 'var testcases = []TestCase{' +// +// for i in range(trials): +// lat = round(-90 + 180*random.random(), PLACES) +// lng = round(-180 + 360*random.random(), PLACES) +// hash_str = geohash.encode(lat, lng) +// hash_int = geohash.encode_uint64(lat, lng) +// print '\t{0x%016x, "%s", %s, %s},' % (hash_int, hash_str, lat, lng) +// +// print '}' + +var testcases = []TestCase{ + {0xc28a4d93b20a22f8, "sb54v4xk18jg", 0.497818518, 38.198505253}, + {0x003558b7d15148f1, "00upjeyjb54g", -84.529178182, -174.125057287}, + {0x949dcd034ca43b30, "kkfwu0udnhxm", -17.090238388, 14.947853282}, + {0x7d44be93dc8c3d1f, "gp2cx4ywjhyj", 86.06108453, -43.628546008}, + {0x801e4ccef502f590, "h0g4tmrp0cut", -85.311745894, 4.459114168}, + {0xd90e166bb45673b6, "v471duxnbttv", 57.945830289, 49.349241965}, + {0x81d1418fe43c871f, "h78n33z47k3j", -69.203844118, 11.314685805}, + {0x7ef3c3f9b722a109, "gvtw7yer4bhh", 77.073040753, -3.346243298}, + {0x03adcf02afef7916, "0fqwy0pgxxwj", -76.156584583, -136.834730089}, + {0x644a3e6ab5fbafd6, "dj53wuppzfrx", 28.411988257, -85.123100792}, + {0xbcd540239bd297fb, "rmbn08wvubcz", -11.597823607, 146.281448853}, + {0xb663a5ee6c8fa0df, "qtjucvmdjyhe", -16.010823784, 120.67064801}, + {0x4f24b9f758e2851e, "9wkcmxuswb2j", 35.419323354, -105.572143468}, + {0x49c85bd32bc93b07, "9745rntct4xh", 17.482266365, -120.621762327}, + {0x73213a6da3c7ca39, "fdhmnve3sz53", 57.159413941, -61.222135062}, + {0x0f044c9a65ca1bc2, "1w24t6m5t8ew", -54.391332719, -112.262179799}, + {0x575f88776cb53f16, "bxgshxvdqnzj", 89.33987042, -152.372551026}, + {0xf435c4f4150cf21b, "yhuw9x0p1mt1", 72.901011648, 96.39410362}, + {0x3cdb6f164961823d, "7meqy5k9d613", -12.857855257, -28.909737376}, + {0xc1d93b954a63f23d, "s7dmr5bbdgt3", 20.631055855, 14.722834905}, + {0x62f7bbb928678a26, "dcvvrf98dy52", 10.780452593, -47.821303925}, + {0xb2179f2d04b17845, "q8ctyc84q5w4", -39.761862556, 114.906271436}, + {0xa4b27243b74f03d0, "nkt74hxr9w1x", -64.134116191, 108.730725942}, + {0xa179bd0cca8bb24a, "n5wvu36bjft4", -69.294877597, 99.682985421}, + {0xa079222feba1cd94, "n1wk4czcn76t", -80.849571944, 98.920826744}, + {0x1530f246840cc164, "2nsg4jn41m0q", -7.882319175, -173.224128085}, + {0xdfbd1106fd0b9b7b, "vyyj21rx1fer", 83.899684283, 87.197879407}, + {0x6965c308fd2fd59a, "e5kw627x5zbt", 19.382500173, -38.568029617}, + {0x0c7f5145e6e372d7, "1jzp2jg6wete", -56.349801648, -125.151500328}, + {0x4aef4c3a5c4dcbb8, "9crnsfkw9r5v", 8.187341946, -91.191271032}, + {0x80c854b7dee04393, "h3459eyyw11t", -83.738044201, 14.137947303}, + {0xd493a59a7aebaf1d, "uk9uc6muxfrj", 71.160606083, 13.774107755}, + {0xe890a8694e4600a3, "x28bhubf8s0b", 2.839043217, 147.514480254}, + {0x5eed92fed26ac0d8, "cvqt5zqkec0e", 75.450760056, -91.935835199}, + {0xf3674c82c1a0a21a, "yemnt0q1n2j1", 64.425373268, 119.75949151}, + {0x198d449ce5fcca9e, "366n9775zm59", -31.182648256, -120.878132359}, + {0x0b81884da977eb75, "1f0shme9fzpr", -78.016323338, -100.355173113}, + {0x405e9f9198615636, "81g9z4dsd5c3", 10.165892946, -174.766986944}, + {0xded0cd3060d500b0, "vv8dud30un0c", 76.433419177, 79.652424948}, + {0xd2c0a330de5f2835, "uc0b6d6ycwn3", 50.682496153, 34.918400151}, + {0x0fa45fc018f86aec, "1yk5zh0sz1pf", -54.16177647, -95.316385169}, + {0xd8b7c528794d300c, "v2vwbb3t9ns0", 50.407625844, 64.019442379}, + {0xbc64d034131b0daf, "rjke0e0m3d6u", -14.924012658, 141.350509363}, + {0xc3b6ae4b11e3f9b9, "sfvbwksjwgwv", 15.582323691, 42.116387781}, + {0x860cb92613863fa9, "hs6ck9hmhszu", -65.867645766, 26.570981636}, + {0x0b7c08facfc37b3d, "1ey0jyqgsexm", -68.871297151, -103.800341659}, + {0x59e1d741422b35b2, "c7hxfhb25duv", 63.263412836, -117.333484316}, + {0xab8c832252abce1d, "pf6868kkpg71", -77.297727015, 172.381661592}, + {0x8c67ac1d8c9c3fc7, "jjmus7ddmhzw", -59.658095434, 53.27636308}, + {0xdbf4141d69242eb8, "vgu187c94hrc", 66.378215883, 84.388142665}, + {0x669c31b670e9bbc6, "duf33emhx6xw", 26.95711635, -53.01283175}, + {0x742220e83702e057, "fhj21u1r0ch5", 67.523180302, -82.5385289}, + {0xe359b46aba47ee30, "wedv8upu8zr3", 20.677081069, 116.410831463}, + {0xee06355c03e2103f, "xs33br03w883", 24.252579808, 159.269421738}, + {0x4adbbece3f7e83cd, "9cevxmjzgu1w", 9.433115669, -95.649899396}, + {0xa247bed92c4c93c4, "n93vxq9d9k9w", -81.965793153, 115.281656662}, + {0x754e47e28a17c1f1, "fp74gsnb2z0z", 86.286702788, -85.618138439}, + {0xf00c1f2fe9e69795, "y061ycz9wuct", 46.723764176, 93.119722304}, + {0x9e9d94357103c03a, "muft8ecj0g03", -17.292979155, 82.289003746}, + {0x3998ebdf47185f7b, "76dfrru731gr", -30.498851637, -29.55832495}, + {0x397522c6ea3b5789, "75uk5jrb7ecs", -23.174222138, -38.880806118}, + {0x634e27bd64ca5046, "de72ggc4t984", 18.434122958, -62.763283163}, + {0xffbd3be866569556, "zyymru36bubp", 83.915446347, 177.881387638}, + {0xf4e3c34a93c67fbd, "ymjw6knmstzv", 74.246581453, 109.092038728}, + {0x440dfed688e34088, "8h6zxpn8we08", 25.263163488, -175.815989102}, + {0x943316e1bf44ce8f, "khtjesez8m78", -18.694668135, 7.190224189}, + {0xa2744de6729fb223, "n9u4vtmkmyt2", -79.643247942, 118.374162886}, + {0xca090a1eba8d939f, "t84hn7pujq9t", 0.720326999, 70.598028855}, + {0xfeb10afcb6cd21fe, "zushpz5qtnhz", 71.055226408, 174.720260646}, + {0x09b7a446601542b8, "16vu8jm02p1c", -73.711314615, -115.657193453}, + {0x21e9a733be3b3202, "47nufdxy7dt0", -72.27521983, -69.137149187}, + {0x97afa4cc827cc4ff, "kyru9m42gm2g", -9.025110998, 44.706599785}, + {0xe40058e30a272290, "wh05jssb4wj9", 23.052095343, 90.248337023}, + {0x92c8e81139c5a8f1, "kc4fh49tsqng", -39.008727685, 37.795220131}, + {0xfef36777a7f97b07, "zvtqfxx7z5xh", 77.165885506, 176.252942053}, + {0x48eb0cc05e9fbd24, "93phth2ymyyk", 6.440471427, -113.685238245}, + {0xe035861cf09a7be1, "w0usd77hm9xy", 5.028357065, 96.431368314}, + {0x86b89245d29818d4, "huw94jfkm0de", -64.479400185, 42.981823476}, + {0xb178156141d4b0e4, "q5w1bsb1uksf", -24.978465313, 98.459661462}, + {0xb27858439e718379, "q9w5hhwyf61r", -36.009266318, 121.122649451}, + {0xc8fbcb691f7baf53, "t3xwqu8zgfrp", 9.562195714, 67.09470629}, + {0x6e21a5b8e4e5e28b, "eshucf74wrj8", 23.347787328, -15.739012399}, + {0xe8e564792f938b87, "x3kq8y9gkf5s", 8.210103138, 152.26213344}, + {0x2be6497a09d58914, "5gm4kyh9uq4j", -71.289949664, -4.003644877}, + {0x7adbb62f9b416b53, "gcevdcwv85pp", 54.413498088, -5.846433654}, + {0x00792f91a41f9f24, "01wkz4e43ygk", -80.713382948, -170.899120913}, + {0x107c6a33c25bf95d, "21y6ndy2cgwp", -34.789432458, -170.916626518}, + {0x2e3c6b38ae986f65, "5sy6qf5fm1rq", -62.874361764, -13.408932436}, + {0x0efbde6b8fe8086b, "1vxxwuwgx046", -57.718820187, -90.396920678}, + {0x5e0e5d7ed58cb48c, "cs75uzqpjku8", 69.606482362, -108.06403843}, + {0x316ccd8e3e92e6bb, "65qdv3jykcmc", -26.228778598, -80.62055385}, + {0x399860e50705c0f4, "76d61t870r0g", -30.555171945, -30.519664463}, + {0x9acca5fa5d40e9cb, "mc6bcykx83nw", -37.801287909, 82.70029131}, + {0xa35a5c5355ffd0c8, "nee5snupzz8d", -69.658815818, 116.900357853}, + {0xb3ec2adcaad5b793, "qgq2pr5buqvt", -26.680131978, 132.863005776}, + {0xde0310d1ae158079, "vs1j1nef2q07", 68.415030813, 68.955356447}, + {0xb9e8aa6437e90f04, "r7nbnt1rx47h", -28.096233881, 156.02985314}, + {0x6bb2d8cdc72ad95e, "eftejmf75cdp", 14.621953772, -3.281658328}, + {0x971e33c2b61d343e, "kwg37hpq3nu3", -6.788441405, 27.2121537}, + {0xa006ff78ee7f75ca, "n03gyy7fgxuw", -87.899723375, 92.762874736}, + {0x715fcd43e48514f9, "f5gwuhz4hnbg", 67.306746065, -84.892557798}, + {0xc655d6df20070a22, "stbxert00w52", 33.703340295, 23.352839671}, + {0x821fac203d3b0577, "h8gus81x7d2r", -84.988976419, 27.973402046}, + {0xba0a1537cadab1ef, "r851beybvbsy", -44.671650847, 161.7502458}, + {0x9649dcaf9e2a7ea2, "kt4xtcwy59zb", -15.547361482, 26.27774099}, + {0x93f78b0d22c13481, "kgvsq392s4u8", -23.150839292, 41.760927592}, + {0xb48eb9f8807d003f, "qk7cmy40gn03", -20.840903219, 106.778999007}, + {0x03822b21e0677854, "0f12q8g0dxw5", -78.701808161, -144.202307721}, + {0xff6072fd9331ad71, "zxh75zdm66qr", 84.944457022, 163.644552332}, + {0x5d914a977e57ddc2, "cq8np5vybzfw", 82.638988192, -123.434450713}, + {0x38822b95c6a4cc3e, "7212r5f6nm63", -44.935083556, -31.681208896}, + {0x0a5f398a1ce6711c, "19gmm2hwwtsj", -79.232252184, -107.692510243}, + {0x1e3f62abea3c4206, "3szq5bzb7j10", -17.222372775, -102.129119318}, + {0xed7ca14641cf4b2d, "xpyb2jk1tx5k", 43.666830267, 144.497921239}, + {0x307329bc9c914127, "61tkmg4wk50k", -35.797860399, -82.360896608}, + {0xa758f0f1ce825ed8, "nxdg1wffh9ge", -47.247724148, 116.43708}, + {0x315106109eec4435, "658hd44yxj23", -24.509353774, -89.90803204}, + {0x1a616764d7c73e99, "39hqft6rswz9", -38.15831357, -106.410280948}, + {0x9f9b485baec0b68b, "myenhqxfs2v8", -7.346673701, 83.166350612}, + {0x4a70e1709b1f5520, "99sf2w4v3xbk", 8.866910289, -105.79449857}, + {0x51d6ae8bf83dff41, "b7cbx2zs7rzn", 66.186532697, -165.960010429}, + {0x902722c8c964f89b, "k0mk5k69dmw9", -42.866991819, 7.529173579}, + {0xdcdf60963dc866e4, "vmgq15jxt1mf", 78.416211599, 60.872033224}, + {0x49f6f6b8ed7da0c1, "97vgef7egqhd", 21.722020296, -115.494315527}, + {0x7f469f0ba28c8649, "gx39y2x2jk34", 86.091636985, -20.105783569}, + {0xb81303eefcaeceb7, "r09h7vrwpv7c", -41.410532148, 136.581682342}, + {0x5c355cd30587fdf2, "chuptns5hzyz", 73.073372688, -129.149567248}, + {0xf98661d671264eb4, "z6363pmj4t7c", 58.092469996, 148.058736614}, + {0x0a23394cd708ad53, "18jmkm6r12qp", -89.04710512, -104.927268171}, + {0xb6f07346fe1a90e9, "qvs76jry3b8f", -13.461295854, 129.825172431}, + {0x56067f0a1bcbe19c, "bs37y2hvtght", 69.566390855, -155.460753178}, + {0x8e647a7aff5c77fa, "jtk7nyrzcjvz", -59.90571157, 73.783887292}, + {0x94779d1f29d45ab4, "kjvtu7t9ujec", -11.625951544, 7.9288724}, + {0x95176160ac64b2e1, "kncq2s5ddktf", -5.910190836, 1.784695975}, + {0x1192d6cfa1743a8c, "269eemx1fhx8", -30.291728586, -166.488093614}, + {0x53e16b7b96580320, "bghqqywqc01k", 63.010497772, -139.968178907}, + {0x7939eb9639f99caa, "g4wyr5jtz6fb", 60.178614149, -35.192327116}, + {0x7e9d55a77447745a, "gufpc9vn8xu5", 73.09182562, -8.364688476}, + {0x347fabe1331126c5, "6jzurs9m24md", -11.883546663, -78.770248663}, + {0x6bc0d1be3c0447ee, "eg0e3gjw0j3y", 17.463827805, -10.462414963}, + {0x7be4fcf49d8397d0, "ggkgtx4xhfcx", 63.936154081, -4.324974161}, + {0x6b6f953fbfaa2000, "eertbgxzp8h0", 19.312419902, -11.90919577}, + {0xffd0292bd133bee6, "zz82kbyj6fzf", 87.236481597, 169.318671057}, + {0xb28429a04af3551b, "qb22m82byebj", -43.548281648, 124.344570889}, + {0x2ff878b8ee91dca3, "5zw7jf7fk7fb", -47.2723902, -2.202915933}, + {0x889a76d4ac796d17, "j2e7ep5dg5qj", -86.533323002, 60.957091889}, + {0xa64d72c1eed822f0, "nt6r5hgfv0jg", -59.211716108, 115.801270607}, + {0x6de12385a9b3c725, "erhk71e9qg3k", 40.130542888, -27.636503665}, + {0xa91c68725710ab31, "p4f6hwkr22pm", -74.144109778, 138.367673384}, + {0x2320fe0d0bfe1335, "4dhgw38czs9m", -78.126200344, -60.544300755}, + {0x027a80739310a2fb, "09x80wwm22jg", -81.525892653, -146.922550519}, + {0x21841e3ff1d4b439, "4621wgzjuku3", -77.058466003, -78.443567838}, + {0xb4d80a297160c86e, "qmd0nbcjd346", -14.05743018, 104.360603995}, + {0x0b3c44c4acffb8eb, "1dy49j5dzywf", -74.063823715, -104.013406233}, + {0x1f548e56f0afa283, "3xb8wprhpyj8", -1.277815059, -111.523249963}, + {0x647277d3fad89e61, "djt7gnzuv2g6", 31.634578359, -82.47447887}, + {0x15859b21a56f9a8d, "2q2tq8e5eye8", -8.917516032, -167.756944729}, + {0x4d4a72f0b86e984f, "9p575w5seud4", 39.936099822, -130.270908449}, + {0x3c2652a704f480a1, "7hm559s4yk0b", -20.557672925, -37.809178971}, + {0x74074eeab7679d7c, "fh3nxuprdyfr", 70.072125724, -88.243096236}, + {0x034cb1e48c64e4e9, "0e6c3t4ddmkf", -71.471099912, -153.563358182}, + {0xbc3c2c5c399c8bd3, "rhy2sr1tmk5x", -18.153983311, 143.978120168}, + {0x248a5dae2871ad8c, "4k55vcj8f6qs", -66.835185565, -74.270905883}, + {0xad931bfdf6706b83, "pq9jrzgqf1ps", -52.470911617, 148.001385373}, + {0x329a1ffa7c242246, "6be1zymw4hj4", -41.844498665, -51.683028079}, + {0xce48f2bd98fb351e, "tt4g5gdszduj", 28.672400911, 71.535743214}, + {0x3a1bc7fdcd39fe39, "78ewgzfe77z3", -40.957826277, -17.409747485}, + {0x6575273193a30df9, "dpukfddmnd6z", 44.443315464, -83.910201452}, + {0x2ab232a81adbd94f, "5bt35b0uvgdn", -87.010883438, -3.701112851}, + {0xf615482fdf91fbd2, "ysbnhcyzk7xx", 72.784315039, 112.71820254}, + {0x644098f2862d51ef, "dj09jwn65p8y", 28.334124955, -89.046462627}, + {0x84bb434a02c59119, "hkxn6kh2sq8j", -63.566772948, 21.198646014}, + {0x11e0a1117eb796db, "27hb24cyqyce", -28.064838838, -162.067617252}, + {0xfc8f27393d8e2d2c, "zk7kff9xjsqk", 69.756169059, 150.943466677}, + {0x0bdc3c3586912753, "1gf3sed6k4mp", -68.622952499, -97.884915192}, + {0x2a0b67b10d860b7d, "585qgd8ehs5r", -88.799205881, -17.774994607}, + {0x40bf19e1ae01731e, "82zjmsef05tj", 5.166683833, -158.659393946}, + {0xa18d7bd37cc1068e, "n66rrnvws438", -76.031129099, 104.729406777}, + {0x0bb955bf327ad7b8, "1fwpcgtkgccv", -74.555143607, -92.728222397}, + {0xa7673717c62be991, "nxmmf5y65gnt", -48.187048118, 119.979455926}, + {0x93cae3d985a59166, "kg5f7qd5nq8q", -27.693235665, 39.169275058}, + {0x0b540afa9e71c00c, "1eb0pynyf700", -68.872116083, -112.150057306}, + {0x35406a588c448ee4, "6p06nq4d8k7f", -5.240026109, -89.370330229}, + {0x1d53134c667d2b30, "3p9j6m36gnpm", -1.860304227, -133.493017911}, + {0x63cae0934b9e1c40, "dg5f14ucmsf4", 17.241909117, -50.925855316}, + {0xe34598a5b1a98f3c, "we2tj9ejp67m", 19.169267305, 113.449259778}, + {0x96eeab1da2927881, "kvrbq7e2k9w8", -15.405527603, 44.927760749}, + {0x3efadaa322296007, "7vxep8t255h0", -13.532393409, -0.366191153}, + {0x69bba51cbfaf3db4, "e6xub75zpwyv", 14.91517922, -22.835095332}, + {0x9959ef7bada09e47, "m5dyyyxen2g4", -24.089751522, 49.174390405}, + {0x41a1d7becc2f0bbb, "86hxggqd5w5v", 12.630533887, -162.247998186}, + {0x2459f84cb08d52c9, "4jdzhm5hjp9d", -57.803838136, -85.941708225}, + {0x846b7aceddd87798, "hjprpmqxv1vt", -60.614351527, 10.523064536}, + {0xdf6cae82261e934b, "vxqbx0j63u9n", 85.869552091, 77.307091165}, + {0xd3b4eb99347dc86a, "ufufr69ngr46", 60.879187338, 40.749807126}, + {0xb3d58806497f0f6b, "qgbsh1k9gw7q", -23.195923556, 124.635240938}, + {0xaaabed662e474a42, "pbpyutjf8x54", -88.785543693, 179.854113465}, + {0xc24bf3d2eaaee230, "s95z7nrbpvj3", 6.933773356, 27.916256142}, + {0xe59e09b41f1e07f4, "wqg0me0z3s3z", 38.030443437, 105.711545941}, + {0xa4e23bb7a8644ffd, "nmj3rex8dj7z", -61.635932068, 108.972793127}, + {0xce9b1148c7e8b5e9, "tuej2k67x2uy", 26.259303417, 82.983143917}, + {0x1f91141927ae3436, "3y8j8697psu3", -7.456429463, -101.236969348}, + {0x2906ea4d88a83da3, "543fnmd8p0yu", -76.961970353, -42.26066146}, + {0x718011ba0fecf168, "f6013fhgxmsq", 56.481344253, -78.666255562}, + {0x106fa6f3ef1a9fc9, "21ruewzg3bgw", -37.140092525, -168.937030933}, + {0xbb2561786ae8eaf7, "rdkq2y3bx3pg", -31.210693076, 163.512246418}, + {0x6e3ba5fa3e6e3554, "esxucyjyesup", 26.181562495, -11.516606163}, + {0xac35960eec28bd78, "phutd3rd52yr", -62.307238141, 141.437449685}, + {0x4f9ca63bb506547e, "9yfbdfxp0tb7", 38.0716066, -97.252327803}, + {0x1974c2348eede140, "35ud4e4fxrhn", -23.53774028, -128.557918654}, + {0x723e10c4d8c257d6, "f8z11j6ss9cx", 49.424153496, -57.608676241}, + {0x0054ece783a1e995, "01bfttw3n7nt", -79.686395731, -178.694753248}, + {0x69caae0e3c2a4962, "e75bw3jw594q", 16.969420143, -28.194197856}, + {0x67098c6a028eaa05, "dw4ssuh2jup0", 34.563009892, -63.769542088}, + {0xe56c2f7bd244302a, "wpq2yyyk8hs2", 40.950961411, 99.094282424}, + {0xabb1266ea2cde246, "pfskdvp2trj4", -75.118894724, 174.857600601}, + {0xad6c33245bbcd04a, "ppq3692vrm84", -48.991225895, 143.900267218}, + {0x8bb966b6b5262e40, "jfwqeepp4sr4", -74.777217594, 87.702600863}, + {0x336af88194f8595b, "6epgj0dnz1dp", -27.593741671, -56.378785941}, + {0x83b781ec5f457ee7, "hfvs3v2z8pzf", -73.754011045, 41.562319763}, + {0xba3d179636b2bd95, "r8yjg5jqqbyt", -39.752950409, 166.07684412}, + {0x1b73f809d5305534, "3etzh2fp61bm", -24.076656493, -104.224462129}, + {0x065a8bb3cb5d4490, "0te8rdyccp29", -59.003108987, -152.239221778}, + {0x41aa8a4d6fa20848, "86p8nmcgn844", 11.282101037, -157.925781695}, + {0x109195ea6841a214, "228tcum886j1", -41.153315052, -167.962400122}, + {0x5804a882967411f7, "c02bj0nqfh8z", 46.407434186, -133.716915929}, + {0x218044c23fa40c8f, "46049hjznh68", -78.287349629, -78.697897133}, + {0xf9bba0aa9c2fc2e8, "z6xu1bnw5z1f", 59.766692856, 157.234431711}, + {0xd5acdb2f0793fd57, "uqqeqcs7kgyp", 80.736358154, 20.69330437}, + {0x175a0999812c5481, "2xe0m6d15jb8", -2.75461922, -153.047661045}, + {0xcdc1779640c0b1f7, "tr0rg5k0s2sz", 40.755247298, 56.739080506}, + {0xe44983a5e3884050, "wj4s79g3j105", 28.88186092, 93.674111409}, + {0x8cf0a3af1f884bd5, "jmsb7cszj15x", -59.009106646, 63.101224828}, + {0x251f01dc6bf915d5, "4ngh3r3cz4bx", -51.244037755, -85.723614006}, + {0x9d23a3b6a81cc9eb, "mnju7ep83m4y", -10.486379403, 53.250103389}, + {0x004a2ca9733b7961, "0152tbcm7ewq", -84.282068442, -175.175126694}, + {0x068cc76193a3ecd7, "0u6dfsdmngqe", -65.58475595, -142.621123326}, + {0x9162d79171745765, "k5jeg4cjfjcq", -27.449695074, 7.867671798}, + {0x08d7c2456ec35bd1, "13cw4jcfseex", -79.069527639, -121.550133282}, + {0xcde57b1a980a4d43, "trkrq6ns196n", 42.067344805, 62.510214248}, + {0x7785c23591b2a599, "fy2w4edjqbkt", 81.231067058, -55.433969231}, + {0x92e993fcf1e53492, "kcnt7z7jwnu9", -38.41134245, 43.059803426}, + {0x91a314f018c46376, "k6jj9w0ssjjr", -32.749443572, 18.348033481}, + {0xe6d2b6cc21c1e779, "wv9cem11s7mr", 31.228913873, 126.355322683}, + {0x2532702659827cc4, "4nt709kth9yd", -52.902421671, -82.588833124}, + {0xbb0fab4ed3a881b6, "rd7uqmqmp20v", -31.566981307, 163.056991}, + {0xf97357bad1cd230e, "z5tpgfqjtnjh", 66.063141403, 142.204506242}, + {0x88211182e24df459, "j0hj30r29ru5", -89.075647156, 50.678960877}, + {0x07967ce907913957, "0yc7tu87k4wp", -51.390723738, -144.238978774}, + {0x76ad363726a41e9c, "fuqmdet6nhg9", 69.892642032, -47.343602073}, + {0xc31e8ae307e281a1, "sdg8pss7wb0u", 15.494070806, 27.757592078}, + {0x7a76d2913b36d969, "g9ve549v6vdq", 55.385758458, -14.631264556}, + {0x4960fe88cdbd0ce4, "95hgx26ern6f", 17.492199549, -127.997971661}, + {0x6d47eb56235438e5, "ep3yqpj3bhwf", 41.918659973, -42.268171747}, + {0xffc5cbbd970fab95, "zz2wrgdr1ypt", 86.900343686, 169.796876292}, + {0xa0468a1705f4e523, "n138n5s5ymkk", -82.948851312, 92.378814054}, + {0x8d001694414acbd2, "jn01e5219c5x", -55.968211293, 45.131915857}, + {0xdd7be916c4073a7c, "vpxyk5q40wx7", 88.304364401, 56.082489913}, + {0xa55396981881fbc9, "np9te60sh7xw", -46.834010299, 92.253076544}, + {0x4ed5370d21b685bf, "9vbmf391qu2v", 33.362970305, -100.797896317}, + {0x54da25c91f8d2002, "bme2ck8zjnh0", 76.095289025, -164.123505847}, + {0xc5df8e5923e0bac1, "srgswq93w2xd", 44.420731774, 16.448528122}, + {0xc2d6340f13d5d50c, "scc383smurbh", 10.11669101, 35.524817909}, + {0x54e2d7ac6e01c1c7, "bmjegc3f070w", 73.791406708, -160.848415332}, + {0xfdddf2365140faa8, "zrfz4ekj83xb", 89.843023722, 150.232565253}, + {0x21e7755ae6fe94b5, "47mrbqr6zubc", -70.321615187, -71.345911146}, + {0xe1bc9e1d7a52e74d, "w6y9w7cubcmn", 15.75384448, 110.667725535}, + {0x97fd120f009b45f2, "kzyj43s0me2z", -0.519042479, 42.292107281}, + {0x18fcd8df7a6a7a6c, "33yejrvue9x6", -34.585553258, -114.370605536}, + {0x3310679a1c9626b3, "6d86g6hwksmc", -30.442019315, -66.999234504}, + {0x921c6195cbb17a55, "k8f635fcq5x5", -40.364908835, 25.712051288}, + {0x414957b90667870b, "854pgf86dy3h", 18.251501014, -177.022242118}, + {0x6e22e434fcd90da4, "esjf8e7wv46u", 22.958477453, -14.387065014}, + {0xb642ce1d24379def, "qt1dw7946yfy", -16.415897213, 114.885528531}, + {0x1c08e3997817e3a1, "3h4f76cs2zju", -22.088618148, -130.987890322}, + {0xdcf36ce3523ae4a6, "vmtqtsuk7ckb", 77.106907029, 63.880515608}, + {0x2dde6bca2dd22260, "5rg6rkjeu8j6", -45.988125445, -28.853330697}, + {0xed36967b5b9fc6e6, "xnv9dyuvmz3f", 38.270441272, 142.86199694}, + {0xcd2ab1031f4a4034, "tnpc20sz9903", 33.973766249, 55.905025397}, + {0x981af71a9481445f, "m0egf6nnh525", -41.516285912, 50.380726163}, + {0x4b024479b2fddd7f, "9d148yekzrfr", 11.726016998, -111.056013377}, + {0xb4225551156dd0a4, "qhj5bn8per8b", -21.803786944, 97.031390564}, + {0xa75eaa41d89522ce, "nxgbnhfsknjd", -46.379395267, 118.040721974}, + {0x9bcfb03b27856998, "mg7v0ft7hpnt", -25.825557438, 84.063786811}, + {0xa7a860bee59ae10e, "nyn61gr5mchh", -55.880020688, 132.625832284}, + {0x4929086bda4d8734, "94nhhuyu9q3m", 11.980024465, -126.344434719}, + {0x880df1755ed30a76, "j06z2xbyud57", -87.275593884, 48.890387735}, + {0x7d0e9415ca960f18, "gn7985fbks7j", 80.4405878, -40.07415109}, + {0x1fb167468cd11e77, "3ysqfjndu4g7", -7.223023253, -95.176444317}, + {0x930b261684fa4f60, "kd5kd5n4z97q", -32.942025092, 27.166769115}, + {0x2065d51feb5b952e, "41kxb7zccfbk", -81.585530027, -83.650169911}, + {0x0d0f543d08861b6f, "1n7p8g88hseq", -53.506139278, -130.747406584}, + {0xfdb9b904ec22d00d, "zqwvk17d4c80", 82.492569546, 155.922893473}, + {0xba7dd1dad2c3fae1, "r9yx3qqksgxf", -33.846711997, 166.704353996}, + {0x72dbb4d1437956b3, "fcev9nb3g5cc", 54.441696479, -50.93214262}, + {0xdb6a46a5243959f6, "vep4e99475dz", 62.323099183, 77.499064445}, + {0xfea3f9b86dce2065, "zujzmf3etsh6", 68.787396437, 177.090924755}, + {0xd7ff465d3ab3411b, "uzzndr9uqe0j", 89.778273394, 43.695309442}, + {0xe30c9eb472c06d35, "wd69xe3ks1qm", 12.938554819, 116.347111562}, + {0x0a81c0e155722406, "1b0w1sbpf8k0", -88.917888143, -100.480844224}, + {0x304f9eb0db27722b, "617txd6v4xt2", -36.988694891, -84.744643972}, + {0x0760c3152a380eaa, "0xhd659b707b", -50.210221848, -151.081447066}, + {0xa761f2e763437efd, "nxhz5tv38ezg", -49.362667309, 119.340737928}, + {0xac8636febb64924b, "pk33ezpvdk94", -65.790657299, 148.183348596}, + {0xc69d423b75ea79ca, "sufn4fvpx9ww", 27.789840214, 36.69054697}, + {0x017c3902f23d4826, "05y3k0rk7p42", -68.684382924, -171.025068012}, + {0xc8c7e8962a15ebab, "t33yj5jb2rpu", 8.102498188, 58.938581893}, + {0x465d913d51ab125f, "8tft2gbjpd95", 33.288073282, -153.951092549}, + {0x6d833e2be7154d54, "eq1mwbz72p6p", 34.721514826, -31.685597596}, + {0xc1d4b9949315237b, "s7bcm54m2njr", 21.330890683, 12.527511343}, + {0x7a60c4dbb6535153, "g9hd9qxqbe8p", 51.101337403, -16.106965452}, + {0x4eefe9b5311cf1db, "9vryme9j3msx", 30.650040867, -90.108429988}, + {0x8a88038df4bc3932, "jb4073gnrhwm", -89.945343763, 81.709751025}, + {0x753f90faaa33bcfb, "fnzt1ypb6fyg", 83.880673463, -79.365450329}, + {0x79457221c32b29cc, "g52r48f35dnw", 64.516022144, -44.535325172}, + {0x392aa078cd516c3e, "74pb0y6eb5q3", -33.715006298, -34.065161625}, + {0x95f6c48b9814ecd0, "krvd92ws2mqe", -0.963292033, 19.0482525}, + {0xe6fe497f5d59abcf, "wvz4kzuxc6pw", 32.783172074, 133.808734045}, + {0x5627e5b154357fcb, "bsmycdbn6pzw", 70.108995162, -149.348048354}, + {0x9009c38f8f6bddde, "k04w73wgegfx", -43.892505214, 3.667881573}, + {0x9746037052c40213, "kx306w2ksh11", -4.139678243, 24.016629695}, + {0xdbde585c49f4ecf0, "vgg5hr29ymqg", 66.661248283, 83.156481556}, + {0x8d0f11c5ad1074bd, "jn7j3jee21uc", -53.890118417, 49.267508433}, + {0xd6937574caa0f1b5, "uu9rbx6bn3sv", 71.714639157, 35.533836312}, + {0x3d29b7b3e4352472, "7nnvgdz46nk7", -10.223731305, -35.344299204}, + {0xa690d114d84adc27, "nu8e256s9cf2", -64.097576152, 124.456636451}, + {0x9b040e4c2a77815c, "md20wm1bfy0p", -32.228231359, 67.777179722}, + {0x911a4791eb7f7e01, "k4e4g4gcgxz0", -30.438653303, 4.355895389}, + {0xae0b6b6fbf7a8aa5, "ps5qqvxzgb5b", -66.369824278, 162.377752374}, + {0x1a9388581d4ab8a3, "3b9shq0x9bwb", -41.450125815, -98.953086536}, + {0x0927f87161bd6da0, "14mzhwc1rpqu", -76.075948224, -126.714625801}, + {0x5ea5707fd8700f40, "cukr0zysf07n", 70.180108551, -95.23146224}, + {0xa5b474938385a3c0, "nqu794w3hqjw", -51.402077841, 107.279280649}, + {0xb76c98f783f39c6d, "qxq9jxw3yff6", -4.001454391, 121.891200161}, + {0x22bb35f5f0599831, "4bxmcxghc6d3", -86.133356746, -45.984592938}, + {0x1a588dc90eab85fd, "39d8vk8fpf2z", -36.405588101, -108.752298327}, + {0x3ce3d042801388ff, "7mjx0hn02f4g", -15.622504311, -26.007349126}, + {0xde2580bb658bee63, "vsks1fv5jgr6", 69.625013998, 73.912152369}, + {0xbdb63acee3bd6226, "rqv3pmr3rpj2", -6.826377404, 153.961679969}, + {0x5242d672815c078c, "b91edwn1ch3s", 51.273517345, -155.272476811}, + {0x6d4b0d4efc71ccfc, "ep5humrwf76g", 40.239976801, -40.584082282}, + {0xda64d7a6d6e9aff7, "v9keg9qqx6rz", 52.698425339, 73.990838337}, + {0x727e5360ad6509d6, "f9z56s5edn4x", 55.437646016, -57.541490887}, + {0x8918ac9b2ac51b1d, "j4dbt6tbsnej", -75.8357548, 49.105972725}, + {0x4a039b45f13e827c, "981tqjgj7u17", 0.955360555, -110.122670401}, + {0x727f49edf574b4db, "f9znmvgpfkue", 55.975324948, -57.399341693}, + {0x3d08eee88d40c987, "7n4fxu4e834s", -10.787967233, -40.788788926}, + {0xbca4e9345ba3a794, "rkkfke2vnfmt", -20.679518093, 153.128768503}, + {0x3efb2175f57fa71d, "7vxk2xgpgymj", -13.271492785, -1.028427097}, + {0x9117747a241650a8, "k4cr8yj42t8b", -28.17951586, 1.797660642}, + {0x45d792764eafe255, "8rct4xkfpzj5", 44.512863228, -166.523899227}, + {0xe149e4cda5754c99, "w54y9me5fp69", 18.048473371, 93.926328418}, + {0xeaed6602687dec5b, "xcqqd0m8grq5", 8.17536961, 177.634654081}, + {0x635b4af6bd0964d1, "deenpxpx15ke", 20.781859278, -62.94131152}, + {0x74c43a29c433132a, "fm23nbf46d9k", 74.711551472, -78.098950795}, + {0xc87633a8e8a3d48f, "t1v37b78ngb8", 10.064870108, 52.552704621}, + {0xc535eb48dc982ec6, "snuyqk6wm0rd", 39.091803421, 6.958003719}, + {0xe53c7526c4e71a8d, "wny7b9q4wwe8", 38.635260316, 98.819558794}, + {0x1a04433f9c0f26f5, "38246gww1wmg", -43.177951963, -112.370151904}, + {0xe2ed61250249f19a, "wcqq298297st", 8.13822801, 132.56143848}, + {0x11a300d99a39e4d1, "26jh1qdu77ke", -33.01042188, -161.659896224}, + {0xa639f3f3eedaf8c5, "nswz7wzfvcwd", -63.375526469, 122.156894646}, + {0xd0a5b1239d635fd2, "u2kv28wxdegx", 47.333156933, 17.960698682}, + {0x5b0018bc87437203, "cd01jg478et0", 56.442880409, -112.244196291}, + {0x8299873e4298db5d, "hbdsfgk2m3ep", -86.334633991, 37.392540603}, + {0xe10b3eb128b9771e, "w45mxd98r5vj", 12.230593743, 94.902269671}, + {0x9ea3667455e55e69, "mujqdx2pwpg6", -21.316267015, 86.242937807}, + {0x943e8f520bdcdf75, "khz8ynhcvmgr", -18.116122501, 10.817299708}, + {0x61a234dfa7bb58b5, "d6j39rx7redc", 11.555447598, -71.301973925}, + {0x2a52e6fc7bddee62, "599fez3vvrr6", -81.082195144, -19.871629182}, + {0x15928a736d402328, "2q98nwve80jk", -8.399819618, -166.347420103}, + {0xdbb86d9fc060d7ee, "vfw6v7y0d3cy", 59.566594134, 87.77811433}, + {0x80163040cdce3350, "h0c30h6etstp", -85.58149667, 1.761488477}, + {0xb1c5ad6b920362dd, "q72uuuwk0eje", -25.85836643, 102.522039876}, + {0x717775c6b8dd8787, "f5vrcjpsvq3s", 67.484370258, -82.562715093}, + {0x4b5591109e63ce75, "9ebt244ydg77", 22.028721787, -111.792976011}, + {0x24749ac1821078e6, "4ju9phd221wf", -57.45569812, -83.36115872}, + {0x65c34de9975d481e, "dr1nvudrcp41", 40.587610891, -77.08792867}, + {0x7b0518c41859f7af, "gd2jjj0sc7vu", 58.563458382, -22.279525146}, + {0x2337ba92feb292cf, "4dvvp4ryqb9d", -73.638908095, -59.09551034}, + {0x8dbd555146552ae5, "jqypbnb6bnpf", -50.631361709, 64.687853736}, + {0x09626aefc4246c6d, "15j6pvy44jq6", -72.741479843, -127.268277151}, + {0xd8a03b2837e49692, "v2h3qb1rwkc9", 45.221039685, 62.525180293}, + {0x92d7769bf96d29d8, "kccre6ztennx", -33.821806916, 35.661072156}, + {0x36606548c2f72aca, "6th6bk62ywpd", -16.368094339, -61.509072006}, + {0xb824ef03caae9f85, "r0kfy0ybpugs", -43.106205377, 141.95296639}, + {0xecaa4145ee35a35c, "xkp42jgf6qjp", 22.927513188, 156.099003325}, + {0x617427d8e9e41bb4, "d5u2gq79whev", 21.260200794, -83.875546146}, + {0x41a223967663016f, "86j275mqdd0q", 11.312925785, -161.228033882}, + {0xf07c44902d1a2be4, "y1y4941e38py", 55.294748463, 98.483580034}, + {0xd2e54b1540518948, "ucknq5b0b64n", 53.150622693, 39.638685527}, + {0xb903a7738ed927a6, "r41ufwwfv4mu", -32.878848578, 137.580292922}, + {0x77f45a4cd5cee45b, "fzu5nm6ptvk5", 89.151249063, -50.347342816}, + {0x13a9d29a8994ce27, "2fnx56n9km72", -32.508302055, -136.957439484}, + {0x39a27df66be4f566, "76j7vxmcwmuq", -33.050705409, -26.117317833}, + {0x60a4b9ab914c4bb9, "d2kcmbwj9j5v", 1.62969501, -71.809339034}, + {0x6baad8c36b51c8aa, "efpejhvcb74b", 11.803752814, -0.475487279}, + {0xdaa3ef42eef0e8eb, "vbjyyhrfy3nf", 46.21034723, 87.110529941}, + {0xa833ab379209be19, "p0tuqewk16z1", -86.420501966, 143.380228311}, + {0x26bfa7978a976d6d, "4uzug5wbkxqq", -62.426980009, -45.210255152}, + {0x4a787ecc00c34d8f, "99w7xm00se6s", 9.080304269, -103.392137253}, + {0x3889e57658133e9a, "724ybxks2dz9", -43.772906626, -29.854628185}, + {0x4a94e26ae86ed789, "9bbf4ur8evcs", 4.593775848, -100.064000085}, + {0xde03c89da3a102ae, "vs1wj7e3n41b", 68.574096073, 69.844811248}, + {0xcf07f5be013f01a1, "tw3zcgh17w0u", 36.535281661, 70.043486252}, + {0xa02796789d3f0218, "n0mtdy4x7w11", -87.59271724, 97.858809605}, + {0xb8e9dc889fa7812e, "r3nxt24zny0k", -38.055406028, 155.625404752}, + {0x51f8c8da4da0088b, "b7wdjqken048", 65.073909816, -159.37222115}, + {0xa61ea22b53280ab9, "nsgb4bum505c", -63.276271593, 117.900257605}, + {0xb14565e60ac09fd6, "q52qcthbs2gx", -25.504669597, 90.4241908}, + {0x9d334fb35fc0bd65, "mntnzduzs2yq", -7.234577737, 52.367553765}, + {0x4e65816d119bc423, "9tks2v8jmg22", 30.309451424, -106.138668797}, + {0x0b894dd228435356, "1f4nvnj88e9p", -77.530411696, -98.210197416}, + {0x1ff4283fb13936fa, "3zu2hgxj74vg", -1.38610742, -95.054947518}, + {0x6a2cd56279967f96, "e8qebsmtktzt", 2.089708626, -13.329645979}, + {0x9cc1e334ea676f67, "mm0y6e7bdxrq", -15.758388546, 57.419817726}, + {0x42eb4a9d65c82ed3, "8cpnp7c5t0re", 6.700889579, -136.086048439}, + {0xa9a7bc4669ec9ca4, "p6mvsjm9xkfb", -76.34783093, 154.519586743}, + {0x9a43b50da1b1c198, "m91vb3e1q70t", -38.355786541, 69.97631283}, + {0xcad7d3311095457b, "tccx6d8hkp2r", 11.132666651, 80.969410535}, + {0xa3bf8caeec5f58ae, "nfzstcrdcxdb", -73.732855533, 134.559928543}, + {0x142625b8191929f8, "2hm2cf0t34nz", -20.950014595, -172.539544342}, + {0xcbf6667411c70780, "tgv6dx0jsw3s", 21.572633647, 86.242870941}, + {0x9fb8aec6e697094c, "mywbxjr6kw4n", -8.320350728, 88.559957094}, + {0x28b20d3fd572bdbb, "52t0ugypfbyv", -87.033729383, -26.501642681}, + {0x66f3c9572e303f1b, "dvtwkptf60zj", 32.077722718, -48.33185183}, + {0x1de35991a3d81beb, "3rjpm4e3v0ey", -4.336548861, -116.49434033}, + {0x89f989cf213a39ec, "j7wsmmt178wy", -69.534998905, 65.628363792}, + {0xedf862d65bf4450d, "xrw65pkvyj2h", 42.579903975, 155.17767929}, + {0xe3820f2e2ca897d6, "wf10ycjdp2cx", 11.387677714, 125.460747215}, + {0x6e6a514a91accf3d, "etp52knjpm7m", 28.719149175, -12.636708233}, + {0x4298307b9a57f4ef, "8bd30ywubzuf", 3.024844283, -143.043669915}, + {0x9efd2101df1cef80, "mvyk20fz3mrs", -11.903786429, 87.542902761}, + {0x5c31bf0ed27c8d06, "chsvy3qkgk6h", 71.330947589, -128.036928985}, + {0x455c8febc280fc26, "8pf8zuy2h3y2", 43.751687942, -176.135024881}, + {0xb5db477fb7a31b6f, "qrenfzxrndeq", -1.583561657, 105.599839552}, + {0xa9129b104f5c8017, "p499q42gck01", -75.704748335, 137.374130583}, + {0x2cdc5b702e3d5c2a, "5mf5qw1f7pf2", -57.051574118, -30.649320129}, + {0x3f7913662af40774, "7xwj6tjbyh3r", -1.862029784, -13.944481115}, + {0x9e3b5c5523f04c6c, "msxpsp93y166", -18.327634354, 77.521508572}, + {0x0a2780c9bc46d8a3, "18ms1kew8vdb", -87.864760401, -104.705850856}, + {0x8368146e659ad079, "hen18vm5mc87", -72.831922486, 30.977572755}, + {0x517f5e456634a919, "b5zpwjc66knj", 67.444103682, -169.890756684}, + {0x1e1a1a3da0775bcc, "3se1nge0fxew", -19.492322601, -107.9803871}, + {0xbb753332313157a7, "reum6djj65cu", -22.971489972, 163.593380563}, + {0xdb3695e87c262227, "vdv9cu3w4sj2", 60.800764719, 75.313453694}, + {0x3ef808e7720b12d9, "7vw0jtvk1d9e", -14.030213702, -2.56352437}, + {0x3ff7756e93ae4b82, "7zvrbvnmpt5s", -0.015593652, -3.82531878}, + {0x7ec9dda6d7885de4, "gv4xv9qrj1fy", 74.49538327, -7.48387441}, + {0x405eac10538f29fa, "81gbs42mjwnz", 9.944891947, -174.550192576}, + {0x4082576d263e803f, "8215fv967u03", 0.689800101, -167.221022239}, + {0x994404103f1dacf3, "m520841z3qqg", -26.618594408, 45.002466048}, + {0x2a4ab21c3b5c5501, "595c471vcjbh", -84.18172062, -17.125239371}, + {0xd49a1866723555ea, "uke1htmk6pby", 70.517892509, 15.67380079}, + {0x7a85ab398edd5559, "gb2uqfdfvpbp", 47.167568093, -9.894690297}, + {0xaa9b959c561723a4, "pbetc72q2wju", -86.157799163, 173.727179142}, + {0xe2ba16e28c68a01d, "wbx1esnde2h1", 3.098574018, 133.756629994}, + {0x41ce2e7c3033a82a, "8772wz1h6fn2", 18.40833307, -163.881556628}, + {0x579c3103817caebd, "byf320w1gkrc", 83.191546645, -143.077547657}, + {0xa75f4423cc029a04, "nxgn88yd0be0", -45.259205887, 116.749685302}, + {0x9c400fa44c9bc290, "mj00z92dmg19", -16.73589509, 45.330525298}, + {0x21631c4868be6e0a, "45jjsk38rtr0", -72.134786059, -82.779590971}, + {0xfbb31b085a3e69d5, "zftjq22u7tnx", 59.98748254, 176.057097164}, + {0xe259a59377b26ff3, "w9duc4vrq9rz", 9.288819634, 116.418632075}, + {0xfabf940ebbdab7bc, "zbzt83pvvbvv", 50.192032042, 179.318758984}, + {0x0e38995e110d9208, "1sw9krhj1q90", -64.42842811, -103.167059044}, + {0xfe7b4820d0628bbb, "ztxnh86hdb5v", 76.994334182, 167.544373807}, + {0x096454f583fd4c10, "15k59xd3zp61", -71.061977426, -129.305689025}, + {0xc1088e6d90ac99f1, "s448wvdhpkdz", 11.368815049, 3.815317323}, + {0xbd42793cfe56cc97, "rp17kg7ybv69", -5.034670975, 136.971738266}, + {0xafe2b316c87ee309, "pzjc65q8gvjh", -50.387264355, 176.932922395}, + {0x1e1526800945e4e4, "3sbke0098rkf", -17.489937564, -112.015897789}, + {0x388f35cfca82f7b0, "727mcmybhcvv", -42.551411861, -29.115274577}, + {0x05c575b20575b266, "0r2rcdh5fqt6", -47.84477995, -168.326922617}, + {0xc76498144b723bcc, "sxk9h52cf8xw", 40.97518822, 29.005053733}, + {0xfc1b12a0714dd793, "zhej583j9rct", 71.193766169, 139.373987638}, + {0xddb2dde92c944594, "vqtevu9dkj2t", 82.246814277, 64.239292896}, + {0x81320ba2879a87a0, "h4t0r8n7mb3u", -75.892994773, 7.369672037}, + {0xfa03efbad683d96e, "z81yzfqqhgdq", 46.199932908, 160.310303869}, + {0x35342d481d1093eb, "6nu2uk0x229y", -6.876193109, -83.835969887}, + {0xf421294db30801c9, "yhhkkmem100w", 68.278142076, 96.167837094}, + {0x68fef03951cb78a9, "e3zg0fbjtewb", 10.387162925, -22.818358217}, + {0xddf1b22c79066f87, "vrsv4c3t0trs", 88.074159995, 63.052628652}, + {0xf6b847681ac9c19e, "yuw4fu0ut70t", 70.818660623, 132.309605847}, + {0xe94e595a51e5288a, "x575kqkjwnn8", 18.887847673, 139.41127725}, + {0x26eb5ff4ef46e8f4, "4vppzx7g8vng", -60.472241644, -46.071468996}, + {0x0c92bbb62700df6c, "1k9crej703gq", -64.450768468, -120.952247566}, + {0x7217bc7e8a3823f1, "f8cvsznb70jz", 50.224043166, -64.820916046}, + {0x8e4efd3010f3c7ba, "jt7gud0hyg3v", -59.797750289, 72.971483721}, + {0xe9d7f570cbffccb0, "x7czbw6czz6c", 22.490726471, 148.737023022}, + {0x087f031a6bb86d32, "11zh66mcr1qm", -79.396597919, -125.049167743}, + {0xf2e7d6e5708ad443, "ycmxetchjcb4", 53.381891519, 131.639803772}, + {0x4cbd3ae25ee459ed, "9kympskywjdy", 27.622148639, -114.624563122}, + {0x97f3bfe4b31321bf, "kztvzt5m2dhv", -1.77337907, 42.170018667}, + {0x784a1c70d7f0a9d4, "g151sw6ry2nx", 50.924335822, -40.580129215}, + {0x4382d4cd4e6b7749, "8f1e9mbfeevn", 11.897270419, -144.084506284}, + {0x7ab31062a34ed019, "gbtj0sp39v81", 48.713666148, -4.186745697}, + {0x83669767be74b93a, "hem9ftxyfkwm", -71.379736685, 30.354986484}, + {0xdb2b94181a244c9a, "vdpt860u4j69", 57.228498191, 78.058980751}, + {0x5c5f1bff55cd9f8a, "cjgjrzuptqgs", 78.310497928, -130.434950342}, + {0x2e55713a2fac300a, "5tbr2fjgphs0", -56.370310444, -22.107276013}, + {0xb8bd18b271fcd813, "r2yjjdmjzmd1", -39.888967335, 154.936379605}, + {0x88db6339cddd0c85, "j3eq6ffevn68", -80.448083566, 60.944813309}, + {0xb2fba5c24e801cf5, "qcxuchkfh0fg", -35.703845116, 134.699081326}, + {0xdbe7fba504643940, "vgmzr984dhwn", 64.564358139, 87.16562129}, + {0xaee0453159ed896d, "pvh4bdbtxq4q", -61.37551787, 174.397973633}, + {0x241538164aa27c57, "4hbmh5kbn9y5", -62.384480912, -89.465845971}, + {0x2105a252b683827e, "442u4npqhf17", -76.606624712, -88.847259679}, + {0x155b73b9a4925f83, "2per7fe4k9gs", -1.523956653, -175.26057473}, + {0xa51e6f03d7aec4ae, "nng6y0yrpv2b", -51.542501781, 94.842901341}, + {0xccf7ca4bb9a96ed8, "tmvwnkxtp5re", 33.424032999, 64.269643561}, + {0xdc2aa40d537f3e7c, "vhpb83bmgwz7", 67.598527546, 55.909927326}, + {0xae913adbe8afb99d, "pu8mpqz8pywt", -63.771479045, 169.430807203}, + {0x6099513d6a249689, "d2dp2gcb4kc8", 4.107538955, -75.902038969}, + {0x461b126ef08c909e, "8sej4vrhjk89", 26.220956413, -153.150561094}, + {0x51812f62d015e580, "b60kysqh2rks", 57.109076047, -168.104536408}, + {0x4b3dc81df501fe4c, "9dywh7gp07z4", 16.545259059, -103.168467215}, + {0x777a18d8ab546aa0, "fxx1jq5cbjpb", 87.396567496, -57.420376798}, + {0x79f9a80b92baecb3, "g7wuh2wkrcqc", 65.394108084, -24.06212008}, + {0x9a15a82666cecaeb, "m8buh9m6tv5f", -40.070802167, 68.759903229}, + {0xdf9c1838a8609431, "vyf1hf58d2b3", 83.155607683, 81.776143678}, + {0x2e8eea0c7a931fab, "5u7fn33ukdgu", -65.734575997, -5.699302582}, + {0xfe39f52f1dce0907, "zswzbcsxts4h", 71.684354181, 167.031565108}, + {0xfd667bece2909305, "zpm7rv72k29h", 86.381424583, 142.728035671}, + {0x8ea7d87a732e1ba3, "jumxhymm5seu", -64.828065803, 86.700480334}, + {0xcc10902477bbc0e7, "th89093rrg0f", 25.496408812, 45.727148003}, + {0xa4f0cc861a74fb0a, "nmsdt1hufmxh", -58.616708138, 107.804480916}, + {0x80b0b2004ceeffba, "h2sc402dxvzv", -87.009884586, 18.018608057}, + {0xaa5fb4462c2ccde2, "p9gv8jjd5m6y", -79.161614789, 162.781133873}, + {0xef22931469a6a8bf, "xwj96539nunc", 33.987772794, 165.324624557}, + {0x1fcf87025d146c05, "3z7sf0kx2jq0", -3.381142933, -96.234046443}, + {0x09a8bfcdb081b1a9, "16nczmehh6su", -78.411469628, -113.934900111}, + {0x28c59a927429d7c4, "532tp4mn57cw", -82.07643292, -32.732249828}, + {0xac4b82a0ea71bdb7, "pj5s587bf6yv", -61.170357214, 140.08093919}, + {0x0f0953e16c715606, "1w4p7scdf5c0", -54.949001099, -109.531544184}, + {0xf9cb3914cb220063, "z75mk56c4806", 62.815876418, 150.999978025}, + {0x5ca762a86ab50dad, "ckmq5b3bqn6u", 69.962388071, -116.199727913}, + {0xaca9e134d1bc15f0, "pkny2e6jrhbz", -66.382586521, 155.767208183}, + {0xb8aaabe6b8adbaaa, "r2pbrtpspqxb", -44.927867336, 157.488648822}, + {0xb8dcd98eaad7b587, "r3fem3pbuyus", -34.579302009, 150.007190894}, + {0x541dcf0cb7a0341b, "bhfwy35rn0u1", 72.911972468, -176.204990472}, + {0x6f976becf5673ff1, "eycqrv7pdwzz", 39.097546817, -9.147373702}, + {0xc7c8c63827921a30, "sz4ddf17k8e3", 39.82600024, 37.388388751}, + {0x4be12db06a4acd7a, "9ghkvd3b9c6r", 17.722414274, -95.029256593}, + {0xecd374574c67affa, "xm9r8puddyrz", 32.29889527, 148.014121009}, + {0xad7a8e7586fd9f07, "ppx8wxd6zqgh", -47.68290156, 145.83592471}, + {0xc233c22fd99e2110, "s8tw4cytmshj", 3.877724094, 30.364392472}, + {0x3f1017f2a8744477, "7w81gwp8fj27", -8.096766264, -22.335805391}, + {0xa5879b3f1ce966fb, "nq3tqgswx5mg", -53.900538568, 103.662494113}, + {0x073d22fd567b0ee1, "0wyk5zbqgd7f", -51.284381802, -148.545643946}, + {0xfedb1d3d34da153b, "zvejug9nv8bm", 76.968631895, 173.179100184}, + {0xfa83e108ee674b03, "zb1y227fdx5h", 46.100473522, 171.22718512}, + {0xfc09a1fb76c33f11, "zh4u3yvqsdzj", 68.285280632, 138.951504123}, + {0xf0d7772ee7ced6ac, "y3crfcr7tvcb", 56.213552892, 103.138867807}, + {0x54b56ac428da0ae9, "bkuqpj18v85f", 72.801032461, -162.463519106}, + {0x110f18fe0d76685b, "247jjzheftn5", -31.425720479, -175.522269015}, + {0xbb77b85602765e95, "revvhph2ftg9", -22.988731675, 165.767667224}, + {0xd2a16240024adfa5, "ubhq4h029cgu", 46.076756191, 39.814876654}, + {0x23027273281e474b, "4d174wt83t3n", -78.186878516, -65.624705105}, + {0xb0fe42eb6f09a447, "q3z45uvg16k4", -34.778067435, 111.266500465}, + {0xa5092ba1d950cadc, "nn4kr8ftb35e", -55.49781679, 93.497101209}, + {0x51f942c7b41ff933, "b7wn5jxn3zwm", 65.773513922, -160.170967403}, + {0xfe1567246779ea23, "zsbqf937g7p2", 72.912799798, 157.963291723}, + {0x3d77c6876b21db1f, "7pvwe1vc47ej", -0.253867417, -37.125791736}, + {0x089a8b06cee19584, "12e8q1qfw6bs", -87.136243536, -118.554914488}, + {0xb9585c7ec90d326d, "r5d5szq91nt6", -24.65723318, 138.030216772}, + {0xeef31beff7c7e22d, "xvtjrvzrszj2", 31.8932624, 176.131993661}, + {0x6aa88ba64aae2f70, "ebn8r9kbpsrr", 0.050835763, -1.772930373}, + {0x06e776d165755e66, "0vmrenc5fpg6", -59.112626008, -138.733887955}, + {0x2307beeb23cecd7e, "4d3vxut3tv6r", -76.351946968, -64.691026442}, + {0x2ba2d6975ba80a36, "5fjee5uvp053", -78.113307679, -3.376961027}, + {0x1315a9bec80cab0e, "2dbumgq81kph", -28.766304684, -156.183637233}, + {0xb8a4c7b61107930f, "r2kdgehj0y9h", -43.092978075, 152.737460576}, + {0x7d7a159d9e846128, "gpx1c7dyhjhk", 87.515400764, -35.097364238}, + {0x5e274df27cde2e0f, "csmnvwmwvsr0", 70.128287385, -105.219251924}, + {0xa31c7cbc6d2c2941, "ndf7tg3e5hnn", -73.897625328, 115.91894482}, + {0x9f589cffd3ece564, "mxd9tzymxmkq", -2.50528271, 71.277210496}, + {0x3a76fd6093f5a68a, "79vgus4myqm8", -34.47407707, -14.212942781}, + {0x9082db76e8c8a8f6, "k21eqxr8t2ng", -44.388799043, 13.655553675}, + {0x77c42560b6a91857, "fz22bs5qp4d5", 85.936102602, -55.871698449}, + {0x5de70092a33d20a2, "crmh14p37nhb", 86.495608109, -116.664713082}, + {0x8139243baba447b9, "h4wk8fxcnj3v", -75.132550753, 8.832924574}, + {0xbe618089a37b5b01, "rths12e3geeh", -16.168808484, 163.887674133}, + {0xfbbf24ee068165ed, "zfzk9vh6h5ky", 61.287593371, 179.028231502}, + {0xda87b59fe94d2b47, "vb3vc7z99npn", 47.437882387, 81.276221283}, + {0xa6cf66132422656e, "nv7qd4t449kq", -59.312087806, 128.415182864}, + {0x885bebd62dc60e0d, "j1eyrpjess70", -80.424791293, 50.58880516}, + {0xa427b479428b085d, "nhmv8yb2jd45", -65.089862902, 98.119478739}, + {0x91da708b2a053a74, "k7e712tb0nx7", -24.782374196, 15.883150359}, + {0x6bceef92118ad772, "eg7fz4hjjccr", 18.776503642, -5.663203586}, + {0xd33b6e5ae585e3cc, "udxqwqr5hrjw", 60.239964034, 32.979773753}, + {0x04c471e3508d629c, "0m273suhjpj9", -59.870643932, -168.32680424}, + {0x95f74b8a00878e35, "krvnr2h0hy73", -0.307583339, 18.605554896}, + {0x566a65af81726edb, "btp6ccw1f9re", 73.616945866, -147.219425847}, + {0xe38ddc6b15c70f4a, "wf6xsuspsw7n", 14.000650635, 127.480055804}, + {0xc05ef5420caa8a66, "s1ggbhhdpb56", 10.525246102, 5.279954415}, + {0x23cb41b1075f44b6, "4g5n3d87cx2c", -72.011951385, -51.964923232}, + {0x5f3e192543f33f79, "cwz1k9b3ydzr", 83.198406006, -102.4578639}, + {0xcc477238ea2d5c6b, "tj3r4f7b5pf6", 30.774120335, 46.883943984}, + {0x9dfe42ac106eab2c, "mrz45c0heupk", -1.048399647, 66.258715806}, + {0x46c81ae42aab7cb9, "8v41pt1bpeyc", 28.328267407, -143.105175764}, + {0xf3bd926a06387f18, "yfyt4uh671zj", 61.370025372, 133.017449435}, + {0x35398ec7b9877f18, "6nwsxjxthxzj", -7.615371236, -80.541255438}, + {0xf5ebb97d1664829c, "yrpvkz8qdk19", 85.34018784, 112.357619797}, + {0xfb08ce94ad10abc5, "zd4dx55e22pw", 56.70649166, 161.328069523}, + {0xdd6738dc155bb771, "vpmmjr0pcfvr", 86.699954775, 52.613607561}, + {0x407b398c733dd037, "81xmm33m7r83", 9.368159767, -169.572118067}, + {0xb07574e7849bff25, "q1ur9tw4mgzk", -33.807271037, 96.050976769}, + {0x26aa55b6c2a37e6e, "4up5ceq2nez6", -66.822946989, -46.331460013}, + {0xb6c59ed1efa67892, "qv2txnggntw9", -14.464329604, 124.766179813}, + {0x9451e8831809824e, "kj8yj0ss1614", -13.004368086, 1.28064912}, + {0x89ec432d69bd52e1, "j7q46cc9rp9f", -71.313373936, 64.810712334}, + {0xd9d3d5349bf70c14, "v79xbe4vyw61", 66.067305045, 58.385405882}, + {0x6089edc437d1db19, "d24yvj1ru7ej", 1.215339322, -74.661182681}, + {0x5f740f64c3d50c69, "cxu0yt63un66", 88.754759373, -106.586090683}, + {0x68bf08e1f433956e, "e2zhjsgn6fbq", 4.949056274, -23.660312275}, + {0xa663b56d09f2996c, "ntjvbv89ybdq", -60.833741212, 120.619876043}, + {0x695ee81a5152c33f, "e5gfh6kjbc1m", 21.458665527, -39.534267613}, + {0x4dd9c05dcb5d35c3, "9rdw0rfccnuw", 43.2850972, -120.219563051}, + {0x64ee547a9fb01e4e, "dmr58ynzq0g4", 30.180692351, -68.863759478}, + {0x860a91f743bf54b4, "hs593xu3rxbc", -67.237444987, 27.493952339}, + {0x693aedbb4b329424, "e4xfvfuc6bb2", 14.56122333, -33.842235449}, + {0xa74449aea5113fd8, "nx24mcp524zx", -48.817175176, 112.762308851}, + {0xb42992968ff6e834, "qhnt55ngyvn3", -21.603943429, 99.282028766}, + {0x603f8f6bb8e0e27c, "d0zsyuxsw3j7", 5.079208642, -79.145918947}, + {0xf459c512e5849453, "yjdwb4r5hkb5", 77.136924087, 93.52542057}, + {0x72c6bcfbd3eb17db, "fc3ctyymxdcx", 52.33296086, -53.527469756}, + {0x98268e47c00c22a5, "m0m8wjy01hjb", -43.474252098, 53.006337322}, + {0x2f0591fba45bd138, "5w2t3yx4cg8m", -53.884701177, -21.710282014}, + {0xcd1806a39571f925, "tnd0e8wpf7wk", 36.654487165, 47.974653342}, + {0xf8397e27a64829ad, "z0wrw9x690nu", 49.139529101, 144.084715556}, + {0x8fe44fce0a0b1a39, "jzk4zmhb1de3", -48.707872115, 84.700195625}, + {0xa259f43dba46aa0a, "n9dz8geu8up0", -80.224120596, 116.405338166}, + {0x5898dd02b92716b8, "c2deu0pt4wcc", 48.47257439, -120.048184246}, + {0xfdb09c9c010a3c71, "zqs9t70118y7", 81.842827617, 152.808910692}, + {0xcb2e2899c4473a60, "tdr2j6f48wx6", 12.671820565, 77.928803174}, + {0x43ea78e42153a684, "8gp7jt11bfm8", 17.430122166, -135.811575419}, + {0xa18babb4e8fb99f0, "n65ure78zfdz", -77.984933539, 106.858170174}, + {0x95205488b5d3688d, "knh5925puen8", -10.633419004, 5.684253532}, + {0x265e095ec1aa0f5f, "4tg0krq1p87p", -57.572304925, -63.085916506}, + {0x3403f04e5e875f66, "6h1z0mkyhxgq", -21.2396195, -87.521355399}, + {0x667d4665071f43de, "dtyndt873x1x", 33.51713725, -58.952226683}, + {0x45a4df5ae7d6f02c, "8qkeyqr7uvs2", 35.850435317, -162.137050721}, + {0x091ed62cceb4a773, "14gedc6fqkmr", -73.908738896, -129.953223468}, + {0xf6d9bf094e1b507b, "yvdvy2bf3e87", 76.95276763, 127.892940711}, + {0x508b981e0132c06c, "b25th7h16c06", 45.895606122, -163.635745664}, + {0x445b2388b134fbc0, "8jek725j6mxw", 31.685502054, -175.282638668}, + {0x6ec67f039f20c71f, "ev37y0wz433j", 30.194385573, -9.219147713}, + {0x75747fce75ffa943, "fpu7zmmpzynn", 89.283132413, -83.697626724}, + {0x381aaea153af2b43, "70ebx8bmpwpn", -42.094597951, -39.396297945}, + {0x71fae8d11568ac2d, "f7xfjn8pe2q2", 65.076057006, -67.631687836}, + {0xd063cc33d553eaff, "u1jwsdypbgpg", 51.784031429, 7.940411525}, + {0xec8d1712b3458694, "xk6jf4pm8q39", 24.928956439, 149.16035975}, + {0x8fe3b5fd3f4b8dc3, "jzjvcz9z9f6w", -49.571758497, 86.915323645}, + {0xe368dea8e950912c, "wenexb79b28k", 17.491911543, 121.986018979}, + {0x4d7e3e1f0f35f2c0, "9pz3w7sg6rtd", 43.877246451, -124.523402226}, + {0x39af734972575059, "76rr6kckbx85", -31.042387215, -23.454072307}, + {0x54f22a5ca708f7e6, "bmt2nr5713vy", 75.976477648, -161.088004586}, + {0x9e98f63bed767b15, "mudgdfzeftxj", -19.056485209, 82.748453469}, + {0x3e4b579b96b51be2, "7t5pg6wqqney", -15.497853627, -18.129581979}, + {0x266813fdb4343646, "4tn17zen6hv4", -61.612975894, -58.893492582}, + {0x9c42e89b8f67b15f, "mj1fj6wgdysp", -16.509067566, 47.70104378}, + {0xe55d81ea96c1da61, "wpfs3unqs7e6", 44.36392793, 93.601299145}, + {0x0fc97e96cf17e5b1, "1z4rx5qg2zkv", -49.288190155, -97.769011972}, + {0x14007a29379e0fd5, "2h07nb9rms7x", -21.968641017, -179.34984922}, + {0x6fbde71459ce249c, "eyyyf52ttsk9", 39.17409341, -1.66899297}, + {0x0c939ab3faa5d4e2, "1k9tpdzunrbf", -63.792758988, -121.300122784}, + {0xae4332ea4a92f530, "pt1m5ukbkcum", -60.972694644, 159.42934016}, + {0xe2d219fbfebc9f8e, "wc91myzyrkgs", 8.695404576, 125.419894712}, + {0x3572df138360812e, "6ptey4w3d20k", -2.139329145, -81.993272869}, + {0x1e896fbf98ddc812, "3u4qzgwsvr41", -21.293397362, -97.736206842}, + {0x5078cd61a3c8ed60, "b1wduse3t3qq", 53.945882893, -170.656924161}, + {0x40a8ab8547bd4154, "82nbr1b7rp0p", 0.054155096, -158.94955152}, + {0xf933ba144a3879d9, "z4tvn52b71wx", 59.959311212, 143.350776989}, + {0xa44a42548cab9ffe, "nj544p4dpfgz", -61.484625984, 94.31041181}, + {0x6b4a06fdb9b99a3c, "ee50ezetr6e3", 17.005004256, -18.111332879}, + {0x18b98dc954feb94a, "32wsvkbnzuwn", -41.325262652, -114.378321198}, + {0xcfae93d589905078, "tyr97pd9k187", 35.417398421, 89.432316372}, + {0xfa08c9c0794ef685, "z84dmh3t9vv8", 45.419828869, 161.237494024}, + {0xe54fc9a6cc94e19b, "wp7wm9qdkmht", 41.887163217, 95.172689355}, + {0xd33472fddeab842b, "udu75zfypf22", 61.03970786, 28.645467218}, + {0x73181def1192e69d, "fdd1vvsjkcm9", 59.401237815, -64.429109894}, + {0x4f86ee747d63d927, "9y3fwx3xdgdk", 35.636835638, -98.501236911}, + {0x11bfaac64e6781ac, "26zupjkfdy0u", -28.79882377, -157.537302968}, + {0x6193ac7146b7f9b9, "d69uswb6qzwv", 14.891021842, -76.090666363}, + {0xb7e759c6792e5a34, "qzmpmjmt5te3", -2.914608744, 131.008684247}, + {0x71eab34f27013f5a, "f7pc6mt704zp", 62.125469879, -67.745466089}, + {0xcf0725b37d4cd64d, "tw3kcdvx9mc4", 36.007635909, 69.331338727}, + {0xe27ac378f6615114, "w9xd6y7qd58j", 8.868475615, 123.172273694}, + {0x8277abad4d76647c, "h9vurcbeftk7", -79.398894142, 30.927313505}, + {0xea07e9b8403457e8, "x83ymf206jcy", 2.5173172, 160.213711455}, + {0x37623576df02d7a3, "6xj3bxqz0ccu", -5.276345602, -60.085910448}, + {0x33460ce07841865f, "6e30ts3s8635", -26.606728212, -65.849978472}, + {0x3eb8304a7e18847d, "7uw30kmy3227", -19.487297946, -2.442000567}, + {0x9d08d40fa371d0f0, "mn4e83x3f78g", -10.626206162, 48.536665292}, + {0xbd5749da644a636b, "rpcnmqm499jq", -0.272849315, 136.643901057}, + {0xa36811f690dbab2f, "nen13xnhvfpk", -72.865994342, 121.011914535}, + {0xa15c2de93a7a0d45, "n5f2vu9ug86n", -68.748876935, 93.419304267}, + {0xee87e8417a73f323, "xu3yhhcufgtk", 24.987864948, 171.389247177}, + {0xe5ec9f107f9e83a1, "wrq9y43zmu1u", 41.102493553, 110.656949298}, + {0x9d38cd65fa32fa16, "mnwdutgu6cx1", -7.921778009, 54.34365629}, + {0x29d9c9bdcf646c3c, "57dwmgfgdjq3", -69.192638585, -29.977818476}, + {0x72e32a631cb46c02, "fcjknsswqjq0", 51.353945315, -48.575097478}, + {0xbe69624c371eb7e7, "rtnq4m1r3uvy", -15.791575482, 166.389738978}, + {0xbdddd635e99cf9cf, "rrfxdeg9mmww", -0.06704603, 149.880529079}, + {0x721f9561c83d0d9c, "f8gtbsf87n6t", 50.255662105, -62.552586786}, + {0xf2a5cb8fc14b8f6a, "ybkwr3y19f7q", 47.514766456, 130.405048053}, + {0xc54869f7218ea62d, "sp46mxt1jum2", 39.811901143, 3.412882204}, + {0xd7572dc4939c0657, "uxckvj4mmh35", 89.457101418, 24.480845052}, + {0xe412c5a9723c5995, "wh9dcbck7jdt", 25.800778346, 92.188128079}, + {0xb528046077a52274, "qnn08s3rnnj7", -11.137528999, 98.461453853}, + {0x2aa740d14c2800a4, "5bmn1nbd500b", -87.501640224, -4.173988227}, + {0xc05fbbdbc5ffc2b6, "s1gvrqy5zz1c", 10.804361968, 5.600615854}, + {0xf2d7e0766b7a6a9b, "yccy0xmcg9p9", 55.938568937, 126.240967162}, + {0x21e9a836e18fefa0, "47nuher1jzru", -72.403810964, -69.050188367}, + {0x0af94001a3230f11, "1cwn00e34d7j", -80.504881303, -92.807925452}, + {0x5484c281324467aa, "bk2d509k8jmu", 69.261360424, -167.913314437}, + {0x5a07fda5b5596b89, "c83zv9epc5ps", 47.778141621, -109.793190228}, + {0xc385704f50703d41, "sf2r0muhf0yn", 13.919124925, 34.118135885}, + {0x7b096a79120dba2e, "gd4qny8k1qx2", 57.341114681, -19.038899695}, + {0x5ea04009bdd0abd9, "cuh402exu2px", 67.855640688, -95.609014534}, + {0x1710906b4d58af91, "2w890uuec2rt", -8.234980264, -156.757672469}, + {0x5a4905729c179c09, "c94hbwnw2yf0", 51.494029097, -109.656565862}, + {0xb2e1d8870942cf40, "qchxj1s98c7n", -38.136026298, 130.304067661}, + {0xaa5b063e2a5890b1, "p9ehdgjbc28c", -80.754873206, 161.84755074}, + {0xa72c4d05df9bddd6, "nwq4u1fzmgfx", -54.349472598, 121.117308028}, + {0xc78dea66a5835547, "sy6yntp5hebn", 36.238939554, 37.912638274}, + {0x0292fa4ae50b3deb, "0b9gnkr51dyy", -86.63627948, -143.504717574}, + {0x2678a4b4bb35c0b9, "4twb9e5v6r0c", -58.957190871, -57.936646943}, + {0x15921e1e7277deb7, "2q91w7mkfzgc", -8.155118064, -167.061758862}, + {0x53d2a4ad3b125c38, "bg9b9c9v29f3", 64.784541183, -143.709730231}, + {0xcc91859f7f1384f6, "tk8sc7vz2f2g", 26.169317535, 57.015986344}, + {0x8db293dbc826f579, "jqt97qy84vur", -53.180662479, 64.13624761}, + {0x87ae05ac3eefe23e, "hyr0cc1yxzj3", -54.705267259, 43.673397475}, + {0x0b95a31d459840a2, "1fbu67b5m10b", -73.763017045, -100.096219834}, + {0xb10945bea90e7dd2, "q44ncgp91tyx", -32.546793361, 92.899777202}, + {0xedb731fde5768b0f, "xqvm3zg5fu5h", 38.934839598, 153.713959032}, + {0x252af57f4944e48d, "4npgbzu98mk8", -55.547962503, -79.062406745}, + {0x52d19b7455cbaf63, "bc8tqx2ptfrq", 54.401477592, -145.260973865}, + {0xa7806b89dc5a8ff3, "ny06r2fwcb7z", -55.849211023, 124.423679518}, + {0xf3aa5ed24f59447b, "yfp5xnkgc527", 56.900228383, 133.907933868}, + {0x4b4258d9496b04a5, "9e15jqb9ed2b", 17.439692301, -110.862199178}, + {0xf0fa1e5bfb2747cb, "y3x1wqzv4x3w", 53.739149008, 111.379160827}, + {0x5d3ccf7a9fa9ed00, "cnydyynzp7qh", 83.48632718, -125.55315523}, + {0xdde4bd513558ef8d, "vrkcun9pc3rs", 86.125910672, 63.106905991}, + {0xdbc9069450028404, "vg4he52h0b20", 62.684556481, 81.694368179}, + {0x99938e10165bd429, "m69sw40qcgb2", -30.13431914, 58.623465842}, + {0x42da7ed016a48f24, "8ce7xn0qnk7k", 9.086746917, -141.371454355}, + {0xb2c44d797d23c268, "qc24uycx4g16", -37.44705463, 123.960920041}, + {0x6fe272f2ab94ad0d, "ezj75wpckkqh", 39.935540934, -3.702545238}, + {0x4a0ca85c37069226, "986bhr1r0u92", 1.445928014, -108.44429435}, + {0x6b77c8555eb1dd50, "eevwhpbyq7fp", 22.192103721, -14.588542853}, + {0x62b337516d0c230b, "dbtmfnce1hjh", 3.860858217, -48.777187023}, + {0x492c3c1417782b92, "94q3s50rg0pt", 12.937732583, -126.034674013}, + {0xeb1c0c370be3c1af, "xdf0sescwg0u", 15.576141094, 160.517067987}, + {0x4cce290b018454b8, "9m72k2s1hjbc", 29.578144574, -118.987253868}, + {0x17a8878ab07dd5d8, "2yn8g2phgrbx", -11.117305905, -136.956799355}, + {0xaa91d46ea50b27b5, "pb8x8vp51dmv", -85.841146771, 169.495769186}, + {0x2e6396922891aa2b, "5tjte4j8k6p2", -60.897163091, -14.62604353}, + {0xfdbf484a2850d4fe, "zqznhkj8b3bg", 84.045548906, 156.288082716}, + {0x7257216c488fc606, "f9ck2v28jz30", 55.619698412, -65.708293667}, + {0xe2adfea07948ec4b, "wbqzx83t93q4", 2.726933923, 133.573899286}, + {0x1c8c936ca215bdaa, "3k696v522qyu", -20.846477798, -120.109042131}, + {0xb1437ba5786cf380, "q51rr9csemts", -26.840170238, 92.089609178}, + {0x615631b72fddb862, "d5c33etgvqw6", 21.333381589, -88.168139283}, + {0xc9332ae2c2f02168, "t4tkpsq2y0hq", 14.789100706, 52.721248845}, + {0xc3d4a2165d713f5d, "sgbb45kxf4zp", 21.112949216, 34.898853402}, + {0x0877435c3b117a49, "11vn6r1v25x4", -79.018244635, -127.867460155}, + {0x569c09bb218d8788, "buf0mft1jq3s", 71.776634092, -143.177720433}, + {0x742905c7e36805d8, "fhnhcjz3e02x", 68.366806647, -81.508468696}, + {0x56cd7ffc583292a5, "bv6rzz2s6b9b", 75.93411253, -142.744554286}, + {0xff62df0118e8dc6d, "zxjey08sx3f6", 85.037708624, 165.499047846}, + {0x6063d84ed7844c67, "d1jxhmqrhj66", 6.885538121, -82.070099555}, + {0x1bc842bb6172a6a2, "3g445fv1fbmb", -27.758029514, -98.265712778}, + {0x8465271b0a28de9e, "hjkkf6sb53g9", -59.620046475, 6.082107014}, + {0x779a5605c60221a3, "fye5d1f608hu", 82.187691448, -51.940241546}, + {0x73fbb6d1fd1f6a43, "fgxvengx3xp4", 65.692658229, -45.214846158}, + {0xf8e8981f2b49dee5, "z3n9h7tc97gf", 50.820285412, 155.58534769}, + {0x86f1f1265637cd51, "hvsz29kq6z6p", -57.780106468, 40.457617373}, + {0x72d9f2e954df536f, "fcdz5ubnvx9q", 54.695261382, -52.217779549}, + {0x2b1140e62a73371f, "5d8n1tjbfdvj", -74.855202179, -22.426071281}, + {0x518f20657a56e19e, "b67k0tcubvht", 58.391803869, -164.155273034}, + {0x9c0186201f3266c0, "mh0sd80z69md", -21.707732767, 45.814132967}, + {0x55a8f01c7f5b3925, "bqng073zcdwk", 79.296541156, -159.244350103}, + {0x11260b5cb930daea, "24m0qr5t63ef", -32.260442922, -172.689183427}, + {0xc14695b065a2575d, "s539cd35n9cp", 18.601752505, 2.176946769}, + {0x2c38ee0e93f50041, "5hwfw3nmyn04", -64.241534057, -35.22431366}, + {0x95d5b34a71154fbd, "krbv6kmj2p7v", -0.459111393, 12.410436124}, + {0x6f05263b7e7277a6, "ew2kdfvyf9vu", 35.963540692, -22.019576374}, + {0xd66146d18c13e7ae, "uthnendd2gmu", 74.303688133, 28.260309385}, + {0x1b7641e78e7eda5b, "3ev43twfgve5", -23.480027642, -105.393397049}, + {0xc3dc777c84f88cdb, "sgf7fz44z26e", 21.791855811, 37.037973462}, + {0x1a55e9aa1d1817f4, "39bymbhx30cz", -34.056367344, -111.186401854}, + {0x28d1343a48ba40c7, "538m8fk8r90d", -80.583294765, -33.358976019}, + {0x4ca3a63c9c0a9399, "9kjudg4w1b9t", 23.308528668, -115.539702371}, + {0xaae243e562ccb894, "pcj47tc2tkw9", -83.947796249, 175.937009232}, + {0x279c212ac7fb8a16, "4yf22bq7zf51", -51.985276683, -53.044058673}, + {0xa8de218a0b31445a, "p3g232hc6525", -80.112072151, 150.881853423}, + {0xd857d55c0d7ecf9d, "v1cxbr0egv7t", 56.245179702, 47.121213778}, + {0xd95d1ac24a0cc03b, "v5fjphkb1m03", 66.996029243, 48.126694608}, + {0x95318b9ec224d724, "knssr7q24mck", -7.672545289, 6.655409882}, + {0xcb4d48e39262ba1f, "te6njswkdbx1", 19.361432214, 70.562909947}, + {0x3b5489a2657d5e00, "7eb8m8m5gpg0", -23.860246083, -21.548176494}, + {0xc7c33abd5d2cb80e, "sz1mpgbx5kw0", 40.275731486, 35.849223149}, + {0xd0c2c8f0e3ff1545, "u31djw73zwbn", 51.011232532, 13.605859323}, + {0xf0aebc6ad5d1c561, "y2rcsuqpu72q", 46.694616694, 112.365600288}, + {0xa9da7adb0a49adc6, "p7e7pqsb96qw", -69.749351659, 151.1455016}, + {0xff62186359fd9bd1, "zxj1hsutzqex", 84.577896928, 164.735499552}, + {0xe4335db764943c10, "whtpvev4khy1", 26.695815971, 97.279995411}, + {0x29f787c441adb378, "57vsgj21pqtr", -68.042242219, -25.883470666}, + {0xda6b7a8201fc8488, "v9prp0h1zk28", 51.855791973, 78.008734843}, + {0x798da554ef51b565, "g66ubp7gb6uq", 58.531696024, -29.877646162}, + {0x66c02fa6ee0ff17f, "dv02z9rf1zsr", 28.264087274, -55.558123519}, + {0xbdff94eba154dda6, "rrzt9ux1bmfu", -0.414401853, 156.883406846}, + {0x69c582d32209be52, "e72s5nt216z5", 19.020095837, -32.907766134}, + {0xa23dab25edc2e3f1, "n8yuq9gescjz", -85.02395924, 122.282850111}, + {0x6c9f138581bd469b, "ekgj71d1rp39", 27.650095002, -29.396364765}, + {0x752294a119b223b8, "fnj9988tq8jv", 79.017320536, -82.198733526}, + {0xfd738734244cd1cb, "zptsfe149m8w", 88.03940015, 142.845666174}, + {0x6accf357ec516f0a, "ec6g6pzdb5rh", 7.645601288, -7.284612509}, + {0x8bf429650a89988f, "jgu2kt8bj6d8", -68.832078727, 84.925575335}, + {0x968f000953be5237, "ku7h02bmrt93", -20.385574081, 37.980403344}, + {0xbc5b2c5cdad7ca3c, "rjeksr6uuz53", -13.230805715, 139.761065059}, + {0x7bd9b87f22b39aa0, "ggdvhzt2qfeb", 65.607661316, -7.166567722}, + {0x07f5a6808279eee2, "0zuue042g7rf", -45.615087852, -139.435236651}, + {0x1ceea8462de93bd5, "3mrbhjjex4xx", -15.440668648, -112.667917635}, + {0xef1147ee6e633ebd, "xw8ngvmfddzc", 37.778306769, 157.672809274}, + {0xc386898b02e764b8, "sf38m2s2wxkc", 12.703066797, 36.096209936}, + {0xc4688118785c65ec, "sjn8263scjky", 28.182149606, 9.153720783}, + {0xe5f252a082a59122, "wrt55842nq8k", 42.714879312, 108.438417195}, + {0x39c6f4177dfd8f9c, "773g85vxzq7t", -26.081551393, -31.281193339}, + {0x22bced2feb5b71cc, "4byfuczccesw", -85.287918213, -46.538358668}, + {0xa7da5119f86de408, "nze526gserk0", -47.225293359, 127.984687901}, + {0xa9a3d0a639581c22, "p6jx19jtc0f2", -77.513046991, 154.057892086}, + {0x3b382a76653a39d1, "7dw2nxm578wx", -30.89711276, -13.418267578}, + {0xdf8bd9547455b03d, "vy5xkp3nbqs3", 80.065433379, 83.849046139}, + {0x55acab1deb4bf82e, "bqqbq7gc9gw2", 80.221071056, -158.977920712}, + {0x2f617d4c623f0729, "5xhrum327w3k", -49.233779018, -16.33480118}, + {0xa268767a687245b0, "n9n7dym8f92v", -83.725297057, 121.417573564}, + {0xe9e2cb029df211e3, "x7jdq0nxy88y", 17.271841927, 154.257253309}, + {0x69e5d07192118876, "e7kx0wdk2647", 19.548165217, -27.396798598}, + {0x15454445ebb82e7c, "2p2n8jgcr0r7", -3.044369663, -179.994539847}, + {0x75f9c2122084ec19, "frww44j0hmq1", 88.253202206, -69.514427307}, + {0x648465abe3e57438, "dk26cbz3wpu3", 24.394067551, -78.311313459}, + {0x9dc88fac44b2e488, "mr48zc24qck8", -5.485904634, 60.10649713}, + {0x79acaec94bf2090e, "g6qbxkbcy84h", 57.770534325, -23.937897093}, + {0xc87506735cdc4a57, "t1uhdwuwvj55", 10.673030648, 50.741261803}, + {0xadf4bb646dcc5258, "prucqt3etj95", -46.157053465, 153.217608062}, + {0xc0514083142d3e09, "s18n10sn5nz0", 9.496000319, 0.049576836}, + {0x1b35a7a1b615fa0f, "3duug8eq2rx0", -28.692429224, -105.662020294}, + {0xcd9cf8637e67ce2f, "tqfghsvydz72", 38.523341097, 60.322960486}, + {0x7fadc55a946a9ae5, "gyqwbqnnebef", 81.376851369, -2.089982939}, + {0x95871a7fec1a84e4, "kq3jnzzd3b2f", -8.921883702, 12.963256709}, + {0x6b159b48c3a8d5b4, "edbtqk63p3bv", 16.415129488, -21.518814691}, + {0x3b8e30a8e3b21d3a, "7f731b73q8fm", -32.166376011, -6.598037733}, + {0xe888a7f67800abec, "x24bgxms02py", 0.172349141, 150.278570548}, + {0x40180d30a48c0006, "80d0ud54jh00", 2.955687164, -176.985411505}, + {0x4f1540d3ed77df47, "9wbn1nzefzgn", 39.061202835, -112.445632873}, + {0x57283df70ca9a492, "bwn3vxsdp6k9", 79.099171513, -148.462741148}, + {0xaaf630f4a35d06bc, "pcv31x53cn3c", -79.941682395, 176.203239151}, + {0x5fb69ed159b88b49, "cyv9xnbtr25n", 83.270403043, -93.20700583}, + {0x5206dad9cc2dffc9, "b83epqfd5rzw", 46.971058582, -155.068438478}, + {0x7e3b4e6917c2aed7, "gsxnwu8rsbre", 71.481086827, -12.359061803}, + {0x8bd7289a730e4761, "jgckj6mm1t3q", -68.18987784, 80.74580122}, + {0x831b9fe352fd6bcc, "hdetzsukzppw", -74.899811245, 27.757611984}, + {0xd9ced76a7fcf3c9e, "v77efumztwy9", 63.965099585, 61.30086606}, + {0x0cef6baa978ce531, "1mrqrbnrjmkm", -59.368886495, -113.205297238}, + {0xb3e633d061e056ac, "qgm37n31w1cb", -26.46442417, 131.266281771}, + {0x2fb49bae0bf118e7, "5yu9rchcy4df", -51.805715979, -4.574514366}, + {0xabb9c594a9517ab9, "pfwwc559b5xc", -74.73417765, 177.939387462}, + {0xaafff31a2ae3bfe2, "pczz66jbwfzy", -78.870747996, 179.755511074}, + {0x5a9eebabf8e778a3, "cbgfrbzswxwb", 49.619190448, -95.625398517}, + {0x73f44a2b9c99a223, "fgu4nbwwm6j2", 66.44914274, -50.319210306}, + {0x6c8a0902f3b4aa5e, "ek50k0rmqkp5", 22.54624191, -29.345234479}, + {0xaaf738d23c3ccbcd, "pcvmjnjw7m5w", -79.243283917, 176.360236575}, + {0x81bf8168715e6d62, "h6zs2u3jctqq", -73.759817486, 21.83128012}, + {0x4286eb8d29b69f0a, "8b3fr399qugh", 1.810238292, -143.468105039}, + {0x4ff4518ed8e17847, "9zu533qsw5w4", 44.172697242, -95.560876269}, + {0x0fc45e59004073d1, "1z25wq8081tx", -48.567714965, -100.975334357}, + {0xf20a1c6dd27003ef, "y851svfkf01y", 45.296074102, 116.930668613}, + {0x03e5d33503c1500d, "0gkx6e83s580", -70.424833449, -139.811496548}, + {0x79ae696dc2bac09c, "g6r6kvf2rc09", 58.083392051, -23.342523904}, + {0xb9612b2f0a141a8f, "r5hkqcsb2he8", -27.369622725, 141.279721502}, + {0x0b61a58c8e97d645, "1ehuc34fkzc4", -72.284117625, -105.761399103}, + {0x1f46f62f0f20092d, "3x3gdcsg404k", -3.594760089, -109.911600731}, + {0xbeb8ced5d7c6be13, "ruwdxpfrsuz1", -19.204161661, 178.201545442}, + {0xd5b69903b7e022b2, "uqv9k0xrw0jc", 83.192510812, 19.170377919}, + {0x5b2202bd6df6e478, "cdj05gceyvk7", 56.271271792, -105.301599589}, + {0xd8c973a43a4db90f, "v34r791u9qwh", 51.905715408, 59.570336307}, + {0xac687af2b560c395, "pjn7pwppd31t", -61.313403474, 144.128363995}, + {0x20d1177ffa69b618, "438jfzzue6v1", -80.508398389, -78.618360964}, + {0x31cc80c6dca9a37f, "67681jqwp6jr", -26.688868873, -75.181184834}, + {0x018f7cc2d36259d8, "067rthqmd9dx", -76.001091314, -163.951267024}, + {0xd4b1246ebbf685d9, "uksk8vpvyu2x", 71.131992534, 17.270455034}, + {0x82c1ba5e5260bd3d, "hc0vnrkkd2ym", -83.455490834, 35.085287363}, + {0xaf7db1ac2c0435f2, "pxyv3c1d0huz", -45.477535295, 167.071158213}, + {0x85c8cd0b9e53da5d, "hr4du2wybge5", -50.137676834, 14.961698723}, + {0x71b13968925d8e44, "f6smku4kcq74", 60.008173586, -72.561549624}, + {0xbb18a6b9765622d9, "rddbefcqbsje", -30.833322436, 161.533728075}, + {0x37360807a71e545f, "6wv0h1x73tb5", -7.022420874, -60.282947726}, + {0x0756fc466126a67b, "0xcgsjm14um7", -45.761981502, -154.856289403}, + {0x13509cfbfd821652, "2e89tyzxh8c5", -25.0105431, -156.533695192}, + {0x456fd4cede930d5c, "8prx9mqykd6p", 42.129534723, -169.388727594}, + {0x27fe4055cb0164cb, "4zz40pfc05kd", -46.011925984, -46.40246708}, + {0x35727f7bd474142d, "6pt7yyynfhb2", -2.115057094, -82.312230494}, + {0x4a766dabccc366ca, "99v6vbydsemd", 10.331715258, -104.855374757}, + {0x55b3748ad3ac8fc9, "bqtr92qmpk7w", 82.883114822, -161.303356778}, + {0x9b4d60c4eef5cc47, "me6q1j7fyr64", -25.634711962, 70.713428717}, + {0xb7f381ad07cf9ddd, "qzts3c87tyfx", -2.056551579, 131.561872946}, + {0x2fe82e3a5597b609, "5zn2wfkpkyv0", -50.523468949, -2.158602747}, + {0xe04ab38d397ae5c4, "w15c739tgckw", 5.853961169, 95.418486684}, + {0x17411be492d274d8, "2x0jrt4ku9ue", -4.673857318, -157.167120905}, + {0x34a674bba9f4b8d9, "6km79fx9ykwe", -20.464457588, -71.279706401}, + {0xdaa87b920f3aa78b, "vbn7r4hg7bms", 45.582835374, 87.853371406}, + {0x3ca89ffcd8195825, "7kn9zz6s35d2", -22.151808872, -24.265319618}, + {0x546311ef792c8b73, "bjjj3vvt5k5r", 74.080318236, -172.884148621}, + {0x59a0d3ad30d03116, "c6he7c9hu0sj", 56.830347837, -117.255529634}, + {0x72f630dfb5380c1f, "fcv31rxp7061", 55.0619762, -48.802511121}, + {0x59a0ef1930929de5, "c6hfy69hkbfy", 56.747866091, -116.794073427}, + {0x8539f53321b7abdf, "hnwzbdt1qypx", -52.061214313, 9.521326626}, + {0x2c21048017e8a22a, "5hhh900rx2j2", -66.7076969, -39.33039293}, + {0x08dca7ec4598f34a, "13fbgv25m3tn", -79.995007963, -119.717785303}, + {0x1599c2a08f502c2a, "2qdw584gb0q2", -7.382167407, -165.076781139}, + {0x500673eeade0f397, "b0377vpew3tt", 47.00561475, -178.066817184}, + {0xc11c8694cf11babf, "s4f8e56g26xc", 15.575065049, 3.651258913}, + {0xe0a2055f91c877ea, "w2j0brwjt1vy", 0.173989205, 108.300698652}, + {0xed0dff63e7126674, "xn6zysz729m7", 36.545212424, 139.162816926}, + {0x654695890efdfde0, "dp39c28fzryy", 41.092128454, -87.83434319}, + {0x698edc4f7758a7eb, "e67esmvrc2my", 13.304402833, -28.634086037}, + {0x5a2f988f86425517, "c8rtj3w689bj", 47.293835517, -101.713807425}, + {0xd32637c0f1e38541, "udm3gh7jwf2n", 57.988170427, 30.01905803}, + {0x44909862cc6e4aa5, "8k89hsqdet5b", 25.512083327, -167.840039441}, + {0xa4796c174577962a, "njwqs5u5fyc2", -57.898642528, 98.97045692}, + {0x2d470a83d7eda9fa, "5p3hp0yrxqnz", -48.510184302, -43.277229286}, + {0x528db9cfd2dea4bd, "bb6vmmykvukc", 47.361525746, -142.14326317}, + {0x1d27bb72330395d6, "3nmvqwjm0fbx", -8.887065126, -126.621175224}, + {0xdb74652aaab22e65, "veu6bbpbq8r6", 66.577193182, 73.520453246}, + {0x318221929e184ede, "661234ny317e", -33.69398817, -76.938924582}, + {0x4397fcdc333a3e93, "8fcztr1m78z9", 16.826466983, -143.556472602}, + {0x51188c0d04aee609, "b4d8s384pvm0", 59.1590019, -176.297267909}, + {0xf8e63f472360198c, "z3m3yjt3d0ds", 52.369340543, 153.903783571}, + {0x3350158f28e24bf5, "6e81c3t8w95z", -24.996550538, -67.437231226}, + {0xdfe25a7fc29a0151, "vzj5nzy2m80p", 84.94495992, 86.08670001}, + {0x76030b81da6f3775, "fs1hr0fuewvr", 68.25200018, -65.782198982}, + {0x977dfeefc9bcf811, "kxyzxvy9rmw1", -0.056060606, 32.34200929}, + {0x413e3a5426de7235, "84z3np16vtt3", 15.683485297, -169.539055932}, + {0x3d289c8f18a4a49b, "7nn9t3ssnkk9", -10.977378722, -35.622207718}, + {0x92de9b54f71bcb67, "kcg9qp7r3g5q", -34.895436194, 38.940089649}, + {0xe1f3403615b9d701, "w7tn0ehpr7ch", 20.759932393, 108.309028314}, + {0x6b15492b5cb09be0, "edbnkbuwq2ey", 16.572579262, -22.284806638}, + {0x6d5ccb7bafde817a, "epfdqyxgvu0r", 44.025629761, -41.176853731}, + {0xb79c52d915d873fe, "qyf55q8pv1tz", -6.466860965, 126.705544817}, + {0xa128734329acb544, "n4n76ht9pkun", -78.153793444, 98.884822749}, + {0x406abdc6f60bec56, "81pcvjrq1gq5", 5.962504302, -168.87179631}, + {0xba22331ea654ad87, "r8j367p6bkqs", -44.763298263, 164.991665351}, + {0x2b22219473e7f5fa, "5dj2353mwzuz", -78.687214984, -15.071229199}, + {0x69e795c1ccbd8404, "e7mtchfdrq20", 19.318504578, -25.967935245}, + {0xa98399c0503b3b86, "p61tmh2h7dxs", -77.803058736, 148.579261949}, + {0x7ac589a42e65284a, "gc2sm91fdnn4", 52.784275175, -10.302678391}, + {0x01d3dae70b518481, "079xptscb628", -69.051506288, -166.304500612}, + {0x847260a5598303d1, "hjt619bthd1x", -58.700454182, 7.449612302}, + {0x1452cebad176e7a6, "2j9dxfqjfvmu", -13.609671248, -177.541683234}, + {0x0293643f347fdf66, "0b9q8gtngzgq", -86.024494417, -144.452196679}, + {0x10a671d9450d2cab, "22m73qb51nqb", -42.984833495, -161.312203621}, + {0x1d7b65c92790320e, "3pxqck97k0t0", -1.600696637, -124.74786059}, + {0xbd4cf1df395426b8, "rp6g3rttbhmc", -3.605252183, 138.929680175}, + {0x8cde6bea74bcdf0c, "jmg6rumnrmgh", -57.236291726, 61.168070476}, + {0x555b5d2c3c17d81c, "bpepuc1w2zd1", 88.556411233, -175.570414713}, + {0x0d5a85eab98e5476, "1pe8cuptjtb7", -47.657801647, -129.990684512}, + {0x29823ade9c926dbc, "5613prnwk9qv", -78.534686061, -31.664455249}, + {0x9c0927398efe4100, "mh4kffdfzt0h", -21.650806589, 48.289010577}, + {0x11af531f421ecadc, "26rp67u23v5e", -31.048666246, -158.801456524}, + {0xbdbcfac8cb98568e, "rqygpk6cm1c8", -6.480336238, 156.064784418}, + {0x816787a30360e995, "h5msg8s3d3nt", -70.880778641, 7.894125393}, + {0x4bb650e5be0271cb, "9fv51tey09sw", 16.027345234, -94.147654386}, + {0x2c5617b7438860a0, "5jc1geu3j1hb", -57.327856382, -43.433883971}, + {0xab3fdd3b93cde3aa, "pdzxufwmtrju", -73.154230042, 168.264431616}, + {0x365fd9dd6d595a38, "6tgxmrcec5e3", -11.338601213, -62.345304664}, + {0xefbef4a2a6e84cfa, "xyzg98p6x16g", 38.584420771, 179.724616212}, + {0xa57c10d14ac28c0c, "npy11nbbsb60", -46.193302609, 98.482682031}, + {0xf49908f76bee1558, "ykdhjxvcxsbp", 71.058481196, 104.312417746}, + {0x0041e13de3b6fc0e, "010y2gg3qvy0", -83.255522302, -178.907592322}, + {0xbcf1443118c563c9, "rmsn8d8sspjw", -12.905377851, 151.897838516}, + {0x6457788f182ff38a, "djcrj3ss5zts", 33.583186065, -88.005126209}, + {0xaa0852f74b00268e, "p8455xuc00m8", -89.429911121, 160.472839412}, + {0x5e7344e063690708, "cttn9s33e43h", 77.103694319, -105.400985114}, + {0x40f621978f6ae41b, "83v235wgeck1", 9.907529142, -161.313805843}, + {0x1e93c87c15ec0359, "3u9whz0pxh1p", -18.593050204, -98.931582829}, + {0xf353d945fd435361, "ye9xkjgx8e9q", 65.994807066, 114.789985964}, + {0x317fbff030dbb27c, "65zvzw1hvft7", -22.861719438, -78.770345335}, + {0xb34ff60c5ddd68ca, "qe7zd32xvpnd", -25.392155879, 117.87322471}, + {0xa1bdd9cba11efdb2, "n6yxmkx13vyv", -73.231870397, 110.631035215}, + {0x586d65c78b6d0d2f, "c1qqcjwcen6k", 53.248277687, -126.157590376}, + {0xdfa5b037820d4663, "vykv0ew21p36", 81.054421607, 85.460287928}, + {0x83a9a927e80ceffe, "hfnuk9z81mrz", -77.993287196, 43.450305435}, + {0x1060c886ab14988d, "21hdj1pc2kd8", -39.017705458, -173.441491119}, + {0x0e79b2af80d626f7, "1twv5cw0usmg", -58.175201828, -102.834576916}, + {0xe886040d528f87c8, "x23083bkjy3w", 1.504474877, 147.667828631}, + {0x46a9a98b81c78dfc, "8unum2w1sy6z", 23.250109197, -136.518652094}, + {0xc4406a02e0476efb, "sj06n0r08xrg", 28.478061086, 0.624879516}, + {0x7e04d5f56fdefad2, "gs2ecxcgvvxe", 69.60867545, -21.72829686}, + {0xbd89a3e59f05b533, "rq4u7tdz0qum", -10.471478527, 150.274789216}, + {0x70653c925414ed34, "f1kmt4kn2mqm", 53.01150849, -83.798198618}, + {0x74a7371a2f9ba5b4, "fkmmf6jgmfkv", 69.928549415, -71.260160916}, + {0x46541dc213ddd018, "8tb1vhhmvr81", 32.674367304, -157.274211462}, + {0xeb3586ecaa7f1c13, "xdusev5bgwf1", 16.287396627, 163.998222773}, + {0xe0b738e95b89fb00, "w2vmjubvj7xh", 5.124626564, 108.886762713}, + {0x46020e6457a1cd47, "8s10wt2rn76n", 22.617949969, -155.807491087}, + {0x1ea50ac52ba1f64b, "3ukhpj9cn7v4", -20.360220417, -95.314704315}, + {0x704363be0580fc73, "f11q7gh5h3y7", 51.740637224, -88.071708495}, + {0xc9b2bef95eb9f09f, "t6tcxybyr7s9", 14.364342723, 64.677861123}, + {0x65970914664d89fc, "dqchk5369q4z", 38.734135707, -77.166194584}, + {0x3b57895851b258be, "7ecskq2jq9dc", -23.123937885, -20.203574458}, + {0x5e8d2dfec0d9d69a, "cu6kvzq0v7c9", 69.781185413, -97.824784579}, + {0x4c97be7abf81dd93, "9kcvwypzh7ft", 27.719728502, -120.981602729}, + {0xc685a3c640bdccda, "su2u7jk0rr6e", 24.682241751, 34.94233136}, + {0x6be77083af265f07, "egmr10xg4tgh", 19.515011978, -3.812487762}, + {0x64b423b8b7e039b1, "dku27f5rw0wv", 26.774972677, -72.603912784}, + {0xf8971d3539d55a97, "z2cjue9tupe9", 50.24974586, 147.85624033}, + {0xf98fa4c4c3d59218, "z67u9j63uq91", 58.476440197, 151.570656618}, + {0xa9813ff43ed7dd8d, "p60mzx1yuzfs", -77.699604569, 146.933762975}, + {0xd4612fcd8eb1e57b, "ujhkzmdfq7kr", 73.990578242, 6.299216752}, + {0xdcfcb4ca0b6a36f5, "vmyc9khce8vg", 77.629656708, 65.803800234}, + {0x2cfd565991f59af2, "5mypdqdjyqeg", -56.301162616, -25.210603106}, + {0x5171a40528e54df1, "b5su8198wp6z", 65.486881343, -173.317991349}, + {0xae7cdcc8e35e92e3, "ptyetk73cu9f", -57.017344731, 166.875878696}, + {0xc73dcb853ebcb8b3, "swywr19yrkwc", 39.076719831, 31.950965802}, + {0x0f3d497ea6d6c240, "1wynkzp6uv14", -50.893665795, -103.843598212}, + {0x8cb21a3f5523133b, "jkt1ngup4d9m", -64.489903427, 63.583483249}, + {0x009a2dc551bf1c7e, "02e2vjbjrwf7", -87.023140326, -163.959635787}, + {0xe7c87574af234f4a, "wz47bx5g4e7n", 40.073165276, 126.941296599}, + {0x47b19aae0c6c7fc1, "8ystpchdejzw", 37.44736047, -139.574980179}, + {0x9f88db3a6581af12, "my4eqfm5h6rj", -10.665823422, 82.569314181}, + {0xd2a99eb941e50889, "ubntxfb1wn48", 45.982192807, 43.231462262}, + {0x28e24ab61cf4c31f, "53j4pehwym1j", -84.005771503, -26.382710476}, + {0x3e928a8159ddd5ff, "7u98p0btvrbz", -19.682350179, -8.832094531}, + {0xdd089d8851dbbcee, "vn49v22jvfyf", 79.059992733, 48.746593993}, + {0x24317b04421ab965, "4hsrq1223bwq", -63.406172728, -83.759338781}, + {0x4da70dba4039304a, "9qmhvfk074s4", 36.003627256, -116.460436858}, + {0x00ffde5ab3dcf1a0, "03zxwqpmvmsu", -78.803914391, -157.918278692}, + {0xbdb045117147772b, "rqs4b4cj8xvk", -7.938008909, 151.876402092}, + {0xd949b1a7187a2a1e, "v54v39ssg8p1", 62.806906967, 48.939445144}, + {0x7fc6d4d46ddb8dc3, "gz3e9p3evf6w", 86.436966699, -9.094369496}, + {0xd7a24f944cd35c18, "uyj4z52duef1", 79.251743604, 41.089749867}, + {0x9fa9a825cb33ada7, "mynuh9fc6fqu", -10.537034509, 88.443845142}, + {0xcced6070b889038b, "tmqq0w5sj41s", 30.619594592, 65.066058527}, + {0xc671728cc0c98125, "stsr5360t60k", 32.174932569, 28.622357009}, + {0x65d02e1b5354b48b, "dr82w6umbku8", 42.291509429, -78.11792578}, + {0xe46e79cbd23571ed, "wjr7mkyk6psy", 30.129402532, 100.434700872}, + {0x2a2e2d4653e3e2a9, "58r2ujkmwgjb", -88.432114667, -12.122772173}, + {0x86af9eaf3b32911e, "hurtxctv6b8j", -65.117808552, 44.645465894}, + {0xec9783f94c211bc7, "xkcs7ybd44ew", 27.503257036, 148.524947219}, + {0xd5baff01d45234fa, "uqxgy0fnb8ug", 82.226962817, 22.414884081}, + {0x1e3991ca70c54036, "3swt3kmhsp03", -18.740492184, -103.297404742}, + {0x4229221c05d94e45, "88nk4705v574", 0.720266129, -148.611841933}, + {0xb0f4ae3f82909de6, "q3ubwgw2k2fy", -35.049085233, 108.235087598}, + {0xc069ee0f9b67a4e2, "s1nyw3wvdykf", 6.776795909, 9.77623994}, + {0x044a97c2b5e00f1d, "0j59ghppw07j", -61.544120349, -174.936414298}, + {0xc51fd8582f2f2262, "sngxhq1g5wj6", 39.232725256, 5.111203169}, + {0x5db84b674c56ed2e, "cqw4qtudbvqk", 81.990094502, -115.020634785}, + {0x547a976bc82e3d33, "bjx9fuy85sym", 76.271236093, -169.323190329}, + {0x98bd95ccdbdcbeec, "m2ytcm6vvkzf", -39.740655716, 65.449569104}, + {0x99bf985fb66422d2, "m6zthrxqdhje", -28.610007231, 66.993692273}, + {0x060141feee4833d9, "0s0n3zrf90tx", -66.361108671, -157.412402172}, + {0x01915b972ed5bf2c, "068pr5tfuqzk", -74.643350817, -168.434294036}, + {0x35f8f82da6e7c54d, "6rwghce6wz2n", -2.276447292, -69.044319179}, + {0x1d0356206078f77f, "3n1pd830g3vr", -9.93012788, -133.482366615}, + {0x6c4b58509f415ba9, "ej5phn4z85eu", 29.389734691, -40.601686942}, + {0x28b1d9e3609fb859, "52sxmsv0myw5", -85.886915075, -27.173054266}, + {0x99f6f4d51cc5319c, "m7vg9p8wsnst", -23.248665279, 64.380747396}, + {0xc35ec1601bbefb89, "segd2s0vrvxs", 21.512161919, 27.445220512}, + {0x8750f118d4629a03, "hx8g266ndbe0", -47.227732776, 23.568542575}, + {0xc5ca00edc0ce43d6, "sr501vf0tt1x", 39.406699605, 15.548639062}, + {0x891a2cb80e2e4bdf, "j4e2tf0f5t5x", -75.83825157, 49.824183238}, + {0x1c68b62b2e3ddf54, "3jncdbtf7rgp", -16.608152437, -125.378922901}, + {0x7fbcd378cd1c3db3, "gyye6y6e3hyv", 83.574955616, -1.985040044}, + {0x387afe2862451078, "71xgwb328n87", -35.945771101, -33.803214498}, + {0xf2e5ff885ababc47, "yckzz22urby4", 53.395662051, 130.749661711}, + {0x4a30c4f7a77cc84c, "98sd9xx7gm44", 3.293822828, -106.095858339}, + {0xb7e25038c0873c81, "qzj50f60hwy8", -5.085260345, 130.817157452}, + {0x52596fb3f9f9feb0, "b9dqzdztz7zc", 54.640136978, -153.995726422}, + {0xf944131bd7de7f76, "z52166yrvtzr", 63.517445269, 135.10770592}, + {0xaaa220f8341063b6, "pbj21y1n21jv", -89.965961822, 176.211097927}, + {0x5c184b5eb206990c, "chd4qrpk0udh", 70.747171532, -131.902850359}, + {0xa7fbb0f3771f2f66, "nzxv1wvr3wrq", -46.895235703, 134.721639499}, + {0xc78d819abe7791d9, "sy6s36pyfy8x", 35.915501456, 37.331318534}, + {0xcd423781546b1193, "tp13g0bned8t", 39.687867156, 46.889799435}, + {0x6753a801fc065b49, "dx9uh0gw0ten", 42.895805434, -64.85844902}, + {0x2da523e3c20b17ad, "5qkk7sy21dcu", -54.070571608, -27.61097891}, + {0x5d45419ebf911bf8, "cp2n37pzk4ez", 86.897621453, -134.934248474}, + {0x3a90ee00676ce511, "7b8fw037emkj", -41.746043699, -9.92977914}, + {0x1c2c3484d4e789c7, "3hq3916nwy4w", -20.822062524, -126.163952483}, + {0xa4510b8a46b16533, "nj8hr2k6q5km", -58.313651836, 90.324703286}, + {0xa8ac8c02b0a30819, "p2q8s0phnd41", -88.50516176, 155.576301116}, + {0x0081b0cd5921b44c, "020v1mbt46u4", -89.088634576, -167.639592091}, + {0x56e9518527b58948, "bvnp3197qq4n", 74.408244931, -137.766566985}, + {0x48973d3490cd7210, "92cmue4htpt1", 5.246784571, -121.791465649}, + {0x66a028b193578795, "duh2jddmby3t", 22.514754485, -50.028614101}, + {0x04b52e02d70b2402, "0kukw0qr1dk0", -62.487647383, -162.501112666}, + {0x68c1456b1fe39b6a, "e30nbuszwfeq", 6.837544557, -33.710223525}, + {0xf1a2b8d1905ac8fb, "y6jcjndhcc4g", 56.462308121, 109.558489701}, + {0x6378ac4f51050cb3, "dewbsmuj0n6c", 19.807868384, -57.815547958}, + {0x6967007f50ee922a, "e5mh0zuhxu92", 19.027743645, -37.929963314}, + {0x381bb7825f198413, "70evg0kz3621", -41.174127672, -39.588149652}, + {0xa1d8517ee0b0bf6f, "n7d52zr0q2zq", -69.701337387, 104.105350288}, + {0x42f0ed2ad9dc80e7, "8csfubqtvk0f", 8.923280716, -139.352419412}, + {0x7cd6aa93c59bdc95, "gmcbp4y5mgf9", 77.359434551, -30.972955442}, + {0x9d5b112984075c47, "mpej2bd40xf4", -1.886516484, 49.254479852}, + {0x720a8b97b2d6d2ef, "f858r5xkuv9f", 45.064016751, -62.260344998}, + {0xc9cfa0923b9cd234, "t77u14jvmm93", 18.996292311, 61.575506283}, + {0xb6e167e4ab8cf661, "qvhqgt5cjmv6", -15.660807836, 129.885753342}, + {0xdc975e0759d4c088, "vkcpw1utum08", 73.047736326, 57.926284978}, + {0x1c80ed8d0ea0c4a1, "3k0fv38fn32b", -22.008011724, -122.463300358}, + {0xd8797319f6bd44ed, "v1wr66gqrp2f", 54.728132528, 53.892704303}, + {0xca86e7acfbd8b0f1, "tb3fgc7vv2sg", 1.897505203, 81.381113978}, + {0xd0f601387562d079, "u3v02f3pdc87", 54.901351007, 18.315700821}, + {0x6c724fb8798a7cd4, "ejt4zf3tj9ye", 31.434126353, -37.625869088}, + {0x760604ecafdd7343, "fs309v5gvptn", 69.022291478, -66.011474495}, + {0x9830dbfa2ad1ee70, "m0seryjbu7r7", -41.583105233, 51.676789737}, + {0xde447be72eaea4ed, "vt27rttfpukf", 75.133118068, 68.189387965}, + {0x33b793180a5941eb, "6fvt660bc50y", -28.597266188, -48.415673892}, + {0xe1e6f3cb6d97c1f0, "w7mg7kvekz0z", 18.879228256, 109.486517784}, + {0x49b01f7d6ae39f1a, "96s1yzcbwfgj", 14.412790637, -117.82567189}, + {0x3316b6df9f32e61b, "6dccerwz6cm1", -29.22512793, -64.886845486}, + {0x7a15c0ed0388c726, "g8bw1v83j33k", 50.303829037, -21.719400277}, + {0x9437d4b61dda8eef, "khvx9ehxvb7f", -16.945078948, 7.806724897}, + {0xaf998dfa9a578e27, "pydsvynuby72", -52.56873102, 172.527616959}, + {0x96895d4ddfa6ce90, "ku4pumfznv79", -21.104879648, 36.753338546}, + {0x2e6a8099ca1423c0, "5tp816fb2hjw", -61.859828398, -11.894409357}, + {0x4ebc8aac62a8efd6, "9uy8pc32p3rx", 26.725624421, -91.766760935}, + {0x36fe31339eff02d2, "6vz32dwyzw1e", -12.421599105, -46.023121151}, + {0x270033a8fdf9f5f9, "4w037b7xz7uz", -56.027548425, -66.978518307}, + {0xebbe9c4b5eed1124, "xfz9skuyxn8k", 15.759665839, 179.490466492}, + {0xee6ed884484027cb, "xtrej12880mw", 30.065548433, 168.267295839}, + {0x2485fb76a5f3bdbb, "4k2zqxp5yfyv", -64.780224387, -77.399755922}, + {0xdd2588802584744e, "vnksj015hju4", 80.859921273, 51.549402156}, + {0x2df4e23175312465, "5ruf4dcp64k6", -46.03832421, -26.95898434}, + {0x9f7e206f6b5d1af1, "mxz20vvccneg", -1.374328478, 77.736215923}, + {0x5ef5f8dc92144a1f, "cvuzjr4k2j51", 78.613427597, -94.336505417}, + {0xa909f84de616e6fc, "p44zhmg62vmg", -77.487530532, 139.058458405}, + {0x5956b85afc1d5057, "c5cchqrw3p85", 66.3049773, -132.341952167}, + {0xf47f9dd35b53fd37, "yjztvnuvbgym", 78.392579185, 100.773165423}, + {0x6d796a16098ae1ae, "epwqn5h9jchu", 43.258844806, -35.940832355}, + {0x89c9949a058bc194, "j74t96h5jg0t", -72.146684776, 59.826297304}, + {0xf9d1ae09d9c48417, "z78uw2ftsk21", 65.483602742, 147.582961442}, + {0xe6ad8d0ef8358f03, "wuqsu3rs6q7h", 24.748841229, 133.087792941}, + {0x63c4b84f92a8a13e, "dg2chmwkp2hm", 18.487931249, -54.999644723}, + {0x2a2b85d576cf4603, "58pscpcqtx30", -89.121312303, -11.907225546}, + {0x92024695200caa5a, "k814e5901kp5", -44.5412988, 24.039522575}, + {0xf1847d250ab6680d, "y627u98bqtn0", 58.323739327, 101.800633553}, + {0xc19860cc2b2b9023, "s6d61m1c5f82", 14.441713466, 14.471558833}, + {0x3cadf8771d272390, "7kqzhxsx4wjt", -19.82084783, -24.053764081}, + {0x112aa8615d9e1e76, "24pbhsbxmsg7", -33.722636841, -168.902881409}, + {0x8690fa3e5e82a40c, "hu8gngkyhbk0", -64.141272071, 35.10805094}, + {0x296ba2c6072c8c82, "55pu5jh75k68", -72.393871381, -33.963747131}, + {0xe505f087535587ce, "wn2z11umbq3w", 36.397353461, 91.104482341}, + {0x2509425a1fab1f76, "4n4n4qhzpdgr", -55.161135839, -87.081773601}, + {0xd31ac1fe07a4fe6d, "uded3zh7nmz6", 59.497006264, 27.504893955}, + {0x09ce479d626e95fa, "1774g7c2eubz", -71.21463971, -119.386548919}, + {0x0aabc854d82873e7, "1bpwhp6s51ty", -88.904791225, -90.523773913}, + {0x95bbf9abd62be7e8, "kqxzmbyq5gmy", -7.15791765, 22.409875005}, + {0x870717c7ade1b310, "hw3jgjxew6tj", -53.80218032, 24.048660583}, + {0xae9923281d6ea24f, "pudk6b0xeuj4", -63.939120294, 172.035766682}, + {0x95ac75f8d65c511d, "kqq7cy6qcj8j", -9.149048849, 20.119099822}, + {0x44c0012d0abe4dad, "8m002c8brt6u", 28.177256934, -168.715685261}, + {0x8b75feb8f7e32703, "jeuzxf7rwdmh", -67.574230729, 74.525012682}, + {0x88f5b41d73788a4d, "j3uv87cmg254", -79.167866222, 62.942534144}, + {0x48c39e5d855457fe, "931twrd5bjcz", 6.633671458, -121.363217561}, + {0x3ed4a2ddcc1de240, "7vbb5rfd3rj4", -12.613253851, -10.048996623}, + {0x91bc9bdb32eadc70, "k6y9rqtkxcf7", -29.275035502, 20.716773926}, + {0x3dde0acfa9ecfe47, "7rg0pmx9xmz4", -1.375748758, -29.202026117}, + {0xd8cdc5bfd1af061d, "v36wcgyjpw31", 53.239264955, 59.851092489}, + {0xc3d3c052a59fa009, "sg9w0np5myh0", 20.775736639, 35.869240338}, + {0xc49a147717ae8f93, "ske18xsrpu7t", 25.618595945, 15.496896559}, + {0x0007fa932d022662, "003zp4te08m6", -87.349031664, -177.223863626}, + {0x529f6af566e3e6f5, "bbgqpxc6wgmg", 50.316456758, -141.348083649}, + {0x36cd9c4105df631e, "6v6tsh85vxjj", -14.476551678, -52.558350683}, + {0xab0b79fc47f1cfbc, "pd5rmz27y77v", -77.435098388, 162.323614843}, + {0x39bed91850186f99, "76zek62h31rt", -28.946864157, -23.016304219}, + {0xc172daffec09c008, "s5tepzzd1700", 20.257775219, 8.085304703}, + {0xc0b498d9bc4cb92a, "s2u9jqew9kwk", 4.431377428, 17.813706757}, + {0x4ca8417b645cdf31, "9kn42yv4cmgm", 22.933090469, -115.272616513}, + {0x54d381bbe684fffc, "bm9s3fz6hmzz", 76.700051974, -166.553571364}, + {0x4cde0f6de81c8856, "9mg0yvg83k45", 32.507236617, -119.229756202}, + {0xb3b0e4626edadb3c, "qfsf8smfvcem", -30.474219876, 130.459807886}, + {0xf68079b776b0cd5a, "yu07mevqq36p", 68.092969199, 124.350742692}, + {0xd9e71b210b7d8ab2, "v7mjq88cgq5c", 64.207180914, 63.568069106}, + {0x6a1f4685fe696956, "e8gne1gye5np", 5.372074342, -18.144127304}, + {0x59f1de0a89f9072f, "c7sxw2n9z43k", 66.00617269, -117.137988005}, + {0x2f98b381a3ef88cb, "5ydc70e3xy4d", -53.214736783, -7.246177152}, + {0x6ad41380a505a1cb, "ecb170550qhw", 10.064024785, -11.114027166}, + {0x09913a8b55de990d, "168mp2upvudh", -75.053118541, -123.074091148}, + {0xd4fd2dfca4cf071b, "umykvz54tw3j", 78.217626672, 20.296105618}, + {0xb55e35c441ea134a, "qpg3cj21x89n", -1.069532518, 94.614581286}, + {0xe44c9dcd8b12d4d7, "wj69vmdc2cbe", 29.869304207, 93.750147439}, + {0x6f073afd6ef7a3e1, "ew3mpzcfyyjy", 36.078233599, -20.39891064}, + {0x0592f21396c5177d, "0q9g44wqsncr", -52.895270024, -166.192415264}, + {0x237d7d01ac746047, "4eyru0edfjh4", -67.540699344, -58.530258441}, + {0x1042e63238e3b345, "211fddjswftn", -38.923773526, -177.421349652}, + {0x1d73d51af0d9c887, "3ptxb6rhv748", -1.437004044, -127.244796829}, + {0xd97f75c848070c66, "v5zrck280w66", 67.479434615, 55.250955416}, + {0x31d81c3618e3981e, "67d1sehswfd1", -25.031562487, -75.733273602}, + {0x5ea9926efb2e6342, "cunt4vrv5tjn", 68.408631537, -91.977725354}, + {0xbd9436eb896c90bc, "rqb3euw9ek8c", -6.742576966, 146.775424485}, + {0x2633ca477e4edd3d, "4stwnjvy9vfm", -63.600079169, -59.493978414}, + {0xce6f2160cb12de5d, "ttrk2s6c2cg5", 30.301890867, 77.721098091}, + {0x2bb9adc734bdcd18, "5fwuvjtnrr6j", -75.071214038, -1.530905451}, + {0xcfbc4452c8d7bd7c, "tyy48nq8uyyr", 38.442701626, 87.196638829}, + {0x4b7c0ce24439cc50, "9ey0tsk47765", 21.205390784, -103.815165448}, + {0x130c4704f5c2ce8a, "2d64f17psc78", -31.852190916, -154.595280656}, + {0x70c2697ace3e746b, "f316kyqf7tu6", 51.055257584, -76.774021384}, + {0xe95aa666f8c5f17b, "x5ebdtrssrsr", 19.805043693, 140.393788851}, + {0x7a681de73eed7767, "g9n1vttyxpvq", 50.963988107, -13.812596797}, + {0x5743824997c138fe, "bx1s4kdrs4wg", 85.104145714, -155.288477126}, + {0x6b7f4058a73dc049, "eezn0q577r04", 22.181992345, -12.640660831}, + {0x893478449cf6cec0, "j4u7hj4wyv7d", -73.975253202, 51.1560714}, + {0x42893cd8437214da, "8b4mtq23f8be", 1.001434099, -142.854773}, + {0x911fbe43d333eb76, "k4gvwhym6gpr", -28.537442002, 5.545820346}, + {0xf3271de69e00353b, "ydmjvtny00um", 58.695492907, 119.782224939}, + {0xc33d08337d05f95a, "sdyhhdvx0rwp", 16.188224246, 31.142826045}, + {0x15e3852c30e60f0b, "2rjsbc1hws7h", -4.783749894, -160.981009482}, + {0x42c99c42fb4999db, "8c4tshrv96dx", 6.616101611, -142.547893488}, + {0x547920253d6e0b72, "bjwk099xes5r", 76.650174864, -171.186750213}, + {0x57bf055e915f6b21, "byzhbrnjcxpk", 83.843188403, -136.386949772}, + {0x49a3de197b64fcb3, "96jxw6cvdmyc", 12.584440781, -115.738457831}, + {0xe8518857be9869d8, "x18shpxym1nx", 9.182903182, 135.889772736}, + {0x10e099c0a9aba9ef, "23h9mh59pfny", -39.133117234, -162.197000015}, + {0x934ddab617494703, "ke6xpehr953h", -25.470496125, 26.351095707}, + {0x232b11d8728d0f4d, "4dpj3q3kjn7n", -77.792094828, -57.599382973}, + {0x5e6308583ff6e778, "ctjhhq1zyvmr", 73.862443721, -105.279282239}, + {0xf1e83dccf8711009, "y7n3vm7sf480", 62.212285171, 110.274667977}, + {0x232e687a369bf62b, "4dr6hyjqmgv2", -76.958135441, -57.088483416}, + {0x9746a950f16178f3, "kx3bkn7jd5wg", -4.139508914, 25.140933635}, + {0x80aa7e7898c40260, "h2p7wy4ssh16", -89.35101233, 21.745549982}, + {0x598bae2715f9c79c, "c65uw9spz73t", 57.050604133, -118.185111483}, + {0xf20842a3db5c39c3, "y84458yvchww", 45.356694618, 115.475630086}, + {0xd47db919e64c1d4b, "ujyvk6g69hfn", 78.282163481, 9.683464192}, + {0xdeac35198efdc34d, "vuq3b6dfzr1n", 69.22811088, 87.554138036}, + {0xc5d65439618c3b48, "src58fc1jhxn", 44.224287415, 12.690806422}, + {0x619ea0cf1cce16ac, "d6gb1mswtscb", 15.500103807, -73.415212452}, + {0x186c1fdc39cdf4cd, "31q1zr1ttrud", -37.621694476, -126.241605031}, + {0xd3ce5d9fc248c618, "ug75v7y29331", 63.961121759, 38.208101181}, + {0xba216eb5a958818a, "r8hqxee9c20s", -43.837894711, 163.811013453}, + {0xed367e09bbc20532, "xnv7w2evs82m", 38.587677298, 142.662814085}, + {0xf9d0aad0ebde17ee, "z78bpn7cvscy", 64.722159254, 147.617693173}, + {0x1d9c927439ff8818, "3qf94x1tzy41", -6.815997177, -120.122116259}, + {0x2d5708e4dddb876f, "5pchjt6xvf3q", -45.672942281, -43.348368848}, + {0x90618a6f5225ada2, "k1hsnvuk4qqu", -38.639568444, 6.630698045}, + {0xb22ee9087cdb2296, "q8rfk23wvdj9", -43.195699168, 123.58750852}, + {0xafbb84f332c44161, "pyxs9wtksj0q", -52.609978458, 179.370174806}, + {0x6a1c8e21950e3a20, "e8f8w8dp1sx2", 4.310612985, -18.695909714}, + {0xd2da810195c2b315, "uce820dpsbtj", 53.485483042, 38.674832184}, + {0x65dbbb502f8538e1, "drevqn1ghnwf", 43.143860804, -73.210306585}, + {0xbe2d7c1554f9f778, "rsqrs5bnz7vr", -19.75361122, 166.465162114}, + {0x9c9f255b9ba28d7e, "mkgkbqwvnb6r", -17.409722587, 60.840861526}, + {0x23984ebbf59e0ea9, "4fd4xfzpms7b", -75.48167327, -53.087069534}, + {0x069d19726afa033d, "0ufjkwmbz81m", -62.323936438, -143.23152591}, + {0xc26a189c131dcee4, "s9p1j70m3r7f", 5.818201288, 32.574864914}, + {0xbd8845b58b61027a, "rq44cedcd417", -10.747106737, 149.13228232}, + {0xb3aba56b54d2383a, "qfpubuunu8w3", -32.887784953, 134.687091445}, + {0xb1340f3cf9290b8f, "q4u0yg7t545s", -29.380691416, 95.926571122}, + {0xd4277351de6f59f6, "uhmr6nfyexdz", 70.21890023, 7.4746337}, + {0x4bc6dfe35dc4d871, "9g3ezsuxsmd7", 18.967839761, -98.804669866}, + {0xc702c929d7994cd8, "sw1dkbfrm56e", 34.150890181, 24.821423239}, + {0x12a3a742f246ba6c, "2bjufhrk8ux6", -44.140896078, -138.066173433}, + {0xb8bf4bfa90a7bdec, "r2znrynhnyyy", -39.648934225, 156.442864854}, + {0xd0bd9bfc1ef6704a, "u2ytrz0yyts4", 50.181242272, 20.732515732}, + {0x2ff7887ce258ca02, "5zvshz72c350", -45.66316541, -3.302363795}, + {0x2ab0ea0f06ad8402, "5bsfn3s6pq20", -86.827320864, -4.289506547}, + {0x983b09f35469fb10, "m0xhmwune7xj", -41.402215412, 55.091091268}, + {0xe5bc4464ca53f43a, "wqy48t6bbgu3", 38.437191279, 109.713287419}, + {0xf99becd4b270b4f4, "z6eytp5kf2ug", 60.244350293, 151.747730236}, + {0x256adba7ca9aa55e, "4pper9ybmbkp", -50.044052383, -79.114013}, + {0x4b7cd6bb2ea8d3c6, "9eyeeftfp39w", 21.723069748, -103.186370382}, + {0xc0bc7962da443d1f, "s2y7ksqu8hyj", 4.814183959, 20.246094434}, + {0x6119edf8cfe471e1, "d4dyvy6gwjsy", 15.283987095, -85.876086468}, + {0x43fb0a835c3ed7fa, "8gxhp0uw7vcz", 20.395849811, -136.092289463}, + {0xc0bc98e0ca9fa85c, "s2y9js6bmyn5", 4.41795292, 20.636355767}, + {0xe4c1e30bc1b9cc9d, "wm0y62y1r769", 29.227985014, 102.412118932}, + {0xee9997d70b6e4797, "xudtgpscet3t", 26.364727579, 172.404136432}, + {0xae0c866b5b69c9b6, "ps68duuve74v", -65.978805663, 161.143141012}, + {0x02b63ba99e61e2e9, "0bv3rbdyd7jf", -85.557644582, -138.522730307}, + {0xc984e5c0b75aee08, "t62fch5rccr0", 13.162958184, 57.353180718}, + {0x83ba1e27778448ed, "hfx1w9vrhj4f", -75.662986155, 43.886779905}, + {0xdf53fc8f65799573, "vx9zt3v5g6br", 88.516132116, 70.19865682}, + {0xc4abe59628a06a14, "skpyc5j8n1p1", 23.70300856, 22.200202824}, + {0xe28f780de7a87df8, "wb7rh3g7p1yz", 2.646857354, 128.511852905}, + {0x4792224ae84f0641, "8y924kr89w34", 36.585965694, -144.382945078}, + {0x1a68f73feaa620e4, "39ngfgzbnshf", -38.695199058, -102.876034558}, + {0x6dbc32756c6a2b10, "eqy34xcde8pj", 38.187533272, -24.848854647}, + {0x3efc47ae252acf26, "7vy4gcj55c7k", -12.166835938, -2.640673188}, + {0xd3730d4c02041036, "uethum020h83", 65.549951182, 29.71836132}, + {0x18ab1d22496ceefc, "32pju8k9emrg", -43.987598269, -113.702166226}, + {0xdafd5485e92d016d, "vcyp91g95n0q", 56.171927429, 87.236381083}, + {0xb9d373956a1ca646, "r79r75cb3km4", -24.017420436, 148.142112163}, + {0xd111d1fed6bcdf52, "u48x3zqqrmgp", 60.377844432, 0.788927657}, + {0x2231a29da31b1c2d, "48su57e33df2", -86.464919467, -60.672959463}, + {0xafbc932665ad0b57, "pyy969m5pn5p", -51.804108479, 178.007659264}, + {0x345e7447f542a348, "6jg78jzp8bjn", -12.008141451, -85.42003532}, + {0xe62580fa8a881f49, "wsks1ynbj0gn", 24.642339085, 118.914517829}, + {0xecd20b2ecb48bc73, "xm90qcqc92y7", 30.988573861, 147.962212761}, + {0xda2ff7ecb9664834, "v8rzgv5tdt43", 47.796991735, 78.568149296}, + {0x52ed2dbca26d6e9e, "bcqkvg52epr9", 52.882815433, -137.203649666}, + {0x68f2838e81f80273, "e3t873n1z017", 8.487239435, -25.864260812}, + {0x70ff2cb8b8de92e8, "f3zktf5svu9f", 55.646591825, -68.296946092}, + {0x93569483862444ab, "kec990w64j2b", -23.63946, 24.661990521}, + {0x0139816eb3594d10, "04ws2vpmc56j", -75.1619585, -170.816414043}, + {0x057caf8048d2aa3d, "0pybz028ubp3", -46.272911693, -170.199294924}, + {0xe2ea021151e3f1c5, "wcp044bjwgsw", 5.641070246, 133.681936417}, + {0xc663677162d5848c, "stjqfwc2uq28", 29.348764693, 29.99457568}, + {0x861f3ac8ff048f02, "hsgmpk7z0k7h", -62.377772681, 27.39408077}, + {0xabc943c323b38342, "pg4n7ht3qf1n", -72.001421611, 171.701836889}, + {0x6a4fb3c350a55cbd, "e97v7huhnpfc", 7.980922607, -17.08897198}, + {0x8312c9a0dd74c770, "hd9dm86xfm3r", -75.539259165, 24.854605685}, + {0x88a56e4f5b9cc4e8, "j2kqwmuvmm2f", -87.418656451, 62.507970324}, + {0x1ea9c9a58e62a33e, "3unwm9dfdbjm", -21.392697349, -91.863773905}, + {0xd6db83d53a4c60f5, "uves7p9u9jhg", 76.726568996, 38.806163508}, + {0xe6c05eca398799eb, "wv05xkjthydy", 28.763101285, 124.07636443}, + {0xd6509a9c5ace59e0, "ut89p72uttdy", 76.131937056, 23.522998269}, + {0xda855217e7757511, "vb2p45z7fpuj", 47.658004334, 78.8479382}, + {0x92df1318217f804a, "kcgj6611gy04", -34.22207916, 38.069161445}, + {0xe0eb80f9a541ffc0, "w3ps1ye587zw", 6.364452541, 111.87791998}, + {0xc57694083e2553ea, "spv9821y4p9y", 43.858493097, 7.7478531}, + {0x8868d84df3dca804, "j1nehmgmvkn0", -83.815061958, 54.332089133}, + {0x02f45b18e4d816ff, "0cu5q674v0cg", -79.572124567, -140.346004702}, + {0xc72a1628f97d8932, "swp1db7tgq4m", 34.016065272, 32.469548778}, + {0xef6fe09144c5b27c, "xxry14b4sqt7", 41.851508346, 168.44257246}, + {0x9944b2d27bbd78b2, "m52c5nmvrpwc", -26.507694844, 46.194729487}, + {0xc5332b9332bd12c3, "sntkr4tkrn9d", 37.324068042, 7.697942036}, + {0xbc04d03915cae3f0, "rh2e0f8ptcjz", -20.551379336, 135.736338519}, + {0x39fd4b36d9debcb2, "77ynqeqtvuyc", -22.788751693, -25.017673148}, + {0x5eec0d19b4e86c8d, "cvq0u6enx1q8", 74.677941504, -92.6213032}, + {0xfa1de3678da93046, "z8fy6twep4s4", 50.348124032, 161.486283048}, + {0xccf4e9317bdc5a56, "tmufkdcvvje5", 32.755381175, 63.130063363}, + {0xce594d78d45acdb4, "ttdnuy6ncc6v", 32.159522092, 70.52406565}, + {0xe20f8613ec4071e5, "w87sd4zd81sy", 2.212810186, 117.520071747}, + {0x066c7dbe267be873, "0tq7vgj6ggn7", -59.79260211, -148.45087272}, + {0x65c3d95e5b54d479, "dr1xkrkvbmb7", 40.69025797, -76.447322974}, + {0x22d47f96f667d3af, "4cb7z5rqdz9u", -79.478060667, -55.580743269}, + {0x8296310c7408c3f9, "hbc3233n131z", -85.553620394, 35.520228358}, + {0xa1d46d925cd7b29a, "n7b6v4kwuyt9", -68.409298164, 101.827680393}, + {0xf94f247818f04ff7, "z57k8y0sy17z", 64.106048017, 139.604220813}, + {0x509bb68af0d73925, "b2eve2rhuwwk", 48.781521254, -163.323925673}, + {0x5adf11a1317310b9, "ccgj389jfd8c", 55.770348839, -96.963850245}, + {0xc335ac88bc4a583a, "sduut25w99d3", 16.260889678, 29.415274583}, + {0x83cc97b21a2d3539, "hg69gdhu5num", -71.399422729, 37.426091406}, + {0x13b891d83c88216d, "2fw93q1wj0hq", -30.683783332, -137.052162917}, + {0x7ee66caf5c1e64a9, "gvm6tcuw3tkb", 74.98141727, -3.608251157}, + {0xe086a1916d9f56b6, "w23b34cemxcc", 1.465901256, 103.757181226}, + {0xca091bb524140193, "t84jre942h0t", 0.942486254, 70.643463922}, + {0x6e3cabbb119fdeb7, "esybrfsjmzgc", 26.777371354, -12.661491247}, + {0x3ce66e03c77d59b9, "7mm6w0y7gpdv", -15.024492148, -26.094799908}, + {0xb707ece6bf08643a, "qw3yttpz11k3", -8.672497533, 115.213328671}, + {0x029db43bad4dde5c, "0bfv8fxe9rg5", -84.800077211, -142.339495992}, + {0xaee5a5e49ea4cbd7, "pvkuct4ynm5x", -59.605265128, 175.49965504}, + {0x5d5f6a3bbb0ed17d, "cpgqnfxv1v8r", 89.663059554, -130.122337953}, + {0xf0eb9d71a09ff0c3, "y3ptuwe0mzsd", 51.671531299, 111.99900169}, + {0xdadf57a94b68a38f, "vcgpgbbce2js", 56.210432874, 83.134722024}, + {0x55196e6bcb46a15b, "bndqwuyc8uhp", 82.731450842, -176.529999077}, + {0x84894eb0ddc549f9, "hk4nxd6xsp4z", -66.34373602, 14.395698654}, + {0x0cdfed7c68b7a9b7, "1mgyuz38qynv", -56.429825027, -118.265463514}, + {0x85a93a5fb5c34e89, "hqnmnrxpse78", -55.328590042, 20.323531776}, + {0xb294b0b3e02e960b, "qbbc1dz05uc0", -40.590337001, 124.880381107}, + {0xf863076e12bdb8b9, "z1jhfvhkrqwc", 51.488191398, 142.158256526}, + {0x253d2b4e2f66cdbf, "4nykqmjgdv6v", -51.256083901, -80.928260899}, + {0xb28a8a8e8e85f672, "qb58p3nfhrv7", -44.994121398, 128.999937778}, + {0xe9d3e7b2f3107c9c, "x79ygdrm21y9", 20.887293922, 148.874711342}, + {0x838e48d54bf4a4fa, "hf74jpbcykkg", -76.949291766, 38.189781432}, + {0x52303b1c688e983a, "b8s3q738jud3", 48.050103661, -151.246469255}, + {0xfb89f2dc78230c08, "zf4z5r3s4d60", 57.520992907, 172.762179844}, + {0x04fa6a4d064cdece, "0mx6nm869mgd", -58.680264646, -158.279627316}, + {0x0832fdb49d09146a, "10tgve4x14b6", -86.510624067, -126.668886486}, + {0x755c7a13805ed793, "fpf7n4w0cvct", 89.134987152, -86.56394861}, + {0xcb7c4b723e62a718, "tey4qwjydbmj", 21.523334787, 76.231165907}, + {0x2b3ed8096e5d976c, "5dzeh2cfcqcq", -73.999276984, -11.763898615}, + {0x8d0285f7ac3b745a, "jn18cxxd7eu5", -56.076558466, 47.185748545}, + {0x2ede234d549a6d65, "5vg26mbnm9qq", -57.579638259, -6.580566121}, + {0xcad67b9994d79eab, "tcc7r6dnuygb", 10.42996694, 80.829371777}, + {0x50b3d3cfe42200cc, "b2tx7mz4480d", 49.118843502, -160.863081525}, + {0x90f6ecd6b07b449a, "k3vftpphge29", -34.677511721, 19.56542828}, + {0xed1fcb415cce4af0, "xngwqhbwtt5g", 39.094618478, 140.186474625}, + {0xfd65e96886e28834, "zpkyku46wb43", 86.902284896, 141.891810399}, + {0x09eabb3bf1604006, "17pcqfzjd100", -72.889217633, -112.545232613}, + {0x8b8e74b6ca00f510, "jf779eqb03uj", -76.710652581, 83.395516277}, + {0x9b7eacbb764cdd78, "mezbtfvq9mfr", -23.802105335, 78.658389687}, + {0xacf2410fad9363e6, "pmt423xekejy", -58.658177336, 153.302736928}, + {0x13775deecc50e84b, "2evpvvqdb3n4", -22.514628365, -150.207119013}, + {0x9ee26940f90b8637, "mvj6kh7t1f33", -16.455275546, 86.313477197}, + {0x9d13fa4f92198b6a, "mn9znmwk365q", -7.176077535, 47.744237094}, + {0xfc9d9c9068d7ddd8, "zkftt438uzfx", 72.698077995, 149.987618945}, + {0x6aece4e234da5aa5, "ecqf9sjnv9eb", 7.493842681, -1.684786938}, + {0x55af7f7411d71a77, "bqrryx0juwe7", 81.558029396, -158.268845383}, + {0x62696fb4e78daccc, "d9nqze77jqqd", 6.829925176, -58.376650259}, + {0x2a22612d9689b3aa, "58j62cdqj6tu", -89.595208375, -15.080905299}, + {0xe13b7867b4c99dd9, "w4xrhtxnt6fx", 15.32431292, 100.402908276}, + {0x4904a1c00de54f47, "942b3h0ewp7n", 12.722808829, -133.900418862}, + {0x8fa1a911b562fcc8, "jyhuk4epdcyd", -55.487899557, 85.609715543}, + {0x8384cae7736bf1a8, "hf2dptvmegsu", -76.959638212, 34.790092096}, + {0xa80f75b04fc2811b, "p07rcd2gsb0j", -87.218483986, 139.637464586}, + {0x4c04b3d24bc13594, "9h2c7nkcs4ut", 24.160582044, -133.80677569}, + {0xf557cbc517be393b, "ypcwrj8rrswm", 89.723864782, 92.417666016}, + {0xd3cfb6952747d9c0, "ug7ve5978zdw", 64.267915004, 39.157025605}, + {0x3e0086bffcd5e5f8, "7s08egzwurkz", -22.39031121, -21.621591408}, + {0xb7ef04e1f9efda24, "qzrh9sgtxze2", -3.400657377, 133.664811468}, + {0xb39cf20d00668e42, "qffg4380du74", -28.995557539, 127.716186603}, + {0xe087416cb72756e6, "w23n2v5r4xcf", 2.533592257, 102.693781935}, + {0x03e8a23d2c2841be, "0gnb4g9d510v", -73.105424284, -136.634772916}, + {0x9a49bcec1afa8c24, "m94vtv0uzb62", -38.379920461, 71.62123869}, + {0x3de58e3324fdcd99, "7rkswdt4zr6t", -3.413489607, -27.129049331}, + {0xcdd8a96184b71c25, "trdbksd4qwf2", 42.25658716, 60.317970012}, + {0x8b3c4bd2d462aae1, "jdy4rnqndbpf", -74.100293715, 76.253485382}, + {0xa3ca9837575592c1, "ng59heurbq9d", -72.927253723, 128.875478159}, + {0x6d9ab2eb3043f57d, "eqec5uth8gur", 36.763794416, -28.304863059}, + {0xcb4d91c55ea30f4d, "te6t3jbynd7n", 19.236730432, 71.060883447}, + {0x1b7922ee0348356f, "3ewk5vh390uq", -24.581646526, -103.540257143}, + {0xc0d79761ce722bed, "s3ctfsfff8py", 10.881057948, 13.47313274}, + {0x6fe0526642a23cfa, "ezh54tk2n8yg", 39.931187425, -5.509011921}, + {0x99762d02ece2cd16, "m5v2u0rdwc6j", -23.772603988, 52.569185894}, + {0x17e3b38a69940695, "2zjv72m9kh39", -4.700537457, -138.013513432}, + {0x3cfe482442f58511, "7mz4h922yq2j", -12.29765788, -23.707884474}, + {0x7829c0e3a0ce9c08, "g0nw1sx0tuf0", 46.07951805, -35.783594361}, + {0x6db32a608844dcff, "eqtkns488mfg", 37.287714978, -26.078095055}, + {0x68e7d5015460e4a2, "e3mxb0bnd3kb", 8.398797072, -26.015521914}, + {0x1eceedf16f2acf60, "3v7fvwcg5c7q", -14.947750008, -95.732294673}, + {0x46587d27ece8279d, "8td7u9zdx0mt", 31.606724217, -154.127575883}, + {0x4afc729707ae5393, "9cy755s7pt9t", 10.390865198, -92.3229411}, + {0x6d5513fdf18cde3b, "epbj7zgjjmg3", 44.560062968, -44.830855262}, + {0x48a34cd16e759a3c, "92jntncffqe3", 1.180164175, -116.49651904}, + {0xab2d29c213882a7c, "pdqkmhhmj0p7", -76.573848107, 166.514850092}, + {0x2cbad3800a3bbf8d, "5kxe700b7fzs", -64.116152473, -23.07008791}, + {0x3d72a4681bf4f60d, "7ptb8u0vymv0", -2.701618673, -36.879798427}, + {0x462fc3d1af491cdb, "8srw7neg94fe", 25.041204531, -146.81609264}, + {0x796560b62ef3aca4, "g5kq1ejfyfqb", 64.352901256, -38.949324341}, + {0xe1eabb02e04a78dc, "w7pcq0r099we", 17.096194357, 112.421795717}, + {0xa2a8dd488f82bef9, "nbneuk4ghbzg", -89.318328098, 133.081383586}, + {0x17252b7b40ef85dd, "2wkkqyu0xy2x", -9.059480161, -151.220979968}, + {0x85ceee77e8da0ffe, "hr7fwxz8v87z", -48.736593482, 16.81962308}, + {0x2577db02ce3ff99f, "4pvxq0qf7zwt", -45.13003519, -81.992513509}, + {0xb663f638ff4d2b62, "qtjzdf7z9npq", -15.542960323, 120.711989732}, + {0x2edeb2fece22e880, "5vgc5zqf4cn8", -57.440294617, -5.802371107}, + {0xd8f04e1cd62b7ede, "v3s4w76q5eze", 53.895856596, 62.152908695}, + {0xef2e082aa7e2d8c0, "xwr0hbp7wcdd", 35.15685922, 167.562739657}, + {0xdb494ec96c72c4b8, "ve4nxkcdfc2c", 63.044149631, 70.633282644}, + {0xaf60fde8b25c784a, "pxhgvu5kcjw4", -49.943002406, 164.436887512}, + {0x0563627d9fd4b07c, "0pjq4zdzuks7", -49.527758638, -172.492373062}, + {0xf4f79b1aec39693f, "ymvtq6rd75nm", 78.279364514, 109.269470275}, + {0xb0f3a47e6609ae68, "q3tu8zm616r6", -35.731303207, 109.376169865}, + {0x2c88504c8bcc4f24, "5k450m4ctj7k", -66.94490401, -30.922518128}, + {0x35716a191c47a05c, "6psqn68w8yh5", -1.742930986, -83.748054877}, + {0xd9193e412569ce26, "v4dmwh95e772", 60.054634462, 48.429251586}, + {0x4bc59ec8e0b7a746, "9g2txk70qymn", 19.271470389, -100.223855113}, + {0x8c72d1fd78e3bb4d, "jjte3zcswfxn", -58.447851888, 52.813638523}, + {0x800e4da9bed27f74, "h074vbeyu9zr", -88.106435597, 4.476788272}, + {0x67e2c45039878e4e, "dzjd8n1thy74", 39.848304396, -48.513356883}, + {0xd0a36cab34e64138, "u2jqtbtnwt0m", 46.146468192, 18.892644023}, + {0x2197a7c9a2d28473, "46cugke2ub27", -73.671439182, -76.141572738}, + {0x4a5aaa2460a6e660, "99ebn930nvm6", 8.444394978, -106.939247946}, + {0xee1705df5883e81f, "xschcrushgn1", 27.596986007, 158.96757364}, + {0x6cacba3a4b12bb46, "ekqcnfkc2bxn", 24.094609243, -23.954616829}, + {0x2ddc904d0bb92c6c, "5rf90m8cr4q6", -46.200029259, -30.22204973}, + {0x3c44dae8d5c6c100, "7j2epu6psv0h", -14.916745347, -43.953348381}, + {0xc5a67976b9a23f7b, "sqm7kxptn8zr", 35.766854776, 18.841155089}, + {0xe91d19089f0a23c6, "x4fjk24z18jw", 16.392804314, 138.003115981}, + {0xad580bb72fbc7976, "ppd0retgrjwr", -47.748739929, 138.150295142}, + {0x228fbba741d2b239, "4b7vr9u1ubt3", -87.660982071, -50.64126838}, + {0xf1bf86fd67ae5cb1, "y6zsezc7ptfc", 61.30288369, 111.963712114}, + {0xb005c77bc855d953, "q02wfyy8brdp", -42.369977496, 90.832914536}, + {0xb7e570f9e5aada3c, "qzkr1yg5pce3", -2.950679098, 129.807923798}, + {0x6934f78ccf33a0cc, "e4ugg36g6fhd", 16.135365204, -38.173589451}, + {0xb7bf23fccda2be3f, "qyzk7z6enbz3", -6.243834946, 134.113840376}, + {0xbd2e4456ac4b0edd, "rnr48ppd9d7e", -9.365402849, 144.854118967}, + {0xce50eee148004171, "tt8fxsb8010r", 31.403052263, 68.884964364}, + {0x18523a6d0919c5bd, "3193nv89372v", -36.356273331, -132.944815144}, + {0x3b019a817676f0de, "7d0tp0cqfvse", -32.865785063, -21.487416813}, + {0x884f2b525c05c3b6, "j17kqnkw0r1v", -82.186278944, 49.840177355}, + {0x83a734590a544051, "hfmm8q8bbj05", -76.341091443, 41.144828807}, + {0x1f31f139a65aae39, "3wsz2fe6cbr3", -7.148879371, -105.782805291}, + {0xadf99d881a2cc175, "prwtv20u5m0r", -46.801043021, 155.622507705}, + {0x4fa56eeda8aeda6c, "9ykqxve8pve6", 36.329070463, -94.927717648}, + {0x458581bb108bb129, "8q2s3fshjfsk", 35.917753992, -167.964224886}, + {0x1d1ae9988087df20, "3nefm640hzgk", -8.030963593, -129.492894785}, + {0x9b337d43021ea21a, "mdtruhs23uj1", -29.550411496, 75.064512335}, + {0x8d79bdadfa1eaf95, "jpwvvcgu3urt", -46.791391427, 54.750108097}, + {0xc2f9c458c386ce80, "scww8q63hv78", 9.61461026, 42.904909789}, + {0x49980aa71adb22f6, "96d0p9suvdjg", 14.071566481, -120.601143158}, + {0x4650bfd25b63b049, "8t8cznkvdfs4", 31.280407394, -156.131048577}, + {0x9a712465c20dfef0, "m9sk8tf21rzg", -35.739856576, 73.501689126}, + {0x7257b6841e35e0cc, "f9cve10y6rhd", 55.817150905, -64.906093814}, + {0x3c4887f9b52aa36f, "7j48gyep5bjq", -16.706255717, -41.315291939}, + {0x3101219a11853171, "640k36hjhnsr", -32.991048927, -89.587835315}, + {0x632c3516adefdbea, "ddq3b5pexzey", 12.980988763, -58.700299913}, + {0x69eb2d47177eff4b, "e7pkujsrgvzn", 17.741535649, -23.372899121}, + {0xeb0d3ccc226826bb, "xd6mtm12e0mc", 13.65060011, 160.896628792}, + {0xb785babb478989ea, "qy2vpfu7j64y", -8.949210829, 125.15132948}, + {0xafac454a155d43c5, "pyq4bkhpcp1w", -54.337009839, 177.204024452}, + {0x69c81e588d62edf4, "e741wq4edcqz", 17.172239644, -30.659281446}, + {0x7b1e9eb09fd2acc3, "gdg9xd4zubqd", 60.744740299, -17.244545908}, + {0x739ed9b82d5fe424, "ffgemf1eczk2", 61.051708118, -51.073298702}, + {0x9a3ebd0edda3bd8a, "m8zcu3qxnfys", -40.465549305, 78.594430159}, + {0xa189ba3395e9e5c0, "n64vndwpx7kw", -77.856054903, 105.411389005}, + {0xdf25736accb71957, "vwkr6uqdqwdp", 81.454432429, 73.606620225}, + {0x70bc362f83ce978c, "f2y3dcw3tucs", 49.490945355, -69.831255798}, + {0x38aa9661f1d6b767, "72p9dsgjuuvq", -44.709221927, -23.088930802}, + {0x9504acb9baf65479, "kn2btfeuytb7", -9.741279653, 1.312802363}, + {0x6a509c7b131cb830, "e989sysm3kw3", 8.737803008, -21.582236222}, + {0xd709b2d4bcf20fe6, "uw4v5p5wy87y", 79.66851958, 26.504114081}, + {0xca68b48fbbc23f7c, "t9nc93xvs8zr", 5.897861066, 77.057965217}, + {0x97ffac79faa2a614, "kzzusygunbm1", -0.577467262, 44.862624018}, + {0xef989872cdd7b7a1, "xyd9hwqeuyvu", 36.773294184, 172.472516936}, + {0xa6d5293233dea5aa, "nvbkkdjmvuku", -56.897182802, 124.306779619}, + {0x3b649e9d8d5dd9bf, "7ek9x7decrdv", -26.43516709, -15.849780645}, + {0x11a6e9b3a2855ba0, "26mfmdx2hpeu", -31.934468332, -160.412230226}, + {0xe85ca20a3483d169, "x1fb42jnhg8q", 9.844799816, 138.973135306}, + {0x3e0cffc81f757444, "7s6gzk0zfpu4", -20.411225275, -18.313087808}, + {0xfe1a9c1afe48061d, "zse9s6ry9031", 70.589648928, 162.619330079}, + {0x1e70564a61512d82, "3ts5dkm1b4qs", -13.423606508, -106.769247785}, + {0x62925797ba057b84, "db95g5xu0pxs", 3.491633435, -54.701260972}, + {0xb44fb9f77c1eced5, "qj7vmxvw3v7e", -14.502224718, 95.522769692}, + {0x92d29ec0b59c9c12, "kc99xh5pmkf1", -36.275585262, 36.171340167}, + {0x571562ac662274fb, "bwbq5c3649ug", 84.030657221, -156.981812035}, + {0x8bcb1008e5899136, "jg5j0275j68m", -72.244191116, 82.98408197}, + {0xa1292a8ddebc0f69, "n4nkp3fyrh7q", -78.036165206, 99.111747541}, + {0xabb43ab9040be4f4, "pfu3pf841gkg", -74.341374479, 175.067220214}, + {0xa60f8ef38271a663, "ns7sxww2f6m6", -65.266887615, 117.7601516}, + {0xc5b60eb3617424f4, "sqv0xdv1fhkg", 38.072070626, 18.617798554}, + {0xdfe32ef7769df90c, "vzjkxxvqmrwh", 85.209701521, 86.469845725}, + {0xee68ac70cb9760d2, "xtnbsw6ckxhe", 28.247475534, 167.193917266}, + {0x2aee04391ccadc5f, "5cr08f8wtcf5", -82.866000639, -1.372354648}, + {0x46a2332cbceffd8f, "8uj36c5wxzys", 22.726377923, -138.741189758}, + {0x54ea554dab9d8a65, "bmp5bmecmq56", 73.814639317, -158.889884117}, + {0x20cb634dc6823adf, "435q6mf6h8xe", -83.244434836, -74.07751719}, + {0xfd228ec79ee0e7ea, "znj8xjwyw3my", 78.869226558, 143.051538135}, + {0x5876bb66f917491d, "c1vcqtrt2x4j", 55.093255452, -126.618093902}, + {0x8f373f819486dbde, "jwvmz0dnhvex", -51.016700483, 75.193385114}, + {0x349ef09319aba054, "6kgg14stpfh5", -17.739303687, -73.426099352}, + {0x38d97f9458b4225d, "73drz52sqhj5", -35.181590842, -30.277369293}, + {0x28da7f171b6465c8, "53e7y5svdjkw", -80.88312092, -28.909400423}, + {0x4d4195e7458ef3d2, "9p0tctu5jvtx", 40.417873578, -134.225209596}, + {0x3e253a7a81f93fb2, "7skmnyn1z4zv", -20.181568748, -16.218255937}, + {0x03757174525847e6, "0eur2x2kc13y", -67.591187282, -151.501076119}, + {0x0249b69cf006211a, "094ve77h0shj", -83.389641397, -153.485843257}, + {0x89bcbce8d50c8cbc, "j6yctu6p1k6c", -74.243007468, 65.99767713}, + {0x18024ef7c16bd792, "3014xxy1egct", -44.517696391, -133.255757369}, + {0x6c52d1a759c827c1, "ej9e39utt0mw", 31.519348648, -42.818305228}, + {0xe1371239bfb8f37f, "w4vj4fezr3tr", 16.362643083, 97.157567937}, + {0xdf3c4b999fa8bced, "vwy4r6dzp2yf", 83.379196858, 76.260200491}, + {0x1cc45e8a152cdfe6, "3m25x2hp5mgy", -14.85228202, -123.425758652}, + {0xf15ce5114cc807ed, "y5ffb4bdt03y", 66.592686294, 93.86809125}, + {0x6f128968a32a8e31, "ew98ku535b73", 36.628591193, -20.177256195}, + {0x5f9d1eca28e5f691, "cyfjxkj8wrv9", 83.957647178, -98.111067724}, + {0x4048f9418c4e88b0, "814gkhdd9u4c", 6.221459094, -175.953519178}, + {0x5faeb19a69969369, "cyrc36m9ku9q", 80.388575971, -90.288872042}, + {0x93297a039a93a59a, "kdnrn0wukfkt", -32.516042172, 31.562214258}, + {0x6cf06df2e4dead8d, "ems6vwr4vuqs", 31.455726413, -27.52186955}, + {0x2b9e719ba0fb0a2c, "5fg736x0zd52", -73.946088456, -6.614816418}, + {0xc255e62ec410f57c, "s9bydcq423ur", 10.993591498, 23.683793482}, + {0x6c51bf34309a3e77, "ej8vye1hm8z7", 31.965455902, -43.658048809}, + {0x3c8e13bb96252aa5, "7k717fwq4npb", -20.859228243, -29.357775728}, + {0xb939c89e43e25ed6, "r4wwj7k3w9ge", -29.864692216, 144.377458163}, + {0xf7d21d556ced50ef, "yz91upcdxp8f", 87.538159059, 125.334392921}, + {0x31ae299876f50144, "66r2m63qyn0n", -32.286253046, -68.322000451}, + {0xa006e675c65fe35a, "n03fdxf6czjp", -88.111214042, 92.573972812}, + {0x5e271cedb6527765, "csmjtveqb9vq", 69.904428535, -105.211572717}, + {0x4d02ad868ec2f065, "9n1bv1nfscs6", 33.887766769, -132.309856636}, + {0x65f355b8de7ad134, "drtpcf6ygc8m", 43.563331995, -71.63790784}, + {0xe69b873c24252528, "wuesfg144nkk", 26.164317953, 128.794189681}, + {0xbb5e3ad50e139028, "reg3pp8f2f82", -23.688870344, 162.378992366}, + {0xa5cdb40a20c45ecf, "nr6v82j0sjgd", -48.251835977, 105.135216927}, + {0x7aead0abc2f94810, "gcpe1by2z541", 51.156608662, -0.617334514}, + {0xab5b29de63ec4e9c, "peekmrm3xj79", -69.525318319, 162.308540512}, + {0x02cd9d7af7b82357, "0c6tuyrrr0jp", -81.922429662, -142.515371171}, + {0xff3d8c26dffbae24, "zwyss9qzzfr2", 83.767974637, 166.847990992}, + {0x73f0bfe1bf863bbf, "fgsczsezhsxv", 65.021063174, -49.235369018}, + {0x091bbbf5b03304ec, "14evrxeh6d2f", -74.972707649, -129.392744289}, + {0xabba52717a9afa26, "pfx54wcumcx2", -75.372339689, 178.706273141}, + {0x559dbd8169ab7160, "bqfvv0c9pesq", 83.983803514, -164.66069843}, + {0x5e0e8cec9812dd01, "cs78tv4s2cfh", 69.022346116, -107.321971331}, + {0x7e7f933f03834cb1, "gtzt6gs3he6c", 78.286016979, -11.826242052}, + {0xdff3210f858bc937, "vztk23w5jg4m", 87.943342117, 86.152288443}, + {0x60c164b8c4960a15, "d30q9f64ks51", 6.780345765, -78.31858944}, + {0x7f71c3d3d734bb29, "gxsw7nyr6kxk", 88.324481093, -16.031349543}, + {0x32ba92f30ca933c6, "6bx95wsdp4tw", -41.975655353, -45.542828693}, + {0x4c2d573c4fd830a9, "9hqpfg2gv0sb", 25.28705393, -126.440399273}, + {0xacd024e05c43c702, "pm829s2w8g3h", -58.950129492, 146.668200753}, + {0x793b4e9db9dc7472, "g4xnx7etvju7", 60.225322602, -34.8326196}, + {0x34329efd581494fa, "6ht9xzbs2kbg", -19.380500556, -81.924350117}, + {0xd7501cd25e30bcf3, "ux81tnky62yg", 87.486581617, 22.726355406}, + {0x72982780324ea465, "fbd2g01k9uk6", 47.945131696, -52.952304081}, + {0xfe956f9184849d00, "zubqz4d4hkfh", 72.919375841, 169.412111389}, + {0xd833c5bc29bc1330, "v0twcg19rh9m", 49.015742508, 52.813641329}, + {0x894d1fca1dc3c84e, "j56jzkhxsg44", -70.684731163, 48.137490136}, + {0x16abe4bcf163647f, "2upy9g7jdek7", -21.338606887, -135.270425063}, + {0x6078eed84b193eac, "d1wfxq2c34zb", 8.911514842, -80.188125532}, + {0x290f53fa64dabe26, "547p7ym4vbz2", -76.034527467, -40.60933197}, + {0x3de9fca7cc033fc6, "7rnzt9yd0dzw", -4.296668397, -24.00715525}, + {0x459ec495fb42eac3, "8qgd95gv8cpd", 38.42975238, -163.778987435}, + {0x8d64944cb07591e0, "jpk98m5hfq8y", -48.926761014, 51.343328849}, + {0xfab40f41e8cea9df, "zbu0yhg8tunx", 49.376787063, 174.643734203}, + {0xf4fad16eac618f00, "ymxe2vpdd67h", 76.5366968, 111.840234291}, + {0x5dcb98566cda0a08, "cr5thpmdv850", 85.294203761, -118.644550379}, + {0xa018314013b36b6b, "n0d32h0mqepq", -86.944882408, 93.164694807}, + {0xa8ea9d9894ba36e6, "p3p9v64nr8vf", -84.055319157, 157.030664135}, + {0x1356c7dfd371fe04, "2ecdgrymf7z0", -23.379272277, -155.239113679}, + {0xc998c46cb78fad3d, "t6dd8v5rjyqm", 14.530655094, 59.803302539}, + {0x166020e8eec2100d, "2th21u7fs880", -16.851222051, -151.441190035}, + {0x66e82fb7c078e3db, "dvn2zey0g3jx", 28.277570642, -47.122960771}, + {0x5f1055f493224fdc, "cw85cx4m497x", 82.260998119, -112.430879942}, + {0xb583441963531b1b, "qq1n86c3bdej", -10.092000942, 102.668978683}, + {0xa819c9f2b19bc212, "p0dwmwpjmg11", -86.054990708, 138.767185458}, + {0xfc0a677bb3b0e2ea, "zh56fyxmq3jf", 68.020010928, 139.701394703}, + {0x70f42a3837e98c4e, "f3u2nf1rx664", 54.856036306, -72.474775884}, + {0x3631892bd36ad26d, "6ssskbymec96", -18.935357134, -60.954388898}, + {0xd78c633fe3da4859, "uy666gz3v945", 80.572663527, 37.045107359}, + {0x7f7c4e4cb5dd312c, "gxy4wm5pvnsk", 89.062034981, -13.783501477}, + {0x2196e38ebc4e0dbc, "46cf73pw9s6v", -74.129109487, -76.135872294}, + {0x2df91245f4d7c2aa, "5rwj4jgnuz1b", -46.900811781, -25.220282985}, + {0xe282ced309927930, "wb1dxns9k9wm", 0.475382175, 126.173373543}, + {0x32b3c0b6dfc5c0a9, "6btw1eqzsr0b", -41.113634654, -48.440253707}, + {0x35bef4a98ce1a73c, "6qzg9bddw6mm", -6.412826858, -67.770949483}, + {0xfb8639279de0c07c, "zf33k9wxw307", 57.885509728, 170.714761554}, + {0x9fe2ef63026672e8, "mzjfyss2dttf", -5.116766634, 87.127533722}, + {0xf07896bb365cc40e, "y1w9eftqcm20", 53.716091767, 99.312683627}, + {0x2b65b45fa50cd291, "5ekv8rx51m99", -70.71020966, -15.799657423}, + {0xe7391e5839679a42, "wwwjwq1tdye4", 37.563235205, 121.214340338}, + {0x4dd6f10dc85af868, "9rcg23f8ccw6", 44.174789238, -121.274559226}, + {0x21cc28432ee6c38a, "4762hhtfwv1s", -71.693574087, -75.401967802}, + {0x19f4a7a4ffc28ab4, "37ubg97zsb5c", -23.766259895, -116.911145152}, + {0x77e8b12497643226, "fznc294rdht2", 84.60153145, -46.732656908}, + {0xa6659543e840fef2, "ntktbhz883zg", -59.431819548, 118.838445957}, + {0xb427710bab99859e, "qhmr22xcm62t", -19.816361754, 97.404668083}, + {0x257f3d2835bf7a91, "4pzmub1prxx9", -45.394222787, -79.594242026}, + {0x7be69ce12a834754, "ggm9ts9bhe3p", 63.569659829, -3.271328041}, + {0xe9b7a674d950d36b, "x6vudx6tb39q", 16.300587219, 154.449246712}, + {0x3125413d9d4135fa, "64kn2gdx84uz", -31.224587706, -84.338601144}, + {0x0d75e8779759f596, "1puyhxwrc7ut", -45.309012328, -128.11391572}, + {0x756dfdaa4073c39d, "fpqzvbk0fg1t", 87.145073798, -80.249513571}, + {0x2bf39965b8c8fe0d, "5gttktest3z0", -69.358654038, -3.312829685}, + {0x32ee286a619e8e07, "6cr2hum1mu70", -37.945166511, -45.83882989}, + {0x17f32c4acd1c0eb7, "2ztkskqe3h7c", -1.997557426, -138.67144544}, + {0x22e4e2867d6a377a, "4ckf51mxe8vr", -82.609028379, -49.430765896}, + {0x08eadb450a8ed981, "13peqj8bjvds", -83.773468279, -112.938172981}, + {0xd718be1d32cc8cdc, "uwdcw79ktk6e", 81.846193836, 26.643791158}, + {0x4dba293c8fa3bfdd, "9qx2kg4gnfzx", 36.623455859, -113.341870778}, + {0x9ac1774633c13a23, "mc0rfjjms4x2", -37.984271895, 79.196844385}, + {0xa1a195f45785e191, "n6htcx2rhrht", -77.69819228, 107.644574758}, + {0x8553d37c7901af42, "hp9x6z3t06rn", -46.497389112, 2.232304796}, + {0x3ec11d95c2a28c88, "7v0jv5f2nb68", -15.843657081, -11.026890257}, + {0xf93bc3769ecb8be2, "z4xw6xnytf5y", 60.200712509, 145.6662599}, + {0x63db20448889b29c, "dgek0j48j6t9", 20.418104484, -51.676021368}, + {0xf29a023e0116412d, "ybe04gh12t0k", 47.829221722, 128.095114782}, + {0xe75c2559f7077f38, "wxf2bqgr0xzm", 43.763908856, 115.679543422}, + {0xa9facfc5a1283de2, "p7xdzje150yy", -69.798712462, 157.108749984}, + {0xb659982608abb52f, "qtdth9h8pfuk", -13.178085019, 116.219897298}, + {0x62ca50d34d288129, "dc551nue520k", 6.189938341, -51.98098484}, + {0x655a3cc568701ef6, "dpe3tjc8f0gg", 42.482890946, -85.207809979}, + {0x91c9737d2cfc6bca, "k74r6z9dzjpw", -26.808887272, 14.537282765}, + {0x905b38211675ae05, "k1emh88qfqr0", -35.679654764, 4.768515682}, + {0xfab6421de980fbb4, "zbv447g9h3xv", 49.591092536, 175.88512631}, + {0xd1b1a7ce86d5a6be, "u6sugmn6uqmc", 59.92543264, 18.081283258}, + {0xca3a3222edffb6fc, "t8x348rezyvg", 2.990335068, 77.815815985}, + {0xa98f5647ab5b8bde, "p67pdjxccf5x", -75.994865826, 150.567363703}, + {0x296440938b2615b3, "55k414wc4sbv", -71.35325655, -39.321676557}, + {0xc46a6a72ae485a8f, "sjp6nwpf91e8", 28.509958836, 10.491647904}, + {0x6f9a6d9927cf9602, "eye6v697tyc0", 37.060268412, -6.447009771}, + {0xd410d6a545dff8bb, "uh8ee9b5vzwc", 70.938031585, 0.857189728}, + {0x8391515519ef2f95, "hf8p2p8txwrt", -74.620908569, 33.75101864}, + {0x38a4087cc48def52, "72k0hz64jrrp", -43.553541154, -27.913278374}, + {0x5f1a1a9a535fe57a, "cwe1p6kmczkr", 81.751668023, -107.956728924}, + {0x5cd9a880eafe985e, "cmduj07bzud5", 76.642151794, -119.657600554}, + {0xf550df0e18b90798, "yp8ey3hsr43t", 87.852915339, 90.984265623}, + {0xd991539cfa9252bd, "v68p777uk99c", 60.355504771, 56.398167768}, + {0x88d8a5963481c3cc, "j3dbc5jnh71w", -81.413137404, 60.168183901}, + {0x11568e0a85dce712, "25c8w2n5vmmj", -23.817686611, -177.607494954}, + {0x7c68faaf73e25411, "gjngpcvmw9b1", 73.662910552, -35.15974753}, + {0x093da53fbfcff6e6, "14yubgxztzvf", -73.675733503, -125.463955911}, + {0xc82c8afefae7f48e, "t0q8pzruwzu8", 1.4468896, 54.492140164}, + {0x644f8f1fc7392e34, "dj7sy7y774r3", 30.387380562, -84.794745212}, + {0x16fc9b5268b481a0, "2vy9qnm8qk0u", -12.402126079, -136.837880758}, + {0xe486602be42967bd, "wk360bz455mv", 24.262294115, 103.050521166}, + {0xb0355a38fda24e19, "q0upnf7xn971", -39.53721306, 95.926720587}, + {0x3f28d38c1bac0edb, "7wne730vph7e", -10.672336082, -13.215217413}, + {0x858efecf87620743, "hq7gxmw7d83n", -54.197699991, 16.850732858}, + {0x17d0d6d820df7a42, "2z8eeq10vxx4", -2.164137287, -145.402433883}, + {0x03d378e4b8295b0c, "0g9rjt5s55eh", -69.053859218, -144.245548817}, + {0x3289d364b7b46c8b, "6b4x6t5rqjq8", -43.696847332, -52.619782062}, + {0x1295b7694a480f72, "2bbvfubb907r", -39.744326908, -145.073385536}, + {0x12938f355410ff95, "2b9syebn23zt", -41.330856447, -143.854959655}, + {0x63d7d1b229e37e82, "dgcx3dj9wez8", 22.37942802, -54.066864764}, + {0x4b94714c3ff786e4, "9fb72m1zyy3f", 16.06886924, -100.884755871}, + {0xf1f30f01ff59d083, "y7thy0gzc788", 65.527930009, 108.55012616}, + {0x85a69ef7f3a198f8, "hqm9xxzmn6dg", -54.536633854, 19.324193318}, + {0xbfd02894e6257546, "rz82j5764pun", -2.794261195, 169.325843613}, + {0xe65fa172584475bf, "wtgu2wks8juv", 33.125956875, 117.801596639}, + {0x14f41b75bebc8bdb, "2mu1qxeyrk5x", -12.394229129, -162.833889466}, + {0x39b65afe94830e65, "76v5pznnhd76", -28.964411577, -26.369736584}, + {0x5d8eb12b6cfb8caf, "cq7c2bvdzf6b", 80.380580715, -118.435713878}, + {0xf42f1cbf3191d2ee, "yhrjtgtjk79f", 69.893193397, 100.103487011}, + {0xabf06bf57e9c651d, "pgs6rxcymjkj", -69.873318136, 175.058775713}, + {0x68c6dc5e7f61bf7d, "e33esrmzd6zr", 7.687613334, -31.445854485}, + {0x694fc11167d7535b, "e57w24c7ux9p", 19.395674221, -40.076213669}, + {0xb830817a8e247dea, "r0s82ynf4jyy", -42.110220526, 141.370448884}, + {0xe7650ec91d29faa8, "wxkhxk8x57xb", 41.598205277, 118.4444402}, + {0x43bf43fb9957e43d, "8fzn7ywtbzk3", 16.604114654, -136.232490628}, + {0x2a31183ea214649a, "58sjhgp22jk9", -86.292043008, -16.656297085}, + {0xe578c009b9dd5801, "wpwd02etvpd0", 42.542836415, 99.15663516}, + {0x549d240859c362cc, "bkfk822tsejd", 72.512099596, -165.574063841}, + {0xd42363dbc66bff7a, "uhjq7qy6egzr", 68.636162131, 7.534388789}, + {0x5c4139cf8c71d20b, "cj0mmmwdf790", 74.078554864, -134.408700009}, + {0x681b6828240326df, "e0eqhb140dme", 3.867543556, -40.219544825}, + {0x2a9655c33e58d76a, "5bc5chtyc3cq", -85.09618194, -9.79185265}, + {0xb619c5042f3f46f0, "qsdwb11g7x3g", -18.494886074, 116.018180871}, + {0xb52ba7d43ba6509e, "qnpugp1vnt89", -10.37569882, 101.032956644}, + {0x3a2fd71c80a1f984, "78rxf740n7ws", -42.214946257, -11.851224481}, + {0x3b438c5d8dfeaecf, "7e1ssrdezurd", -27.292118619, -20.200081222}, + {0xfe7faab5add1b524, "ztzupeeeu6uk", 78.066760341, 168.733021958}, + {0x55bf4751c4e82600, "bqznfnf4x0m0", 84.192782794, -158.815305625}, + {0x7d85b3b7e7307e44, "gq2v7ez761z4", 81.100268663, -32.531452299}, + {0xe2b735e6855e03a7, "wbvmctn5cs1u", 5.257623998, 131.20703707}, + {0x3d920e448ea7468c, "7q90wj4fnx38", -8.32176122, -32.076020414}, + {0xf001fd701525c9f5, "y00zuw0p4r4z", 46.396504161, 91.252541714}, + {0xf80ab311bcb7cc41, "z05c64ewqz64", 45.234571659, 140.366427037}, + {0x46ea09dc5f1fd0fa, "8vp0mr2z3z8g", 28.210056048, -136.174430729}, + {0x49f8486d67d12f12, "97w4hvc7u4rj", 20.07130487, -115.101861968}, + {0x2ff50af5a79da12e, "5zuhpxe7mqhk", -45.661335389, -5.290715668}, + {0x1006df161896b09e, "203ey5hskus9", -42.917336829, -177.620563178}, + {0x8f1123d3f32217f7, "jw8k7nzm48cz", -52.652487193, 67.993464527}, + {0x6bedec397bab81f5, "egqysfcvpf0z", 19.439804666, -1.546335369}, + {0x11fa1ee597019052, "27x1xtdr0685", -25.017400176, -158.573559436}, + {0x067d229345384a1f, "0tyk54u57151", -56.937455219, -148.573475304}, + {0xfecb64fbc87889b1, "zv5q9yy8g24v", 74.304786642, 173.406286427}, + {0x20d77d50d0caee3c, "43crun6htcr3", -78.758833579, -76.813403007}, + {0x8cc0b79427a297ce, "jm0cg517nbcw", -61.550383199, 57.438532017}, + {0x9f97b6e1787b4f78, "mycvescsge7r", -6.037526596, 81.366960842}, + {0x160150148e9f9d87, "2s0p054fmyfs", -21.252628496, -157.495973246}, + {0x525518addb1853b7, "b9bjjcfv319v", 55.733179531, -157.243492534}, + {0x16339409257df487, "2stt8295gru8", -18.717271082, -149.753119712}, + {0x3fed4fd4fe72496a, "7zqnzp7yf94q", -2.99123601, -2.499622381}, + {0xf3dc91f9dc82c0cf, "ygf93yfwhc0d", 66.351591228, 127.346167339}, + {0x80de616f3b78eedb, "h3g62vtvg3re", -79.72953548, 15.861317715}, + {0x65f9db45fbf66821, "drwxqjgvytn2", 43.494513714, -69.340265876}, + {0x8dfdf8e52cabfba0, "jryzjt9dpgxu", -45.145206066, 65.986289712}, + {0x8a59072b99ddd9dc, "j9dhfbwtvrdx", -80.723763925, 70.442504884}, + {0x66ebae1f24de0c8b, "dvpuw7t4vs68", 28.935736628, -45.069798202}, + {0x2aaace318f676730, "5bpdwddgdxmm", -89.546173383, -0.413589558}, + {0x14baa92b22e4720e, "2kxbkbt2wjt0", -19.64069266, -157.63534802}, + {0x89b4338043a41a79, "j6u37023nhe7", -74.30995377, 62.359003858}, + {0x8544addb1f1cd822, "hp2bvqsz3md2", -49.049934413, 1.291980333}, + {0x7fc36b01922304d1, "gz1qq0dk4d2e", 85.477078678, -9.225317743}, + {0xed011a8708b5fe5d, "xn0jp1s8qrz5", 34.637231154, 135.31407481}, + {0x828e946626441ea6, "hb798tj68hgb", -88.302157582, 38.701062579}, + {0xeb3f9964656fb5c2, "xdztkt35eyuw", 16.421078735, 168.246169256}, + {0xcd7c3f0e48a7de73, "tpy3y3k8nzg7", 43.908275768, 54.070194665}, + {0xcd6d1d0485aed111, "tpqju145pv8j", 41.798030749, 53.616360446}, + {0x83284d5b32df11eb, "hdn4uqtkvw8y", -78.230044674, 31.131714692}, + {0x38e7d0209ebb6b18, "73mx084yrepj", -36.737191418, -25.989544329}, + {0x4a73a9f5844e806d, "99tumxd49u06", 9.226220143, -104.169541041}, + {0xa31e5b25f0e8493a, "ndg5q9ghx14m", -73.949569202, 117.00881814}, + {0x39d3c51431bb9a83, "779wb51jrfe8", -24.108582354, -31.63891431}, + {0xe6ab412799169dfc, "wupn29wt2ufz", 23.607800549, 133.624685141}, + {0x5ea44c5512e45fa8, "cuk4sp8kwjgu", 69.387705855, -95.448612661}, + {0x0075ba8e13b96043, "01uvp3hmr5h4", -79.270932865, -172.99556656}, + {0x91579b405bcc3371, "k5ctqh2vthtr", -22.95908298, 2.374298502}, + {0x72efc5918636d56b, "fcrwc4d66vbq", 53.231924493, -45.655971154}, + {0xfb2ad1655b761e9b, "zdpe2tbvfsg9", 56.853887509, 168.069989881}, + {0x91f59187897cc1ee, "k7ut31w9gm0y", -22.974830331, 17.631137454}, + {0x6c235eab71a14325, "ehjpxbvjn51k", 23.823354461, -37.621048146}, + {0x25f16dc5d1b4b36c, "4rsqvjfjqktq", -46.593464381, -72.550688911}, + {0x637ff621b7fbe504, "dezzd8erzgkh", 22.416204531, -56.486898048}, + {0x494d86b0631d8b16, "956sed333q5j", 19.084872951, -131.32879197}, + {0xff02652d7de90ad0, "zw16bccxx45e", 79.244310167, 159.293136567}, + {0x99c13049763c1cb4, "m70m0kcq7hfc", -27.21890258, 56.614397909}, + {0x33f53edf3acab54e, "6gumxrtutbun", -22.897476791, -49.946685129}, + {0xa85736376a3eccf7, "p1cmdevb7v6g", -79.168782046, 136.875736967}, + {0x678f57e6f0e40416, "dy7pgtrhwh21", 36.548189342, -51.867570733}, + {0xc08adb1a68128675, "s25eq6m82b37", 0.583693602, 16.45412021}, + {0x35140b335d42da5a, "6nb0qdux8ce5", -6.970902448, -89.708139145}, + {0x5bdfec2fcde40b9d, "cggyscyewh5t", 67.246564422, -95.758633499}, + {0xed0d6d36144132ff, "xn6quehn84tg", 36.360382801, 138.367317601}, + {0x7029f985aee9dd06, "f0nzm1efx7fh", 46.283103874, -80.282622189}, + {0xf964d001ca26f819, "z5ke00fb4vw1", 63.812743374, 141.332027822}, + {0x3b87640ee79e5140, "7f3q83r7mt8n", -31.19371701, -9.471008747}, + {0x897d3444a5799103, "j5ym8j55g68h", -67.911329411, 53.793321963}, + {0xf4fd0511cc016c62, "ymyhb4fd05q6", 78.194178257, 109.690941704}, + {0xa05a53b302cc1135, "n1e57ds2th8m", -80.977367068, 94.378609997}, + {0x3894a394206a0a3c, "72bb7510e853", -40.720739094, -32.56194846}, + {0xa8250e112d74aaab, "p0khw49efkpb", -87.788336222, 140.890839092}, + {0x1c058a64a9823c4b, "3h2snt59h8y4", -20.362983112, -134.006222483}, + {0x4c24bd222c1f9925, "9hkcu8jd3ydk", 24.214289107, -128.114927348}, + {0xed22d7c185f7e156, "xnjeghd5yzhp", 34.434581477, 142.869252652}, + {0x634b5fff15029735, "de5pzzsp0bcm", 18.279710052, -62.935146772}, + {0x12b274ca554dd804, "2bt79kkp9rd0", -41.547591865, -138.806706298}, + {0x3521a656247572b0, "6nhudpj4fptc", -10.420019717, -83.225461868}, + {0x02a6d611dbc71268, "0bmed4fvsw96", -87.962430045, -138.423762817}, + {0xe99e2e8945c94450, "x6g2x2b5t525", 15.561379286, 151.1391306}, + {0xda6b51b39ec7d607, "v9pp3dwyszc0", 51.914304508, 77.419143033}, + {0x4fb700c0e425d2c7, "9yvh1h744r9d", 38.695604995, -94.170586088}, + {0x1a7bd00a59039d98, "39xx02kt0fft", -35.329784036, -101.935923104}, + {0xd9fd1cc6788422db, "v7yjtjmshhje", 67.090094671, 64.914958962}, + {0x4ad38c87b10588dd, "9c9st1xj0q4e", 9.237646494, -98.91127108}, + {0x2d5c51464c46b095, "5pf52jkd8us9", -45.805668239, -42.181281895}, + {0xf46a507db219d16c, "yjp50zek378q", 73.694291943, 99.881226338}, + {0x6cb3a59b13b1bb81, "ektuc6smq6xs", 26.162109213, -25.603016064}, + {0xc664f467ef89d707, "stkg8tzgj7ch", 30.178606005, 29.21252997}, + {0x65c6fabdc658bd95, "dr3gpgf6c2yt", 41.329670188, -75.945334019}, + {0xf531f51da9698689, "ynszb7e9e638", 82.944300396, 96.695622047}, + {0x3d63f2096c31d7ea, "7pjz42cd67cy", -4.390003981, -36.813026415}, + {0x472a2116fd97d23d, "8wp225rxkz93", 33.813083172, -147.294182117}, + {0x40f7be7acf55aa24, "83vvwyqgbqp2", 10.845555182, -160.358141466}, + {0x0ab4451c3568e9d8, "1bu4b71pe3nx", -85.280078059, -95.612491962}, + {0xa963acea1cbd6101, "p5jutuhwrphh", -72.310900551, 143.345108709}, + {0x663f1d58bd27c65a, "dszjuq5x4z35", 27.763692226, -57.464556272}, + {0xbeaaf210b4e56c63, "rupg445nwpq6", -21.960514823, 179.740713994}, + {0x7c10c4c657ec8319, "gh8d9jkrxk1j", 70.782102073, -44.246780478}, + {0x30f51d56470e2c94, "63ujupk71sq9", -34.105144314, -72.943309171}, + {0x301ed1d5369e7eab, "60ge3p9qmtzb", -40.167658413, -85.032216346}, + {0x6197357abb3d2cc0, "d6cmbypv7nqd", 16.51338626, -76.948448006}, + {0x7c8a35df63a62634, "gk53crv3nsm3", 67.85038393, -29.117260081}, + {0x9e2eae19f416c022, "msrbw6gn2v02", -20.989653446, 78.677248241}, + {0xbd8921522c16ce9f, "rq4k2njd2v79", -10.469555997, 149.421652794}, + {0x2347f8403b9c8689, "4e3zhh1vmk38", -70.465384527, -64.860650423}, + {0x2390488e5363f0ca, "4f84j3kmdgsd", -75.578107792, -56.013326297}, + {0xfb61ad777b97d84c, "zehuuxvvkzd4", 62.753474855, 164.385544588}, + {0xfdfd24ed917f74f1, "zryk9vdjgxug", 89.416007224, 155.118869967}, + {0xf1586e8c95e8b4d4, "y5d6x34px2ue", 65.133738398, 93.485729824}, + {0xefd6b26c2d71d326, "xzcc4v1ef79k", 43.797660369, 171.333945437}, + {0x027bed227a9a6a35, "09xyu8mum9p3", -80.373868018, -146.395666373}, + {0x10ad56f264cc634c, "22qpewm4tjjn", -42.240601254, -160.151603492}, + {0x252b6bb104052234, "4npqrd840nj3", -55.13725851, -79.475090553}, + {0x2d85af194fe86eb6, "5q2uy6bgx1rc", -53.993075084, -32.419313407}, + {0xbb85982879b93ee0, "rf2thb3tr4zf", -31.462554135, 169.664235979}, + {0x70987ee4889a1b03, "f2d7xt48m8eh", 48.455246545, -75.252674225}, + {0x37eabaaa17a96e8a, "6zpcpbhrp5r8", -5.447999665, -45.004839079}, + {0xd99c255f0468dbfc, "v6f2brs4e3ez", 60.642223164, 59.43068673}, + {0x79a5d4d097ed3e94, "g6kx9n4rxnz9", 59.008892315, -27.374529246}, + {0x1eb280e76316a86b, "3ut81tv32un6", -19.655677873, -93.442456364}, + {0xa86b20d4d29e72cb, "p1pk1p6kmttd", -83.631290084, 145.24259146}, + {0x3cb1ffd460be233a, "7kszzp30rsjm", -18.285304651, -26.760992805}, + {0xd557bc61150d0f8d, "upcvss8p1n7s", 89.586502101, 2.658739211}, + {0x84964c914a51c4ac, "hkc4t4bbb72b", -62.826544391, 12.877018509}, + {0x5c45d142b8f65e00, "cj2x2hpsytg0", 75.82848296, -134.286292312}, + {0xfcf7ce82a4a1dbb8, "zmvwx0p4n7ev", 78.486691068, 154.301878678}, + {0xfd8fc00158b549a8, "zq7w00bsqp4u", 81.215825195, 151.172823055}, + {0x1558903b3f26d6bd, "2pd90ftz4vcc", -2.621753043, -176.443398998}, + {0x8d49b59ba519163e, "jp4vc6x534c3", -49.599951961, 48.93177708}, + {0x75324aa5b9268a8a, "fnt4p9et4u58", 81.923182032, -82.6342306}, + {0x9936c76ea920d5fb, "m4vdfvp943bz", -29.020203477, 52.865622113}, + {0x7b6ae1fe6556e489, "gepf3zm5bvk8", 62.311005209, -11.517753278}, + {0xd6719a5da04619a5, "utstnre08sdu", 76.857715767, 29.106928236}, + {0xd397a9e6cbff0006, "ufcumtqczw00", 61.244992121, 36.46222845}, + {0x666fd2090c3aa98b, "dtrx428d7bns", 30.7648523, -56.853391498}, + {0xe3974f0437fe5466, "wfcny11rztb6", 16.662128275, 125.421960775}, + {0x9762f841624797e2, "kxjghhc28ycy", -5.071440667, 30.763470017}, + {0x782ddd89c140af8c, "g0qxv2f182rs", 47.772934162, -35.625894566}, + {0x4dd180267586aa0f, "9r8s09mphup0", 42.898714696, -123.017822346}, + {0x7d3e5e7a9a7c1a0e, "gnz5wynughe0", 83.617782987, -34.850216331}, + {0x2600e68552f6a9b2, "4s0fe1bkyunv", -67.05009614, -66.312834407}, + {0x9f6ddf1d07fe9195, "mxqxy787zu8t", -2.836550385, 76.915959854}, + {0x5128c5457606c8fb, "b4ndbjcq0v4g", 56.766041467, -170.85762219}, + {0xb3aafc4cd03fa46e, "qfpgsm6h7yk6", -33.105162983, 134.838118567}, + {0xc78cc84fa3aa4171, "sy6dhmx3p90r", 35.538203152, 37.46267118}, + {0x72399ba068cff649, "f8wtr838tzv4", 48.736852673, -58.027471509}, + {0x830921cd839e69c5, "hd4k3md3mtnw", -77.972474714, 25.722328868}, + {0x0f7a169a450e0b8e, "1xx1e6k51s5s", -47.535931272, -102.507865184}, + {0x37052a61a491d62b, "6w2knse4k7c2", -9.115499351, -66.858488765}, + {0x4d2c3a9b8439de74, "9nq3p6w477g7", 35.346171129, -125.883950375}, + {0xbb2c14ca5cef027f, "rdq19kkwxw17", -32.055583552, 165.998935254}, + {0x2d78e2fc0892e224, "5pwf5z08kcj2", -47.422436986, -35.342119996}, + {0x5aff33f2aec7dd93, "cczm7wpfszft", 55.800032175, -90.890028612}, + {0xd17ca37afa301e07, "u5yb6yru60g0", 66.172761306, 9.623770125}, + {0xc865366cc204008d, "t1kmdv620h08", 8.026907587, 51.100502913}, + {0x89ae04add79e9744, "j6r09cfrmucn", -77.244975196, 66.173992348}, + {0xebdc6c5bfff568d4, "xgf6sqzzypne", 21.571650503, 172.111739303}, + {0x4cb56b8676af5b56, "9kuqr1mqpxep", 27.825320733, -117.458284527}, + {0xf032f908783c3411, "y0tgk23s7hu1", 48.385917612, 98.274899165}, + {0xaab5d88b0c922ee7, "pbuxj2sdk8rf", -84.547646608, 175.315221347}, + {0xca3d129be206d917, "t8yj56z20vdj", 5.112792927, 76.090313607}, + {0xf930691583fe3976, "z4s6k5d3zswr", 59.477559706, 141.155763997}, + {0x44a2de10d4658559, "8kjew46ndq2p", 23.128744541, -160.749109745}, + {0x884ebb1c53534f69, "j17cq72mbe7q", -82.730165097, 50.548465076}, + {0xd541440e5403dd75, "up0n83kn0gfr", 85.525496002, 0.016514743}, + {0xda5d532d12509edd, "v9fp6c8kb2ge", 56.127223876, 70.433708593}, + {0xecce6935fe2edee1, "xm76kegy5vgf", 29.948418387, 151.023382211}, + {0x4640bc78db5d54cd, "8t0csy6vcpbd", 28.424033257, -156.232751935}, + {0x6b0557ff7be3622f, "ed2pgzvvwej2", 14.062087066, -22.327022354}, + {0xb4feb0ea698f4d11, "qmzc1um9jx6j", -12.456911369, 112.233133643}, + {0xbbc99c3e335e6a81, "rg4tsgjmctp8", -27.1407097, 172.481649476}, + {0x8625e4bc79c3e8ea, "hsky9g3tsgnf", -64.93235849, 29.258864894}, + {0xe696ba61d77f9178, "wuccnsfrgy8r", 26.921990204, 126.499833449}, + {0x7bb71331bab003db, "gfvj6deuq01x", 61.406065287, -4.103477098}, + {0xcc9c023f4827f4cf, "tkf04gu84zud", 26.739391427, 59.189653784}, + {0xf098847274042531, "y2d88wmn0hkm", 47.935776575, 104.794469885}, + {0x3a3f48ac89426e9b, "78znjc4989r9", -39.720804907, -12.400099438}, + {0x9283f17d635b8a8c, "kb1z2zc3cf58", -43.682702669, 36.245693075}, + {0xa9592b1e3b500747, "p5dkq7jvb03n", -69.54796064, 138.446618571}, + {0xba5d15a55b4b12fd, "r9fjc9bv9d9g", -34.134936577, 160.379514835}, + {0xa5b53f5a3fac10e6, "nqumyqjzph8f", -50.986322615, 107.509418697}, + {0x29597f88ecc09a9d, "55drz27ds2e9", -68.948389889, -41.512340134}, + {0x44bccff38a8fcc5c, "8kydzwwbjz65", 27.237893516, -159.270265815}, + {0x43eed732de45d5ef, "8grefdqy8rby", 18.953947826, -135.58397997}, + {0x80c8d48c79173c78, "h34e933t2wy7", -83.751961223, 14.822646651}, + {0x57057a36f55543e6, "bw2rnerpbp1y", 81.40594059, -156.853177446}, + {0x898651282d201f5b, "j6352b1e40gp", -76.771940692, 57.691359088}, + {0xafe64dcf56804318, "pzm4vmuqh11j", -48.702729665, 176.017972561}, + {0xc04d1c4d4ae34ccb, "s16jsmbbwe6d", 8.029736241, 3.000580122}, + {0x226dd83d827bfe5d, "49qxhgd2ggz5", -81.71890567, -58.147374312}, + {0x1b9a006a78a2dcb1, "3fe00umsncfc", -30.913457795, -96.990444542}, + {0x87638e080183868b, "hxjsw201hf38", -49.833800583, 30.509239381}, + {0x45f1514e75666cb8, "8rsp2mmpdtqc", 43.492068992, -163.107030628}, + {0x420d8dca4992a53c, "886svkk9kbkm", 2.26477373, -153.747272578}, + {0x160f7c7346624f50, "2s7rswu6d97p", -19.737874728, -152.72598577}, + {0xa919d40d397f7631, "p4dx839tgxv3", -74.609872039, 138.528828512}, + {0x80f8817853e942c3, "h3w82y2mx51d", -81.483261803, 20.424229867}, + {0xa20e594d730c353f, "n875kmcm1hum", -87.989990461, 116.907283071}, + {0x4ef60436b44843ce, "9vv08epn911w", 32.449242373, -94.1871191}, + {0xfce99dbce6b8d812, "zmntvg76r3d1", 74.153989434, 155.648087717}, + {0x6bbf5e29aabe76ba, "efzpwbebrtvc", 16.789929895, -1.104139465}, + {0xbd9e2662d58f6725, "rqg2dsqpjxmk", -6.91877209, 150.93865867}, + {0x05b98a939fa49ec6, "0qwsp4wznkgd", -52.719414527, -159.292215099}, + {0xd5bf6b22e8a76536, "uqzqq8r8nxkm", 84.068795958, 21.74154147}, + {0xe5c8d0782838c8a4, "wr4e0y18734b", 39.935351107, 104.800787151}, + {0x834ff8fe4a7d66a7, "he7zjzkbgpmb", -70.448288042, 28.032782777}, + {0x8bb15909416cc291, "jfspk2b1em19", -74.658681513, 84.561909134}, + {0xe49e0b98ed5265bf, "wkg0r67eb9kv", 26.775706353, 105.792188188}, + {0x3c2d58587ea0dabc, "7hqphq3yn3ec", -19.827910668, -36.373055684}, + {0x591ce61a7f42eda2, "c4ffd6mz8cqu", 60.92185762, -131.025997764}, + {0xee51b4a8dcdd29c9, "xt8v9b6wvnnw", 31.906862147, 158.635248509}, + {0x567037c022a0f0a8, "bts3gh12n3sb", 76.267097929, -151.38961018}, + {0xf5453f8a917db424, "yp2mz2njgqu2", 86.793015538, 90.678551017}, + {0x37066a6d524f801c, "6w36nvbk9y01", -9.459796862, -65.445138013}, + {0x6e2c55148e3415cb, "esq5b54f6hbw", 24.582321977, -14.058636678}, + {0x2819bd5cdf855bbd, "50dvur6zhpev", -86.135689527, -40.942091477}, + {0x0e24ac22b8d452b2, "1skbs8psuj9c", -66.005014302, -105.612084761}, + {0x3fc4c574a383d8f3, "7z2dbx53hgdg", -3.696708404, -10.520231479}, + {0xf29f7a36a83251a4, "ybgrnep8698u", 50.465749977, 128.616365049}, + {0x2b82e8239d072542, "5f1fh8wx0wkn", -78.394454734, -8.582355278}, + {0xab40a1ab19418ab4, "pe0b3bst865c", -73.07735297, 158.63778742}, + {0x5ade11db5581982c, "ccg13quph6d2", 55.10177056, -96.970639609}, + {0xbd0f27ac64d6899c, "rn7kgc34uu4t", -9.00142813, 139.736688094}, + {0x5b81c949c2c9fc5c, "cf0wkkf2t7y5", 57.374831882, -100.356783924}, + {0x4b0b3e2005eaa485, "9d5mw805xbk8", 12.217399182, -107.643704297}, + {0x31cb1b019ebdbaef, "675jq0dyrqxf", -27.198293612, -74.263479755}, + {0x466b8c7169f7903b, "8tpsswc9yy83", 28.953429995, -146.75302087}, + {0x341f347205d6b165, "6hgm8wh5uusq", -17.280825131, -85.402012056}, + {0x80733d58ea1e8130, "h1tmuq7b3u0m", -80.517360293, 7.574805394}, + {0xd7e3c425a7b01974, "uzjw89e7q0dr", 85.526379713, 41.511071431}, + {0xf32bf810c8157d77, "ydpzh4682pyr", 57.492914195, 123.577660503}, + {0x09a7d86ebbb670be, "16mxhvpvqtsc", -76.084884438, -115.795956106}, + {0x80a86bf86a05b26a, "h2n6ry3b0qt6", -89.570124668, 20.382059959}, + {0xa54d2bd364fea117, "np6krnv4zuhj", -48.434106337, 93.478884255}, + {0xe151e166d2432d46, "w58y2tqk8dqn", 20.81575757, 91.085272853}, + {0xeb41239371b77389, "xe0k74vjqxts", 17.638118683, 157.990551457}, + {0x7f0fc0dea05b02b7, "gw7w1rp0cd1c", 81.249529434, -17.51351344}, + {0x18cd1d15ca8ec9d1, "336ju5fbjv4x", -36.93738038, -120.757692013}, + {0xb7cfdfe18eb79c12, "qz7xzsdfqyf1", -2.83130344, 129.005533852}, + {0x4dcc0e8a2f92fcb0, "9r60x2jgkcyc", 40.869708094, -120.610787137}, + {0xc06673144faca383, "s1m7652gpkjs", 7.620929039, 7.472051733}, + {0x040e763717c85e85, "0h77desrt1g8", -65.45799254, -175.313768452}, + {0x65d895a73338c6d5, "drd9c9tm733e", 42.504265499, -75.161105698}, + {0x95aea4bcd927b78c, "kqrb9g6t4yvs", -9.737111308, 22.228900656}, + {0x2d46694f0626f3a4, "5p36kms64vtu", -48.792656162, -43.049457696}, + {0x696283069b9f2f9b, "e5j861nvmwrt", 16.925374143, -37.168218242}, + {0xa1f872b27f3c8fdb, "n7w75dmz7k7x", -69.77152806, 110.200911993}, + {0xd0c94c7498c4cc3c, "u34nsx4ssm63", 51.8068312, 14.263872778}, + {0xab8c6644e9ab8155, "pf66dj79pf0p", -76.87527404, 172.007092259}, + {0x233dda2c1ff680e2, "4dyxnc0zyu0f", -73.293935889, -58.061423374}, + {0xee8d32787b080e04, "xu6m4y3v1070", 24.820348286, 172.037362334}, + {0xd7940cd83de9d7e4, "uyb0tq1xx7cy", 83.0909083, 33.983086377}, + {0xfd974794462fcfad, "zqcng5265z7u", 84.173509258, 147.788595447}, + {0x5076c422b9ad15ae, "b1vd88ptpnbu", 55.284098433, -172.233050973}, + {0x7639c0689bf84ce2, "fsww0u4vz16f", 71.390154255, -58.322335246}, + {0x5895e9e13ed30382, "c2byms9yud1s", 50.343272165, -122.451014785}, + {0x4f03d588b2d959d5, "9w1xc25kv5dx", 35.11314016, -110.331012172}, + {0x91c03f21f00ca935, "k703y8gh1knm", -27.812553866, 11.891388958}, + {0xb8f1b7f570fb91cb, "r3svgxchzf8w", -35.508355931, 153.085202974}, + {0x559bf78ab73cde25, "bqezg2pr7mg2", 82.926080212, -163.323639964}, + {0x746404b5222939ab, "fjk09e9254wu", 74.638380829, -84.307227354}, + {0xf5c3d258c7efb54f, "yr1x4q67xyun", 85.6404392, 103.461680128}, + {0x3d4b6486687e8bc2, "7p5q91m8gu5w", -4.475404192, -40.378023331}, + {0x6c56f345031fe3e9, "ejcg6j833zjy", 32.94550465, -42.450745553}, + {0x1449b8b91870248a, "2j4vjf8sf0k8", -15.981544084, -175.87934812}, + {0xd0dc6dbb1d240f7f, "u3f6vfsx4h7r", 55.342107079, 14.673018372}, + {0xc6876637d993e634, "su3qdeytkgm3", 25.070346399, 35.626813087}, + {0x950a1eb226c88258, "kn51xdj6t215", -10.974912135, 4.555776474}, + {0x519cda546b3288a4, "b6fenp3c6b4b", 61.036133875, -164.96817809}, + {0x0ab83fadcac8248d, "1bw3zcfbt0k8", -86.870182455, -92.116363979}, + {0x4aeded2274bb3ffc, "9cqyu8mnrdzz", 8.220235663, -91.552859688}, + {0x02c854cc8468cc15, "0c459m44e361", -83.731863629, -143.379679553}, + {0x485de64cc086d371, "91fydm60hv9r", 11.015197977, -131.03098342}, + {0x07fb645cb6d77420, "0zxq8r5quxu2", -46.630269403, -136.039039131}, + {0x10f1ba4fc5cb0bd8, "23svnmy5td5x", -35.65139531, -161.78717358}, + {0x2a5ec17c69cb569d, "59gd2z39tec9", -79.720638819, -17.542867783}, + {0xd8d591cd3be9705d, "v3bt3m9vx5s5", 55.797777541, 57.010765873}, + {0xc05525a028926a1c, "s1bkc818k9p1", 10.678759471, 0.419743143}, + {0xd45407b6b40e6b31, "ujb0gepn1tpm", 77.493123114, 0.163495748}, + {0xb10d296d1fe94b48, "q46kkv8zx55n", -31.565162688, 93.374137371}, + {0x5db85dac8aed17e4, "cqw5vc4bxncy", 82.227296016, -115.055735099}, + {0x4b8d80ae57960372, "9f6s1ckrks1r", 13.367508673, -97.651439301}, + {0xa9bea97ce41817cf, "p6zbkz7430cw", -74.447087855, 157.361342867}, + {0x213767fea37245fb, "44vqgzp3f92z", -73.305966079, -82.442327658}, + {0x7261b028120eb7a9, "f9hv0b0k1uvu", 51.504619093, -60.78692721}, + {0xf7239d4b45652485, "ywjtuku5dnk8", 79.787469192, 120.426727615}, + {0x0fdfc3f290c02967, "1zgw7wnhs0nq", -45.273884557, -96.165896522}, + {0xcd466b9f6f74e0ce, "tp36r7vgfmhd", 41.198028172, 47.084415295}, + {0x79a45d2d28d9dcbf, "g6k5uc98v7fc", 58.323818628, -27.913970526}, + {0xd9bbd5c34af8adf5, "v6xxchubz2qz", 60.451028612, 66.84766364}, + {0xb820865a2b56991a, "r0h8dqjcbudj", -44.878824992, 141.434933549}, + {0x1782dcc1da71bdcf, "2y1ethfuf6yw", -10.607841787, -143.917016359}, + {0x349b4898b04c430c, "6kenj65h9j1h", -18.621026109, -74.296372789}, + {0xc6415fb64dbc2133, "st0pzekerhhm", 29.50573773, 22.836075921}, + {0x1e5084b6f3da6493, "3t889ermv9k9", -13.955762594, -111.720758454}, + {0x137e0f4bb6483945, "2ez0ykxq90wn", -23.748575187, -147.371584399}, + {0x7344b497c37ed984, "fe2c95y3gvds", 63.565851912, -66.39261977}, + {0xbce3ee29b9e885ec, "rmjywbetx22y", -15.728728722, 154.637687195}, + {0x46a8d35655ba5f06, "8une6pkpr9gh", 23.112369206, -137.015665192}, + {0xa56f86eb26f7515f, "nprseut6yx8p", -48.402501516, 100.719158935}, + {0x04e57d452f491d4c, "0mkruj9g94fn", -59.075616214, -162.595207224}, + {0x1ec5be4381d206d2, "3v2vwhw1u83e", -14.476931609, -99.923205586}, + {0xff39d439e7837bfe, "zwwx8fg7hexz", 82.896500602, 166.678250995}, + {0x6c1a1ee5e17a27cb, "ehe1xtg1g8mw", 25.608060372, -40.447382387}, + {0x29e0fdc333e1833c, "57hgvhtmw61m", -72.440145265, -26.843106058}, + {0xd7780df679de16af, "uxw0vxmtvscb", 87.360174045, 31.186990998}, + {0x9432e05d92e25f17, "khtf0rdkw9gj", -19.293955941, 8.100296871}, + {0x7703a2fb58fc3745, "fw1u5yuszhvn", 79.49104587, -64.867780585}, + {0x419bf25ed431e3ea, "86ez4rqn67jy", 15.333884202, -163.369341271}, + {0x93be7f69ee226ad2, "kfz7yugf49pe", -28.845628555, 44.247210699}, + {0x3664d51ab3a48b26, "6tkeb6pmnk5k", -14.797703447, -61.150659575}, + {0x172a45b14a7df075, "2wp4cdbbgrs7", -10.751325753, -147.589156884}, + {0x6dba5d12faaf8383, "eqx5u4rupy1s", 37.234758862, -23.719490991}, + {0xe945c53657a6bef0, "x52wbekrnuzg", 19.486853845, 135.731234286}, + {0xb782ac45a0489d1c, "qy1bsje092fj", -11.131806177, 126.39089515}, + {0x37b7c34a49f3bef8, "6yvw6kk9yfzg", -5.908955538, -48.41026799}, + {0xb58a7458d4a5498b, "qq578q6nnp4s", -10.59936508, 105.834306553}, + {0xab855b83cd0ce038, "pf2pr0ye1mh3", -76.064674084, 169.066603168}, + {0xe7a4c5bf17c5a4e1, "wykdcgsrsqkf", 35.660195911, 130.161054551}, + {0x1b4f68df22bf536c, "3e7qjrt2rx9q", -25.622779531, -107.691441117}, + {0xd78d78bc8f0b35ed, "uy6rjg4g1duy", 81.403729134, 37.17059553}, + {0x99e5a18d69653c04, "m7ku33c9dny0", -25.961772847, 62.986772977}, + {0x275d14ffaa04553b, "4xfj9zxb0jbm", -45.398222642, -64.599952087}, + {0xf76d0bbc1b068dde, "yxqhrg0v0u6x", 86.54568219, 121.279142008}, + {0xe2e731e369fbf9d6, "wcmm3sv9zgwx", 7.980514539, 131.206624978}, + {0x27c9b8bd60a6a7fc, "4z4vjgc0numz", -49.725470315, -52.12845606}, + {0x0e36b858196092cf, "1svchq0td29d", -63.071562448, -104.226509411}, + {0x4bc0d6b4304bae04, "9g0eee1h9fr0", 17.507498507, -100.391608568}, + {0x2bdeead5f72ef019, "5ggfppgr5vs1", -68.510884113, -5.664315606}, + {0xfde39cbb6967dbcc, "zrjttfv9dzew", 85.3572019, 154.244736535}, + {0x8ffb5b3460b7471a, "jzxpqe30qx3j", -46.520150485, 88.881048369}, + {0xbfaea13c8685dafb, "ryrb2g46hreg", -9.782941071, 179.684674084}, + {0x9cd2e1d21b9a4c0c, "mm9f3nhvm960", -13.633125156, 58.761644861}, + {0xe5703d0af4464a6e, "wps3u2rn8t56", 42.497633522, 96.172969238}, + {0x29bfeed4c4b8b0a5, "56zyxp64r2sb", -73.348457372, -22.540881142}, + {0x518b4b7c477ede96, "b65nqz27gvg9", 57.389133099, -164.234110104}, + {0x46930354bc790140, "8u9h6p5wg40n", 26.099192935, -144.750924106}, + {0xeec52bfbecf58446, "xv2kryzdyq24", 30.315905191, 169.45270667}, + {0xc8e381e6e22bf97c, "t3js3tr25gwr", 6.400929465, 64.060418888}, + {0x54749f64e801463f, "bju9yt780533", 77.680224036, -173.381422018}, + {0x9416eb6751441cdb, "khcfqtuj8hfe", -17.853186305, 2.752078813}, + {0x7365ad806ed5123a, "fekuv03fun93", 64.118091336, -60.598009022}, + {0x371051f3600d02ef, "6w853wv01n1f", -7.829099429, -67.42717003}, + {0xa2782a7dc5fddf6d, "n9w2nzf5zrgq", -81.519241536, 121.58875567}, + {0x7606677d4392afe4, "fs36fzb3kbry", 69.432437496, -65.620780268}, + {0x0413228e34df8cc9, "0h9k53jnvy6d", -63.977689181, -178.092248465}, + {0x733ad069d424adbd, "fdxe0ufn4kqv", 59.616990033, -56.917313582}, + {0x11b09420956e3d77, "26s9884pesyr", -30.67251386, -162.396997092}, + {0xcea721cf8381b47d, "tumk3mw3h6u7", 24.683719761, 86.196515523}, + {0x18f8ffbd1ba8ac78, "33wgzg8vp2q7", -35.883234046, -113.915886635}, + {0x41cdd70f3411c57c, "876xf3tn272r", 19.652886019, -165.128620337}, + {0xd676c5ead65302e4, "utvdcuqqbd1f", 77.851664277, 30.319886241}, + {0xbeeaa4a7455c923b, "rvpb99u5ck93", -16.776828249, 179.719904228}, + {0xe34555e55fd99f83, "we2pctbzv6gs", 19.676486238, 112.567178074}, + {0x9c59bf48135a0323, "mjdvyk0mc81k", -13.028797352, 49.142255184}, + {0x9e23477686118853, "msjnfxn62645", -21.274626878, 74.649709863}, + {0x5d29fc715572c8d6, "cnnzswbpfc4e", 80.106774583, -125.309937082}, + {0x41ec62ad646e79f9, "87q65cc4etwz", 18.642885423, -159.794609785}, + {0xf387caff97d6d06c, "yf3wpzwruv86", 58.753496569, 126.208738663}, + {0xc52daf2fae5aba12, "snquycxfcbx1", 35.999925634, 9.799545958}, + {0xa4479e40c0d2fa59, "nj3twh60ucx5", -59.478469813, 92.376006936}, + {0x8cfe3c20c6ddbe9a, "jmz3s866vqz9", -57.390696353, 66.646391975}, + {0x9a7572ec58a45789, "m9ur5v2snjcs", -33.896223925, 73.642303748}, + {0x8c1bb16ee215e617, "jhev2vr22rm1", -63.73572722, 50.316370369}, + {0xc2b1923bc0bf476d, "sbst4fy0rx3q", 3.706595401, 40.207538122}, + {0x53f08f59ee1bee28, "bgs8yqgf3gr2", 64.856818483, -139.641981943}, + {0x21439eb1963e71ff, "451txddq7tsz", -72.143366492, -87.557788375}, + {0xabc2eefa8549f952, "pg1fxyn597wp", -72.651967117, 171.559815254}, + {0xf1d085aa91a8d923, "y788cbnjp3dk", 64.820203006, 102.038583303}, + {0x33d86fc0e6276244, "6gd6zh764xj4", -24.805374281, -52.773743089}, + {0xc75935e4f2d7ced8, "sxdmct7kuz7e", 43.227936106, 25.73465239}, + {0xb70e523245c27010, "qw754dk5s9s1", -9.303437581, 116.834304961}, + {0x2e8ec8815da0b364, "5u7dj0bxn2tq", -65.736862204, -6.107436455}, + {0xc9e68adc905aad86, "t7m8pr4hcbqs", 18.320519646, 64.305809041}, + {0x448ebdcb2512cd84, "8k7cvkt52c6s", 24.239151818, -163.238947584}, + {0x56a1100d2e72a5b0, "buhj039ffbkv", 68.387620263, -140.611485965}, + {0xb863f081780a67d6, "r1jz10cs19mx", -38.13971673, 143.132014591}, + {0x9e70cbc8ff95647c, "mtsdrk7zkpk7", -13.642362185, 74.152055888}, + {0xf42f37a7277cafe2, "yhrmg9t7gkry", 69.92589949, 100.356480852}, + {0x4f23b16698750d59, "9wjv2tnsfn6p", 34.701167523, -104.383074728}, + {0x355031cedb324efd, "6p833mqv697g", -2.563025741, -89.584123636}, + {0xe2fb3bec228ff341, "wcxmrv12jztn", 9.387858697, 134.287858814}, + {0xab272d165e6227c0, "pdmku5kyd8mw", -76.489818122, 165.06523157}, + {0x0b47dee4b350af9a, "1e3xxt5mb2rt", -70.371935572, -110.056550871}, + {0x3ff35bc77aeacf9a, "7ztprjvuxc7t", -1.505720221, -3.902898808}, + {0x64e3e503389c3955, "dmjyb0tsmhwp", 29.315025023, -70.656286744}, + {0x4ca139b994d6a607, "9khmmfdnuum0", 23.437766131, -117.517794679}, + {0x7995a7961c396d34, "g6bug5hw75qm", 61.321281677, -32.557159604}, + {0x7c42d93f2d0b899b, "gj1ekgte1f4t", 73.716041765, -42.674252887}, + {0x6514fcd5a5937464, "dnbgtpe5keu6", 38.625761573, -88.721267182}, + {0x6c47a5169ff10ab9, "ej3ub5nzy45c", 30.38403178, -42.529530094}, + {0x0f5b0a60bf6f7c34, "1xehns5zexy3", -47.086073027, -107.990296966}, + {0xc34b036a7f6c99a5, "se5h6umzekdu", 17.646728664, 26.847639244}, + {0x3d6e42993208fd45, "7pr4569k13yn", -3.852757405, -35.011649354}, + {0xa627508c7c00245f, "nsmp133w00k5", -64.855382842, 119.588247218}, + {0x088d261314461036, "126kd4sn8s83", -87.787861244, -120.492531756}, + {0x1c84968d64f98b2d, "3k29e3c4z65k", -20.819981469, -122.902363779}, + {0x291b6530dde5577e, "54eqbd6xwpcr", -74.737286642, -40.404022113}, + {0x750b39278d97d89a, "fn5mk9wekzd9", 79.681689613, -85.222799713}, + {0x22229a04789dfe22, "48j9n13smrz2", -89.816581147, -59.499658033}, + {0xac4ed700de768b62, "pj7ef06yfu5q", -59.807015822, 140.013664686}, + {0x203ba10157055e3b, "40xu20br0pg3", -86.43506636, -79.101214559}, + {0xdb0c98931a81e7d7, "vd69j4suh7mx", 57.84646956, 71.242064589}, + {0x88f33e1ae76f218c, "j3tmw6r7ewhs", -80.582709731, 63.917583516}, + {0x031a7db6848fbb4e, "0de7ven4jyxn", -75.261461531, -152.679492132}, + {0x4afc0fe6c16f4d6d, "9cy0ztq1ex6q", 10.004722567, -92.474516961}, + {0xa8a9c062c886813f, "p2nw0sq8hu0m", -88.921944126, 155.421728563}, + {0x5cdc729ff2a4348a, "cmf757zknhu8", 77.89240526, -120.432895035}, + {0xb34826ebac1433d3, "qe42euxd2htx", -28.011978528, 115.839164567}, + {0x09ad38f732472f7c, "16qmjxtk8wrr", -76.422837738, -114.711997115}, + {0x71d2211447ce8bea, "f7922527tu5y", 64.749921384, -76.991592111}, + {0x29082a5f514a5985, "5442nruj99ds", -78.706474974, -41.55571834}, + {0x5b80aaade3fb3d0b, "cf0bpcg3zdyh", 56.259928945, -99.84994293}, + {0x0d3d2a2f26b868fe, "1nyknct6r1ng", -51.319493029, -125.906787112}, + {0x447e0df297e7b7a5, "8jz0vwnrwyvu", 32.509869424, -169.905670283}, + {0x283ba59060147d79, "50xuc4302jyr", -86.340104428, -34.056235267}, + {0x68f02314f38a50f2, "e3s2657mj98g", 8.500164974, -27.680846555}, + {0x36f8ce21c835961a, "6vwdw8f86qc1", -13.618847693, -46.820199044}, + {0xd95c2b8394df48d5, "v5f2r0wnvx4e", 66.141638803, 48.480158825}, + {0xdb5ae42d89b8e465, "veef8cd9r3k6", 65.135414637, 72.810146623}, + {0x2d92ecf6d90a3efa, "5q9ftxqt18zg", -52.957358576, -31.038361831}, + {0x78a47267398f483a, "g2k74tttjx43", 46.964702219, -27.655781752}, + {0x0f456bd6c398196b, "1x2qrpq3m0dq", -48.080073417, -111.832019324}, + {0x1fc8f1b423879df1, "3z4g3e13hyfz", -5.03702235, -97.31497032}, + {0x158c86920ba737dd, "2q68e4hcnwvx", -9.744663839, -165.095729391}, + {0x9471aa9e7c7adeeb, "kjsup7mwgcgf", -13.340353698, 7.006010334}, + {0x03c53bbac48cda87, "0g2mrfq4jme8", -70.783165838, -145.549391421}, + {0xafb87444da154c8f, "pyw78j6u2p68", -52.792657925, 177.542842714}, + {0xc18ce1db8a1517d6, "s66f3qwb2ncx", 13.087543764, 15.181391028}, + {0x8d57c8f3943e44b2, "jpcwjwwn7t2c", -45.314755704, 47.359465332}, + {0xfdcc135e1db91688, "zr616rhxr4c8", 86.040688012, 149.167859413}, + {0x43093bcfae2e64b9, "8d4mrmxf5tkc", 12.203435676, -154.006534156}, + {0x2ba246ccf9c9cc26, "5fj4em7tt762", -78.280746083, -4.070892745}, + {0x3e7a160ff1a0b79c, "7tx1d3zjn2vt", -13.788351803, -12.547484218}, + {0xa22e334703d97f55, "n8r36js3v5zp", -88.343489196, 122.789263639}, + {0xe7f84e406ee334b0, "wzw4wh3fwduc", 42.650743154, 132.453860477}, + {0x69abcdce1c7efec5, "e6pwvmhwgvzd", 12.465179307, -22.966061259}, + {0x312c121aa4dd9dc6, "64q146p4vqfw", -32.156473021, -81.453781235}, + {0xe1d71bd6cc8cae2d, "w7cjrpqdjkr2", 22.056793267, 102.973028851}, + {0x023bcb0e11000bb8, "08xwq3hj005v", -86.082514842, -146.672968425}, + {0x69627c903f3638d1, "e5j7t41z6swe", 17.502489974, -37.394941593}, + {0xb0c3bd2056fc8120, "q31vu82qzk0k", -38.361703644, 103.909346219}, + {0x1af99489b7af737f, "3cwt92erpxtr", -35.591713377, -92.049650998}, + {0x2bad80bb828e4293, "5fqs1fw2jt19", -76.626865056, -2.023649454}, + {0xeaa650c5acde8c83, "xbm51jedvu68", 1.964301067, 175.83025205}, + {0x809125b3da8930f1, "h28kcdyuj4sg", -86.336732257, 11.676971153}, + {0xf12295b32dfbc301, "y4j9cdtezg1h", 56.572010642, 97.808180063}, + {0xff77031eaa85d374, "zxvh67pbhr9r", 89.357341301, 164.640954088}, + {0x156e976908543acd, "2pr9fu88bhxd", -3.88626021, -169.331578519}, + {0x208c8fe4bd04c099, "4268zt5x0m09", -88.433219577, -74.899967}, + {0xb5d468f6cd3bd608, "qrb6jxqe7gc0", -1.014284011, 101.852350588}, + {0x9edccca76e085bd5, "mvfdt9vf11ex", -12.206831169, 82.515268288}, + {0xd683950342cea9f8, "uu1tb0u2tunz", 68.51497056, 35.865467482}, + {0x3eb078915e07d1e5, "7us7j4by0z8y", -19.143978708, -5.052647706}, + {0xdcd6128951cdcaff, "vmc152bjtr5g", 77.524633443, 57.799302873}, + {0x232ae81b518c3fd0, "4dpfh6ujjhzx", -78.382446322, -56.409077041}, + {0x29aaf07ea481d208, "56pg0zp4h790", -78.183841972, -22.808806514}, + {0x10d77424bdc677ab, "23cr895xstvu", -33.831078418, -166.96520716}, + {0xb5180b446e93ce80, "qnd0qj3fkg78", -8.364311901, 93.07878369}, + {0x655867b0d7a78f65, "dpd6gd6rny7q", 42.684494365, -86.678744961}, + {0x6047bb054bab6371, "d13vq1bcpejr", 7.963903582, -87.274031742}, + {0x4da1307f515fe927, "9qhm0zujcznk", 34.672503694, -117.73490171}, + {0x9f347e498fcedf5b, "mwu7wkdgtvgp", -6.390663498, 73.755248299}, + {0x7b842bab31ef4610, "gf22rbtjxx31", 57.703925124, -10.550671652}, + {0x11f5766b9eeb1de8, "27urduwyxdfy", -22.5620398, -162.642992435}, + {0x2fcb19585202da03, "5z5jkq2k0ce0", -49.66712141, -6.844102724}, + {0x002dc0a52972980f, "00qw1999fbd0", -87.530519531, -170.791276102}, + {0x10ef26b27cc06ebb, "23rkedmws1rc", -37.164252027, -158.393143428}, + {0xd642d9313d220f4a, "ut1ekd9x487n", 73.711225985, 24.809300406}, + {0x12de7720fc56052a, "2cg7f87wbs2k", -34.494514836, -141.564995652}, + {0xf157f3406f97f9af, "y5cz6h3gkzwu", 67.392108943, 92.551444551}, + {0x0d67dc6b4827093e, "1pmxsuu84w4m", -47.874264965, -127.050594337}, + {0x8562fa9dea47e677, "hpjgp7gb8zm7", -50.076931512, 8.409730244}, + {0xb68d065a368e8c09, "qu6hdqjqju60", -20.268722559, 126.668836378}, + {0x182d3b37b01c2b77, "30qmqexh3hpr", -42.650920041, -125.915627159}, + {0x975fe5eba7387095, "kxgycux771s9", -0.19444106, 27.860432979}, + {0x44dbc81754238673, "8mewh5un4f37", 32.013829478, -163.646730749}, + {0xa0c33095cd3b9273, "n31m15fe7f97", -83.474922499, 103.055353764}, + {0xa8d658e85288037e, "p3c5ju2kj01r", -79.604872362, 147.90949541}, + {0xd6d65521ccf5b1d0, "uvc5b8fdyqsx", 78.007557699, 35.181930172}, + {0xe7bb02522f5eef3d, "wyxh4njgcvrm", 37.299256855, 133.689622066}, + {0xd2018c1f1070c87e, "u80ss7shf347", 45.811063051, 23.395485607}, + {0x9c22025b2f0c3cff, "mhj04qtg1hyg", -22.463753298, 52.138075645}, + {0x0d12c63553af1f86, "1n9ddebmpwgs", -52.976552219, -132.78009171}, + {0xb4d8479a12a4f410, "qmd4g6hknmu1", -13.567397552, 104.211432382}, + {0xc92202c60e78d1dc, "t4j05jhfg38x", 11.277947207, 52.169749426}, + {0xf216beac4aae33e0, "y8ccxc2bpsty", 49.489313511, 115.302873267}, + {0x75e1d5b0e15dac10, "frhxcd71cqq1", 85.749998426, -72.351775434}, + {0x9732cc34f43a0040, "kwtdse7n7804", -7.979120847, 30.436398986}, + {0xd94d5a6c0bc6bb0e, "v56pnv0csuxh", 64.539466992, 48.11037433}, + {0x9c9073faf9ffe1be, "mk877yrtzzhv", -19.080853127, 56.776996334}, + {0x4ad43fecf5eae997, "9cb3zv7pxcnt", 10.18150011, -100.553400016}, + {0x1d808114155a85d5, "3q08250pcb2x", -11.188242178, -123.046799155}, + {0x6459225bf64c97f7, "djdk4qzq9kcz", 31.678846441, -86.727048049}, + {0x4347b108076acb52, "8e3v2207ec5p", 19.204708943, -155.027567767}, + {0x964e1f0c788743e1, "kt71y33shx1y", -15.153541094, 26.995663632}, + {0x76880e0e2fbdcbc0, "fu40w3jgrr5w", 67.593980277, -53.1546295}, + {0x1b13bddc551ab770, "3d9vvr2p3bvr", -29.885682621, -109.80826742}, + {0x32dabc3f473408f4, "6cecsgu76h4g", -36.277648786, -50.761896195}, + {0x11db6d27ce688cee, "27equ9yfe26f", -24.115932572, -163.972520428}, + {0x1780f1e5536bc07e, "2y0g3tbmeg07", -10.646164419, -145.128889654}, + {0xc49dee77a48896e8, "skfywxx4j2cf", 27.902874574, 15.412672962}, + {0x09e720a2c77aab9f, "17mk18q7gbpt", -71.01360699, -116.292515174}, + {0x860bd2d3c2f49563, "hs5x5ny2ykbq", -66.232297083, 27.562562685}, + {0xaea4e02d9e10e600, "pukf0cdy23m0", -65.732868147, 175.466440524}, + {0x4e69aaa60a5d515a, "9tnup9hbcp8p", 28.833788317, -102.671656341}, + {0x7a35a37df41edbed, "g8uu6zgn3vey", 50.009495887, -15.695262663}, + {0xbb9f4e670b887cdf, "rfgnwtscj1ye", -28.358277782, 173.261141262}, + {0x2556a137833ad7b4, "4pcb2ew37ccv", -46.342853725, -87.508343489}, + {0xd52d36ee671a81ef, "unqmevm73b0y", 81.15244503, 8.961143605}, + {0xfbf3028b2bb9d655, "zgth52tcr7c5", 65.393606921, 175.932281196}, + {0xbef13c68eaf22a02, "rvsmsu7by8p0", -13.072228396, 174.94074107}, + {0xd486a5cbd7e85c99, "uk3bckyrx1f9", 69.065475562, 13.77475614}, + {0xab1d322b63e744a9, "pdfm4bv3wx2b", -73.647927421, 160.792402017}, + {0x3afa863516935e7b, "7cx8de8qkeg7", -36.454289737, -0.592720644}, + {0x7d8574277af16efe, "gq2r89vuy5rg", 81.485055847, -33.368300205}, + {0x21a2c4752171e89a, "46jd8x91f7n9", -78.269031459, -70.992173509}, + {0x9aa5b43f4fd1a413, "mbkv8gugu6k1", -42.605697744, 85.469357513}, + {0xfc31b7d66c3d73c8, "zhsvgpmd7ptw", 71.363495109, 141.81921288}, + {0x06145e317ed82a19, "0sb5wdcyv0p1", -62.649750487, -157.21172806}, + {0xb9d87dd6d8cfa8df, "r7d7vpqstyne", -24.612689557, 149.642971222}, + {0x1337a109c185d388, "2dvu22f1hr9s", -28.779847229, -149.400144965}, + {0x4ff8ed2b50ef8340, "9zwfubuhxy1n", 42.67582399, -91.543245155}, + {0xa616434b1b8ebb7e, "nsc46ksvjuxr", -62.860139219, 114.011906892}, + {0x4f007b51783457a1, "9w07qncs6jcu", 34.359129056, -111.882617497}, + {0x85fc5c3991c13e7f, "hry5sfdjs4z7", -45.776323179, 19.899168466}, + {0x8463d866dda833a8, "hjjxhtqxp0tu", -60.614487143, 7.941363592}, + {0x52b010cad1cde926, "bbs11kqjtrnk", 48.012609678, -140.561594822}, + {0x6c582c62908fd7ee, "ejd2ssnhjzcy", 31.048092593, -41.629694411}, + {0x589963b9794b2c8b, "c2dq7fct9dq8", 48.927195298, -120.419009218}, + {0x737a178c314f041c, "fex1g31j9w21", 65.00158828, -57.511989875}, + {0x7f61641425699904, "gxhq8515e6dh", 85.534672619, -16.521921999}, + {0xf6c4f18af83c53d8, "yv2g32rs7j9x", 75.10467262, 124.870049778}, + {0x8f485ae113b731c6, "jx45ps8mqwsw", -50.071999823, 70.642718262}, + {0x17281cd6e7b4d43d, "2wn1tpr7qmb3", -10.945913626, -148.832548303}, + {0x68d4ba1f14d51575, "e3bcn7snunbr", 10.03995356, -32.414989136}, + {0xd763c9a80dfed500, "uxjwmb0ezvbh", 85.474308068, 30.488079803}, + {0xe57c2e3f806e3c7d, "wpy2wgw0esy7", 43.700978667, 99.094091921}, + {0x115555d40d2d87c2, "25bpcp0e5q3w", -22.504943628, -179.955226442}, + {0x972d656a5ac71fbd, "kwqqbukuswgv", -8.63307093, 31.328743003}, + {0xcbbdf4864ae865a5, "tfyz91kbx1ku", 16.794069164, 88.292962724}, + {0x9e689e78bf8a62d7, "mtn9wy5zj9je", -16.577161516, 76.942649215}, + {0xe86c364702fecb3d, "x1q3djs2zv5m", 7.325290839, 143.883126765}, + {0xfd92eff475cfc02b, "zq9fzx3ptz02", 82.087048962, 149.042147996}, + {0x7d167092ecbc2838, "gnc714rdrhn3", 83.508861272, -43.187633523}, + {0x1dd9e2021ec8e5cf, "3rdy40hyt3kw", -1.756688828, -119.788167125}, + {0x7d103a812def1ac2, "gn83p09exwed", 81.74166387, -44.338433674}, + {0xa32808634ae53df2, "ndn0hsubwnyz", -78.723784246, 121.142043692}, + {0x8f277e26e330ee4c, "jwmrw9r363r4", -53.518302437, 75.17851951}, + {0x00d7ae6976ed7ad9, "03cuwucqxpxe", -79.33798524, -165.99040432}, + {0x150bb7f7c99efe72, "2n5vgxy9mvz7", -10.196439858, -174.563570933}, + {0x3c03ac9209a3adc7, "7h1ut4h9nfqw", -21.697813172, -42.312857584}, + {0xb8aad6b9980ffadf, "r2peefds1zxe", -44.370305171, 156.965188566}, + {0xdd112dad4c37d9c6, "vn8kvcbd6zdw", 82.407501557, 45.605056149}, + {0xf2b77ccb88505b78, "ybvrtkw8b1er", 50.561966612, 131.372456316}, + {0xd6260103a7395966, "usm020x775dq", 68.953519701, 29.54133826}, + {0x6fa50262a5d89404, "eykh4sp5v2b0", 35.881995503, -5.505298329}, + {0x8bf71b5295766e0e, "jgvjqnnpftr0", -67.949080813, 86.053278583}, + {0xf2667eef5d6cec87, "y9m7xvuxemq8", 52.679385858, 120.229716806}, + {0x5c93f1070bb37a41, "ck9z21scqex4", 71.595387567, -121.282250961}, + {0x1d2085e31fbacb66, "3nh8csszrc5q", -11.092193795, -128.599096809}, + {0x3386f5bb3b6f18b4, "6f3gcftvewdc", -31.669858131, -53.704107837}, + {0x0bddbff2dcfac855, "1gfvzwqwzc45", -67.860010828, -97.043960961}, + {0xb1750ddc3ea8e96a, "q5uhvr1yp3nq", -23.031800546, 95.858435982}, + {0xb2c9552f211e99c3, "qc4pbct13udw", -38.004215766, 126.602404023}, + {0x2301e5d90c7eeb7e, "4d0ycq8dgvpr", -77.527270837, -66.3895233}, + {0x75893b955c61845e, "fq4mr5bwd625", 79.69457941, -75.277536837}, + {0x73ec57161a9bd8e4, "fgq5f5humgdf", 63.957657942, -47.717835722}, + {0x9ff4caaf90b0329e, "mzudpcwhq0t9", -1.045715236, 85.427206063}, + {0x9bc05f227ebf79b8, "mg05y8myrxwv", -27.463333049, 79.043872101}, + {0x11ea13ed9b38d2be, "27p17vdv739c", -27.874151478, -158.737536444}, + {0xcb6e04ebb7948448, "ter09uxrkk24", 18.395127561, 77.43079329}, + {0xe0178fdbb68a740d, "w0cszqxqj9u0", 5.090455987, 92.438176515}, + {0xb7306fa21d1577eb, "qws6z8hx2pvy", -7.95281439, 118.812340152}, + {0x417637ffaaa9857d, "85v3gzxbp62r", 21.442579326, -172.441437897}, + {0x9c73ffc9815573ee, "mjtzzkd1bpty", -12.675134311, 53.407295611}, + {0x30ab61438a717c58, "62pq2hwbf5y5", -43.876498576, -68.545323706}, + {0xac2ba4ed9b96149b, "phpu9vdvksb9", -66.677845024, 145.979312412}, + {0xb0d2c56c5b10a02f, "q39dbv2v22h2", -36.049361083, 103.393380636}, + {0x496b7cb4bc7cb6f5, "95prte5wgkvg", 18.211023728, -124.558034493}, + {0x4c24062e90c0e832, "9hk0dcnhs3n3", 24.000411826, -129.24571977}, + {0x4f40a095be38ba27, "9x0b15ey72x2", 39.395301782, -111.396068041}, + {0x65083d452ccb05b5, "dn43uj9dtd2v", 34.0882718, -86.657859251}, + {0xdb0cb09bb6128390, "vd6c16xq2b1t", 57.846837889, 71.43210976}, + {0x31952a5e77fa0efa, "66bknrmrz87g", -28.786967328, -78.116242369}, + {0x7b3efe410e5dbfd1, "gdzgwh8fcqzx", 61.10921313, -11.336796933}, + {0xb7b76bd74c22d11e, "qyvqrpud4c8j", -5.889692858, 131.44672777}, + {0x879a559a7f003266, "hye5c6mz00t6", -52.764755962, 38.03158524}, + {0xb89d625663c03a13, "r2fq4pm3s0x1", -39.686476671, 149.509344105}, + {0x8eeed3414fa361d1, "jvre6hbgnehx", -59.870836076, 89.38608058}, + {0x459e4972eb796a1e, "8qg4kwrcg5p1", 38.398906871, -164.322714632}, + {0xa349201f3b2102eb, "ne4k07tv441f", -72.401779484, 115.683033699}, + {0x7e5f4133bd3a7d6c, "gtgn2dxx79yq", 78.457370786, -18.248818826}, + {0x6a8952314ea6ec8a, "eb4p4dbfnvq8", 1.245946475, -8.326308058}, + {0x5c4f78215b167a21, "cj7rh8bv2tx2", 75.766769408, -130.230872362}, + {0x9022c450105429c3, "k0jd8n0hbhnw", -44.526749978, 7.734383835}, + {0xc97f523125a5dcd3, "t5zp4d95nrfe", 22.338508784, 54.955258313}, + {0xe9b0c198cc97fc27, "x6sd366dkzy2", 14.470795402, 152.636702197}, + {0x0d19a7b31a500852, "1ndugdsub045", -52.58799025, -130.972477957}, + {0x4929843e8d4078e9, "94ns8gne81wf", 12.058104376, -125.817480402}, + {0x4987fe6384c8b90f, "963zwsw4t2wh", 13.999761257, -120.994944539}, + {0xfd7211aaa6920328, "zpt13bp6k81k", 87.407613511, 142.118305639}, + {0xfd35b1bcb9bd392a, "znuv3g5trnwk", 83.909017834, 141.761707417}, + {0xde6020fc6ab57f1e, "vth21z3bqpzj", 73.16491089, 73.556137224}, + {0xcdd8f19b626ecc59, "trdg36v2ev65", 42.77400982, 60.17949276}, + {0xb57e2512ba93ab96, "qpz2b4pukfpt", -1.262686518, 100.206169559}, + {0x038bfc41d76e8ade, "0f5zshfreu5e", -77.404238563, -140.797525442}, + {0x2726a1743ed0a9ef, "4wmb2x1yu2ny", -54.76019269, -59.389494876}, + {0x15320615d65b074e, "2nt0d5fqcd3n", -8.327838097, -172.877703449}, + {0xfe33ba6f40de3c6d, "zstvnvu0vsy6", 71.223146672, 165.888306168}, + {0x8637b4adf406d3d8, "hsvv9cgn0v9x", -62.303779405, 30.666996084}, + {0x47efdfab7d439786, "8zrxzbvx8fcs", 42.148977528, -135.354961419}, + {0xe40ad70d34ff1201, "wh5ef39nzw90", 23.168613097, 95.02244847}, + {0xd091829bc528c13b, "u28s56y5530m", 48.531252467, 12.104327082}, + {0x886bb3eb872da4d1, "j1pv7uw75qke", -83.426880142, 56.071961041}, + {0x34948dcd28c578c8, "6kb8vm98spwd", -18.119075223, -77.813921566}, + {0xa10e46f290405774, "n474ewnh81cr", -76.870554768, 94.380800054}, + {0x21c5b4d91c77a9a0, "472v9q8wfynu", -70.715055947, -77.639567143}, + {0x87b907727e524482, "hywhfwmyb928", -52.56704134, 42.305281992}, + {0x002df37faffc310d, "00qz6zxgzhsh", -87.27746851, -170.376013912}, + {0x2d785c9baee078c5, "5pw5t6xfw1wd", -47.183095028, -36.32087786}, + {0x1a7f97ff26aa7094, "39ztgzt6p9s9", -34.103957421, -101.780792319}, + {0x1cfa1193df1e926b, "3mx134yz3u96", -13.82639817, -113.852957889}, + {0x3d37156ff4aeaa89, "7nvjbvznpup8", -5.987870618, -37.925835135}, + {0xe38d05b8515f821b, "wf6hcf2jcy11", 13.504590026, 126.639481045}, + {0x1ee83f1f724a5e4a, "3vn3y7vk99g4", -16.546001232, -92.179000852}, + {0xfc3307a4b6f3be99, "zhthg95qyfz9", 71.154127802, 142.189821772}, + {0x6bce328672c56c70, "eg7351mkspq7", 18.4647096, -6.540461659}, + {0x37c0972596504292, "6z09f9dqb119", -5.307978781, -55.433919666}, + {0x2194040c8188492c, "46b08341j14k", -74.437688414, -78.736049263}, + {0x719eab19bf29a642, "f6gbq6ez56m4", 60.527642267, -73.196608174}, + {0x7eb1ae110401e931, "gusuw48407nm", 71.117608842, -4.306621474}, + {0x7a9feb77438b0eeb, "gbgyqxu3jd7f", 50.36013897, -5.684840117}, + {0x95ad9887feeae51d, "kqqtj1zyxckj", -8.954107238, 20.621332705}, + {0x8be00eb9dea4dcc6, "jgh0xffynmfd", -73.02094191, 84.719624444}, + {0xc9db6fc44e3b2a91, "t7eqzj2f7dp9", 20.90325976, 61.129119957}, + {0x3a63bc32797ac52f, "79jvsdmtgc2k", -38.394848941, -14.208593892}, + {0x827f14c8c22cbcfc, "h9zj9k625kyg", -79.166081074, 32.401920322}, + {0x3b621fd9d71e32ac, "7ej1zqfr3stb", -27.779035167, -15.14698428}, + {0xd2fc09425904f9fd, "ucy0khkt0mwz", 54.911930374, 42.369481079}, + {0x2966568334f0e8fb, "55m5e0tny3ng", -71.099604415, -37.829770036}, + {0x23eeabdb8356d10d, "4grbrqw3bv8h", -71.638768387, -45.024343527}, + {0x008fdbc8b92466d1, "027xrk5t4jme", -87.296476236, -163.50462175}, + {0x7ce038584d69a039, "gmh3hq2ee6h3", 73.33572532, -27.585837921}, + {0x3d9abaf8391a1e60, "7qecpy1t38g6", -8.227854166, -28.1338576}, + {0x5e3d844c92cc836c, "csys8m4ktk1q", 72.538026493, -103.34507156}, + {0xa1485b3c258837cc, "n545qg15j0vw", -72.536711314, 93.110726285}, + {0x3153d2096d3b4484, "659x42ce7e28", -24.077335926, -87.789537193}, + {0x508ffab4ca6585c3, "b27zpe6bdq2w", 47.654691965, -163.143098756}, + {0x6755cc94aabc7f8c, "dxbwt55brjzs", 44.752882151, -66.571687898}, + {0x96f8b279de2e13fa, "kvwc4yfy5s9z", -13.848584596, 43.366966475}, + {0xced6f85b0c45c955, "tvcghqsd8r4p", 32.907267361, 81.403898299}, + {0x73548309eb49865e, "feb862gc9635", 66.142085335, -66.692792899}, + {0xd5017a43dcdef2c7, "un0rnhywvvtd", 80.007749987, 0.624415015}, + {0xca6c1ed1ae1ffc01, "t9q1xnef3zy0", 7.331055335, 76.250350186}, + {0x16f27e49154b7a61, "2vt7wk8p9ex6", -13.421239745, -138.592455125}, + {0xae82f7a4ced0907c, "pu1gg96fu287", -66.833478498, 171.368705553}, + {0x2bceb744fc966d65, "5g7cfj7wktqq", -71.381191499, -5.883663787}, + {0xbe003e0da30645fc, "rs03w3e30t2z", -22.227887256, 158.130706601}, + {0x986b4f927a8f0a1b, "m1pnz4mujw51", -38.175397875, 55.159503923}, + {0x85b3e6c778245fd8, "hqtyejvs4jgx", -52.262617487, 19.475417269}, + {0x979cbb377a25f032, "kyfcqevu4rs3", -6.79019702, 37.910830843}, + {0xf6aa3f604b0485b1, "yup3ys2c0k2v", 67.831158485, 134.231998802}, + {0xeec9c3b522a845eb, "xv4w7e92p12y", 29.242866676, 172.421451883}, + {0x2303cc52fe0d05c5, "4d1wsnry1n2w", -77.572024966, -65.204157126}, + {0xbeae20c00c8085e3, "rur21h0dh22y", -21.071431611, 178.990127939}, + {0x35fcd60c72b5d567, "6ryed33kqrbq", -0.783376876, -69.50851271}, + {0x840567e71c4f8667, "hh2qgtsw9y36", -64.875864358, 0.51162779}, + {0x5c53d7b15d305369, "cj9xgdbx619q", 77.316164559, -132.736042029}, + {0x233884b9d777c85f, "4dw89ffrfz45", -75.833134609, -58.279259814}, + {0x9781483e27e0788c, "ky0nhgj7w1w8", -10.178224026, 33.96621641}, + {0xfd0d8899656061ce, "zn6sj6c5d1hw", 80.875088491, 138.747803329}, + {0x900201724e0e9363, "k0102wkf1u9q", -44.921353874, 1.434822654}, + {0xa6cdb5ae7b07c4b6, "nv6vccmv0z2c", -59.450243873, 127.702021542}, + {0xee5bd4c8b2e351ce, "xtex9k5kwe8w", 32.278624413, 162.481549753}, + {0x3752e178b007b6e2, "6x9f2y5h0yvf", -2.383310002, -65.001943605}, + {0x34919ec8b0b72ef6, "6k8txk5hqwrg", -18.697966755, -77.723862176}, + {0x6b2628b250a2a7fd, "edm2jdkhnbmz", 12.669298909, -14.869697478}, + {0xbaae15884049579d, "rbr1c22095ct", -43.284652576, 178.648726813}, + {0x7ac36092a4d24eab, "gc1q14p4u97b", 51.691152681, -9.43843075}, + {0x07825a0b1271db5c, "0y15n2skf7ep", -55.719074238, -144.563154534}, + {0x952a88aad2dab839, "knp8jbqkvbw3", -11.247808397, 10.808398972}, + {0x7e5d37a3564017b0, "gtfmg8uq80cv", 78.359732733, -19.176290035}, + {0x4ee2d5f1fe0297ec, "9vjecwgy0bcy", 28.822293635, -93.444522437}, + {0x0f569efb242129c2, "1xc9xyt444nw", -46.106517498, -110.043087712}, + {0x90614c536be6bb17, "k1hnsnvcwuxj", -38.195060399, 5.808976876}, + {0x211099a1bc684053, "4489m8ewe105", -75.713905134, -89.050240359}, + {0xea1889bdbe9fb868, "x8d8mgeymyw6", 2.876779483, 161.273715701}, + {0xa57f07ba17825193, "npzhgfhrh98t", -45.559092141, 100.014575357}, + {0x95773167eb41bed3, "kpvm2tzc86ze", -0.451539929, 7.415449106}, + {0x2e29770626d3752b, "5snrf1j6ueuk", -66.131709223, -13.615638022}, + {0xb8faa083b7992412, "r3xb10xrm4k1", -36.558496623, 157.202559225}, + {0x9c4db2392c3211a0, "mj6v4f9d688u", -14.575721208, 48.990205419}, + {0x2d4f156335e379f1, "5p7jbstpwewz", -48.181981108, -40.752122861}, + {0xc667b318be384feb, "stmv665y717y", 30.466168544, 30.690098355}, + {0xa90f95f582e62c97, "p47tcxd2wsq9", -76.291700155, 139.991170682}, + {0x725232c80096dbd3, "f9935k00kvex", 53.635327542, -65.599156542}, + {0x5a705bde0a6f602b, "c9s5rrhbexh2", 54.047364636, -106.54971714}, + {0x938d7ff13fe8162b, "kf6rzw9zx0c2", -30.94444821, 37.246357981}, + {0xa6d07b81f6b8cda9, "nv87r0gqr36u", -58.486010735, 124.413957929}, + {0xf4438c8704dd47b3, "yj1st1s4vp3v", 73.924767225, 92.334811765}, + {0xb5958160bd51a311, "qqbs2s5xb6jj", -6.260865006, 101.979921673}, + {0x19d6c8baab73966e, "37cdjfpcffc6", -23.543385585, -121.377176462}, + {0xc40cebe3aaec3d24, "sh6frsxbxhyk", 24.326589129, 4.207729195}, + {0x8f0d259ddcaa3e9a, "jw6kc7fwp8z9", -53.987155532, 70.722759587}, + {0xe16816a9c7bf36eb, "w5n1ebf7rwvf", 17.143386253, 98.605714689}, + {0x4594222b0146ce53, "8qb24bs18v75", 37.971782816, -168.272058409}, + {0x362d6da794ae4002, "6sqqv9wnpt00", -19.897930015, -58.460676543}, + {0xfa729c2473122e3c, "z9t9s93m28r3", 53.708941152, 165.433876945}, + {0x9643c4346a5ccaf2, "kt1w8e3bcm5g", -15.714412988, 24.633809738}, + {0x0b66da548af56066, "1emenp4byph6", -71.152787373, -104.497913216}, + {0xa6e09a32032096a1, "nvh9ndh342cb", -61.688056682, 130.369704935}, + {0xe18feab374c01b02, "w67ypdvns0eh", 13.727162866, 106.860069641}, + {0x1c536148e6354951, "3j9q2k766p4p", -12.940096203, -133.226649463}, + {0x1ef4469cf8e135f4, "3vu4e77sw4uz", -12.198155577, -95.477107273}, + {0x165e6287e8c03e2f, "2tg651z8s0z2", -12.294984465, -152.787370205}, + {0x36e10538e53ba3f1, "6vhhbf757fjz", -16.027109599, -50.587752426}, + {0x22a13f05070e015f, "4bhmy1871s0p", -88.980480293, -50.009357661}, + {0xde67878824d6d940, "vtmsg214uvdn", 75.366713519, 75.378777094}, + {0x6839e1cfd983b40e, "e0wy3mythfu0", 3.943591672, -35.443744921}, + {0x9fe3f048e5cd282c, "mzjz0k75tnn2", -4.370552215, 86.851266867}, + {0xa567f0b92c5f0c47, "npmz1f9dcw64", -47.974042245, 98.164968881}, + {0x2a876bee82e825bd, "5b3qrvn2x0kv", -87.467563195, -9.142721292}, + {0x7893ba1a50f823d3, "g29vn6khz0jx", 48.704582496, -31.008603183}, + {0x596f2ef8566c5bbd, "c5rkxy2qejev", 64.107744187, -124.463633992}, + {0x57ec469153ca3e5f, "bzq4e4bmt8z5", 86.236758153, -137.68007502}, + {0x76712c440d137241, "ftsksj0e2et4", 76.756558505, -61.346941374}, + {0x8adc0ef45f7e7d41, "jcf0xx2zgtyn", -80.027171662, 81.893278081}, + {0xb285f2bbe3d3d555, "qb2z5fz3ugbp", -42.347853206, 124.979642702}, + {0xfec2c680ae5764e6, "zv1de05fbxkf", 73.564964516, 170.996388751}, + {0xc13456d3f5040264, "s4u5enzp0h16", 16.122286777, 5.766450658}, + {0x564851b847bbc067, "bt453f27rg06", 73.709223149, -154.609919366}, + {0x27e818e39e5fa3da, "4zn1jswycyjx", -50.423307434, -47.561448288}, + {0xfc4aa1fd59bdbb3e, "zj5b3zbtrqxm", 73.212454351, 140.351349597}, + {0xcfaa390b26fa93bd, "typ3k2t6zb9v", 33.972948884, 89.139625044}, + {0xdf1b0e1c9627583f, "vwehw74q4xd3", 82.371065629, 71.996608734}, + {0xf28ed76a181b8853, "yb7efuhs3f45", 47.08814318, 128.798982292}, + {0xa3171eddab4baaf1, "ndcjxrec9fpg", -73.522985604, 114.230088909}, + {0x9f89b1961293d04d, "my4v35hkkg84", -10.309920118, 82.667173383}, + {0xe889bdbbbab7d7df, "x24vvfxuqzcx", 1.025247544, 150.380808049}, + {0x8cbf776ea300054a, "jkzrfvp3002n", -61.891305453, 66.576118754}, + {0xc7628223d07a5b34, "sxj848yhg9em", 39.379944261, 30.352632667}, + {0xe5adb376de083567, "wqqv6xqy10uq", 36.119962148, 110.861369238}, + {0x29a9fd36ce558ce3, "56nzueqfbq6f", -77.369336537, -24.050774318}, + {0xe76c1289153c7499, "wxq1528p7ju9", 40.961053433, 121.08045724}, + {0x13d0100c9d0f8376, "2g81034x1y1r", -25.129990494, -146.235503559}, + {0x303f361600cbc8a3, "60zmd5h0tg4b", -39.797871638, -79.711053161}, + {0x7849fa4fdf280f56, "g14znmyz507p", 51.8882587, -40.848751659}, + {0xc7b587c6bd8093ca, "syusgjpxh29w", 38.832382085, 40.220445328}, + {0x9778779997ba3595, "kxw7g6drr8ut", -2.138337855, 31.435302622}, + {0x376abf13e36a219a, "6xpcy4z3e8ht", -5.302018265, -56.327777733}, + {0xe03c2831d3f51c57, "w0y2hdfmynf5", 4.234880126, 98.99016663}, + {0x3d4f7d5693f2671f, "7p7rupnmy9mj", -2.816998407, -40.24503724}, + {0x63491ac18cc0049d, "de4jphdds029", 17.77905629, -64.376277058}, + {0xc3db8f5cfba34e46, "sgesyr7vne74", 20.563162548, 38.951966152}, + {0x8f2d0736eff9770a, "jwqhfergz5vh", -53.99027176, 76.058313667}, + {0x3fbde5a87b682931, "7yyycb3ve0nm", -5.842408212, -1.67836775}, + {0xda490424ac3e88d8, "v94h895d7u4e", 51.421917018, 70.339443683}, + {0x92ac04d3955fdea4, "kbq09nwpczgb", -43.468781742, 42.23976524}, + {0xf157f2121291f2b2, "y5cz44hkk7tc", 67.335953622, 92.554854864}, + {0x94509e20a644e8bd, "kj89w8568mnc", -13.798371835, 0.993252723}, + {0x447c722816f678cc, "8jy74b0qytwd", 32.872282406, -171.089456692}, + {0x0e15089e431c1ab4, "1sbhj7k33hec", -62.56003324, -112.263402618}, + {0xea1715be397c4e50, "x8cjcgjtgj75", 5.246987415, 158.990840212}, + {0x79800e607ccc583a, "g600ws3wtjd3", 56.362382075, -33.462077614}, + {0xd57857546ed68799, "upw5fp3fuu3t", 87.887000896, 8.527999921}, + {0xeb03904942cb7689, "xd1t0kb2tev8", 12.155104798, 159.620948229}, + {0xd0edc8428cecced9, "u3qwhhndxm7e", 53.108368037, 20.575648647}, + {0xc59370d9623f7ef8, "sq9r1qc27xzg", 37.830132637, 13.064621556}, + {0x653cbcfaf2c2ef8e, "dnyctyrkscrs", 38.267534119, -80.244956371}, + {0xd9e675b84c8104b6, "v7m7cf2dh42c", 63.953144976, 63.710576225}, + {0x4dc60fe87cfa00c1, "9r30zu3wz80d", 40.937590984, -122.000791352}, + {0xfbb9c512b2b2de32, "zfwwb4pkqcg3", 60.260748923, 177.900875515}, + {0xdbbfdcb42c6f43c5, "vfzxte1dex1w", 61.804056734, 89.540786447}, + {0x64fb209211565c1b, "dmxk14hjbtf1", 31.652629444, -68.505224601}, + {0xac094348a22bfc3f, "ph4n6k525gy3", -66.379374081, 137.916009753}, + {0x9b43d3f3de631f34, "me1x7wyyddgm", -26.812375602, 69.772564993}, + {0xaf39a9a15599d3b2, "pwwum8bpm79v", -52.685045619, 167.234114485}, + {0xe00088d4f9ffbe6c, "w008jp7tzyz6", 0.040849143, 90.928000599}, + {0xad21b5813afd381c, "pnhvc09uznw1", -55.235660699, 141.726344694}, + {0xe6f6f1b71d327886, "wvvg3esx69w8", 32.935517817, 131.908151434}, + {0xb347dc0895465ae4, "qe3xs24p8tef", -25.399073209, 114.798915711}, + {0xb6434a27ad0a00f8, "qt1nn9xe180g", -15.811557254, 114.202259776}, + {0xd19b8dab5d2e5c22, "u6esvbux5tf2", 59.902813309, 16.430893724}, + {0x3e83499480851f0c, "7u1nm540hngh", -21.384850719, -9.621100955}, + {0xae1c26b9945e8a80, "psf2efdncu58", -63.178446268, 160.831683837}, + {0xb190e573ef63ee7b, "q68fbwzgdgr7", -30.416403874, 102.337431219}, + {0xd4298d904a52994e, "uhnsv42bbbdn", 68.347453097, 9.361416699}, + {0xa4c62768f770ddaa, "nm32fu7rf3fu", -60.312227647, 103.13322582}, + {0x4e3e08ebe805628a, "9sz0juz80pj8", 26.744880108, -102.393257134}, + {0xcb160f2a1d077369, "tdc0ybhx0xtq", 15.601828854, 69.209089413}, + {0x870858b28d08ea56, "hw45jdne13p5", -55.711148984, 25.563188805}, + {0x458965aa91b6fe65, "8q4qcbnjqvz6", 34.937455996, -165.500493992}, + {0x6b7e528006d542d5, "eez55006up1e", 21.621604002, -12.523897015}, + {0x8fe274e961d59f8e, "jzj79uc1uqgs", -49.983335683, 86.211277493}, + {0xc3bdd59c2adc9eaf, "sfyxc71bvkgb", 16.847688573, 42.948190526}, + {0xd16060caaf52ca5f, "u5h61kpgbc55", 62.249184891, 6.042173661}, + {0x14cfb52e454180d2, "2m7vbck5860e", -14.450529296, -163.438098843}, + {0xf66cd461b78c3fb8, "ytqe8serjhzv", 75.172431564, 121.667286024}, + {0x351970f7a76318c5, "6ndr1xx7dddd", -7.165217815, -86.759952522}, + {0xe96fcb71ef7888d9, "x5rwqwggg24e", 19.41760595, 145.83781244}, + {0xb4519ad1396c31d1, "qj8tpn9tehsx", -13.146918666, 91.012936912}, + {0xf4c49b14d62906f7, "ym29q56q543g", 74.769872027, 102.220018036}, + {0xded972195086b4c6, "vvdr46bhhuud", 77.183787525, 82.013149503}, + {0x12f457e1516b482b, "2cu5gsbjee42", -34.470017496, -140.47103785}, + {0x282b571a6902b78d, "50ppf6m90bvs", -88.625159191, -35.049780201}, + {0xe88f954dff8f9bef, "x27tbmgzjyey", 2.44981553, 151.188263269}, + {0xc3730bcb6fe1913d, "sethrkvgw68m", 20.46127807, 29.858018685}, + {0xa77e961b83204fcd, "nxz9d6w3417w", -46.128665679, 123.154425789}, + {0x7f3ad3e1b051274b, "gwxe7sehb4mn", 82.159336833, -11.795189567}, + {0x3d962390b227f3c4, "7qc2745k4ztw", -6.975590384, -31.855762954}, + {0xc2e55e88ac541957, "sckpx25dbhdp", 8.350106894, 39.698412847}, + {0x45f099d5ace6a6d7, "8rs9mpedwume", 42.448877885, -162.19704475}, + {0x1f3bba917b398eff, "3wxvp4cv767g", -7.54257361, -101.29139729}, + {0x62292186366cbd9a, "d8nk31jqekyt", 0.753705608, -58.659633919}, + {0x813999a53cd5d2e0, "h4wtm99wur9f", -75.0052094, 9.384568902}, + {0x53525d3dddf5b1e4, "be95ugfxyqsy", 65.368645396, -155.881301903}, + {0x8f9c2cb7690d8994, "jyf2tev91q4t", -51.922555198, 82.163371822}, + {0x71944d11e394e8e4, "f6b4u4g3kmnf", 60.967496471, -78.569564088}, + {0x59bf68f5b809f57f, "c6zqjxes17ur", 61.565344328, -113.308122095}, + {0x399f8e32f40bd114, "76gswdrn1g8j", -28.726825074, -28.532792279}, + {0x2dd3f92adac33093, "5r9zkbqusds9", -46.535926521, -31.0708533}, + {0xabba934504a1ea64, "pfx96j84n7p6", -75.687201192, 179.385043644}, + {0xdb3595eaf0e1b739, "vdutcurhw6vm", 61.50362625, 73.914917802}, + {0x47641e0c4f8fed6d, "8xk1w32gjzqq", 41.052343372, -151.599056465}, + {0x0269001a47940b67, "09nh06k7kh5q", -83.658934869, -149.045501013}, + {0x3a4ede13c5830e0d, "797ew4y5hd70", -37.337882242, -17.306016063}, + {0x046a8609f36b641a, "0jp8d2gmeek1", -61.782027723, -169.349629017}, + {0x2ff959161ef8e5f3, "5zwpk5hyz3kz", -46.520439798, -2.629878009}, + {0x52855c5992dcd54b, "bb2psqdkvmbn", 47.761162236, -146.059916958}, + {0xb45ca59dd9fee387, "qjfbc7ftzvjs", -12.502800013, 93.925892423}, + {0x5b5e15703cf2a6ce, "ceg1bw1wybmd", 66.435486659, -108.256920075}, + {0xd6b6d48dce857ab5, "uuve93ffhpxc", 72.343981303, 41.54326527}, + {0x8b346c8ec8d39a89, "jdu6t3q8ufe8", -74.084788203, 73.716410528}, + {0x686e7978fa94cb55, "e1r7ky7ukm5p", 7.63762904, -34.590610987}, + {0x6e4eb55c4e89fe4c, "et7cbr2fj7z4", 29.879056491, -17.21431133}, + {0xfbdd64cdb67a7fa9, "zgfq9meqg9zu", 67.267709375, 171.973618148}, + {0x602d303f097220f1, "d0qm0gs9f8hg", 2.304683315, -81.171684975}, + {0x4c50066e43cce3f9, "9j80dvk3tmjz", 31.054515285, -134.873080682}, + {0x54ec139b21adbaab, "bmq176t1pqxb", 74.76491608, -160.162489478}, + {0x169279fd910210aa, "2u97mzdj088b", -19.074151204, -144.236732627}, + {0x8bfa6a26c90a5349, "jgx6n9q9199n", -69.953890259, 89.239949372}, + {0xc20fe8265b4dd9e9, "s87yh9kv9rdy", 2.468789402, 27.977772108}, + {0x513a8f6a6e5bd4d4, "b4x8yumfcgbe", 59.218174677, -169.148521927}, + {0x209a95f36341e5c9, "42e9cwv387kw", -86.842527975, -73.754980277}, + {0xbc8913ba69337360, "rk4j7fm96etq", -21.564554444, 149.234962391}, + {0xdf7750c837ba82f3, "vxvp1k1rrb1g", 89.847436527, 74.588233315}, + {0x189ea77171615f97, "32gbfwcjd5gt", -40.611369962, -118.365235245}, + {0x8e874de08ca8f154, "ju3nvs4dp3sp", -64.884901238, 80.401698979}, + {0xa44e965841a63b05, "nj79dq21nsxh", -60.170549227, 95.021040316}, + {0xb7047275e02eb6ae, "qw274xg05uvb", -9.273808632, 112.96571463}, + {0xf9b1e97ad7155863, "z6sykyqr2pd6", 60.19675025, 153.147013916}, + {0xd054059f331cc978, "u1b0c7tm3m4r", 54.995741231, 0.062198292}, + {0xe0e23dc26ac53770, "w3j3vhmbsnvr", 5.95608636, 108.860614201}, + {0xe88e5b45b9d7927b, "x275qjetuy97", 2.008773958, 150.737434233}, + {0x86fba39595ba973a, "hvxu75dprbcm", -58.294954172, 44.783354583}, + {0x1d0ac959aef17e25, "3n5dkqefy5z2", -10.818294293, -129.885940332}, + {0xd1ccdf98db806a61, "u76ez66vh1p6", 63.953653364, 15.088186513}, + {0x5af6719721898b63, "ccv735t1j65q", 55.434448586, -93.81614598}, + {0x5c5d7a9cf8e01144, "cjfrp77sw08n", 78.592847784, -131.512269949}, + {0xc1276ad0c1bf6beb, "s4mqpn61rxpy", 13.745522975, 7.693508861}, + {0x397f78058cda35f9, "75zrh1ddv8uz", -22.667064161, -34.625230411}, + {0x308cecd60f3853f5, "626ftphg719z", -43.115277464, -74.656431655}, + {0x272accf3a4d6d2dd, "4wpdtwx4uv9e", -55.77433909, -56.701606638}, + {0xbc772f1ddc1241e3, "rjvky7fw290y", -11.799610394, 142.660926551}, + {0xfc2bc1795a2bf9f9, "zhpw2ybu5gwz", 68.63641831, 145.581034144}, + {0xee0fcce4a9d559ea, "xs7wtt59updy", 25.076635603, 162.668556362}, + {0x8c486e3341f30eaa, "jj46wdu1yd7b", -61.420128193, 48.455484506}, + {0xa8ffe4bdc67d94b1, "p3zy9gf6gqbc", -78.99256399, 157.228572217}, + {0x48d49d50fbe8f406, "93b9un7vx3u0", 10.186653097, -122.865627276}, + {0x8be212b67eea84f8, "jgj15emyxb2g", -72.930248299, 85.943288809}, + {0x88dc34c0018b807f, "j3f39h01jf07", -79.870422433, 59.458255196}, + {0x031bda776f4162ed, "0dexnxvg85jf", -74.663841824, -152.284576166}, + {0xc431e380c9902e2e, "shsy7069k0r2", 26.412722146, 6.815138313}, + {0x39a8f4dca392b047, "76ng9r53kbs4", -33.096095743, -24.198208299}, + {0xa19b7700d9f52e24, "n6erf06tynr2", -74.572801351, 105.91190365}, + {0x211d12b9e9d8b74d, "44fj5fg9v2vn", -73.636931953, -87.017666324}, + {0xf68280f1965a60df, "yu181wdqc9he", 67.536870286, 125.928453579}, + {0x751008fb33e2bba2, "fn80jytmwbxu", 81.599153061, -89.739804477}, + {0x17b7b5047a5fdfd8, "2yvvb13uczgx", -6.012783217, -138.161579232}, + {0xab1487c0e1fd272b, "pdb8gh71znmk", -74.375733448, 158.339388541}, + {0x953540046157bec8, "knun0131byzd", -5.969359092, 5.626415731}, + {0xc6d06a5fb4eeea51, "sv86nrxnxvp5", 31.331404435, 34.386175783}, + {0x725ec87941783c44, "f9gdhyb1g0y4", 55.232696222, -62.369247961}, + {0x1b934d02e5ebcaae, "3f9nu0r5xg5b", -29.748986279, -99.658017811}, + {0xaa3406a704234efe, "p8u0e9s44e7g", -85.684758255, 163.284414406}, + {0xe55276cf5c43853a, "wp97emuw8f2m", 42.835448792, 91.906847394}, + {0x1e4bcfb5ffee59f3, "3t5wzegzxtdz", -15.666559001, -107.243059601}, + {0x49c0367440b9faa5, "9703dx20r7xb", 17.178559164, -123.288252655}, + {0xf56cdd0bcf0dff77, "ypqeu2yg1rzr", 86.445107453, 99.336725758}, + {0x8dfe003302d0c32c, "jrz00ds2u31k", -46.392382217, 66.121743397}, + {0xde7bdeb905670129, "vtxxxf85dw0k", 77.270226033, 78.38755902}, + {0x3bcbea91363a959e, "7g5yp49q7bbt", -27.055501732, -5.667066763}, + {0xe3a4f94f4b00fe16, "wfkgkmuc03z1", 13.259306093, 130.622998477}, + {0x237136eebacf6272, "4esmevputxj7", -69.317426741, -61.347756356}, + {0xf232d7e4aeabdff6, "y8tegt5fpggz", 48.499510218, 120.393671192}, + {0x93422aea486978ee, "ke12puk8e5wf", -28.101549014, 24.60470655}, + {0x3b638502696e90a6, "7ejsb0m9eu8b", -27.288384031, -14.757909865}, + {0x73353fea84eae0ab, "fdumzun4xchb", 61.501899444, -61.174282329}, + {0xb2b5a59d8c5a04ef, "qbuuc7ddc82f", -39.926589077, 130.488118082}, + {0xb31030b569a7e5b0, "qd831ec9nzkv", -30.740907984, 112.919836484}, + {0x9b8e85465945f10a, "mf78bjkt8rsh", -32.182089876, 83.678071201}, + {0x70cb9f6acd5adfa6, "f35tyuqeccgu", 51.659742038, -73.5224875}, + {0x1dda47d794f92bd4, "3re4gpwnz4px", -2.28673212, -119.390863793}, + {0x8e32e8ebcc898b80, "jstfjuydj65s", -64.309490235, 75.847779494}, + {0x4bf77a82a6cd9381, "9gvrp0p6tq9s", 22.3246835, -93.549386571}, + {0xdd24ea55dc18bbe8, "vnkfnpfw32xy", 80.551461123, 51.946856814}, + {0x42e0882e056553a5, "8ch8hch5dp9u", 5.63113503, -139.707553354}, + {0xe7e0a1ae7bf00d5f, "wzhb3cmvy06p", 39.426801478, 130.514748694}, + {0x66d30f5d11055981, "dv9hyr8j0pds", 31.81455895, -54.56908841}, + {0x26b24f59596ea886, "4ut4yqbteun8", -64.16605699, -48.943235413}, + {0x3954a33f66be3950, "75bb6gv6rswp", -23.841293767, -43.816923179}, + {0xadee5ca371e8680e, "prr5t8vjx1n0", -48.598446161, 156.342624437}, + {0xd7a915cadb1bcf43, "uynjckqv3g7n", 79.785007858, 42.251780647}, + {0x4d6265b78888d8dc, "9pj6cew8j3de", 39.877633156, -127.542114264}, + {0x35b0c469b8ba7e93, "6qsd8uesr9z9", -7.972588452, -72.383777448}, + {0x9783dba0470a11a7, "ky1xr827188u", -9.973694158, 36.18937368}, + {0x911d8ce8b2234294, "k4fstu5k4e19", -28.717558717, 3.772883096}, + {0xdc3ed8fc0951fd8a, "vhzejz09b7ys", 72.284867115, 55.800266955}, + {0x5260c432abfbfe59, "b9hd8dpczgz5", 51.075760537, -151.138917158}, + {0x4896fd94884b77f5, "92cgv5489evz", 4.894516445, -121.065830594}, + {0x7bbc4a1b6fc329c1, "gfy4n6vgsdnw", 60.836031307, -2.52974347}, + {0x6a6b743e067adb43, "e9pr8gh6gcen", 6.960320069, -12.265726987}, + {0x7dfada6eb45e11d1, "grxenvpncs8x", 87.743493677, -22.896815991}, + {0xab3ed6ff10dca241, "pdzeezshvkj4", -73.87397964, 168.217395144}, + {0x1fa876ef270d80ff, "3yn7evt71q0g", -10.60400541, -92.288877851}, + {0x0b675765f1ff0b6a, "1empftgjzw5q", -70.32383936, -105.354440331}, + {0xe64792444b649f07, "wt3t4j2cdkgh", 30.439279143, 114.698396286}, + {0x280e9e272f46ef0b, "5079w9tg8vrh", -88.321208681, -39.784542173}, + {0xe6ceed2c67ee068a, "wv7fuc37xs38", 30.022138534, 129.23421852}, + {0x9c26048c2c498339, "mhm0931d961m", -20.999925438, 52.088297111}, + {0xdad14643c16bb373, "vc8ndhy1eftr", 54.60644281, 78.84629849}, + {0x0035b437fcb586ff, "00uv8ezwqq3g", -84.792746684, -173.287769268}, + {0x03f31d2037e9721d, "0gtju81rx5t1", -69.300451248, -139.018972148}, + {0xbfb37917e4c30eb9, "rytrk5z4sd7c", -7.142045132, 176.318405108}, + {0xa6ca558c1a3939b8, "nv55c30u74wv", -61.209583324, 128.02484961}, + {0xcaafca045bae0ec7, "tbrwn12vps7d", 2.468685548, 89.561903532}, + {0x17f822c8c5040dc0, "2zw25k650h6w", -2.788615247, -137.315365241}, + {0x2458272521666731, "4jd2f991dtmm", -58.922137768, -86.724586598}, + {0x026c701dbb9f9d70, "09q707evmyfr", -82.421241554, -148.694551546}, + {0xc694390109dc58d8, "sub3k089vjde", 26.941553596, 34.278248559}, + {0x96937fb7a508a67d, "ku9rzex512m7", -18.305452269, 35.847076298}, + {0xfc944595f76a297b, "zkb4c5gre8nr", 72.224036506, 146.298567355}, + {0x21cf3fff58c9d077, "477mzzust787", -70.664643892, -73.832705764}, + {0x42985d625ec4b755, "8bd5uskyskvp", 3.496168237, -143.233033802}, + {0x0c6f4917d8644b58, "1jrnk5ysdj5p", -59.348717347, -124.971452303}, + {0xe9e3a784617e6222, "x7jug131gtj2", 17.71715465, 154.469304081}, + {0x365874a282292bfb, "6td798n254pz", -13.447253657, -64.261297057}, + {0x027cb45c8fd0bf6b, "09yc8r4gu2zq", -79.853477137, -147.9928572}, + {0xe8d3ed297774f457, "x39yubcrfmu5", 9.629505192, 148.921496401}, + {0xcdb2b6b79d50692b, "tqtceewxb1nk", 36.84673443, 64.498681184}, + {0xb339623154283591, "qdwq4dbn50ut", -29.866671403, 121.399060615}, + {0xa62730a1b2751928, "nsmm18ekfndk", -65.211246219, 119.953282711}, + {0x811686c8cf0f4a83, "h4c8ek6g1x58", -74.419460839, 2.256043048}, + {0x3e47cc120ee34f42, "7t3ws4hfwe7n", -14.314737922, -20.208037555}, + {0x690a1bc6fe1dc89d, "e451rjry3r49", 11.499676411, -40.46293257}, + {0xe0cc7e964ec14d44, "w367x5kfs56n", 7.664784842, 104.72837718}, + {0xeea284e36cc77d19, "xuj89svdsxyj", 22.614454794, 176.558047419}, + {0x04d7b0c39a0502b3, "0mcv1hwu0n1c", -56.751905452, -166.235845007}, + {0x1eafc04e38dbe770, "3urw0mjsvgmr", -20.01076268, -90.684331731}, + {0xa76831a41ac746b8, "nxn3390usx3c", -50.398968732, 121.356206082}, + {0xacaa32264d0e5e67, "pkp349ke1tg6", -67.316805963, 156.561424479}, + {0x642df9d353ee00c3, "dhqzmnumxs0d", 25.218708921, -80.281926831}, + {0x90179c1ad405311f, "k0cts6qn0nsj", -39.801027689, 2.304387989}, + {0xcb09417fe4a2e7e8, "td4n2zz4ncmy", 12.391555877, 70.355369631}, + {0x998907c7c04c7b97, "m64hgjy09jxt", -32.883337041, 59.202628796}, + {0x4ea071a00f473670, "9uh7380g8wv7", 23.071926622, -95.206461039}, + {0xde98137d6e64f4ed, "vud16zcfdmuf", 70.575259081, 81.685856025}, + {0x0daf9f63d4177f07, "1qrtysyn2xzh", -53.805800006, -112.909209726}, + {0x172e090ad847a29b, "2wr0k2qs8yj9", -9.797626486, -147.460516167}, + {0x1867d8b340e3364d, "31mxjdu0wdv4", -36.723074004, -127.018146751}, + {0x50503a66657d100c, "b183ntm5gn80", 53.642799062, -179.355797554}, + {0xa24cb0cb0be5007a, "n96c1kscwn07", -82.767959294, 116.428900363}, + {0x7a306349407aebb7, "g8s66kb0gcpv", 48.234235422, -16.424389057}, + {0x78db425d79dcee4c, "g3en4rctvmr4", 54.53577517, -29.430078222}, + {0xc5e27e2188c78ffa, "srj7w8d8sy7z", 39.993101504, 18.922099461}, + {0x056692866a6f3f8f, "0pm951mbewzs", -49.03597945, -172.12573205}, + {0xe39111b7cffc724d, "wf8j3eygzjt4", 15.006624636, 123.825495006}, + {0x952b8d5c3112d4ee, "knpsur1j2cbf", -10.375675863, 10.735049355}, + {0x2a448f8862267723, "5928z2324tvk", -82.835509174, -21.476440417}, + {0x84fbe0f283b84136, "hmxy1wn3r10m", -57.974632635, 22.223239414}, + {0xce410c03842f156b, "tt0hs0w45wbq", 28.9191428, 67.68417187}, + {0xb87a5e08fe867c88, "r1x5w27yhty8", -35.944831498, 145.123760093}, + {0xc38e14f3782c7981, "sf719wvs5jws", 12.957717599, 38.042358575}, + {0x8221448dd270c536, "h8hn93fkf32m", -88.846985802, 28.183118595}, + {0x3d89791d0fbfb162, "7q4rk78grysq", -9.95576607, -30.397801575}, + {0xd32fe800c682f195, "udryh066hcst", 58.712663165, 33.577518547}, + {0xffcc1884dbbd5d20, "zz61j16vrpfk", 85.964841207, 171.786306605}, + {0x5d9211ca2b51f0cc, "cq913kjcb7sd", 81.804518844, -122.280904977}, + {0x8db477caa1bc0d32, "jqu7gkp1rh6m", -51.349859422, 62.379301372}, + {0x3ca89668c6542534, "7kn9du66bhkm", -22.212486378, -24.485429746}, + {0xa92afadc128ec707, "p4pgpr0kjv3h", -78.183488486, 146.217632594}, + {0x46ebf7e29fc35044, "8vpzgsnzse84", 29.510584039, -135.188291002}, + {0x31f8060a193a44cd, "67w0d2ht792d", -25.223700947, -70.207292145}, + {0x1e7ee7d56ee7f83b, "3tzfgpcfwzw3", -12.129809921, -101.467024955}, + {0x700e55f3045a5cc8, "f075cws4c9fd", 47.101617072, -85.709770961}, + {0x22a7fd3e85422627, "4bmzugn588m2", -87.214363529, -47.947053915}, + {0x415f4f99af616be1, "85gnz6egd5py", 22.294624307, -175.457400397}, + {0x86cbba26179ea736, "hv5vn9hrmumm", -60.989332315, 39.315172474}, + {0xf972f5646c1780f5, "z5tgbt3d2y0g", 65.37593788, 143.110003356}, + {0x2031f31993130a88, "40sz66dm2d58", -85.898441043, -83.218319301}, + {0x58d0b2568de00408, "c38c4pnew020", 53.65233559, -122.598237755}, + {0x41b753382669d68a, "86vp6f16e7c8", 16.754599696, -161.59604201}, + {0x5134c98ef1e3d852, "b4udm3rjwgd5", 60.872087705, -173.431256587}, + {0x2bd4ab4ce500da72, "5gbbqm7503e7", -68.83294203, -9.916519183}, + {0x012c242bef6d27fe, "04q28bzgenmz", -77.251103901, -171.167198756}, + {0x52122ed71a53c161, "b892xpsubg0q", 47.942421926, -155.428014601}, + {0x33f9f95f44ada778, "6gwzkru4pqmr", -23.995135859, -46.565233408}, + {0xa2ea50bf7eb564b2, "ncp51gvyqpkc", -83.825944904, 133.678814727}, + {0x62a888ac24c8dece, "dbn8jc14t3gd", 0.00593214, -46.855085854}, + {0x46eea884bfa88c01, "8vrbj15zp260", 29.537946188, -135.126372271}, + {0x1cddcb26e077b8db, "3mfwq9r0fywe", -11.55058655, -119.938990409}, + {0xde650860a483ad48, "vtkhhs54hfqn", 75.256704079, 73.327085867}, + {0xe81525c82a0c94f9, "x0bkck1b1kbg", 5.075709632, 135.408952233}, + {0x1c2479c744beb0f0, "3hk7mju4rusg", -20.490464329, -128.797878805}, + {0x0e8f290f5c1b85fd, "1u7kk3uw3f2z", -65.335980361, -96.486663809}, + {0xa77838dde848f4d0, "nxw3jrg893ue", -47.59405098, 121.524641629}, + {0x5bf7d1b5c8dec5fb, "cgvx3ef8vv2z", 67.388921557, -93.446025738}, + {0xe20060be9d705162, "w8061gnxf18q", 0.369381452, 112.937479497}, + {0xb9d1ffff47d75c81, "r78zzzu7uxf8", -23.90693791, 147.651296665}, + {0x846095b502022c06, "hjh9ce8208q0", -61.548155295, 6.394415901}, + {0xd22a0d33f5fe3da2, "u8p0udzpzsyu", 45.148298694, 32.55144797}, + {0x21da597f1db4a6f2, "47e5kzsxqkmg", -69.698744334, -74.316053896}, + {0xaa3ae688121f3880, "p8xfe20k3ww8", -86.747282424, 168.54167625}, + {0x675177b400233848, "dx8rge004dw4", 43.566297951, -66.994513336}, + {0x48e7d36a0890f7c2, "93mx6uh8k3vw", 8.327690028, -115.888405728}, + {0x523f2c53a61d1e17, "b8zksnx63ng1", 50.04589396, -147.118902799}, + {0x27be993b16f62fc1, "4yz9kfsqysrw", -51.796607947, -45.488259269}, + {0xe056adbe9cd9c4ff, "w1cbvgnwv72g", 9.993242218, 92.72277602}, + {0x25a3bf5f1c49d793, "4qjvyrsw97ct", -55.196922282, -70.383168698}, + {0x30fe9ea72cb5ad96, "63z9x9tdqqqt", -34.883917686, -67.865705033}, + {0x58713a8ec6b5ba9c, "c1smp3q6qqx9", 54.323694092, -128.695972468}, + {0x22d77ea6eeb9787d, "4ccrx9rfr5w7", -78.830618488, -54.151645632}, + {0x841efa5014601060, "hhggnn0nd086", -62.719828421, 5.537195622}, + {0xb8da7329b342ac53, "r3e76bem8bq5", -35.987518315, 150.945665687}, + {0xf089c1762353afa7, "y24w2xj3bfru", 46.137398251, 104.794850127}, + {0x5e516c57ad927885, "ct8qspxek9w8", 77.121842947, -111.962154671}, + {0x328c8400f2571bcb, "6b68807kbwew", -43.503634767, -52.729885385}, + {0x364c98c8f6a94821, "6t69jk7qp542", -15.26857669, -63.748895455}, + {0x124fccbde86dc2d8, "297wtgg8er1e", -36.805448616, -152.320491216}, + {0x393a753d3ed56853, "74x7bg9yupn5", -30.257897398, -34.769145439}, + {0x8e3ea0f01c595fcd, "jszb1w0wc5gw", -63.247111019, 78.465089907}, + {0x2fd57cefaac057d8, "5zbrtvxbs1cx", -45.057581797, -10.634935083}, + {0xe9bb696131752e8c, "x6xqks9jfnr8", 15.186872736, 156.644535834}, + {0xecbcaeba8ae498f8, "xkybxfnbwkdg", 26.817737445, 156.092305466}, + {0x3c155f121c2752c7, "7hbpy4hw4x9d", -16.906888289, -44.730038924}, + {0xa74b3a69658e9a47, "nx5mnuc5jue4", -49.719461616, 117.368567548}, + {0x2eb3d0addc4e13f0, "5utx1cfw9s9z", -63.446277086, -3.435220692}, + {0x070d31e976cccdd6, "0w6m3ucqtm6x", -53.89366065, -154.25708783}, + {0x69c53c9adaa7d578, "e72mt6qunzbr", 19.261135906, -33.158164673}, + {0x8c9e89745024ff90, "jkg8kx2h4mzt", -63.196760889, 61.369735589}, + {0xd3d3c4910aa50466, "ug9w948bnn26", 65.84384501, 35.904608228}, + {0xbfa13e99374f7018, "ryhmx69r9xs1", -10.268142339, 175.046952554}, + {0x8f900a315b49c612, "jy80ndbv9731", -53.421432037, 79.036729611}, + {0x34177f81556887fa, "6hcrz0bpe23z", -16.913535434, -87.934428206}, + {0xb96530ec79edcb87, "r5km1v3txr5s", -25.810022128, 141.055842698}, + {0xac15498a6da94a09, "phbnm2mep550", -62.18071274, 135.238570644}, + {0xa03bbe9788c3e8a7, "n0xvx5w8sgnb", -86.20137497, 101.215194102}, + {0xe1fdea2600e5bdec, "w7yyn9h0wqyy", 22.1540538, 111.033602638}, + {0xde140bf195ab1efc, "vsb0rwdppdgg", 71.799617848, 67.832663565}, + {0x6c5f9dd867279249, "ejgtvq374y94", 33.389374719, -39.845575943}, + {0x8e0124e3ad7854da, "js0k9sxeg1be", -66.683611496, 67.927909894}, + {0xb88836dea0b4ae00, "r243erp0qkr0", -44.697810252, 149.566775454}, + {0x16343326c0620fe0, "2su369q0d87y", -18.054568787, -151.405222096}, + {0x3844e8b00ed5ea99, "712fjd0furp9", -37.605691329, -43.702390627}, + {0x4942d41b9c270b5f, "951e86ww4w5p", 17.505030752, -132.870600668}, + {0x43b72c38957ef253, "8fvksf4pgvt5", 16.272112381, -138.655532888}, + {0x575b75b8d1963742, "bxercf6jksvn", 88.563092037, -152.849836647}, + {0x32c764b0946cd74b, "6c3q9d4nemcn", -36.814037815, -54.423381836}, + {0x74dc2968c4082868, "fmf2ku6410n6", 77.41138494, -75.374399197}, + {0x11da6fb7d47731ac, "27e6zeynfwsu", -24.807307851, -163.841744053}, + {0x782235b7442c4ad1, "g0j3ceu45j5e", 45.32858727, -37.545642866}, + {0x868fe1662b779239, "hu7y2tjcfy93", -64.967315869, 39.053426462}, + {0xf7d0f43ce0badb15, "yz8g8g70rcej", 87.820639087, 124.842103003}, + {0xf54450ae06db9b09, "yp251ch6vfeh", 86.314573142, 90.082991776}, + {0x758c25bb9720f0b8, "fq62cfwr43sc", 80.303028705, -75.500347031}, + {0xafe68ca1dbf9a508, "pzm8t8fvz6kh", -49.125739707, 176.730167473}, + {0x19c9714b5e5c76c0, "374r2kuycjvd", -26.823303477, -120.568377723}, + {0x70e5c49c92a2e483, "f3kw974knck8", 53.191001039, -72.363557613}, + {0x575889ddce5cba2b, "bxd8mrffckx2", 87.274513595, -153.749821876}, + {0xe14284b8410b1e90, "w5189f211dg9", 16.975436856, 92.186348382}, + {0xe22f067b090ab0ae, "w8rhdys91bsb", 2.233145656, 122.470861163}, + {0xeaa5cb144772dab1, "xbkwq527fcec", 2.5233876, 175.342263303}, + {0x90cda0608e574a98, "k36u0s4fbx59", -37.243142527, 15.142963093}, + {0xd7dcb4d9f80dad46, "uzfc9qgs1qqn", 88.895222002, 37.676987516}, + {0xfd3db8724cc85dfa, "znyvhwkdt1fz", 83.882428258, 144.696339758}, + {0x5ff697798c39f84b, "czv9fydd77w4", 88.937478177, -93.391194356}, + {0x478be4d323c72d4f, "8y5y9nt3swqn", 34.92857587, -140.925205969}, + {0x03336d07530cad1a, "0dtqu1um1kqj", -74.740481553, -149.935507764}, + {0x6bfa184c6d6af3d2, "egx1hm3eectx", 19.892730426, -1.217254032}, + {0xb2e34ef21a469a57, "qcjnxwhu8ue5", -38.198665628, 131.117399298}, + {0x4f368c7535c7e42e, "9wv8sx9pszk2", 38.099165826, -104.566287959}, + {0xaac99b02add0327e, "pc4tq0peu0t7", -83.451501419, 172.539775562}, + {0xb2f6a2979f6d2d1a, "qcvb55wzenqj", -35.135702077, 131.977180207}, + {0xcb595e4ea7346be2, "tedpwmp76jpy", 21.033910957, 70.597210913}, + {0xa74c31ff669b79a6, "nx633zv6mewu", -48.956045635, 115.748421751}, + {0x06e225ce3e7126b6, "0vj2cmjyf4mc", -61.714527168, -138.804265797}, + {0xe6e9cf5792112303, "wvnwypwk24jh", 29.353463182, 133.162886757}, + {0x4ec8df68675f20f6, "9v4eyu37cwhg", 28.808202163, -97.435956698}, + {0xba2397034553c041, "r8jtf0u5bg04", -43.984477705, 165.327791032}, + {0x62c14b61a44c944d, "dc0nqse49kb4", 6.748807171, -55.960181705}, + {0xb1d7e9e4a599debe, "q7cymt55m7gc", -22.77957308, 103.956987248}, + {0x324075bef633b3e5, "6907cgrq6fty", -38.69688009, -67.061451084}, + {0xfdd315cb5560d714, "zr9jckupd3cj", 88.225632504, 147.716772759}, + {0x1232602ff5e020b7, "28t60czpw0hc", -41.825036675, -150.074351511}, + {0xec308a8020462c3d, "xhs8p0108sq3", 25.31260893, 141.637145514}, + {0x2fcfd850d63e9496, "5z7xhn6q7ub9", -47.952850341, -6.149092093}, + {0x139cba059e83aeef, "2ffcn1dyhfrf", -29.346186492, -142.115149518}, + {0x7943bc456c1925c6, "g51vsjcd34kw", 62.873781982, -42.361172273}, + {0x9e0f703ae1dfd68e, "ms7r0fr1vzc8", -19.850579553, 72.113133572}, + {0xd100c37c3a89ae0f, "u40d6z1uj6r0", 56.68465884, 0.826612856}, + {0x82c9203228cf0167, "hc4k0dj8tw0q", -83.660769518, 36.943824761}, + {0x4f8b8d57b4a8c93d, "9y5supxnp34m", 34.627195979, -96.142416326}, + {0x5d498d412eb63469, "cp4suh9fqsu6", 85.235092286, -131.305905595}, + {0x6e555070a879bfe6, "etbp0w58g6zy", 33.607322345, -22.473070841}, + {0xde25ef48c2aa1f3c, "vskyyk62p8gm", 70.116124342, 74.457762003}, + {0x2d0e0ae1e07ba06f, "5n70psg0gfh6", -54.817517502, -40.447373383}, + {0x6ea0d33906f1d08f, "euhe6f86y788", 23.085512954, -4.800412883}, + {0xd6f07ab15b41952e, "uvs7pdbv86bk", 76.480909601, 40.057193644}, + {0xa769e7c7e73708ed, "nxnygjz76w4f", -49.406300287, 122.134090966}, + {0xa423ab0a722cc1fd, "nhjuq2mk5m0z", -66.750841605, 98.36794619}, + {0xd8a722ff61a71987, "v2mk5zv1nwds", 47.152154375, 63.804756338}, + {0x4a3dc853e02934ec, "98ywhnz054uf", 5.310531561, -103.173845352}, + {0xeaab775a88210678, "xbprfqn84437", 1.395276272, 179.053203569}, + {0x7b315f4ffcf8bb26, "gdspymzwz2xk", 60.457552561, -16.589721158}, + {0x26487a4fd2093672, "4t47nmyk14v7", -61.315368642, -64.052646157}, + {0xdffbad61f2c431da, "vzxuusgkshsx", 88.049351284, 89.850832623}, + {0xbabba057ca4a455e, "rbxu0pyb992p", -41.44170915, 179.657771912}, + {0x162d4890d5ece06b, "2sqnj46pxmh6", -20.025388203, -148.839709697}, + {0xed53f76d02c8f62e, "xp9zfv82t3v2", 43.580112407, 137.582363069}, + {0xc29f42f0d044c3a1, "sbgn5w6h8m1u", 5.308569788, 38.125318285}, + {0x6ab82282dd840d54, "ebw250qxhh6p", 2.815098995, -2.320000801}, + {0x9bc064a55f2819a6, "mg0699bz50du", -27.674728737, 79.168643013}, + {0x6588473ef64e915a, "dq44fgrq9u8p", 34.252392345, -75.806618642}, + {0xbeb6cda5a6cd677e, "ruvdv9e6tpmr", -17.789142472, 176.730759361}, + {0x63eb3090768c7663, "dgpm143qjjv6", 17.767327067, -46.008803871}, + {0x7b4d843ef9a904dc, "ge6s8grtp42e", 64.090989409, -18.940815023}, + {0x407758b220c903dc, "81vpjdj0t41x", 11.085302914, -172.719967535}, + {0x1e582bc3d605e712, "3td2rhyq0rmj", -13.991392459, -109.019719688}, + {0xa08d4860eaf0a201, "n26nhs7by2j0", -87.515587729, 104.265678709}, + {0x8c04834ed1456500, "jh286mqj8pkh", -66.019981863, 45.810247132}, + {0x7ca269542a563bb0, "gkj6kp1bbsxv", 67.934113688, -26.188971064}, + {0x21be18e02dbd0c4b, "46z1js1ern64", -74.332904269, -68.662187492}, + {0xe076b8648b7a1686, "w1vcht4cg8c8", 10.047301615, 98.287620284}, + {0x78255e204dfc1862, "g0kpw82ezhd6", 47.726650892, -39.088365232}, + {0xcbc33da75ae5b3c6, "tg1mv9uuwqtw", 17.896163665, 80.756310551}, + {0x06c28447508ee2b6, "0v188juhjvjc", -61.754809958, -144.13487724}, + {0x99724199ded30f77, "m5t436fyud7r", -24.900713896, 52.090155799}, + {0xe1115f6de71021aa, "w48pyvg720hu", 15.456948976, 90.30110076}, + {0xbf4714ccc580ecea, "rx3j9m65h3qf", -3.222591988, 158.964119956}, + {0x9dbebd4c7eb16f1e, "mqzcum3yq5rj", -6.693702431, 67.337875543}, + {0x447f2cc94eb4f6ae, "8jzktkbfqmvb", 33.161275327, -169.572668425}, + {0xc5faa5bab350e6a6, "srxbcfpmb3mb", 42.331316122, 22.235316751}, + {0xbb8b91aeba251cee, "rf5t3cpu4nff", -32.820931994, 173.759512091}, + {0x24b24efb5484e759, "4kt4xyunhmmp", -64.209908638, -71.372491333}, + {0x5b7d2b99ed04526c, "ceykr6ge0j96", 66.856471433, -103.387525589}, + {0x61ecdace90c1de4a, "d7qepmnhs7g4", 18.836852509, -69.28234506}, + {0xa10956b455ea177b, "n44pee2px8cr", -77.412495122, 92.966632418}, + {0x53b8090c240802b8, "bfw0k314101c", 59.112281998, -137.624313635}, + {0x383ca01c1e6433d9, "70yb070ydhtx", -40.763629315, -35.495702768}, + {0x3556eba072d6b217, "6pcfr83kuut1", -1.008529055, -87.207545403}, + {0x80afe7d29c4d06ad, "h2rygnnw9n3b", -87.373118104, 22.289245324}, + {0x5156fbec41eb56c1, "b5cgrv21xecd", 66.694156161, -177.198162403}, + {0x3519ba53229232a6, "6ndvnnt2k8tb", -7.522842473, -85.861729905}, + {0x04ac5df2d60b573b, "0kq5vwqq1ecm", -65.399187035, -160.062151397}, + {0x14e30af62a3bc4d7, "2mjhpxjb7g2e", -16.13336185, -161.381102015}, + {0x022a436a83c8e478, "08p46un3t3k7", -89.582254813, -147.526586088}, + {0xdb20801c7009cb2e, "vdh8073h175k", 56.268556299, 73.84054291}, + {0x8019161d55ac9842, "h0djd7bppkd4", -86.198877632, 2.911690861}, + {0x1ed099a42f0c7987, "3v89m91g1jws", -13.836734965, -100.302720757}, + {0x4a21fb704ce7659f, "98hzqw2dwxkt", 1.309215382, -105.533696019}, + {0x909d089befd3158d, "k2fhj6zgudbs", -40.062359502, 14.304049929}, + {0x00ed552e308e1edc, "03qpbcjhjsge", -81.600239403, -160.272433617}, + {0xd89e6e44dc4a97a6, "v2g6wj6w9bcu", 49.688162851, 61.087495169}, + {0x3e11ae0ea95b01a8, "7s8uw3p9cd0u", -18.890679392, -21.160289011}, + {0x12834ce77db8c22e, "2b1nttvxr312", -43.824586153, -144.594184603}, + {0x2bac7f79f964109b, "5fq7yygtdh89", -76.646522945, -2.159413323}, + {0xd40d241c16542b75, "uh6k870qbhpr", 69.714926618, 3.175401902}, + {0xed1b9d95e40fb335, "xnetv5g41ytm", 37.594220748, 140.145803631}, + {0x6818413b09241bea, "e0d42fs94hey", 3.22193754, -42.148270084}, + {0x85dcd3569d514cec, "hrfe6pnxb56f", -45.79516054, 14.862445933}, + {0x5cefd421b177ac30, "cmrx88ejfyq3", 75.853376745, -113.176905991}, + {0x09c08389e36f00b0, "170872g3ew0c", -73.07664495, -122.899438322}, + {0x2d4a1949e39dea08, "5p51kkg3mrp0", -50.378928766, -40.589784317}, + {0x0d20a887c85a9304, "1nhbj1y8cb9h", -56.24025477, -128.091583042}, + {0x94bb903e5da68f26, "kkxt0gkxnu7k", -18.789515756, 21.836307943}, + {0x21a730d9f4ca6968, "46mm1qgnt9nq", -76.426642709, -71.307891386}, + {0x1be9e412db86455a, "3gny84qvht2p", -26.969174572, -91.748349394}, + {0xf43fa8fa8e5d78af, "yhzujynfcpwb", 72.455346447, 101.160445}, + {0xef2c1180e9b5d8a8, "xwq13079qrdb", 35.377604696, 165.986523934}, + {0xd6fc20530c9fdb1b, "uvy20nsdmzej", 77.379882958, 42.545493425}, + {0xcc9d558e8b4bb91b, "tkfpc3nc9fwj", 28.086819645, 59.126784809}, + {0x6668cfde8a580a32, "dtndzrnbc053", 28.646979512, -58.031454332}, + {0x07dcc74ccc0dd762, "0zfdfm6d1rcq", -45.893626543, -142.632009359}, + {0x5f7e991a8683505b, "cxz9k6n6he85", 88.824825553, -101.757580912}, + {0x7c09530cec1f1713, "gh4p637d3wcj", 68.781703879, -42.083750712}, + {0x608aadb1bc77c8ca, "d25bvdewfz4d", 0.14676545, -73.229935094}, + {0x3ad6f33cb08bcb46, "7ccg6g5hjg5n", -34.567777605, -8.663842106}, + {0xfbc058ce07b48bc7, "zg05jmh7qk5w", 62.430390085, 168.986822415}, + {0xaee6224454eab922, "pvm24j2nxbwk", -60.438791728, 176.221044252}, + {0x2dbae1bf669dc880, "5qxf3gv6mr48", -53.020969022, -22.767219738}, + {0x01dc4a48e62207ca, "07f4nk76483w", -68.530995879, -165.65826904}, + {0xa2eda63909ce645c, "ncqudf89ttk5", -82.163715212, 133.363965388}, + {0x8d8f58ccdcea6bba, "jq7pjm6wx9pv", -53.583320097, 60.703228656}, + {0x5c5964f05eff51b9, "cjdq9w2yzx8v", 77.115610264, -131.76866645}, + {0xedebefb0e4b04d58, "xrpyzd74q16p", 40.574277176, 157.482407588}, + {0x3e4cebfd1c2b69f1, "7t6frz8w5enz", -15.030996162, -18.291390414}, + {0x8a97ec33f722baa6, "jbcysdzr4bxb", -84.622361264, 81.418776344}, + {0x9c06bd7aacf9900a, "mh3cuypdz680", -20.752688327, 47.680288866}, + {0x291e21a83f96d977, "54g23b1zkvdr", -74.486029337, -40.350172959}, + {0x217c8c6ddb04890e, "45y8svfv0k4h", -68.785893156, -80.646844551}, + {0x05c69f6e68ae5996, "0r39yvm8ptdt", -48.882263258, -166.336115676}, + {0xbf5d97217854c7f3, "rxftf8csbm3z", -0.390543168, 161.127561431}, + {0xacf5b3fc2718cb55, "pmuv7z17335p", -56.694381703, 153.096256741}, + {0x29c375316e1b9626, "571rbdcf3fc2", -71.74718801, -31.967734668}, + {0x9fc9e55b5bf53c19, "mz4ybquvyny1", -4.400374232, 82.634962698}, + {0x3a6170e3a4e4e8ec, "79hr1sx4wmnf", -38.11935561, -16.44762903}, + {0x65fe6d62844a62ed, "drz6usn499jf", 44.099556093, -68.348621553}, + {0x9466d4adf4c9ce57, "kjme9cgnt775", -14.842768704, 7.815628587}, + {0xd17a1c3638a303d0, "u5x1sejsnd1x", 64.968349917, 10.04933804}, + {0x5f5e6eca9c6963fc, "cxg6xknwe5jz", 89.056308955, -107.602020925}, + {0x39187c24ed6dece2, "74d7s97eerqf", -30.314759097, -41.633228418}, + {0x4150cda0a92ba877, "858dv8595fn7", 20.171081487, -179.050199845}, + {0x3a18bdeae431e59d, "78dcvur467kt", -41.856132207, -18.370411088}, + {0xba3da47962756bd1, "r8yu8yc2fppx", -39.952987975, 167.026959034}, + {0xec6140df79678fb6, "xjhn1rvtdy7v", 29.223238109, 140.687607928}, + {0xc8d395cb297a4e70, "t39tckt9g977", 9.473269077, 58.42201434}, + {0xd3b35e9c4c9eb760, "uftpx72dmuvq", 60.399124897, 41.100794001}, + {0xf991c43ab3ca0973, "z68w8fpmt84r", 60.217009845, 146.996279559}, + {0xee4a9669dcb392b2, "xt59dufwqf9c", 28.415850869, 162.546450029}, + {0x5047d79266d14bfe, "b13xg4m6u55z", 53.40640374, -177.751402304}, + {0x5e4cd7a81ae80c01, "ct6egb0ux060", 75.191203575, -108.818247016}, + {0x16fece70049dbe8e, "2vzdww04mqz8", -12.183415345, -135.417244803}, + {0x176a4b3c54e1c466, "2xp4qg2nw726", -5.210505746, -147.359350473}, + {0x93010617b64efc75, "kd0hd5xq9vy7", -32.938611025, 22.597930485}, + {0x4148dc65fe5fef19, "854estgyczrj", 17.523018556, -176.281386549}, + {0x399e4bfd4ab0f30f, "76g4rzbbq3th", -29.093118388, -29.189368644}, + {0x8efe946e5377f367, "jvz98vkmfztq", -57.362710558, 89.335795622}, + {0x00d4ffcde887f00b, "03bgzmg8hzs0", -79.465444329, -167.371692994}, + {0xfc9fcbec14e0bd34, "zkgwrv0nw2ym", 72.845969388, 151.512727796}, + {0x99548bfab67aa3ba, "m5b8rypqgbjv", -23.828186131, 46.053826488}, + {0x0bb7fd22cb7553be, "1fvzu8qcfp9v", -73.167230434, -92.956950401}, + {0x8dda16072be30c99, "jre1d1tcwd69", -47.540318604, 60.564819543}, + {0xc22accb17f250f23, "s8pdtdcz4n7k", 0.455795223, 33.291067798}, + {0xbaec031ec9363851, "rcq067q96sw5", -37.906712619, 177.295418731}, + {0x3f157cb244411785, "7wbrtdk484cs", -5.700086296, -21.901243097}, + {0x074485b684c12175, "0x28cen4s4hr", -49.069993372, -156.722539895}, + {0xce663b029df47380, "ttm3q0nxyjts", 29.752337118, 75.155675696}, + {0x9ee2df05e62ffa7a, "mvjey1g65zx7", -16.205823193, 86.752680987}, + {0x00a903198e86ec61, "02nh66dfhvq6", -89.238824902, -160.209634241}, + {0x21a3941c8e214b5d, "46jt874f455p", -77.766362904, -71.000772052}, + {0x06ad9453be45b9d3, "0uqt8nxy8qwx", -65.090095664, -137.098712419}, + {0x3637c34d50c00560, "6svw6mbhs02q", -17.150256653, -59.666575996}, + {0x15475460df8fb859, "2p3p8s6zjyw5", -2.875807607, -178.56774576}, + {0xbe292e9dbb728ba1, "rsnkx7evfb5u", -21.688770533, 166.612938987}, + {0xca4ed8301900530c, "t97ehd0t019h", 7.570447222, 72.620317104}, + {0xbd0fdd3a2ca5a78c, "rn7xufjdnqms", -8.470081379, 140.13844415}, + {0x62a8bb671b3259da, "dbncqtsv69dx", 0.250849416, -46.465533912}, + {0x74005a20ab2d00c5, "fh05n85c5n0d", 68.027548088, -89.709076142}, + {0x468f21c50055c762, "8u7k3j80br3q", 24.683701386, -141.635729663}, + {0xf557441f5927e937, "ypcn87ut4znm", 89.757824317, 91.423542685}, + {0xcf390d3ecfa6f515, "twwhugqgnvuj", 37.415860549, 76.155805042}, + {0x1100be2cc2fb90ca, "240cwc62zf8d", -33.479319138, -178.645258146}, + {0xaf36483b445aa7b6, "pwv4hfu4cbmv", -51.664106928, 164.74556636}, + {0x390683763f6e349f, "74386xjzesu9", -32.260039279, -42.772708749}, + {0x191836e878fe5985, "34d3eu3sztds", -30.64963646, -131.668757418}, + {0x771edaa38b590b2c, "fwgep8wcc45k", 83.499152256, -62.239217982}, + {0x7477b9d0b0be2fe2, "fjvvmn5hrsry", 78.300313946, -81.689883646}, + {0xdfca1d53e759f114, "vz51unz7c7sj", 84.720359497, 83.154546626}, + {0xb49d2663251161c2, "qkfkdst525hw", -17.464940112, 104.5307984}, + {0x8e1a968245d74d86, "jse9e0k5ux6s", -64.421771437, 72.559400738}, + {0xacb5b6c3ec2fd3b3, "pkuvehzd5z9v", -62.28797615, 153.071986541}, + {0x3066361f631d2217, "61m3d7v33nj1", -37.684231681, -82.51105105}, + {0xadfe9c28c6941d28, "prz9sb66khfk", -46.140792422, 157.008880022}, + {0x3c29df1cb1abb3cd, "7hnxy75jpftw", -21.120342941, -35.580256778}, + {0x3722744d77c61141, "6wj78mcrss8n", -10.601867257, -60.104291367}, + {0x42c93f6d8b991c71, "8c4myvdcm4f7", 6.666184489, -142.785312424}, + {0xc060d50d42733752, "s1heb3b2fdvp", 6.293937407, 6.339568753}, + {0x14d1bb591b73c966, "2m8vqq8vfg4q", -13.102938624, -167.419503199}, + {0x5d6c18b68b0c1706, "cpq1jenc1hch", 85.973708652, -126.311486704}, + {0xb2c0715bbfcdf111, "qc072qxztrsj", -38.766676795, 124.123422582}, + {0x5281f207a4768e4c, "bb0z41x4fu74", 46.23920381, -145.097686568}, + {0x4c291c1bf2c4d4c8, "9hnjs6zksmbd", 23.482706763, -126.365592788}, + {0xf0f06cb77b071052, "y3s6tevv0w85", 53.898446209, 107.476179692}, + {0x1d812f0d55d2a41f, "3q0ky3bpubk1", -10.404094004, -123.123569777}, + {0x3876313ec8038964, "71v32gq80f4q", -34.918658833, -37.575266857}, + {0x52e82dc2b6ce8e95, "bcn2vhpqtu79", 50.779947487, -137.231003089}, + {0x64a960af9a738cb3, "dknq1cwuff6c", 23.563754919, -69.87464167}, + {0x66a6675152caa862, "dum6fnbktbn6", 24.427500129, -48.778698323}, + {0x1167042db23b7781, "25mh8cek7evs", -25.918744232, -172.931170164}, + {0xd4c4234743cc2113, "um226ju3thhj", 74.607060843, 11.695509674}, + {0xb22cf1506c3595e3, "q8qg2n3d6qby", -42.987705127, 121.994345101}, + {0xcc4459e31667c2b6, "tj25mssqdz1c", 30.128411926, 45.247656233}, + {0xad3b215ed859b899, "pnxk2rqsc6w9", -52.649775225, 145.215287761}, + {0x833c63747e8abcb7, "hdy66x3yjbyc", -74.09488245, 31.401585002}, + {0x5ddc32292e54a8ab, "crf34b9fbknb", 88.772771401, -120.46266456}, + {0xb89aa0a17f4e6ce2, "r2eb18cz9tqf", -42.182064165, 151.591832319}, + {0x6d44a4efac659b4d, "ep2b9vxddqen", 40.899818203, -43.858007722}, + {0x8f0ef6a3f8354360, "jw7ge8zs6p1q", -54.223627604, 72.937633348}, + {0x9cc3f1e4f5b3048e, "mm1z3t7pqd28", -15.570490112, 58.781255296}, + {0x2991aef991b768b6, "568uxydjqxnc", -75.109839766, -32.351701635}, + {0x9fd4bdfe52ce42e5, "mzbcvzkktt1f", -1.058007581, 80.063447991}, + {0x57e1e4d0484233c3, "bzhy9n2888tw", 85.551999997, -139.525651623}, + {0x5b902f77a4df4bf4, "cf82yxx4vx5z", 59.236045558, -100.602938786}, + {0xd35fbe04e0da4540, "uegvw170v92n", 67.067549829, 28.041465355}, + {0xda52fea1ed2b2012, "v99gx8ge5dh1", 54.057380161, 70.295489581}, + {0x05fec7ae7d6c4b1b, "0rzdgcmxej5j", -45.914669938, -158.030643933}, + {0xad869eb17d914c89, "pq39xdcxk568", -54.563709884, 148.691200025}, + {0x7715b27f30a68163, "fwbv4zthnu0q", 83.889564147, -66.31730633}, + {0xea857b48bf88a545, "xb2rqk5zj2kn", 2.703840832, 169.381601245}, + {0x1a2de9094ec3dc4c, "38qyk2bfsgf4", -42.490547613, -102.819808211}, + {0xa91bcfda5a304d0e, "p4ewzqku616h", -74.715907263, 140.247090475}, + {0x6c255b64eecf1483, "ehkpqt7ftwb8", 25.209968608, -39.083968781}, + {0xec9dd51145d2b4d7, "xkfxb4b5ubue", 28.096809307, 149.765834973}, + {0xbf3caf49aa6861a1, "rwybykebe1hu", -6.874602887, 167.272130738}, + {0x21ea01991c732c13, "47p0368wfdq1", -73.066150746, -68.850516235}, + {0x2c16598397ed234b, "5hc5m0wrxnjn", -62.705893646, -43.36513294}, + {0x36646aa3656d27c6, "6tk6p8v5enmw", -15.112432177, -61.186844922}, + {0x9d10a86294bff4b4, "mn8bhsnnrzuc", -8.414412605, 46.26102013}, + {0x43804e0a307335d0, "8f04w2jhfdux", 11.690284476, -145.968361937}, + {0xf09d8394f399bf1a, "y2fs757mm6zj", 49.984589916, 104.902159132}, + {0xfaed169d63502d6f, "zcqje7c3b0qq", 53.018949161, 177.332047574}, + {0x16910a56147e1603, "2u8hnphngsc0", -18.944738597, -145.980683253}, + {0xb409b9e958bc3390, "qh4vmubsrhtt", -21.550301342, 94.120867518}, + {0xd070c401bd7b9c63, "u1sd80exgff6", 53.881045225, 6.333095605}, + {0x10451e773fe2b903, "212jwxtzwbwh", -36.95946351, -179.706161176}, + {0xcb7de7861feeb5ca, "teyyg1hzxuuw", 22.287080571, 77.13088546}, + {0x19e7f8d047bfa62c, "37mzjn27rym2", -25.453357441, -115.443652768}, + {0x50eeeeec3d4c5b51, "b3rfxv1x9jep", 52.499487137, -157.508879422}, + {0xf1f8c9e2319d0d84, "y7wdmsjjmn6s", 65.105915989, 110.639408606}, + {0xa36345e72720ed55, "nejnctt743qp", -71.907741155, 119.604482384}, + {0x109b82da7b533ff1, "22es5qmvbdzz", -41.449039692, -163.677374413}, + {0x7b8ad07f92c42753, "gf5e0zwkshmp", 56.819338987, -6.286404466}, + {0xfe3886aa2a27ffd0, "zsw8ebjb4zzx", 70.400433374, 166.81344448}, + {0xe1550c6e3276b7bc, "w5bhsvjkfuvv", 21.91307328, 90.216075914}, + {0x1b1edbb57fb4a158, "3dgereczqkhp", -28.938094363, -107.245858143}, + {0x2c6f7fab9d8c6096, "5jrrzbwxjjh9", -59.102470004, -34.454964168}, + {0xfefaee20aebaa8cd, "zvxfw85frbnd", 76.377339828, 179.939573354}, + {0xadb48a9a33ad4420, "pqu8p6jmpp22", -52.019366459, 152.904239118}, + {0xce5c59610737c4df, "ttf5ks876z2e", 32.940355746, 70.510716203}, + {0x5b51dafb23504631, "ce8xpyt3b133", 65.953981658, -111.449087309}, + {0xe858c9f754045910, "x1ddmxun0jdj", 8.876640183, 138.762820134}, + {0x4e8599693b4f47e2, "9u2tku9v9x3y", 24.854804875, -100.335664629}, + {0x86ae322dc46b7063, "hur34cf4ees6", -65.907907374, 44.06906475}, + {0xfed15c5321498d69, "zv8psnt1966q", 77.291835568, 168.932704592}, + {0xc8a2a15a42061d5b, "t2jb2qk20sfp", 0.0783043, 64.352784714}, + {0xe6667ca3103f4c6d, "wtm7t8sh7x66", 30.149999899, 120.130158283}, + {0xceb3f1cdf5627db4, "tutz3mgpd9yv", 26.619797545, 86.895105383}, + {0x6bbb78201ce85fc2, "efxrh80wx1gw", 15.294094948, -0.855941703}, + {0x5adb733d8cedad98, "ccer6gddxqqt", 54.731603665, -96.555084598}, + {0xd587f8c174ca914d, "uq3zjhcntb8n", 81.413930907, 13.932284364}, + {0xc89357a97db4688b, "t29pgbcxqjn8", 4.180195974, 57.823371387}, + {0x31cccae8150cbd71, "676dpu0p1kyr", -26.343986477, -74.893736795}, + {0x5eb5fdd51a7f24d1, "cuuzvp8ugwke", 73.123102896, -94.349399651}, + {0x569f6854c773465f, "bugqhp67fe35", 72.813924773, -141.500707412}, + {0x8e58315fdd675d62, "jtd32ryxdxfq", -58.798871339, 70.684085686}, + {0xd6e53c7358775134, "uvkmswusfx8m", 75.535982305, 39.930603886}, + {0xfc3c8b04e46b4d7c, "zhy8q174ee6r", 71.770009616, 144.408570178}, + {0xb783448b19e73b5a, "qy1n92stwwxp", -10.103695078, 125.217650041}, + {0x48f688a191acc4e0, "93v8j8djpm2f", 9.847383426, -115.770866984}, + {0x56ac6ae03fbeaa8a, "buq6ps1zrup8", 69.281051198, -137.128601411}, + {0x2d3802e7664496ae, "5nw05tv68kcb", -53.405459567, -36.401468263}, + {0xf296590d4229480c, "ybc5k3b25540", 49.799668292, 125.343492523}, + {0x2bd3e9dcbc0b34b1, "5g9ymr5w1duc", -69.174370086, -8.553472301}, + {0xeb5bbf4e3a01d9ad, "xeevymju07du", 20.726414247, 163.056006793}, + {0x89d4572f16dc28b7, "j7b5fcsqvhnc", -68.237650074, 56.376909863}, + {0x2434cbf0e3271741, "4hudrw734wcn", -62.851200983, -83.337713286}, + {0x56512253dade5a6a, "bt8k4nyuvte6", 76.678549113, -157.051036444}, + {0xf0fed521579c73d4, "y3zeb8brmjtx", 55.508324901, 111.819412942}, + {0x2668b7493e20574c, "4tncfk9y41cn", -61.541623107, -57.906444925}, + {0x19e51b9d4add8f11, "37kjr7bbvq7j", -25.775135881, -117.805137163}, + {0x3fb1b16fb312036f, "7ysv2vxm281q", -7.483533661, -4.527373751}, + {0x500d188a88c5af8b, "b06jj2n8sqrs", 47.285276327, -176.947667949}, + {0xa47f9fc4a7d50b57, "njztzj57un5p", -56.617364754, 100.859131179}, + {0x4f55b444b90ec836, "9xbv8j5t1v43", 44.58889811, -111.440427711}, + {0x4c7b557310275b84, "9jxpbwsh4xes", 32.336237912, -125.128672172}, + {0x0384c2cdc8bf141e, "0f2d5mf8rwb1", -76.960522417, -145.400297375}, + {0x751e07a69e155542, "fng0g9ny2pbn", 83.107194593, -85.61817154}, + {0x9d9dd57630638e92, "mqfxbxjhdf79", -5.629708441, 59.794586929}, + {0x51f896a58e16ce13, "b7w9e9df2v71", 64.959826241, -159.451753531}, + {0x8f8b39395b3cb65c, "jy5mkfbv7kv5", -55.311114927, 83.530229065}, + {0x77698515e928a5dc, "fxnsb5g952kx", 85.230734591, -58.35442276}, + {0x428b531097882b3b, "8b5p644rj0pm", 1.286602929, -141.940044864}, + {0x2d3a2f077d97cb2e, "5nx2y1vxkz5k", -53.294768167, -34.533254008}, + {0x062f27f604f9c538, "0srkgxh4z72m", -65.219846555, -147.145074094}, + {0x10fc97c6f4eb67c5, "23y9gjrnxemw", -34.818659312, -159.467596763}, + {0x2edb5a56888dc146, "5vepnpn8jr0n", -57.793540453, -6.758426298}, + {0x0aa243545aab180e, "1bj46p2updd0", -89.563966566, -94.129504577}, + {0x3c23f47556dcb085, "7hjz8xbqvks8", -21.137885584, -36.891515159}, + {0x9cb98b3c239fe27a, "mkwsqg13mzj7", -18.923697544, 65.68922658}, + {0x9c99d84cca3fbcda, "mkdxhm6b7yye", -18.428112483, 59.956339064}, + {0x562384ba2c0decb6, "bsjs9fjd1rqc", 68.302384426, -149.681104815}, + {0x81c011e4e4b969bc, "h7013t74r5nv", -72.876031046, 11.320347495}, + {0xa4cf1f9b1fe811a9, "nm7jz6szx08u", -59.442984028, 105.794178272}, + {0x5a86de37b4894221, "cb3ewexnj512", 47.04175657, -98.845151134}, + {0x2320bef20476568c, "4dhcxwh4ftc8", -78.452866118, -60.485120009}, + {0x82d393561c083d95, "hc9t6phw10yt", -80.600161294, 35.953497078}, + {0x7aff9b4b6e28976f, "gcztqkvf52cq", 55.793042581, -0.420428963}, + {0x92a53552be73a718, "kbkmbnpyffmj", -42.548877171, 39.737330409}, + {0x7abfb25f0a1e041a, "gbzv4rsb3s21", 50.138920865, -0.246097719}, + {0xcbb11b87cf81a2e0, "tfsjr1ygh6jf", 14.995490681, 84.692077054}, + {0xf520b6dfb17ed8b1, "ynhcerxjgvdc", 79.055887382, 96.832287441}, + {0x7891728e4496eb46, "g28r53k4kvpn", 49.050249382, -33.249908698}, + {0x407d0cd17f2e6197, "81yhtncz5tht", 10.673073913, -171.340213817}, + {0x082f8772b885c111, "10rsfwpshr0j", -87.725104996, -124.332779592}, + {0xcb7fcdd689364ca0, "tezwvpn96t6b", 22.318968363, 78.275638939}, + {0xca426456647a82a0, "t9168pm4gb1b", 6.104750675, 69.264842244}, + {0xb4ad4aefa1e8829e, "qkqnpvx1x219", -20.008592329, 110.038002649}, + {0x9b3c99402829b00e, "mdy9kh1856s0", -29.289537245, 76.818611289}, + {0xa2047a6e284a5129, "n827nvj8998k", -88.038845837, 113.155811338}, + {0x4593c0c7ae3c5b35, "8q9w1jxf7jem", 37.647816629, -166.585903519}, + {0x6bc172b2e28bc352, "eg0r5dr2jg1p", 18.117845652, -10.734424306}, + {0x4f6c48c80ba910c4, "9xq4jk0cp48d", 41.154970674, -103.830456041}, + {0x2c0af98a695cd1dd, "5h5gm2m9cm8x", -66.927006398, -39.488241985}, + {0x703cbe961a2d757a, "f0ycx5hu5pur", 49.499630762, -80.193537447}, + {0x5fb1749c204836f4, "cysr971090vg", 82.89742933, -95.217081926}, + {0x424dac0547c43d57, "896us1b7shyp", 7.832506298, -153.456508088}, + {0x16504c8be2fecd9e, "2t84t2z2zv6t", -13.618769139, -157.258994485}, + {0x5bc0618ac60b1520, "cg0632q61dbk", 62.272239858, -100.834858063}, + {0xed16c19c5a1d7b7d, "xncd372u3pxr", 38.382881696, 137.16538943}, + {0xb72bc3f8be6ced83, "qwpw7y5yemqs", -10.117263489, 123.216968061}, + {0xb1059507f6478ce7, "q42tb1zq8y6f", -31.32224495, 90.713117388}, + {0x30dde865723d2564, "63fyhtck7nkq", -34.069212599, -74.683207458}, + {0xe53028d1d7940bef, "wns2jnfrkh5y", 36.600846001, 96.199555934}, + {0x222e2120706b2a11, "48r2283hedp1", -88.547647969, -57.281181919}, + {0xb41ec4802a6526bd, "qhgd901bdnmc", -17.841677193, 94.968317358}, + {0xb4996e15e03cdaa2, "qkdqw5g07meb", -18.524250055, 104.681998892}, + {0x01a716b9aad49003, "06mjefebuk80", -76.363067266, -161.548622748}, + {0x918790dec57ccbff, "k63t1rq5gm5z", -31.424346445, 13.422691197}, + {0xdb741d9bebe2a4c0, "veu1v6zcwbkd", 66.416732564, 73.366651689}, + {0xb1b08e90c8d58404, "q6s8x468uq20", -30.837087394, 107.889357855}, + {0xc57b82b8b8c8cc26, "spxs5f5st362", 42.902390608, 10.716704911}, + {0xf9866ea34b83b770, "z636x8uchfvr", 58.100010625, 148.344136442}, + {0x1deab0fa803675e5, "3rpc1yn06tuy", -5.416184842, -112.766304744}, + {0x3299edf2897e6979, "6bdyvwn9gtnr", -40.96768912, -52.132028108}, + {0xf4aedbed8f07c2bc, "ykrervdg0z1c", 69.508304293, 112.141262465}, + {0x1da1f7be1d927f57, "3qhzgghxk9zp", -9.869960577, -116.899135099}, + {0xab55417d9677f617, "pebn2zdqfzv1", -67.765217862, 157.536173707}, + {0xd490291b6b988dfc, "uk82k6vcm26z", 70.371768722, 11.79645572}, + {0xf17c2bab65af67f5, "y5y2rbv5pxmz", 66.142370398, 99.136834731}, + {0x44e096446878d010, "8mh9dj38g381", 28.417647975, -162.331784893}, + {0x8bca613d70729645, "jg562gchfbc4", -72.708072906, 83.354764185}, + {0x000994d583daba3d, "004t9pd3vbx3", -88.991700995, -176.437083118}, + {0x70f903a9546c47b5, "f3wh7bbnej3v", 54.189835112, -70.147573952}, + {0x6a732e53fa619d51, "e9tkwnzud6fp", 9.266382927, -14.842773311}, + {0xc7cc21da167d1383, "sz623qhqgn9s", 40.859348616, 36.974961517}, + {0xa5b05e165ce5838a, "nqs5w5kwwq1s", -52.803264173, 107.145122174}, + {0x5ecc1c00471579cd, "cv61s0272pww", 74.796894467, -98.26136649}, + {0x68479645acd2d3b7, "e13tdjeduc9v", 8.028740367, -42.79772156}, + {0xee06732ea9a764fe, "xs376cp9nxkg", 24.483243375, 159.389247386}, + {0xb4776b2a736d86a1, "qjvqqbmmeq3b", -11.555266168, 97.686794985}, + {0xabc3919054fa255b, "pg1t342nz8kp", -72.188627532, 170.903647819}, + {0x7e9e193b56d4108c, "gug1kfuquh88", 71.954765718, -6.816500733}, + {0x415fb6b9cb00a196, "85gveffc02ht", 22.075825698, -174.557974161}, + {0xe5452461df129808, "wp2k8sfz2bd0", 41.599605405, 90.377346797}, + {0xb9c771b6387d58ae, "r73r3ejsgpdb", -25.427000705, 148.081416298}, + {0xdce8a76e6a109dbc, "vmnbfvmb22fv", 73.28572291, 65.870948191}, + {0x5465d0a8512e137a, "bjkx1b2j5s9r", 75.763975695, -173.59481855}, + {0xf59cf12f8a871e90, "yqfg2cwbhwg9", 83.548315197, 105.15961404}, + {0xab039e7e58a0f3d9, "pd1twzksn3tx", -77.742681737, 159.91246146}, + {0x7b84d61f1f5b7982, "gf2ed7szcews", 58.292060303, -10.44140172}, + {0xd60f0e5785a590ba, "us7hwpw5nq8c", 69.739014386, 26.990931174}, + {0xe6d5bd870142c051, "wvbvv1s18c05", 33.362995351, 125.029939438}, + {0xbc0eaa0cce5d85b2, "rh7bn36fcq2v", -21.086377089, 140.551927036}, + {0x9f24a727bb823a06, "mwkbf9xvh8x0", -9.702813223, 74.300396418}, + {0xea33dd94cb6a1546, "x8txv56ce8bn", 4.192920098, 165.458028403}, + {0x39c3d54ac11a68b3, "771xbkq139nc", -26.739129192, -31.621325335}, + {0x25963a3312704772, "4qc3ndskf13r", -51.840912559, -76.700618852}, + {0xdc10f5d51aab4253, "vh8gcp8upe15", 71.013581722, 46.099986075}, + {0x521ca6e07c06c93a, "b8fbes3w0v4m", 49.331044228, -153.476908506}, + {0x19e9cc8d806a6e2d, "37nwt3d0e9r2", -26.974089449, -114.37575539}, + {0x1088c06caee7342a, "224d0v5fwwu2", -44.620506279, -165.195981254}, + {0x72df407c5dcd62be, "fcgn0z2xtpjc", 55.939588235, -51.997381803}, + {0xe9400c0a91ce3919, "x500s2njtswj", 16.963859805, 135.195251579}, + {0x84dde94975f650a5, "hmfykkcpyt8b", -56.530164613, 15.305608365}, + {0xd65640da6cf11d9b, "utc41qmdy4ft", 77.730132518, 23.968995877}, + {0x4027fb49ce34e2a6, "80mzqkff6mjb", 2.707169771, -171.635523338}, + {0x0d2aeddd5f13d2a1, "1npfvrbz2g9b", -55.722766139, -123.869785066}, + {0x8d0a0ddb8c3ded3b, "jn50vqwd7rqm", -56.082032467, 49.458537454}, + {0x171523a85e262b38, "2wbk7b2y4spm", -6.281754099, -156.982495414}, + {0x6ffa2ed18f3872d5, "ezx2xndg71te", 42.311662771, -0.743157592}, + {0xb6e5cb6e86008556, "qvkwqvn6022p", -14.342305433, 130.383349812}, + {0xfbdb87e2a99b7a62, "zgesgsp9mex6", 65.544667306, 173.836229153}, + {0x862519dd5acbd298, "hskjmrbutg99", -65.127534963, 28.356991879}, + {0xaea6b005544821f5, "pumc01bn90hz", -65.907238599, 176.835986787}, + {0xa425a2ce36ed842a, "nhku5mjqxq22", -65.362009839, 96.830031306}, + {0x19e43a485311970b, "37k3nk2m26ch", -26.518706077, -117.49842359}, + {0x912c8b256fa20d8f, "k4q8q9cgn86s", -32.289674492, 9.428955365}, + {0xdef21018458bc8b9, "vvt10625jg4c", 76.126172176, 85.792486955}, + {0x0e8ccc3fab85ccf4, "1u6dsgxchr6g", -65.634859644, -97.514805643}, + {0xe1a82d6b1ccc888c, "w6n2uuswtk48", 11.407692521, 110.254211374}, + {0x3fa43e16b788bdad, "7yk3w5prj2yu", -9.562392145, -4.999574661}, + {0xc17b90233dde49b1, "s5xt08txvt4v", 20.570510917, 10.576640531}, + {0x3f54ae92faca9c73, "7xbbx4rutbf7", -1.305222681, -21.126802431}, + {0x69d9e11e82b0428a, "e7dy27n2q118", 20.802660616, -29.86298334}, + {0xbc28cde163bf9237, "rhndvsc3ry93", -21.990259257, 144.384375243}, + {0x7850940d52ca6f94, "g18983bkt9rt", 53.711565053, -44.285299247}, + {0x0910f721d5145458, "148gf8fp2jb5", -75.272924541, -133.832702428}, + {0xe1eb722be9e88f30, "w7pr4bz9x27m", 18.109848301, 111.576777307}, + {0x9088eb845d0e36f7, "k24fr12x1svg", -44.596397987, 15.425563584}, + {0x8d5c11b7f3ecafe8, "jpf13ezmxkry", -46.164956089, 47.888696066}, + {0xd6b9a969b4bd6f81, "uuwukuenrprs", 71.085402048, 43.455358138}, + {0x9aecbf2d39a62667, "mcqcyc9tnsm6", -37.652011462, 88.541164412}, + {0xe6a2d5ca4fd70dd8, "wujeckkguw6x", 23.183204075, 131.546026451}, + {0xec8289d653566b78, "xk18mpkmbtpr", 22.584785814, 148.58496944}, + {0x6f1a26ef0ca3c3ad, "ewe2evsdng1u", 36.680963261, -17.758420515}, + {0xa1947c409827329d, "n6b7sh4s4wt9", -73.893321123, 101.780891877}, + {0xa14968308d3a1228, "n54qhd4e7892", -72.058765558, 93.365401717}, + {0x13b5c64bbedb3e04, "2fuwdkxyvdz0", -28.36277907, -139.812109556}, + {0x52a70e854a0451b4, "bbmhx1bb0j8v", 47.20690909, -138.910101775}, + {0xbd1a2b7053ff2afa, "rne2qw2mzwpg", -8.358202641, 139.856632932}, + {0x07ebbebee57abb7f, "0zpvxgr5gbxr", -49.639702599, -135.001202357}, + {0xafa1f6b27946414b, "pyhzedmt8t0n", -54.918308988, 175.591070972}, + {0x431eaa5f463d95db, "8dgbnru67qbx", 15.511745711, -151.945927397}, + {0x5f714f2965bc1d03, "cxsnybc5rhfh", 88.378727429, -106.576692618}, + {0x4dc389972fbca130, "9r1sm5tgrkhm", 40.141876621, -121.412685104}, + {0x199b37639ac21bdd, "36emfswus8ex", -29.901262081, -119.060356495}, + {0x1503edf522052220, "2n1yvx920nj2", -10.022245619, -177.295639551}, + {0xe81e04339a2be6ff, "x0g08dwu5gmg", 4.321078251, 139.25016128}, + {0x7d0f519393d6c5bc, "gn7p34wmuv2v", 81.445413118, -40.728516552}, + {0xd8ac979cbe9b156e, "v2q9g75ymdbq", 46.731435414, 65.538833775}, + {0xb47ec62c043509ed, "qjzddc046n4y", -12.21088415, 100.667814308}, + {0xd0c1111f33f25cd5, "u30j27tmy9fe", 51.568074732, 11.268478442}, + {0x27b93943e827a5af, "4ywmkhz84yku", -52.488521549, -47.274731875}, + {0xa8954ad0c74c1b71, "p2bnpn679her", -84.691604023, 146.560754418}, + {0x1409ef16037f9c81, "2h4yy5h3gyf8", -21.296660398, -175.863139832}, + {0xf63e3168d2b2aa17, "ysz32u6kqbp1", 71.962552381, 122.73166059}, + {0xb3eaea1447a4a72e, "qgpfn527nkmk", -27.755046168, 134.912728227}, + {0x5c1a4e4c3dd874de, "che4wm1xv1ue", 70.7807593, -130.504310991}, + {0xb209098467b96665, "q84hm137r5m6", -44.245487412, 115.534250691}, + {0x11a0240ff4339de0, "26h283zn6ffy", -33.651407691, -162.752716408}, + {0xc60c16548ef6c19f, "ss61dp4fyv0t", 24.208873918, 25.404457789}, + {0x457ad38ec393a78b, "8pxe73q3kfms", 42.765882911, -169.301508185}, + {0x68024f7b08551609, "e014yys8bnc0", 0.519269005, -43.290937816}, + {0xf723effda8658cc1, "ywjyzze8dq6d", 79.977841944, 120.93142016}, + {0xf3019b21a6095173, "yd0tq8e6158r", 57.175961463, 113.493276132}, + {0x88d8b1a2c3260e7a, "j3dc38q34s77", -81.341205173, 60.191800463}, + {0x28f5481ac648e49a, "53unh6q693k9", -79.088766823, -27.929589381}, + {0x775024199df0fa82, "fx8286dxy3x8", 87.29046197, -67.133739551}, + {0x673364ddb49b3456, "dwtq9renmdu5", 37.747365114, -60.05789442}, + {0x2777aa4b59b35cf8, "4xvunkutqefg", -45.67611061, -59.132941671}, + {0xa00491e849daa801, "n0293u29vbn0", -88.350377035, 90.780970781}, + {0x59d6d82ada3db935, "c7cehbqu7qwm", 66.623232447, -121.422467187}, + {0x2097c94851ddeeb6, "42cwkk2jvrrc", -84.658245028, -76.453621573}, + {0xf04a1126dfa98a51, "y15129qzp655", 50.852805956, 94.250307577}, + {0xb1a84b0522135855, "q6n4q1922ed5", -33.346190087, 109.952912709}, + {0x1f62d3b86cd67520, "3xje7f3dutuk", -5.040847697, -104.598571319}, + {0xaaa239fa269a2f66, "pbj3myj6m8rq", -89.746925743, 176.39295374}, + {0x91e8b318d48dfa8c, "k7nc666njrx8", -27.891843492, 20.844046583}, + {0xcd2b3b98cf6f1b00, "tnpmr66geweh", 34.685847575, 55.517846838}, + {0x4df0a1dd5f3c6b49, "9rsb3rbz7jpn", 42.275289769, -117.014212511}, + {0xfa0ea77ac009c96c, "z87bfyq0174q", 46.57243541, 162.902583707}, + {0x90b96eb217686fc6, "k2wqxdhre1rw", -41.032640287, 20.37462778}, + {0x3a6559889b17ee7b, "79kpm24v2zr7", -36.693395269, -16.640468349}, + {0x13489c480dfd6d8b, "2e49sk0ezpqs", -27.838672112, -153.796611384}, + {0xccc3fedd236e7c76, "tm1zxr93ety7", 29.484847063, 59.031416352}, + {0x2370e2889f9018b3, "4esf524zk0dc", -69.959690032, -60.673538206}, + {0x283ae1b9f9fd2667, "50xf3fgtznm6", -86.775864921, -34.019544086}, + {0x60658ac5f4916302, "d1kspjgnk5jh", 7.767050343, -83.359959446}, + {0x883f0af9c8bb1c1f, "j0zhpyf8rdf1", -85.040988142, 55.188084213}, + {0x0863f20524aedb42, "11jz4194pven", -83.135917645, -126.824461862}, + {0xb4769226cd0b22b9, "qjv949qe1djc", -12.47307634, 97.853236993}, + {0xbaad2fc791268d6f, "rbqkzjwj4u6q", -42.727694408, 177.855040736}, + {0x54b4cd430b399e84, "bkuduhsc76g8", 72.227097097, -162.239426393}, + {0xda4842f9ebfc8bad, "v9445ygczk5u", 51.013964024, 70.482761154}, + {0xebba83efdc0aabf3, "xfx87vyw1bpz", 14.139062231, 179.470681917}, + {0x6940c32c0a4724e1, "e50d6c0b8wkf", 17.276120753, -44.174967583}, + {0x6c405131533fc5b3, "ej052dbm7z2v", 28.712336297, -44.97752198}, + {0xb13f71f3eb972188, "q4zr3wzckwhs", -28.219509612, 100.272072849}, + {0xef86e17c3650047c, "xy3f2z1qb027", 35.591370626, 171.245613619}, + {0xb6614d78e4af8d91, "qthnuy74py6t", -15.65376655, 118.338196079}, + {0x4d91067a04b3bb5d, "9q8hdyh4qfxp", 37.386875564, -123.623357984}, + {0xd0a5c1934772064c, "u2kw34u7f834", 47.520634517, 17.628015642}, + {0x35bcae946c326797, "6qybx53d69mt", -6.925112679, -68.948020606}, + {0xed91c055d5a3cdec, "xq8w0pfpng6y", 37.660979716, 146.956165138}, + {0xb626087da7f59f06, "qsm0hze7yqgh", -21.051870573, 119.744725762}, + {0x2886ae84b5b8225f, "523bx15pr0j5", -88.499121024, -30.977018042}, + {0xfeaf6aa0efdc0f3a, "zurqp87gvh7m", 69.962978171, 179.280271374}, + {0x34e54281de322635, "6mkn50fy68m3", -14.408868073, -72.989273136}, + {0x7873bf2ecb9a3d0c, "g1tvycqcm8yh", 54.455328004, -36.607917666}, + {0xaf3752b3cf8fa24f, "pwvp5dygjyj4", -50.785127399, 164.694582083}, + {0x8be37bec0bda63de, "jgjrrv0cv9jx", -71.822813072, 86.474662304}, + {0xe5c1e88f757449b3, "wr0yj3vpfj4v", 40.44065862, 102.542356496}, + {0x64df81be7f62d1f4, "dmgs3gmzdc8z", 33.109969876, -73.743205207}, + {0xd211efc40c593a21, "u88yzj0dc4x2", 49.02697482, 23.863043998}, + {0x27a52a6bf27fefc9, "4ykknuzkgzrw", -54.113677126, -49.966679084}, + {0x10929aaf4020d2b7, "2299pcu0439c", -42.002097342, -166.294456592}, + {0x00972ef05f3955be, "02ckxw2z75bv", -84.954636504, -166.661437619}, + {0xcf7cceb7f65489a8, "txydxezqbk4u", 44.054983351, 76.980185799}, + {0x1002840d03911c8e, "20188383k4f8", -44.903640186, -177.879120072}, + {0x020215574fc71b13, "0811bpugswej", -89.64917421, -156.087029604}, + {0x67cad8f1ed9285a8, "dz5ejwgekb2u", 39.939982557, -51.081414241}, + {0xb423628766b230a4, "qhjq51v6q8sb", -21.435310448, 97.522143576}, + {0xed6668f13407edf2, "xpm6jw9n0zqz", 41.169588111, 142.625926345}, + {0xbc58f45a19ac4d9d, "rjdg8qhtpj6t", -13.413419202, 138.884657639}, + {0xee1f9adb9bfdd9b0, "xsgtpqwvzrdv", 27.634389943, 162.750063118}, + {0x300c03864b750f77, "606071kcfn7r", -43.542602976, -87.049050637}, + {0x11f0de8b9fd3178c, "27sex2wzudcs", -24.693173123, -162.093806436}, + {0x1b627200fb886a8c, "3ej7407vj1p8", -27.595419175, -105.023922178}, + {0xdad7aabee4f821a0, "vccupgr4z0hu", 55.565200553, 81.561433507}, + {0xd3734be035bfadaa, "uetnrs1pryqu", 65.809384213, 29.862555055}, + {0x7587bb448a59aabf, "fq3vqj4bc6pc", 81.106707152, -76.021549738}, + {0x42cce2425ab4f107, "8c6f4hkuqmsh", 7.40691832, -142.288125053}, + {0x161451b0168b29a3, "2sb53d0qjdnu", -17.697933188, -157.433493528}, + {0x7482f4a0c76d2713, "fk1g9867enmj", 68.117242908, -76.219919078}, + {0xcae068d6d14e53c6, "tch6jpqj9t9w", 6.017362674, 84.954595314}, + {0xfcea3313fc35b1d6, "zmp364zw6qsx", 73.360941781, 156.543605518}, + {0x1913545c565d36d7, "349p8r2qcnve", -29.578119631, -133.582369939}, + {0x2f2a9001b1c25573, "5wp900ejs9br", -56.070517411, -11.948811578}, + {0x254dfc7c6331714c, "4p6zsz3365sn", -47.860330717, -85.922264278}, + {0x26556c0b56a21709, "4tbqs2uqn8ch", -56.508517294, -66.955552944}, + {0xf9ade44de3f4fca4, "z6qy8mg3ymyb", 58.830745158, 155.757914305}, + {0xcb22f1fd084dd7d2, "tdjg3z889rcx", 11.862616391, 75.663584252}, + {0x213427801a11e96e, "44u2g00u27nq", -74.398667291, -83.890552155}, + {0x2005828d8524ea98, "402s53d54mp9", -87.881843267, -89.15119932}, + {0x662070278fd1282e, "dsh709wgu4n2", 23.03623804, -61.492014767}, + {0xa125b27915861517, "n4kv4y8phsbj", -76.427910083, 96.800730322}, + {0x4c7cb57882715bc6, "9jycby42f5ew", 32.684474808, -125.471672956}, + {0xc0ba45fde2efc47d, "s2x4czg2xz27", 3.338595821, 21.175450602}, + {0x768b084051aaa899, "fu5hhh2jpbn9", 68.227329493, -51.85512725}, + {0xc756ea8bb67f4c07, "sxcfp2xqgx60", 43.949256849, 25.28965042}, + {0x118bc763541361fd, "265wfsun2ehz", -32.536293739, -163.712740476}, + {0xbc5b90f3af255d24, "rjet1wxg4pfk", -13.147330844, 139.998524893}, + {0x10b807ce0d042f45, "22w0gmhe0hrn", -42.027659294, -160.163488536}, + {0x47861275557c2caa, "8y314xbpghqb", 35.37595648, -144.733748622}, + {0x4beefc8e97fb2405, "9grgt3nrzdk0", 18.90332012, -90.111939248}, + {0x1fb5c77d74497949, "3yuwfzcn95wn", -5.801018916, -94.799600911}, + {0xec5aca389cf82069, "xjednf4wz0h6", 31.301207936, 140.222245391}, + {0x0fca6589fd38128f, "1z56c2gx7098", -50.136234335, -96.619818273}, + {0xc140b80f084cd365, "s50ch3s89m9q", 17.059137491, 1.247690264}, + {0x0241aa50f11b5934, "090unn7j3edm", -83.636621776, -156.177453348}, + {0xb8d74b5eb95c6aff, "r3cnqrptcjpg", -34.018150521, 147.941261525}, + {0xf3152637c82955bf, "ydbkdey855bv", 61.280386104, 112.970482207}, + {0x21eb1e23404d4fae, "47pjw8u09p7u", -72.153957768, -68.615064033}, + {0xfe1940141d5dfa13, "zsdn050xcrx1", 71.385037849, 160.313249842}, + {0x1a065d2d5ea9f79c, "3835ucbyp7vt", -42.923906249, -110.883661067}, + {0x9bd600b56b3c4e09, "mgc01ecc7j70", -23.885407844, 80.224704206}, + {0xdf44b4851237dd32, "vx2c918k6zfm", 86.05393362, 68.599097276}, + {0x517e6ec249e98cc7, "b5z6xhk9x66d", 66.556819129, -169.49057589}, + {0x38356661e8fc385b, "70uqdsg8zhw5", -39.612425885, -38.908458992}, + {0x6c598be02a58b996, "ejdsrs1bc2wt", 31.706675323, -41.152319397}, + {0xc290b2a83b3a9a8b, "sb8c5b1v7be8", 2.989185234, 34.972051608}, + {0xfc54fa6ebb694c93, "zjbgnvpve569", 77.899521371, 136.362093603}, + {0xdea14eda9003fce9, "vuhnxqnh0gyf", 68.676244263, 84.701884721}, + {0x35e759a635efda42, "6rmpm9jpxze4", -2.937514743, -71.469847579}, + {0x0d5a652a7b577834, "1pe6bbmvbxw3", -47.326700728, -130.388802201}, + {0x12e634b120618f99, "2cm39d90d67t", -37.691246424, -138.799794804}, + {0x7966b96f2b035853, "g5mckvtc0ed5", 63.531379973, -36.697401607}, + {0x8a46d1362c973353, "j93e2ejdkwtp", -82.380559115, 69.639100666}, + {0xa7fe6d3a6bb7a6d5, "nzz6ufmcqyme", -45.910243185, 134.162246251}, + {0x12ebdb3d8f432f53, "2cpxqgdg8drp", -38.080745725, -135.402686606}, + {0xf274abbcbe099efe, "y9ubrg5y16gg", 54.905220145, 119.525472466}, + {0x3cc706262c56523b, "7m3hd9jdbt93", -14.671739395, -32.226310189}, + {0xb31b89479aba9d15, "qdeskjwurbfj", -30.159482722, 117.607261287}, + {0x33c4a3987e6d965d, "6g2b763yeqc5", -26.661292393, -55.04994595}, + {0x006dc5aa0dfbca1a, "01qwcbhezg51", -81.781566556, -170.775953958}, + {0x5ddd0d254a18e5ca, "crfhu9bb33kw", 89.438374623, -120.738656156}, + {0x33646b4173c1156e, "6ek6qhcms4bq", -26.296189672, -61.257876763}, + {0x55dc43058eb3e51c, "brf461dfqgkj", 88.997902156, -165.845537551}, + {0xdf9597fbd4ea9c15, "vybtgyynxbf1", 84.017691184, 79.62649497}, + {0x4c91d4c6499e1509, "9k8x9jk9msbh", 26.659939297, -122.996513629}, + {0x7c704b6eb4525ef5, "gjs4qvpnb9gg", 76.361642391, -39.068729592}, + {0xa53f9e5ed70013e7, "nnztwrqr009y", -51.023422207, 100.830118722}, + {0xa27c2de14a74546c, "n9y2vsbbfjb6", -79.998161486, 121.531878053}, + {0xb9a5ff3ee766d7f5, "r6kzygr7dvcz", -30.962959549, 153.236395167}, + {0xa14d07034ca15218, "n56hf0udn591", -70.879306898, 92.906829462}, + {0xe678bb5e65e5a218, "wtwcqrm5wqj1", 31.19768518, 122.273987285}, + {0xa5c9b6f6baafdc98, "nr4vexpupzf9", -49.619022548, 105.28197529}, + {0xf82cac7c69b01eae, "z0qbsz39q0gb", 46.534184764, 144.703250463}, + {0x3821752a495a38d2, "70hrbbk9c8we", -43.636018404, -38.984225453}, + {0xbf3f13b68fbb864c, "rwzj7engrf34", -6.091348539, 167.507162425}, + {0x8d99dbb18be160a7, "jqdxrddcw5hb", -52.149079278, 60.099255373}, + {0x83167fd7d139cb45, "hdc7zpyj775n", -73.828579832, 24.573812974}, + {0x54d2996935153afd, "bm99ku9p2nxg", 76.183225878, -166.430500845}, + {0x5bbdf94c7fef8dfe, "cfyzkm3zxy6z", 61.773325431, -91.568305063}, + {0xc6968ce139dc2e51, "suc8ts9tvhr5", 26.832370272, 36.103358131}, + {0x241dafa39fb5ecc9, "4hfuz8wzqrqd", -62.442258647, -85.793675779}, + {0x3f54852566c44b5d, "7xb8b9c6sj5p", -1.264343734, -21.773009831}, + {0xf6920371c28975e3, "yu906wf2j5uy", 70.393545353, 125.269424285}, + {0x65a8caa2c3d1d694, "dqndp8q3u7c9", 34.103256567, -69.271017639}, + {0xb7dcfcbd1436bfcb, "qzfgtg8n6uzw", -0.770690097, 127.870001443}, + {0x83b644ea226bcb59, "hfv49uj2eg5p", -74.069721047, 40.86552928}, + {0x8716d566ca689f09, "hwcebtqbe2gh", -51.343140626, 24.640761079}, + {0x59923a67dd7144ac, "c693ntyxf52b", 59.271214847, -121.697529519}, + {0xf15d5fcf6c40fda2, "y5fpzmvd83yu", 67.488079615, 93.1386765}, + {0xd0bcaaef587729c4, "u2ybpvusfwnw", 49.2511844, 21.089059538}, + {0x2e443b4d19862f53, "5t23qm8thsrp", -60.217928699, -21.872889981}, + {0x4d830fd2cb382068, "9q1hznqc70h6", 34.619508137, -122.026728476}, + {0x9be3ac3cc12bbe86, "mgjusg615fz8", -27.31594509, 87.047595442}, + {0xef6c471d059a6850, "xxq4f785m9n5", 41.284438285, 166.036621057}, + {0xa6af5b035a9eac98, "nurpq0uumuq9", -64.814463502, 133.864200685}, + {0xb43aa8de4cb3ada8, "qhxbjrkdqfqu", -19.647275555, 101.135629109}, + {0x7c57ae6330a11a6b, "gjcuwsthn4e6", 78.160185304, -42.246289331}, + {0xc8b38721a7d16b49, "t2tsf8e7u5pn", 3.650868424, 64.098882744}, + {0x31bea9fb6e279e27, "66zbmyvf4yg2", -29.449846164, -67.590858016}, + {0x7ed52ef92643adb9, "gvbkxy968fqv", 78.170913327, -10.556103226}, + {0xcc6aec7a62ebceb2, "tjpfsym2xg7c", 28.598888847, 56.114725222}, + {0x75733c6eb412eb80, "fptmsvpn2cps", 88.182841644, -82.398791924}, + {0xb2bc0290665ceaac, "qby05436cmpb", -40.768391403, 132.321116813}, + {0x49ab05f55e42ec0e, "96phcxby8cq0", 12.128655561, -113.839261538}, + {0x12460a1d0b42a3cc, "2930n78c8bjw", -37.949265341, -155.818022046}, + {0x9e44503fe6b66fc5, "mt250gz6qtrw", -14.920391339, 67.543204389}, + {0xfef95d931e068172, "zvwpv4sy0u0r", 77.314590218, 177.41378234}, + {0x3eca8920f00fb004, "7v58k87h1ys0", -16.828959768, -6.126170729}, + {0xc2063cf56d51ab31, "s833txceb6pm", 1.713149272, 24.501592173}, + {0xa41c5548467f253b, "nhf5bk26gwkm", -62.598217873, 92.823985834}, + {0x885347f5ba94c7aa, "j19ngxeukm3u", -80.334019406, 46.565393447}, + {0x9dba0c99af395a69, "mqx0t6eg75e6", -8.335299395, 66.329746019}, + {0x118536c07163c821, "262meh3jdg42", -31.352646882, -168.265107158}, + {0xd3ba0006efc39e62, "ufx001rgsfg6", 59.069982376, 43.604601304}, + {0x96c73869b017b981, "kv3mhueh2yws", -14.564359349, 35.720713567}, + {0x0d6fa66c6e4fce87, "1prudv3f9z78", -48.398427091, -123.978229729}, + {0x0182d2d97197d119, "061e5qcjkz8j", -78.184635048, -166.496225419}, + {0xd7a016d2951ad9e5, "uyh1ennp3cdy", 79.047884602, 39.515154514}, + {0x56254ef952857409, "bsknxybkhpu0", 70.086635277, -151.533903272}, + {0xe03b2e5e235e1bbf, "w0xkwrj3csev", 3.642293303, 100.477250139}, + {0x4c2b33d6ffa74eb4, "9hpm7prznx7c", 23.463917782, -124.661924539}, + {0xc792130293079b7e, "sy9160nm0yer", 36.783121032, 35.252760566}, + {0xc4e7131446839edd, "smmj6526hfge", 30.472313091, 18.36969266}, + {0xb5040d60c968ef35, "qn20us69e3rm", -9.688302951, 90.201336343}, + {0xede1ee84f6d2ab72, "xrhyx17qubpr", 40.525604468, 153.241981539}, + {0x60ddcd62a71afa51, "d3fwusp73cx5", 11.052812596, -75.026580167}, + {0x0728e2869e95f9ee, "0wnf51nykrwy", -55.891830089, -147.866515071}, + {0x0cb1c773ae66c44e, "1kswfwxfdv24", -63.464813247, -117.301250417}, + {0x0a923da833d60b5f, "1b93vb1mus5p", -86.878872971, -99.237587866}, + {0x01b3a47b0f07004d, "06tu8ysg0w04", -75.110231169, -160.624558715}, + {0x2c1ca06461be11e2, "5hfb0t31rs8y", -63.252171125, -41.109143529}, + {0x36c39389d69bb612, "6v1t72fqmfv1", -15.94694075, -53.994459264}, + {0x206e6a242951dc50, "41r6n919b7f5", -82.611373274, -79.516969565}, + {0x00a6ee31c3b5c785, "02mfwdf3qr3s", -88.138936404, -160.37505776}, + {0x0573bfbc617465a2, "0ptvzg31fjku", -46.783575764, -171.572020682}, + {0x9782078eb13c1589, "ky10g3pj7hbs", -11.111743482, 35.3088149}, + {0x467a2080861e63ac, "8tx210463tju", 30.937913856, -147.257579952}, + {0x54534af7e8f593bd, "bj9npxz8yq9v", 77.03492414, -178.253589702}, + {0x83964b7dac18737b, "hfc4qzed31tr", -74.094147934, 35.457737586}, + {0x047b8fb1f79b5ee3, "0jxszdgrmegf", -58.211167813, -169.118830812}, + {0x1841cde95a3063ba, "310wvubu61jv", -38.161648236, -134.043065682}, + {0xda0d1212b1485644, "v86j44pj91c4", 47.297096546, 70.410047936}, + {0x5532126dd0ad0236, "bnt14vfhpn13", 81.770586042, -172.844851607}, + {0x78510ad6b244bbe8, "g18hpppk8kxy", 54.179874801, -44.682405099}, + {0x9af191a36400b3aa, "mcst38v402tu", -35.635181863, 85.150928171}, + {0x544bb5dff3169d7c, "bj5vcrzm2ufr", 74.179242172, -174.661639123}, + {0xa6718c13d26e7cd3, "ntsss4yketye", -58.255574269, 119.012648349}, + {0x84622ff376f88207, "hjj2zwvqz210", -61.704926328, 7.719924704}, + {0x4205ae9ca7209409, "882ux75742b0", 2.214264189, -156.122148978}, + {0x71e2fec1b96bd8fc, "f7jgxhetegdg", 62.515917123, -70.351473986}, + {0x7a419ca183e5964d, "g90tt8d3wqc4", 51.594837659, -21.551816083}, + {0x73468f8143f587e0, "fe38z0b3yq3y", 63.417540775, -65.082393904}, + {0xc4947192c578bd2e, "skb734q5g2yk", 27.303047199, 11.653895705}, + {0xce892b86958a657e, "tu4kr1npj9kr", 23.253773109, 82.230161427}, + {0x7c011765162e1eb8, "gh0jft8q5sgc", 68.542010242, -44.88963797}, + {0xbc144c0fcff84eff, "rhb4s3ygz17g", -17.831532748, 135.196343054}, + {0x27a03f81fcf637aa, "4yh3z0gwysvu", -55.937078046, -49.960726858}, + {0x739f6a2ce80dc12e, "ffgqnc781r0k", 61.530342286, -51.378195854}, + {0x17a6035310617d1e, "2ym06nshd5yj", -9.763305566, -139.125272058}, + {0x912ac270eb64a5b4, "k4pd4w7cdkkv", -33.363823988, 10.661991122}, + {0xba3316ef672090cc, "r8tjevv7428d", -41.188599313, 164.703352151}, + {0xbfcda5b25e9ef955, "rz6ucdkymvwp", -3.370325747, 172.689883489}, + {0x5e225f676e062c3b, "csj5ytvf0sq3", 68.191131744, -105.17517895}, + {0xe3ce8c5db9162398, "wg78sret2sjt", 18.411262888, 128.863478126}, + {0x0fc6a96c3fa50c2b, "1z3bkv1znn62", -49.146103634, -98.577658202}, + {0xb8436ec44bc6a96e, "r11qxj2csunq", -38.203302706, 137.066672144}, + {0x7078c11c2dcd6e33, "f1wd271etpr3", 53.850127244, -80.846104331}, + {0xd1f46578ec96ee3c, "u7u6by7dkvr3", 66.611895402, 17.264541819}, + {0x00c3f6566ef36c50, "031zdpmfyeq5", -83.016325168, -166.192988382}, + {0x62809327e37dd7e3, "db0969z3grcy", 0.229682641, -55.426913403}, + {0x85e0b112587c75e8, "hrhc24ksgjuy", -50.392066561, 17.936002645}, + {0xe1e74c5b6dbd87c9, "w7mnsqverq3w", 19.461499472, 108.475884046}, + {0x2e06eba93502b0b9, "5s3frb9p0bsc", -65.694291051, -19.697074258}, + {0x4e520f8ffb8fdb16, "9t90z3zvjzej", 31.079848463, -110.764252539}, + {0x932411c791ed9bef, "kdk13jwjxqey", -32.092830886, 28.177501484}, + {0x693ea039859b395c, "e4zb0fd5mdwp", 15.483055497, -34.065612615}, + {0x09b53fdd958f569f, "16umzrdpjxc9", -73.478065544, -117.451849043}, + {0x7a769beeeca3d766, "g9v9rvrdngcq", 55.092680233, -14.414457638}, + {0xc3e64005112dc7e9, "sgm4018j5r3y", 18.641950499, 40.781392023}, + {0x3c52972f1e79a6ff, "7j99fcsyg6mg", -13.745471643, -42.763104796}, + {0x11fdd728d9b91693, "27yxfb6tr4c9", -22.5416558, -159.484789619}, + {0x8d3827b35c214e12, "jnw2gduw4571", -53.289510262, 53.949140893}, + {0xf1da8ce5b52f913e, "y7e8ttep5y8m", 64.806840448, 106.417855636}, + {0x9a83b58a7b670c8d, "mb1vc2mvdw68", -43.98690671, 81.273876455}, + {0x972cce71760225af, "kwqdwwcq08ku", -9.366185904, 31.928014245}, + {0x00806ef7b8526112, "0206xxxsb9hj", -89.518526523, -168.058521062}, + {0xe0ab35991af787bb, "w2pmc68uyy3v", 1.025324908, 111.501566437}, + {0x3bfd85f0e0c0e8e0, "7gyscw70s3nf", -23.036865249, -2.039145672}, + {0xa8e290305870bc2e, "p3j90d2sf2y2", -84.186039612, 154.007139532}, + {0x0b342d83fdfeefca, "1du2v0zxzvrw", -74.39393461, -106.293068268}, + {0xd1358705307b7ccf, "u4usf19hgeyd", 61.312786674, 6.417548096}, + {0x587aa232ee0aa7a7, "c1xb4drf1bmu", 53.450205116, -123.980973238}, + {0x9a631647787c872d, "m9jjdjvsgk3k", -38.375778415, 74.626835081}, + {0xed9e16ebe40ea361, "xqg1euz41ujq", 38.258880268, 150.643240127}, + {0xc4a258477f066963, "skj5hjvz0tnq", 23.060158962, 18.464957644}, + {0x7d909e900c888f25, "gq89x40dj27k", 81.837503665, -32.738341423}, + {0x10b8a9ac28d7c122, "22wbmc18uz0k", -42.137894555, -159.00286273}, + {0x1fc199b3fcc35d2c, "3z0tmdzwsefk", -4.685905546, -100.294679729}, + {0x66117e3e0c8177df, "ds8rwghdh5vx", 26.647703643, -66.845447476}, + {0x7b336dd909ea5d2c, "gdtqvq89x9fk", 60.28499667, -14.885462895}, + {0xd434974bddd0d3e2, "uhu9fkyxu39y", 72.053799168, 6.436113191}, + {0x9a28789f25a2fc1d, "m8n7j7t5ncy1", -44.452905486, 76.526939758}, + {0x98824269c92b523c, "m2144uf95e93", -44.622154256, 57.780684914}, + {0x0d1232af0b5c7e7a, "1n935csccjz7", -53.25314724, -133.070816531}, + {0xbc5bb594b70d1766, "rjevc55r1ncq", -13.034039365, 140.321890764}, + {0xafd97c0a5b46e78a, "pzdrs2kv8vms", -46.49179433, 172.107393035}, + {0x146bf26da7cc07c8, "2jpz4ve7th3w", -15.613694188, -168.976032891}, + {0xeced35c2df38616a, "xmqmchqz71hq", 30.566589068, 155.092412233}, + {0x3378b3e6603b8288, "6ewc7tm07f18", -25.063880663, -57.846974296}, + {0x24687efd3d1aa0ad, "4jn7xz9x3bhb", -61.217322194, -80.868219705}, + {0xcf77197d92affcac, "txvjkzdkpzyb", 44.558528633, 74.743422016}, + {0x8c31538cad4b0cdf, "jhsp735e9d6e", -63.406979231, 50.772696813}, + {0xc3b72446e08e32d8, "sfvk8jr0jste", 16.288629383, 41.142669167}, + {0x3a938d4a85250d4f, "7b9sukn54n6n", -41.330016759, -8.945528843}, + {0x1744cb4e0897be0c, "2x2dqmh8kyz0", -3.79569714, -156.515823557}, + {0xbb444be54ac1ff53, "re24rtbbs7zp", -26.291549305, 157.830811691}, + {0x62b7ebb3240cdbee, "dbvyrdt41mey", 5.331489658, -47.827547204}, + {0xec05972b8cbe0440, "xh2tfbwdrs24", 24.920148152, 135.833222949}, + {0x291cac2664f0a70d, "54fbs9m4y2mh", -74.436018684, -40.927916963}, + {0xd45203ca41d54893, "uj907kk1up49", 76.005129429, 1.55474059}, + {0x0c82001ed0d2ff0f, "1k1007qhuczh", -67.481321635, -122.324310414}, + {0x8de36bb0389fff0d, "jrjqrd1smzzh", -49.514608921, 63.96471861}, + {0xc675c7d7e1ae1c1e, "stuwgpz1psf1", 33.573042855, 28.969898799}, + {0x945119e8ab9fff89, "kj8jmu5cmzzs", -13.117418893, 0.258092403}, + {0x361169885591c04d, "6s8qm22pk704", -18.586232769, -66.917542032}, + {0x197c8e0489561fff, "35y8w149bsgz", -23.812538983, -125.592243148}, + {0x883e4b9f131ca466, "j0z4r7sm3kk6", -85.365591651, 55.168249499}, + {0xdac30e1de30c49c9, "vc1hw7g31j4w", 51.43681453, 80.435417916}, + {0x095abf22773da4b6, "15ecy8mr7qkc", -70.002231175, -129.433562204}, + {0x543d4bd3792e314c, "bhynrnvt5ssn", 72.855345243, -171.247173929}, + {0xdee160a9dc56fa80, "vvhq1bfwbvx8", 74.1849956, 84.806942285}, + {0xda4c683e226680da, "v966hgj2du0e", 52.399399802, 70.880131531}, + {0x529ff0776aff51f3, "bbgz0xvbzx8z", 50.491960967, -140.946370389}, + {0xe7fd1cf8d8025bd5, "wzyjty6s09ex", 44.595575143, 132.443644975}, + {0xa1c71330810afbcf, "n73j6d411cxw", -70.784731246, 102.76894535}, + {0x89c676fd99f46262, "j737ezdtyjj6", -71.061302648, 58.176305273}, + {0xb8dab62fe972dd53, "r3ecdcz9fcfp", -36.288904067, 151.654707652}, + {0x6b4c3b42449ffdcd, "ee63qhk4mzyw", 18.524751481, -19.06651678}, + {0x43f4d133fa0f9097, "8gue2dzu1y89", 21.680867105, -139.889183455}, + {0x4a3aedaac0342be9, "98xfvbq06hpy", 3.297337158, -101.340540813}, + {0x243244be7baff050, "4ht49gmvpzs5", -64.229295048, -82.883611244}, + {0x529c367d2866f0f8, "bbf3dz98dvsg", 49.523736455, -142.962903344}, + {0x913905bd2ee5a4c5, "k4whcg9fwqkd", -30.082849831, 8.517081868}, + {0x11b215e8579294c1, "26t1cu2rkbbd", -30.605287911, -161.641297795}, + {0x4639fd557750e20a, "8swzupcrb3j0", 26.718712487, -147.830296929}, + {0xf000222754392332, "y00249un74jm", 45.010697525, 90.467054852}, + {0x79096fd964fc1c02, "g44qzqc4zhf0", 57.474099855, -41.515657438}, + {0x5d2b2ee35ac21aba, "cnpkxsuus8ec", 79.567883501, -124.468376041}, + {0xa094a9db0fef181c, "n2bbmqsgxwd1", -85.700963177, 102.542248266}, + {0xede28e3179b42c10, "xrj8wdctqhq1", 39.478921015, 154.272345073}, + {0x799becc6ca593a4a, "g6eytjqbc4x4", 60.234059754, -28.247513666}, + {0x78435ad1e58414d7, "g11ppng5hhbe", 51.893088733, -43.281840476}, + {0x954368802c0b7471, "kp1qj01d1eu7", -4.56994848, 1.979669085}, + {0x64fcc17591f46290, "dmyd2xdjyjj9", 32.781470623, -69.584390851}, + {0x8c12deafe3e5bb38, "jh9excz3wqxm", -64.062359352, 47.46018566}, + {0x3dee3046709bc9c4, "7rr30jmhmg4w", -4.013382864, -23.547570924}, + {0xf523d7428f8f4d65, "ynjxfhngjx6q", 80.134832361, 97.831774469}, + {0x47eb126d174d4bd6, "8zpj4v8r9p5x", 40.285444936, -136.285009333}, + {0x5b75d1f13519af51, "ceux3w9p36rp", 67.405127481, -106.104520702}, + {0x14c93d339af0263e, "2m4mudwuy0m3", -15.849707915, -165.37864908}, + {0x7e7f39cf3c703684, "gtzmmmtwf0v8", 78.297976804, -12.066328253}, + {0x3d53b2c81e06d9cb, "7p9v5k0y0vdw", -1.910560555, -42.395174415}, + {0x826d43f7e2cf934a, "h9qn7xz2ty9n", -81.827423439, 31.101513342}, + {0x6e91b56240c62e43, "eu8vbsk0ssr4", 26.346697174, -10.167644031}, + {0x6ee3a113e3411dfe, "evju24z384fz", 28.88745004, -3.154102138}, + {0xbfa1be45561b16aa, "ryhvwjbq3dcb", -10.250529789, 175.69376968}, + {0x6e4cc7371b4ff34d, "et6dfesv9ztn", 30.034860018, -18.867906554}, + {0x1d1d8354b1612b9e, "3nfs6p5jd4pt", -6.244771747, -131.392268279}, + {0x3d213d967a965aac, "7nhmv5mukteb", -10.220645878, -38.79561615}, + {0x8b6d9e0277f7616b, "jeqtw0mryxhq", -70.749210824, 76.911791597}, + {0x6891a0a6890c6cbe, "e28u19n91jqc", 3.521318241, -32.620415887}, + {0xa7753a5f8d7b2b88, "nxumnrwegdps", -45.485489882, 118.76030754}, + {0x08f10ffe5eb04c5a, "13shzzkyq165", -80.686633521, -117.777640235}, + {0xc518cc2aea493ff6, "snddsbrb94zz", 37.003428285, 3.73506169}, + {0xc6c30a90ee07e5aa, "sv1hp47f0zku", 28.840867516, 35.469055859}, + {0x58d09dde60f0222e, "c389vrm0y0j2", 53.785071472, -122.809030811}, + {0x7dabb21150b568aa, "gqpv44bhqpnb", 79.6447796, -22.763405043}, + {0xec42401790aada06, "xj1405whpce0", 28.496483359, 136.414826507}, + {0xb5cbaffd5b499822, "qr5uzzbv96d2", -4.746509486, 106.865100407}, + {0x2c57c0b9c8b28073, "5jcw1ff8qb07", -56.586412804, -42.80999732}, + {0x67c05e92df6366ba, "dz05x4qzdemc", 40.003899063, -55.932997807}, + {0xf32b76b432a2becb, "ydpree1knbzd", 57.58552993, 122.851137749}, + {0x0078f0ed74a28025, "01wg1vcnnb02", -81.002540428, -170.429244852}, + {0xddca668145db9562, "vr56e0b5vfbq", 84.819232523, 60.95239562}, + {0x39b5b7e31796ff5c, "76uvgssrkvzp", -28.494511902, -26.910453957}, + {0x8a65f17a98bbef43, "j9kz2ynsrgrn", -81.660628888, 74.221915015}, + {0x63ea2caf4a9228bd, "dgp2tcubk8nc", 16.972546876, -45.795276449}, + {0x1dd421d8ac3eac1a, "3rb23q5d7uq1", -1.328936482, -123.338530049}, + {0x5a172abd693fde4b, "c8ckpgc97zg4", 49.94273091, -110.399386158}, + {0x8778b14cbaf16de9, "hxwc2m5uy5qy", -47.564473733, 32.008590442}, + {0x08cddcdf74d0a7ea, "136xtrvnu2my", -81.606657508, -119.996605272}, + {0x34e12c4e2a6c8204, "6mhksmjbek10", -16.05641115, -72.57863264}, + {0x66c1e9afc6943107, "dv0ymcy6khsh", 29.233657057, -54.933866738}, + {0x3f69a338c9ec11ec, "7xnu6f69xh8y", -4.865287313, -12.883228076}, + {0x5cf7056b44bd067b, "cmvhbuu4rn37", 78.205223783, -116.679995435}, + {0x73203ca47ae6f173, "fdh3t93uwvsr", 56.52134156, -61.279039481}, + {0x2f06f2f9025080f8, "5w3g5y82b20g", -54.280571429, -19.873912305}, + {0xbb420b7d0a6bb5f9, "re10qz8befuz", -28.039754135, 159.204078347}, + {0x0b4bae4faed000a9, "1e5uwmxfu00b", -72.303299857, -106.941088373}, + {0xaa92203d14547057, "pb920g8nbjs5", -87.167085062, 170.540777005}, + {0xc25c5aab74845021, "s9f5pbvnhj82", 10.376273167, 25.660114658}, + {0xceb0cda723eaabc2, "tusdv9t3xbpw", 25.804396687, 85.327376767}, + {0xc383b5202ca6e2ce, "sf1vb81dnvjd", 12.261112755, 36.235267471}, + {0xeabd328a6809c864, "xbym52m81746", 5.099046059, 177.68949456}, + {0x9628e663d91c9275, "ksnfdsyt3k97", -22.033528319, 32.111032502}, + {0xe44856b64282bd8a, "wj45eek2hbys", 28.758091877, 92.972357974}, + {0xa1ddd04df33e0955, "n7fx0mgm7s4p", -67.643271556, 104.781227493}, + {0xbd0d9ac430559cb8, "rn6tpj1hbqfc", -8.936526151, 138.824630104}, + {0xaefbcc95b8d87970, "pvxwt5esv1wr", -57.899871151, 179.521631049}, + {0xab9a2b31cd8bb2ca, "pfe2qdfejftd", -75.87791972, 173.609644659}, + {0x614aa3749a322945, "d55b6x4u68nn", 16.958128189, -84.612807314}, + {0x503424a7bfb409db, "b0u299xzqh4x", 49.316147602, -173.94661535}, + {0x67b881503ad328a0, "dyw82n1uudnb", 36.640230433, -47.106769545}, + {0x30cd3953588beb6e, "636mknusjgpq", -37.008115751, -75.403719639}, + {0xb0075ccffc1a4f03, "q03ptmzw397h", -42.242724604, 91.647331097}, + {0xa5150920285cb857, "nnbhk818ckw5", -51.284026164, 90.199875701}, + {0x5e9fbca2c71b7e06, "cugvt8q73ez0", 72.687498283, -95.726206317}, + {0xc0ef7eba0c994e9e, "s3rrxfhdm579", 8.360999634, 21.792288031}, + {0x49d63ba570a95647, "97c3r9chp5c4", 21.323796836, -121.66092253}, + {0x98c54e64fa006972, "m32nwt7u01nr", -36.796639571, 56.540802891}, + {0x6963dcdf0952e73a, "e5jxtrs9bcmm", 18.234866087, -37.02869279}, + {0x8778c70ba666de91, "hxwdf2x6dvg9", -47.325894741, 31.749581103}, + {0x5624f6b3d03fc2e0, "bskgedyh7z1f", 69.537358048, -150.658100875}, + {0xf9a03cdfb33065b3, "z6h3trxm61kv", 56.555779415, 152.467324074}, + {0x9b6dec6ed956f949, "meqysvqtbvwn", -25.546315158, 77.209894577}, + {0x3012931221ae52a1, "609964j1pt9b", -41.956585889, -87.79554365}, + {0x14ff50c5042ad716, "2mzp1j845ccj", -11.395215198, -158.862142318}, + {0x96b04e723e75530d, "kus4wwjyfp9h", -19.213888118, 39.668628364}, + {0xc82c288eb882235a, "t0q2j3psh8jp", 1.412431014, 54.03027519}, + {0xbec294e01be3215b, "rv199s0vwdhp", -16.588399377, 170.926607531}, + {0x66948ec4620f4bfe, "dub8xj321x5z", 26.835518565, -55.237471491}, + {0xc61c05b033479613, "ssf0cd1m8yc1", 26.862552835, 25.380168267}, + {0x324acbd77a643206, "695drpvudht0", -38.936123452, -62.262518739}, + {0x0235d0533be04b1b, "08ux0ntvw15j", -84.5141253, -151.163716785}, + {0xa211fbf706e567fa, "n88zrxs6wpmz", -85.871417986, 113.890379299}, + {0x60ee4daf0df544b2, "d3r4vcseyp2c", 7.523570987, -68.647125999}, + {0xaae706d4b11d0ee7, "pcmhep5j3n7f", -82.138346998, 175.917253888}, + {0xc061754f448e68ce, "s1hrbmu4jtnd", 7.019260976, 5.993287057}, + {0x680a9763e0f4c63f, "e059fsz0ym33", 0.333866926, -39.958378504}, + {0xf688b342c1b771fd, "yu4c6hq1qxsz", 67.743328619, 127.713603361}, + {0xb08c696f652b94d8, "q266kvv55fbe", -43.166126253, 104.629831018}, + {0x239615e1737afb09, "4fc1cscmgcxh", -74.196544431, -54.775944857}, + {0x48d69f20acdff04b, "93c9y85dvzs4", 10.151879898, -121.349921563}, + {0x7ecf532a4c57d5c7, "gv7p6bkdbzbw", 75.80755214, -6.904187611}, + {0xdf58b3a6735cc57c, "vxdc79mmcm2r", 87.415109491, 71.528260031}, + {0xd087465eda46387d, "u23ndrqu8sw7", 47.589450841, 12.764426704}, + {0x59ff04aebda73b57, "c7zh9cpxnwxp", 66.891496587, -113.818757531}, + {0xc93524a923fcc1d4, "t4uk9b93zm0x", 16.262840712, 51.05549523}, + {0x1bd061d688f2cda2, "3g863pn8yc6u", -24.878403895, -100.845272302}, + {0xaa092830f8a329c4, "p84khd7sndnw", -89.283816877, 160.866910614}, + {0xfce80696f4d917f0, "zmn0e5rnv4cz", 73.231918086, 154.82916593}, + {0x3da526d0a2b38c35, "7qkken52qf63", -9.019720072, -27.63684563}, + {0xe7b4225ea05ea8ad, "wyu24rp0cunb", 38.007352492, 129.835136838}, + {0xd69aa7156669c939, "uuebf5c6e74m", 70.465381448, 39.113187404}, + {0x3ca91e4f77a7ebd3, "7knjwmvrnzpx", -21.500376994, -25.03033213}, + {0x718577227d778ba5, "f62rf8mxfy5u", 59.021291352, -78.28089799}, + {0xf195e592f4b21923, "y6byc4rnq8dk", 61.668709371, 102.358527915}, + {0x7069b465f6b3ae14, "f1nv8tgqqfr1", 51.624467805, -80.481077527}, + {0x11ff043ba6bcbd60, "27zh8fx6rkyq", -23.10108872, -158.863015078}, + {0x4b9a5a27a686a844, "9fe5n9x6hun4", 14.59844873, -96.735437174}, + {0x25eb5ca363b526ba, "4rppt8v3qnmc", -49.302272453, -68.657075466}, + {0x8e88a03a4468af79, "ju4b0fk4e2rr", -67.487208656, 82.655789059}, + {0x7b67dea6ddacdb2a, "gemxx9qxpmek", 64.60770765, -14.426792937}, + {0x24eaaec9ea3ea893, "4mpbxkgb7un9", -61.760952235, -67.52763939}, + {0xcb7b95a9e12621c3, "textcbg14shw", 20.702556258, 78.128012627}, + {0x6d471ed7ddb45385, "ep3jxpyxqj9s", 41.791893744, -43.276946861}, + {0xce7a1dfc53739835, "ttx1vz2mffd3", 31.285943386, 77.596899987}, + {0x23ad9900e5d7e057, "4fqtk075uzh5", -76.418843222, -46.929264511}, + {0xd6f2ede72b05bb5d, "uvtfvttc0qxp", 76.451318675, 42.085553463}, + {0xf72b878f43454306, "ywpsg3u38p1h", 79.594869794, 123.195535208}, + {0x080b2342406ede81, "105k6hk0evg8", -89.229467, -130.336138039}, + {0x37a214e5a8f65ba7, "6yj19te8yteu", -10.955956651, -49.147741373}, + {0xb80399dcb0355c6e, "r01tmr5h6pf6", -44.037924726, 137.344296934}, + {0xbd6cd671add1ce7b, "rpqedweeu777", -3.567148328, 144.255481812}, + {0xf04bd9367d0ab0a9, "y15xkemx1bsb", 51.918471213, 95.127263647}, + {0x4bb8381ebf561ab3, "9fw3h7pzbsec", 14.256115496, -92.263500336}, + {0xea07a03ba34103f4, "x83u0fx3841z", 2.123377408, 160.003855265}, + {0x0fae44c5d4f75a4d, "1yr49jfnyxe4", -54.371511764, -91.359274934}, + {0x1f04054f55edc2e2, "3w20bmupxr1f", -9.679002998, -112.483206889}, + {0x1b4039eb05ea1860, "3e03mus5x8d6", -27.879950352, -111.889933857}, + {0x727fef7d0d565abd, "f9zyyz8ebtec", 56.072145735, -56.30421829}, + {0x47fa366f1e38ffa0, "8zx3dvsy73zu", 42.482467246, -135.92716483}, + {0x2030a8eb1c42ac8a, "40sbjusw8bq8", -87.161663604, -83.061405961}, + {0xf9c72af9af953fa0, "z73kpyegknzu", 64.020675385, 148.353720607}, + {0x3c77e713c7443a0b, "7jvyf4y78hx0", -11.453995341, -36.817579088}, + {0x1b7d6bb2d9563965, "3eyqrdqtbswq", -22.794245229, -103.372391381}, + {0x56076bc85e553aa4, "bs3qrk2ybnxb", 70.029422447, -155.422543579}, + {0x9968568dd67375b1, "m5n5e3fqfeuv", -27.498972871, 53.58352587}, + {0x12e6c1b50cdc692e, "2cmd3e8dvjnk", -37.553516447, -138.44879712}, + {0x683a4132948f624e, "e0x42dnnjxj4", 3.220062103, -35.125794528}, + {0x7bd2da550e1292e9, "gg9enp8f2b9f", 65.25643178, -8.875888372}, + {0x5cf315abb065da04, "cmtjcbxhdre0", 76.951801604, -116.632132049}, + {0xea5d815c82bb012e, "x9fs2r42rd0k", 10.629326892, 161.030023718}, + {0x92f82242d6f30ddd, "kcw24hqqyd6x", -36.537981937, 42.635818725}, + {0x568e90bd075b1f9d, "bu791g87cdgt", 69.10191681, -141.250808106}, + {0x7e4ae4a49e926785, "gt5f994yk9ms", 73.571026679, -17.15666742}, + {0xda0c9aa34c182009, "v869p8ud30h0", 46.586537415, 71.351443102}, + {0x2090ba0f461169ee, "428cn3u625ny", -87.00170225, -77.414808491}, + {0x89dec36af5316eba, "j7gd6urp65rc", -68.486134089, 61.302434142}, + {0x7ddda4ec10835a94, "grfu9v0hhee9", 89.412937153, -29.805710362}, + {0xb9a6a39a0f41be32, "r6mb76hg86z3", -32.28820259, 154.485303464}, + {0x69e8d02dea4037a2, "e7ne0cgb80vu", 17.412047337, -24.571258305}, + {0x8858278c94640004, "j1d2g34ndh00", -81.424033551, 48.309717244}, + {0xffb3696728908395, "zytqktt8k21t", 82.691389123, 176.33830391}, + {0x65cd3b6a730f5f9e, "dr6mqumm1xgt", 41.728348328, -75.282027472}, + {0xd2a34c31b2722f35, "ubjnsdekf8rm", 46.157128741, 40.983584117}, + {0x44f8f0a7aeb3b61e, "8mwg19xfqfv1", 31.47348475, -159.180954862}, + {0x7683b149c3684d57, "fu1v2kf3e16p", 68.449209611, -53.774854741}, + {0x77bbee350e29e29e, "fyxywe8f57j9", 82.724663879, -45.064740829}, + {0xb10f4d305193cd62, "q47nud2jkg6q", -31.143947279, 94.416710918}, + {0x34ab55828205efbf, "6kppc0n20rrv", -21.137655455, -68.853700214}, + {0x6f6161a507150591, "exhq39872n2t", 40.482464883, -16.457175481}, + {0x80ac581187818492, "h2q5h4d7h629", -88.052146115, 19.866554377}, + {0x629c0d762a88022a, "dbf0uxjbj012", 4.389038086, -53.231633146}, + {0x80416211ee2f759e, "h10q44gf5xut", -83.30482045, 0.444759502}, + {0x0b07bedda566c7dc, "1d3vxre5dv3x", -76.335124139, -109.716218836}, + {0xb205d7547b962855, "q82xfp3vksn5", -42.190696532, 113.293620071}, + {0xb6503a28ee7cace7, "qt83nb7fgkqf", -13.884850123, 113.153491828}, + {0x2b04dc071397b5ae, "5d2es1smkyuu", -76.719337654, -21.615046737}, + {0xb450730b0211ae1c, "qj8762s226r1", -13.488409083, 90.45629624}, + {0x1909e35ddfbe4a2d, "344y6rfzrt52", -32.607523672, -131.029832564}, + {0xf2dda8ecb8a2a3b1, "ycfujv5snbjv", 55.575028363, 127.87497697}, + {0x50620d86abaa48b5, "b1j0v1pcp94c", 50.762506454, -172.7380548}, + {0xf1604445ead1c27b, "y5h48jgbu717", 62.346184231, 95.630334203}, + {0x42d1beb70a26b705, "8c8vxesb4uvh", 9.423549253, -144.859074723}, + {0x19352c09db5cb0bf, "34uks2fvcksc", -28.735103014, -128.832833099}, + {0x16afc16f0a003bb4, "2urw2vsb00xv", -19.964901152, -135.66363244}, + {0x3c978ad8d66f3573, "7kcspq6qewur", -17.542639493, -31.318775541}, + {0x84e9eb579985af30, "hmnyqpwthqrm", -60.734275748, 21.014977928}, + {0x4b50e77598948af3, "9e8ffxdskk5g", 20.212848739, -111.33182847}, + {0x92dae029a8e1711c, "kcef0be8w5sj", -36.208085385, 39.06146603}, + {0xce36520f9c784dcb, "tsv543wwg16w", 27.25549997, 74.639185726}, + {0x4cc3badf2032940c, "9m1vprt06bb0", 29.045152041, -120.963474296}, + {0x6630844801fc4dc1, "dss88k01zj6w", 25.422692902, -61.160584909}, + {0x933da2658ed399a0, "kdyu4tdfufdu", -28.797426431, 32.106034935}, + {0xcb5f02e94f211b3c, "tegh5ubg44em", 21.823496862, 71.884665251}, + {0x0d9d66917c076d48, "1qfqe4cw0xqn", -50.872495782, -120.452011909}, + {0xa533e1eec8b80d63, "nnty3vq8r06q", -52.309982731, 98.17207173}, + {0x3c4899f057458af2, "7j49mw2r8q5g", -16.619620994, -41.242316536}, + {0x0e11fea57d04be65, "1s8zx9cx0kz6", -63.358300007, -111.113642263}, + {0x1404a0cf3a9666ee, "2h2b1mtuktmf", -21.062779422, -178.882283327}, + {0x5d498b4634f2b4de, "cp4sqjjnybue", 85.150699491, -131.213540672}, + {0x3c2b59568a0e71ce, "7hppkpnb1tsw", -21.187103156, -34.971128298}, + {0x3c74b0b24cf2437c, "7juc1dkdy91r", -12.467630417, -38.247933891}, + {0x50f25d48f698f814, "b3t5uk7qm3w1", 54.121106819, -161.527285862}, + {0x1837d64919e5dc26, "30vxdk8twrf2", -39.437185577, -127.165790066}, + {0x7c1d03bb3034429b, "ghfh7fth6j19", 72.480309834, -42.015750518}, + {0x2c8026e6caea0b10, "5k02etqbx85j", -67.383183578, -33.235033295}, + {0xfe31435a867ab46d, "zssn6qn6gbu6", 71.44456837, 163.232626895}, + {0x9f180755d8242cda, "mwd0fpfs4hqe", -8.262382093, 70.403918683}, + {0x490e6a78efd742da, "9476ny7gux1e", 13.04282656, -130.127711355}, + {0x504d4ba725bffd38, "b16nr9t5rzym", 53.138722906, -176.850702519}, + {0xee93bc0046153ac0, "xu9vs0262nxd", 26.281091649, 171.38707214}, + {0x153e492ff3a99428, "2nz4kczmp6b2", -6.62525607, -169.937241716}, + {0xbe73de74f91990d5, "rttxwx7t368e", -12.703399901, 165.524880414}, + {0xc6095c64007c4d63, "ss4pst00gj6q", 23.845983157, 25.510385781}, + {0x1b01d7d04ca3cff3, "3d0xgn2dng7z", -32.353001057, -111.664057565}, + {0xce9c9858bd62954b, "tuf9hq5xdbbn", 26.928782779, 82.457317407}, + {0x7c06271cf5b85c0e, "gh32f77pr1f0", 69.05719276, -43.138887313}, + {0x535cab47156d1ce0, "befbqjspenff", 66.169231733, -153.363515012}, + {0x8c604c8c634d6480, "jjh4t3339pk8", -61.428384028, 50.857478458}, + {0x47185861f5f39229, "8wd5hsgpyf92", 37.117280119, -154.485334616}, + {0x346a51f178ab98a4, "6jp53wcspfdb", -16.265931925, -80.087935734}, + {0x6369ad9f4c769b98, "denuv7udfuet", 17.731057354, -57.770796853}, + {0xc5fbadbdcf937d3f, "srxuvgfgkeym", 43.043639297, 22.405101368}, + {0x1ea82ddf8cfda2d1, "3un2vrwdzqje", -22.326460502, -92.220979701}, + {0x90f15e40b5050474, "k3spwh5p0n27", -35.220932197, 17.142792158}, + {0x09b5e9346279f690, "16uyke32g7v9", -73.414615358, -116.870695056}, + {0x06846ca98da7bb78, "0u26tbdenyxr", -65.650999237, -145.642019137}, + {0x16a3cee89049840f, "2ujwxu4h9620", -21.334664514, -138.172248323}, + {0xfe865d67424a778a, "zu35utu299vs", 69.597111275, 170.359912403}, + {0xb51aa6856f5ffb39, "qnebe1cgczxm", -8.339311471, 95.40776171}, + {0x792cb20d0e37ea14, "g4qc438f6zp1", 57.840695072, -35.407778113}, + {0x659d8bc91cfe790a, "dqfsrk8wztwh", 38.741728461, -74.91475462}, + {0xae328c83622a14c9, "pst8t0v258bd", -64.595485087, 165.461462445}, + {0x5e450cf7e6c0f0e0, "ct2htxz6s3sf", 75.365275215, -112.248155614}, + {0x2c366a203e90f87b, "5hv6n81yk3w7", -62.928606003, -37.328948613}, + {0x09ccc20cad90c023, "176d435ek302", -71.361130995, -120.130508638}, + {0xdb988ced125cf87b, "vfd8tv8kcmw7", 59.181448479, 82.518716164}, + {0x57bfb7bb35418a1b, "byzvgftp8651", 83.994523508, -135.179886116}, + {0xedefd1c8aac8193f, "xrrx3k5bt0dm", 42.077726203, 156.857174367}, + {0x21f864ab357b2120, "47w69btpgdhk", -69.868958485, -69.877010776}, + {0x6f06cf85c60d6a33, "ew3dz1f61pp3", 35.649642588, -20.079865183}, + {0x660edd26c397cc27, "ds7eu9q3kz62", 24.572549774, -62.371581058}, + {0x8d60970bf079f73a, "jph9f2zhg7vm", -50.312426581, 51.43676184}, + {0x082a5e8582632991, "10p5x1d2ddnt", -89.376428413, -124.845426899}, + {0x51a784262f130e24, "b6ms89jg2d72", 58.453328879, -160.9857301}, + {0x47e65fd271e88024, "8zm5znmjx202", 41.475706218, -138.903954837}, + {0xe2f1c27dc1ddf444, "wcsw4zf1vru4", 9.535101931, 130.201951871}, + {0x579e9a92ef284c1a, "byg9p4rg5161", 83.157412755, -141.009733058}, + {0x59290a54b9f6d486, "c4nhnp5tyvb8", 56.992595163, -126.293731141}, + {0xd17adb2b5dfb95bd, "u5xeqbuxzfbv", 65.264255065, 10.850019438}, + {0xf5ae48abc82d89aa, "yqr4jby85q4u", 80.511965236, 111.355505358}, + {0xc46c986a8f0007bf, "sjq9hung003v", 29.72952121, 9.358637619}, + {0x96d5b84f335fd06e, "kvbvhmtmcz86", -11.746103606, 34.998740571}, + {0xab71be46ce08ee06, "pesvwjqf13r0", -69.316513879, 164.452692246}, + {0x514d637ff34405d5, "b56q6zzm8h2x", 64.423423082, -176.705130818}, + {0xaddd675c54a83e9e, "prfqfr2np0z9", -45.178866866, 149.51325024}, + {0x7f6658d59d3326cf, "gxm5jpdx6dmd", 86.351049569, -15.245475212}, + {0xd548af5da49a051d, "up4byre4m82j", 84.548423212, 4.14620179}, + {0x1837ee8716830661, "30vyx1sqhd36", -39.629389674, -126.600413979}, + {0xdd4f4545656f62f3, "vp7nbjc5exjg", 86.999998078, 49.22028134}, + {0x964cbafbf514f382, "kt6cpyzp2mts", -15.25461514, 26.71739498}, + {0xf34f3acab25bcad4, "ye7mpkpkcg5e", 64.182960985, 117.398952217}, + {0xf065c24deb9a8d70, "y1kw4mgcmb6r", 53.117740319, 96.432401637}, + {0x2ec619aea45a9041, "5v31mcp4cb84", -60.24305513, -9.581376279}, + {0xc708acd3b1c88719, "sw4btnxjt23j", 33.874542588, 26.596753989}, + {0x294df0e6f4e6ca37, "556z1trnwv53", -70.45829919, -41.056987347}, + {0x1ddc9ed22ab2c140, "3rf9xnjbqc0n", -1.109569827, -119.918571627}, + {0xac51524778565169, "pj8p4jvsbt8q", -57.799599579, 135.095465704}, + {0xf4415c83448299ae, "yj0pt0u4hbdu", 74.447826008, 90.225427527}, + {0x6f693ef8f6036f0c, "exnmxy7q0erh", 40.377177345, -13.36586707}, + {0x5f996cd6df8dcacf, "cydqtpqzjr5d", 82.746143218, -97.8567109}, + {0xe85493eabf8755f8, "x1b97upzhxbz", 10.086693623, 135.87875728}, + {0x683095724a46d6de, "e0s9bwkb8vce", 3.154566738, -38.643344753}, + {0x03a6c476cdb1cac1, "0fmd8xqeq75d", -76.863897055, -138.484453027}, + {0x98abaafe3ae059d3, "m2pupzjuw1dx", -44.257641111, 67.4971711}, + {0x295d1ba9384d0231, "55fjrb9s9n13", -67.979846954, -41.844819267}, + {0x6626924681293d90, "dsm94jn154yt", 24.109684339, -59.669357088}, + {0x97db31f4f2826ee4, "kzem3x7kh9rf", -1.849129213, 38.390897237}, + {0xef5c08e67385098a, "xxf0jtmmhn4s", 43.623480305, 160.561584291}, + {0xc2364356bd509b28, "s8v46ppxb2ek", 4.654043693, 29.62945558}, + {0xbace5e0bfbf70807, "rc75w2zvyw40", -37.348376371, 173.254332963}, + {0x3ca52949cb50e2e0, "7kkkkkfcb3jf", -20.320281012, -27.582874909}, + {0x0f21f107c4d4cbf3, "1whz21y4um5z", -54.965472754, -105.81188523}, + {0xd67cb677724b7742, "utycdxvk9evn", 77.650787518, 32.109331729}, + {0x4c2b370f2a0f75a3, "9hpmf3tb1xuu", 23.51902423, -124.697843247}, + {0x558cb07eabda4ab5, "bq6c0zpcv95c", 80.37078949, -164.838969411}, + {0x4bb025cfee13ac27, "9fs2cmzf2fq2", 14.226320076, -95.207822174}, + {0xef76c3219029ca32, "xxvd68dh5753", 43.992707306, 165.34712885}, + {0x9f38b42b6978e5fa, "mwwc8bv9g3kz", -8.169399941, 77.032845756}, + {0x72f0a0598ed569de, "fcsb0qdfupnx", 53.47371645, -49.555368985}, + {0x84f290ba5ca6473c, "hmt91fkwnt3m", -58.873300062, 19.067739909}, + {0x52c82f4213e92225, "bc42yhhmx4j2", 50.779763592, -142.816121641}, + {0x5a28098ebd7485d8, "c8n0m3pxfk2x", 45.050792857, -103.82139}, + {0x2fbeb571456a71ac, "5yzcbwb5e9su", -51.685944324, -0.329433087}, + {0xcb084abdd0bdc3cb, "td44pgfhrr1w", 11.622930046, 70.65613622}, + {0x26a5d1f93f2ec370, "4ukx3y9z5v1r", -64.782400728, -49.842404765}, + {0x200ff14b5c390ecc, "407z2kuw747d", -87.292158018, -84.709262704}, + {0x26bc2589ab490608, "4uy2c2ec9430", -63.146397905, -47.400811514}, + {0x3873cb4c570e1fd3, "71twqm2r1sgx", -35.433799715, -36.990554247}, + {0xebca4d9d85dc229d, "xg54v7d5vhj9", 17.378290024, 173.202431629}, + {0xc0e7fafd201dec29, "s3mzpz903rq2", 8.302999354, 19.67794922}, + {0x16139a28ec83e389, "2s9tnb7dhgjs", -18.806860304, -155.088976187}, + {0x1d621ab20b2b869d, "3pj1pdhc5f39", -5.438048394, -127.632473912}, + {0x529925c32b892675, "bbdkchtcj4m7", 48.672364568, -143.033874128}, + {0x312100279fb45cd1, "64hh09wzqjfe", -33.037359284, -84.343496731}, + {0xb698f31b2e85c9f9, "qudg66tfhr4z", -19.102095938, 127.724147198}, + {0x4b2f3e502ad90b7d, "9drmwn1bv45r", 13.656146663, -102.038393248}, + {0x14c4ab17d10052d6, "2m2bq5yj019e", -15.403338363, -167.423398748}, + {0xdaa36c52c5563074, "vbjqsnq5bss7", 46.177578898, 86.316860732}, + {0x334b269d64e9ba01, "6e5ke7c4x6x0", -27.312942377, -62.78517139}, + {0xa9c59fbd20f9ede3, "p72tzg90z7qy", -70.688634478, 147.295394787}, + {0xcf178d69abb4bfdd, "twcsuuecqkzx", 38.828671548, 69.823543596}, + {0xeec4d7b6f385fc4b, "xv2egermhry4", 30.209182737, 169.61708068}, + {0xfef9bec2b74168b0, "zvwvxhpr85nc", 76.927573277, 178.559770132}, + {0x82fd010c7c36f50e, "hcyh233w6vuh", -79.401208739, 42.2006699}, + {0x90304c7a0572573e, "k0s4syh5f9cm", -41.714433839, 5.839342663}, + {0xe7265aac95edb5d7, "wwm5pc4pxqux", 35.690411897, 119.874890029}, + {0x6e6d3f6d3a830e73, "etqmyv9uhd77", 30.572903854, -13.411705761}, + {0x6806b7eebe4702aa, "e03cgvpy8w1b", 1.742481028, -42.363600442}, + {0x718909347ab0ed9b, "f64hke3uq3qt", 57.015660258, -75.73706564}, + {0xa5b9fcc290a68f2a, "nqwzthnhnu7k", -52.09645784, 110.97044813}, + {0x3d51df8f77631890, "7p8xz3vrddd9", -1.439281196, -43.970951053}, + {0x8fc5cc5bfd1f0976, "jz2wsqzx3w4r", -48.037815007, 79.650259764}, + {0x23ae4d21fdce1eac, "4fr4u8gxtsgb", -76.854918639, -46.203448291}, + {0xcd7699f6e6143925, "tpv9mxr62hwk", 43.853713209, 52.986038997}, + {0xbd839fca785bf4dc, "rq1tzkmscgue", -10.215075736, 148.685612989}, + {0x23cb541b2ef39d4d, "4g5p86tfyffn", -71.79241975, -52.012074646}, + {0xcb317aff61947bf8, "tdsrpzv1kjxz", 15.335786045, 73.824187369}, + {0xaf3aa249bd5a3f1e, "pwxb4kexc8zj", -53.411445404, 168.502194984}, + {0x85adfff9ce945988, "hqqzzyffkjds", -53.443950018, 21.08671545}, + {0x80e9914a0ae995c3, "h3nt2khbx6bw", -83.430074116, 20.408446428}, + {0xacd80d95b024f9b2, "pmd0v5eh4mwv", -58.910720965, 149.28645215}, + {0x0b3eaee32b676cfa, "1dzbxstcdxqg", -74.418343233, -101.263959621}, + {0x73dc70fafe7428dc, "fgf71yryfhne", 66.656606812, -52.998295461}, + {0x6ca22551ee31e710, "ekj2bngf67mj", 22.669319337, -26.36193431}, + {0xa7156fb24cd50fbe, "nwbqzdkdun7v", -50.831860422, 113.18750916}, + {0x966dd1fdb3d4799f, "ktqx3zemujwt", -14.152119679, 31.722173125}, + {0xe82f28cfccb955f5, "x0rkjmydr5bz", 2.141368381, 145.435253245}, + {0x994b1eca8598e42a, "m55jxkn5m3k2", -27.135665841, 49.545824561}, + {0xc2989a8ac4dd3628, "sbd9p2q4vnv2", 2.990162826, 37.592690085}, + {0xbb03fe6588be5370, "rd1zwtd8rt9r", -32.4013546, 160.250338844}, + {0x46707f8ae01d559e, "8ts7z2r03pbt", 31.598138408, -151.195177068}, + {0x82810c22be2c4374, "hb0hs8py5j1r", -89.207926313, 33.958527376}, + {0xfa643283823789d1, "z9k350w26y4x", 52.209854162, 163.617102915}, + {0x40cf98697ca34944, "837thucwne4n", 7.937295931, -163.617043147}, + {0x72f51c0f3840da7f, "fcujs3ts83e7", 55.819567774, -50.430663923}, + {0x886a783ebf331d81, "j1p7hgpz6dfs", -83.829916727, 55.414806415}, + {0xd1744a846a060f95, "u5u4p13b0s7t", 66.452202757, 5.93504668}, + {0x98d262612d0866df, "m3964s9e11me", -36.185695913, 58.119786301}, + {0x758a992e16bea42b, "fq59kchqruk2", 78.976315464, -73.613209652}, + {0xf0671a05cd7248a5, "y1mjn1fef94b", 52.920418526, 97.298466149}, + {0x203184c1a23dda7b, "40ss9he27re7", -86.371681441, -83.623322377}, + {0x6f0fa98af6b3f291, "ew7um2rqqgt9", 35.905785508, -16.985596129}, + {0xb7d791ac5bc36e2b, "qzct3c2vser2", -0.475570365, 125.937512405}, + {0xa88f340e61ebafdf, "p27m83m1xfrx", -87.619815767, 150.838508187}, + {0x09cc66f2dc47bb89, "1766ewqw8yxs", -71.243813199, -120.423160037}, + {0x2aeaec5cc7210a84, "5cpfsr674458", -83.895195609, -0.161614424}, + {0xf31f40ec82bf9a45, "ydgn1v42rye4", 61.550981566, 116.799080892}, + {0x78d2e98909900fde, "g39fm289k07x", 53.83597161, -31.057486351}, + {0xd7251a1798f3d1c0, "uwkjn5wsyg8w", 81.055217529, 28.397888676}, + {0x7f689a6a729505c4, "gxn9numkkn2w", 84.574891413, -13.055362002}, + {0x9e848cbcaeb23dfc, "mu28tg5fq8yz", -20.988988351, 79.711248593}, + {0xdde9146e1767d04b, "vrnj8vhrdz84", 85.370590779, 64.726413759}, + {0x3584e42b74651cc1, "6q2f8bvndnfd", -9.399024611, -77.655397862}, + {0xab00d6b541a1236e, "pd0eeeb1n4jq", -78.113982742, 158.357198435}, + {0x98f1f7adead2629f, "m3szgcgbu9j9", -35.190447835, 63.099833072}, + {0xf45a9b7aa162dc09, "yje9qyp1dcf0", 76.190452492, 95.228239831}, + {0xc92d347605072ec4, "t4qm8xh50wrd", 13.662047988, 53.816559915}, + {0xe90c24aa12314dba, "x4629bhk656v", 12.744888498, 138.246893096}, + {0xbfda706bd4919a71, "rze70uynk6e7", -2.257976852, 173.361697995}, + {0xbf4ff3ccbddbd24f, "rx7z7m5xvg94", -2.915520485, 162.921314678}, + {0x93bb3aa4f54eafa3, "kfxmp97p9uru", -30.050416291, 44.279107831}, + {0xdbf38e68c6226ca2, "vgtswu6649qb", 65.502211626, 86.784212219}, + {0xa884bfde7c826187, "p22czrmwh9hs", -88.245271236, 147.631043435}, + {0xbc598052097efa59, "rjds0nh9gvx5", -13.326085557, 138.521975323}, + {0x1ee7ccd966ade607, "3vmwtqc6prm0", -14.288710667, -93.282877549}, + {0xb519db854549d580, "qndxr1b597bs", -7.152851286, 93.823296505}, + {0x72580375ae1f6cc2, "f9d06xef3xqd", 53.523069518, -64.572413659}, + {0x1cd538f5c0d92ecf, "3mbmjxf0v4rd", -11.734630445, -123.153766729}, + {0xcd24cb654abaa9c6, "tnkdqtbbrbnw", 35.583387533, 51.615140932}, + {0x5535a238867022ed, "bnuu4f46f0jf", 83.683333882, -173.196279251}, + {0xeb9773fa0b1cfce3, "xfcr7yhc3myf", 16.776368855, 170.679193354}, + {0x9adc553476ce15e2, "mcf5be3qtsby", -34.478075216, 81.586426445}, + {0xbde2569d635261ac, "rrj5e7c3b9hu", -4.988859709, 153.42581693}, + {0x792897f1d428c7b0, "g4n9gwfn533v", 56.595733587, -35.702677949}, + {0x73e82a8dc4844ddc, "fgn2p3f4hj6x", 61.884985809, -47.13941217}, + {0x4ab393d17c232d0e, "9btt7ncw4dqh", 3.773473221, -93.381613484}, + {0xdbb54722732cfdd8, "vfunf8mm5myx", 61.657537068, 84.492221472}, + {0xa4f5a6b3b2508f4a, "nmuuedxkb27n", -56.850683711, 108.093467466}, + {0x3b65419938882bc5, "7ekn369sj0pw", -25.605696467, -16.817783722}, + {0x3ed4c9fa6186d21b, "7vbdmym1hv91", -12.22620853, -10.287117503}, + {0x9c242523c93de750, "mhk2b8y97rmp", -20.957539528, 51.007607786}, + {0xb0e479e1294a85bc, "q3k7ms999b2v", -37.372481927, 107.470397989}, + {0x142d7e69af5a8109, "2hqrwuegcb0h", -19.750026921, -170.909081513}, + {0x97dda04accb9d482, "kzfu0kqdr7b8", -0.679372693, 37.637412165}, + {0x73f7e28aca62f26f, "fgvy52qbdct6", 67.149905159, -48.01184517}, + {0x427169401d266e2f, "89sqkh0x4tr2", 9.559335427, -151.346852305}, + {0x41c11c91da48dcc3, "870jt4fu93fd", 17.857685358, -168.526439767}, + {0x6a311eb0cdf2efc7, "e8sjxd6eycrw", 3.792308068, -16.541677107}, + {0x025f0def2f9a01da, "09ghvvtgm80x", -79.290517585, -153.020430995}, + {0x1f84cdde6d7d60f8, "3y2dvrmegphg", -9.319844305, -100.308473665}, + {0xed55d0fc523b7fd9, "xpbx1z2k7ezx", 44.864795093, 135.780533096}, + {0xce9e2891de5666bf, "tug2j4fybtmc", 26.735043406, 83.543845107}, + {0xec053bacb3319d44, "xh2mrc5m66fn", 24.835511798, 135.696701124}, + {0x661e8daaaf3f051c, "dsg8vbpg7w2j", 26.851178295, -62.314646062}, + {0xc1108a7e0cea5792, "s488nzhdx9ct", 14.101391545, 1.006259659}, + {0x20c60c2632572ee0, "4330s9jkbwrf", -82.874516947, -77.138754435}, + {0xd13e8de4ec9ee643, "u4z8vt7dmvm4", 60.629839844, 10.793634348}, + {0xcd1f88c44db211de, "tngsjj2eq88x", 38.701275951, 50.142568158}, + {0xa897311f88a80618, "p2cm27w8p031", -84.839170845, 148.028027168}, + {0x94e3a900d523d6c3, "kmjuk06p4gcd", -16.125334261, 19.514585462}, + {0x4b998b216845198d, "9fdsq8c88nds", 14.8138117, -97.446666933}, + {0xa45cd7c974577c07, "njfegkcnbxy0", -56.969777334, 93.659850193}, + {0xc70fe4ca091d41d7, "sw7y9kh93p0x", 36.321054238, 27.834592695}, + {0xfaa13b767a8d78cf, "zbhmqxmujpwd", 45.963404382, 175.020327007}, + {0xe84eb6459d78e63b, "x17cdjdxg3m3", 7.326471593, 140.364908178}, + {0x5ce80d970f017238, "cmn0v5sg05t3", 73.276595962, -115.086243065}, + {0xe487ea81f1a1a99d, "wk3yp0gjn6nt", 24.965927409, 104.022951702}, + {0x42fd74be2552fd18, "8cyr9gj5bcyj", 11.179242863, -137.377126282}, + {0x3f591ab674ee4502, "7xdjpemnxt2h", -1.914596304, -19.350721698}, + {0xaf530fb1a92f4d61, "px9hzde95x6q", -46.963594547, 159.240799607}, + {0xa774a2186f0ce2d0, "nxub463g1mje", -46.393348046, 119.281028812}, + {0x9d423471d932577d, "mp138wft69cr", -5.323337358, 46.783327579}, + {0x53b3d94e8d5ed7fd, "bftxkmnecvcz", 60.365055764, -138.319853393}, + {0x3b10df55c66e64bd, "7d8eypf6etkc", -30.235290663, -21.529956463}, + {0x0b88f2f991a649d7, "1f4g5ydjnt4x", -78.186064249, -97.214988572}, + {0x35b60cf1fa114a17, "6qv0twgu2551", -6.905534565, -71.471896765}, + {0xf9402559c61139bc, "z502bqf624wv", 62.044315389, 135.365647989}, + {0xb23c25df24ecae14, "q8y2crt4xkr1", -40.607763095, 121.351181194}, + {0x638c958bdc88885f, "df69c2ywj245", 12.969017694, -52.670288712}, + {0xd8a32109f9364e7f, "v2jk22gt6t77", 45.752120211, 63.648717296}, + {0x7018de1c997fc3f0, "f0dew74tgz1z", 48.445239681, -86.20612022}, + {0x236371fee0da1283, "4ejr3zr0v898", -71.810629009, -60.030432}, + {0x4f85bf8dbca51034, "9y2vz3ewnn83", 36.17629706, -99.871644637}, + {0x68cd89b1d614756f, "e36smdfq2juq", 7.794531379, -29.989579848}, + {0xef130c7618af7361, "xw9hsxhspxtq", 37.392695721, 159.110512663}, + {0x5ae5c98e557bba69, "cckwm3kpgfx6", 53.138093443, -94.685498089}, + {0x99b4954cdbae93d3, "m6u9bm6vpu9x", -29.193910025, 62.593222637}, + {0xa40c9c1bc5dae495, "nh69s6y5vck9", -65.814321191, 93.710885192}, + {0xd93f62b2ee948a6f, "v4zq5drfkk56", 61.536205285, 55.359951175}, + {0xa248879fd5a01d87, "n948g7ypn0fs", -84.221358244, 116.166947941}, + {0x3e59380d3ad10318, "7tdmh39uu41j", -13.174527042, -19.146593411}, + {0x63b96a790ac9b0c4, "dfwqny8bt6sd", 15.152992768, -47.163045276}, + {0x820085b53473797c, "h808ce9nfewr", -89.847759235, 23.270532141}, + {0x7dedda21b4d95961, "grqxn8env5dq", 87.015643873, -24.319392941}, + {0x50ccabea3618ac93, "b36brujq32q9", 52.098242439, -164.534964038}, + {0xb56b6149f18a3cfd, "qppq2kgjj8yg", -4.499411667, 100.210663982}, + {0x13acb6fe79c814dd, "2fqcezmtt0be", -32.039303886, -136.585249055}, + {0xb4db8ddab5183bdf, "qmesvqpp30xx", -13.193331533, 106.412254154}, + {0x8a88be5899d0fbfe, "jb4cwq4tu3xz", -89.702372758, 82.895472028}, + {0x7ad641d0f0dbda66, "gcc43n7hvge6", 55.274424708, -9.795433536}, + {0x48b1d8a4df19989d, "92sxj96z36d9", 4.051093478, -117.176342049}, + {0xa09f6b397d390ad7, "n2gqqfcx745e", -84.666255048, 106.119136826}, + {0xea013ff5b6ef1a40, "x80mzxeqxwe4", 1.05309195, 158.185941463}, + {0xd1159446b97426d6, "u4bt8jptfhme", 61.464023161, 0.713518053}, + {0x06e649431432476e, "0vm4khsn693q", -60.047442238, -139.037366317}, + {0x4e5a427855c44351, "9te44y2psj1p", 31.324710306, -108.160227343}, + {0x4b60a333573e5078, "9ehb6dur7t87", 16.935326104, -105.704461995}, + {0xd306e7fe10b0b82f, "ud3fgzhhq2w2", 58.178832975, 25.131502473}, + {0xba1cbafb41b357ba, "r8fcpyu1qecv", -40.568154333, 161.713538443}, + {0x1c5a10b27a87ca8e, "3je11dmuhz58", -13.87363482, -130.707226802}, + {0xdc041a849282e0bf, "vh21p14khchc", 69.088216728, 45.310917579}, + {0x532d8f698bc8ebdb, "bdqsyudct3px", 58.516194249, -148.058731954}, + {0x6a912f9642fb9e2d, "eb8kz5k2zfg2", 3.665457361, -10.584646626}, + {0x91196f85d9882b28, "k4dqz1ftj0pk", -29.740504531, 3.475337437}, + {0xc33e239f53570c83, "sdz277umbw68", 15.534315329, 32.843996188}, + {0x95fc72fc9327442d, "kry75z4m4x22", -0.839556753, 20.207055175}, + {0xd11bf954c7ec70b1, "u4ezkp67xjsc", 60.377370058, 5.45261547}, + {0x60549d284d1778ea, "d1b9ub2e2xwf", 10.153338981, -89.087417278}, + {0x1d2823613f06e0bc, "3nn26s9z0vhc", -11.18010674, -126.098632203}, + {0xe3c1a3328de2021d, "wg0u6dnew811", 17.633657768, 124.923757636}, + {0xc6e702731458a85c, "svmh4wsnc2n5", 30.271239887, 40.896668339}, + {0x15b6d4ce183df6d6, "2qve9mhs7rve", -6.38777808, -160.954366589}, + {0x13f49b0af10327b6, "2gu9q2rj0dmv", -23.684278836, -139.637574459}, + {0xaac38ed26089f8cf, "pc1sxnm0j7wd", -83.549632839, 171.174092974}, + {0xf675d0f57afbdd39, "ytux1xcuzgfm", 78.617627186, 118.896782032}, + {0x0fd6fcbbb08964bc, "1zcgtfxhj5kc", -45.776578359, -98.526542771}, + {0xbe6276890f6a9214, "rtj7e28geb91", -16.25641526, 165.026827037}, + {0x5b06f966db6dd517, "cd3gktqverbj", 58.257364882, -109.831899247}, + {0x18e331aa3b914b5e, "33jm3bjvk55p", -38.451229837, -116.282210815}, + {0xc046eaf86fe76e0b, "s13fpy3gwxr0", 7.417784529, 2.804205586}, + {0x7e1c51d81a40f2cf, "gsf53q0u83td", 72.323779052, -19.631520014}, + {0x5a31d6a895ad2c67, "c8sxeb4ppnq6", 49.13209503, -106.004024575}, + {0x5e990b8cb258d9d6, "cudhr35kc3dx", 71.065888201, -98.114376127}, + {0x4c0d89a57abc50ab, "9h6sm9curj8b", 24.663692484, -131.239970832}, + {0x1601ff0218f7a9a1, "2s0zy0hsyynu", -21.136846988, -156.175162065}, + {0xbf32049c1e050c80, "rwt0970y0n68", -8.332066347, 164.587215029}, + {0x0cc7394f878d59c3, "1m3mkmw7jpdw", -59.515129763, -121.796618799}, + {0x9ceb5657b1aea495, "mmppdpxjpuk9", -15.514560492, 66.191592361}, + {0xe8ecc064bd638eb3, "x3qd0t5xdf7c", 7.411578064, 155.417527354}, + {0xc94845958fc85eeb, "t544c5dgt1gf", 17.37823511, 47.860441765}, + {0x162e73dad3dbda84, "2sr77qqmvge8", -20.487122735, -147.153030632}, + {0x9fd11c07adc3314d, "mz8js1xesdsn", -1.83684798, 78.936279607}, + {0x2f5a680f84c9e810, "5xe6h3w4t7n1", -47.452252337, -17.734446758}, + {0x85fff499b8020641, "hrzz96es0834", -45.073469345, 22.208198475}, + {0x88e6740f75bb692f, "j3m783vprenk", -82.342641136, 63.650995808}, + {0x8ee676457490cc25, "jvm7djcnk362", -59.820850195, 86.22226184}, + {0xb83bf1b4a9e12b5d, "r0xz3e59w4pp", -40.89633692, 145.969429068}, + {0x103acfe237c563ce, "20xdzsjrspjw", -41.680802803, -169.11614607}, + {0x0d8b513845e954a6, "1q5p2f25x5bb", -54.962605269, -119.497989511}, + {0x79e643f15f9a7dfc, "g7m47wbzm9yz", 63.715091874, -26.563666145}, + {0xdda60f82cff8b98c, "vqm0z0qgz2ws", 80.290106399, 63.59845697}, + {0xfc9a6920f908ec77, "zke6k87t13q7", 70.710246773, 151.022935077}, + {0x61d6c7e8f3c2ce6c, "d7cdgu7msc76", 21.601445649, -76.47115844}, + {0xd6d2a4b3d0b0a549, "uv9b9dyhq2kn", 76.041228794, 36.285369047}, + {0x196916fe4b898c2b, "35njezkcj662", -27.118193872, -126.390953346}, + {0x187eab6c22d24b8e, "31zbqv12u95s", -35.084703986, -123.803016927}, + {0xeb31e9c99c915c1d, "xdsymkdwk5f1", 15.186945383, 164.41400816}, + {0xfe608597ce123316, "zth8c5yf28tj", 73.27782517, 163.881368338}, + {0xda07a7689a48b408, "v83ufu4u92u0", 47.263960121, 70.085622883}, + {0xb6ade2cc850b4044, "quqy5m451e04", -20.011065173, 133.387820762}, + {0xd7625bf05a4fc885, "uxj5rw2u9z48", 84.981431501, 29.861948302}, + {0xcd19d259cd3da54b, "tndx4qfe7qkn", 37.830640121, 48.618080331}, + {0x0f3cc3c99415c81d, "1wyd7kdn2r41", -51.609912193, -103.213792497}, + {0x5ce1c0d80d28ad6e, "cmhw1q0e52qq", 74.213163915, -117.366108591}, + {0x99f350a84e198649, "m7tp1b2f3634", -24.08025925, 63.35923943}, + {0xb7afe60659cb73c6, "qyryd1kttetw", -8.693341398, 134.742751488}, + {0xc2c75bd6aee3cd48, "sc3prppfwg6n", 8.344563839, 35.474802783}, + {0xff31d72286557c02, "zwsxf8n6bpy0", 82.925318363, 163.946579556}, + {0x33bcb800eea1b83e, "6fych07fn6w3", -29.353738456, -46.576604544}, + {0x5fd13da34250eb75, "cz8mv8u2b3pr", 88.20249749, -100.650881287}, + {0x3ed6314a4a5952af, "7vc32kkbc59b", -12.413029817, -9.474632465}, + {0x75ee6e7b8dff92df, "frr6wywezy9e", 86.25708794, -68.248795376}, + {0x5adb18bc0325fd0f, "ccejjg034ryh", 54.33309979, -96.778116196}, + {0xe8f4ff2087aec428, "x3ugy847pv22", 10.503472855, 153.218755098}, + {0xa669ad8dfdc4a1b1, "ntnuv3gxskhv", -61.029116203, 122.227895734}, + {0x99ddd9c837e22ca3, "m7fxmk1rw8qb", -22.608574423, 59.998342661}, + {0xa1ae87b03f5f2145, "n6r8gd1zcwhn", -77.19956403, 111.953156605}, + {0xaf6c4af2ac9c6494, "pxq4pwpdmjk9", -48.833813833, 166.27761019}, + {0xfc67aad46502884e, "zjmupp350b44", 75.274715856, 143.394963151}, + {0xa849c7cfce677bdf, "p14wgmyfdxxx", -83.156420177, 138.667834659}, + {0xaf17a05cf113bc0a, "pwcu0r7j2fy0", -51.287383562, 159.976084259}, + {0x764fd65365eedfbd, "ft7xdnv5xvgv", 75.88732087, -62.483030113}, + {0x39e7280a0b89e7f6, "77mkh2hcj7mz", -26.015434642, -26.173663656}, + {0xe6614c62c3e001dd, "wthnssq3w00x", 29.291182895, 118.331595326}, + {0xf8e389e43863497d, "z3jsmt1sde4r", 51.400326027, 154.228244707}, + {0x0a1abad2cc793011, "18ecpnqdg4s1", -86.976900779, -106.90988479}, + {0xd2418d7e11b6c582, "u90suzhjqv2s", 51.49934315, 23.417648894}, + {0x92480a8dbc9cfd6a, "k940p3ewmmyq", -39.365655496, 25.636144008}, + {0x33c9cd8b1f69a78f, "6g4wv2sze6ms", -26.934429639, -52.496991862}, + {0x8c1dee9196a2ff64, "jhfyx4dqnczq", -62.123898484, 49.178194037}, + {0x8a00f24e56f8fe99, "j80g4mkqz3z9", -89.442648795, 68.659722351}, + {0x8593f3ae3cbaf84c, "hq9z7cjwrcw4", -52.156511416, 13.883626414}, +} diff --git a/vendor/github.com/mmcloughlin/geohash/util_test.go b/vendor/github.com/mmcloughlin/geohash/util_test.go new file mode 100644 index 00000000..99294060 --- /dev/null +++ b/vendor/github.com/mmcloughlin/geohash/util_test.go @@ -0,0 +1,61 @@ +package geohash + +import ( + "math" + "math/rand" +) + +func RandomPoint() (lat, lng float64) { + lat = -90 + 180*rand.Float64() + lng = -180 + 360*rand.Float64() + return +} + +func RandomPoints(n int) [][2]float64 { + points := make([][2]float64, n) + for i := 0; i < n; i++ { + lat, lng := RandomPoint() + points[i] = [2]float64{lat, lng} + } + return points +} + +func RandomBox() Box { + lat1, lng1 := RandomPoint() + lat2, lng2 := RandomPoint() + return Box{ + MinLat: math.Min(lat1, lat2), + MaxLat: math.Max(lat1, lat2), + MinLng: math.Min(lng1, lng2), + MaxLng: math.Max(lng1, lng2), + } +} + +func RandomStringGeohashWithPrecision(chars uint) string { + const alphabet = "0123456789bcdefghjkmnpqrstuvwxyz" + b := make([]byte, chars) + for i := uint(0); i < chars; i++ { + b[i] = alphabet[rand.Intn(32)] + } + return string(b) +} + +func RandomStringGeohashesWithPrecision(n int, chars uint) []string { + geohashes := make([]string, n) + for i := 0; i < n; i++ { + geohashes[i] = RandomStringGeohashWithPrecision(chars) + } + return geohashes +} + +func RandomIntGeohash() uint64 { + return (uint64(rand.Uint32()) << 32) | uint64(rand.Uint32()) +} + +func RandomIntGeohashes(n int) []uint64 { + geohashes := make([]uint64, n) + for i := 0; i < n; i++ { + geohashes[i] = RandomIntGeohash() + } + return geohashes +} diff --git a/vendor/github.com/nats-io/gnatsd/LICENSE b/vendor/github.com/nats-io/gnatsd/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/vendor/github.com/nats-io/gnatsd/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/nats-io/gnatsd/conf/lex.go b/vendor/github.com/nats-io/gnatsd/conf/lex.go deleted file mode 100644 index f9603a99..00000000 --- a/vendor/github.com/nats-io/gnatsd/conf/lex.go +++ /dev/null @@ -1,1141 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Customized heavily from -// https://github.com/BurntSushi/toml/blob/master/lex.go, which is based on -// Rob Pike's talk: http://cuddle.googlecode.com/hg/talk/lex.html - -// The format supported is less restrictive than today's formats. -// Supports mixed Arrays [], nested Maps {}, multiple comment types (# and //) -// Also supports key value assigments using '=' or ':' or whiteSpace() -// e.g. foo = 2, foo : 2, foo 2 -// maps can be assigned with no key separator as well -// semicolons as value terminators in key/value assignments are optional -// -// see lex_test.go for more examples. - -package conf - -import ( - "encoding/hex" - "fmt" - "strings" - "unicode" - "unicode/utf8" -) - -type itemType int - -const ( - itemError itemType = iota - itemNIL // used in the parser to indicate no type - itemEOF - itemKey - itemText - itemString - itemBool - itemInteger - itemFloat - itemDatetime - itemArrayStart - itemArrayEnd - itemMapStart - itemMapEnd - itemCommentStart - itemVariable - itemInclude -) - -const ( - eof = 0 - mapStart = '{' - mapEnd = '}' - keySepEqual = '=' - keySepColon = ':' - arrayStart = '[' - arrayEnd = ']' - arrayValTerm = ',' - mapValTerm = ',' - commentHashStart = '#' - commentSlashStart = '/' - dqStringStart = '"' - dqStringEnd = '"' - sqStringStart = '\'' - sqStringEnd = '\'' - optValTerm = ';' - topOptStart = '{' - topOptValTerm = ',' - topOptTerm = '}' - blockStart = '(' - blockEnd = ')' -) - -type stateFn func(lx *lexer) stateFn - -type lexer struct { - input string - start int - pos int - width int - line int - state stateFn - items chan item - - // A stack of state functions used to maintain context. - // The idea is to reuse parts of the state machine in various places. - // For example, values can appear at the top level or within arbitrarily - // nested arrays. The last state on the stack is used after a value has - // been lexed. Similarly for comments. - stack []stateFn - - // Used for processing escapable substrings in double-quoted and raw strings - stringParts []string - stringStateFn stateFn -} - -type item struct { - typ itemType - val string - line int -} - -func (lx *lexer) nextItem() item { - for { - select { - case item := <-lx.items: - return item - default: - lx.state = lx.state(lx) - } - } -} - -func lex(input string) *lexer { - lx := &lexer{ - input: input, - state: lexTop, - line: 1, - items: make(chan item, 10), - stack: make([]stateFn, 0, 10), - stringParts: []string{}, - } - return lx -} - -func (lx *lexer) push(state stateFn) { - lx.stack = append(lx.stack, state) -} - -func (lx *lexer) pop() stateFn { - if len(lx.stack) == 0 { - return lx.errorf("BUG in lexer: no states to pop.") - } - li := len(lx.stack) - 1 - last := lx.stack[li] - lx.stack = lx.stack[0:li] - return last -} - -func (lx *lexer) emit(typ itemType) { - lx.items <- item{typ, strings.Join(lx.stringParts, "") + lx.input[lx.start:lx.pos], lx.line} - lx.start = lx.pos -} - -func (lx *lexer) emitString() { - var finalString string - if len(lx.stringParts) > 0 { - finalString = strings.Join(lx.stringParts, "") + lx.input[lx.start:lx.pos] - lx.stringParts = []string{} - } else { - finalString = lx.input[lx.start:lx.pos] - } - lx.items <- item{itemString, finalString, lx.line} - lx.start = lx.pos -} - -func (lx *lexer) addCurrentStringPart(offset int) { - lx.stringParts = append(lx.stringParts, lx.input[lx.start:lx.pos-offset]) - lx.start = lx.pos -} - -func (lx *lexer) addStringPart(s string) stateFn { - lx.stringParts = append(lx.stringParts, s) - lx.start = lx.pos - return lx.stringStateFn -} - -func (lx *lexer) hasEscapedParts() bool { - return len(lx.stringParts) > 0 -} - -func (lx *lexer) next() (r rune) { - if lx.pos >= len(lx.input) { - lx.width = 0 - return eof - } - - if lx.input[lx.pos] == '\n' { - lx.line++ - } - r, lx.width = utf8.DecodeRuneInString(lx.input[lx.pos:]) - lx.pos += lx.width - return r -} - -// ignore skips over the pending input before this point. -func (lx *lexer) ignore() { - lx.start = lx.pos -} - -// backup steps back one rune. Can be called only once per call of next. -func (lx *lexer) backup() { - lx.pos -= lx.width - if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { - lx.line-- - } -} - -// peek returns but does not consume the next rune in the input. -func (lx *lexer) peek() rune { - r := lx.next() - lx.backup() - return r -} - -// errorf stops all lexing by emitting an error and returning `nil`. -// Note that any value that is a character is escaped if it's a special -// character (new lines, tabs, etc.). -func (lx *lexer) errorf(format string, values ...interface{}) stateFn { - for i, value := range values { - if v, ok := value.(rune); ok { - values[i] = escapeSpecial(v) - } - } - lx.items <- item{ - itemError, - fmt.Sprintf(format, values...), - lx.line, - } - return nil -} - -// lexTop consumes elements at the top level of data structure. -func lexTop(lx *lexer) stateFn { - r := lx.next() - if unicode.IsSpace(r) { - return lexSkip(lx, lexTop) - } - - switch r { - case topOptStart: - return lexSkip(lx, lexTop) - case commentHashStart: - lx.push(lexTop) - return lexCommentStart - case commentSlashStart: - rn := lx.next() - if rn == commentSlashStart { - lx.push(lexTop) - return lexCommentStart - } - lx.backup() - fallthrough - case eof: - if lx.pos > lx.start { - return lx.errorf("Unexpected EOF.") - } - lx.emit(itemEOF) - return nil - } - - // At this point, the only valid item can be a key, so we back up - // and let the key lexer do the rest. - lx.backup() - lx.push(lexTopValueEnd) - return lexKeyStart -} - -// lexTopValueEnd is entered whenever a top-level value has been consumed. -// It must see only whitespace, and will turn back to lexTop upon a new line. -// If it sees EOF, it will quit the lexer successfully. -func lexTopValueEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case r == commentHashStart: - // a comment will read to a new line for us. - lx.push(lexTop) - return lexCommentStart - case r == commentSlashStart: - rn := lx.next() - if rn == commentSlashStart { - lx.push(lexTop) - return lexCommentStart - } - lx.backup() - fallthrough - case isWhitespace(r): - return lexTopValueEnd - case isNL(r) || r == eof || r == optValTerm || r == topOptValTerm || r == topOptTerm: - lx.ignore() - return lexTop - } - return lx.errorf("Expected a top-level value to end with a new line, "+ - "comment or EOF, but got '%v' instead.", r) -} - -// lexKeyStart consumes a key name up until the first non-whitespace character. -// lexKeyStart will ignore whitespace. It will also eat enclosing quotes. -func lexKeyStart(lx *lexer) stateFn { - r := lx.peek() - switch { - case isKeySeparator(r): - return lx.errorf("Unexpected key separator '%v'", r) - case unicode.IsSpace(r): - lx.next() - return lexSkip(lx, lexKeyStart) - case r == dqStringStart: - lx.next() - return lexSkip(lx, lexDubQuotedKey) - case r == sqStringStart: - lx.next() - return lexSkip(lx, lexQuotedKey) - } - lx.ignore() - lx.next() - return lexKey -} - -// lexDubQuotedKey consumes the text of a key between quotes. -func lexDubQuotedKey(lx *lexer) stateFn { - r := lx.peek() - if r == dqStringEnd { - lx.emit(itemKey) - lx.next() - return lexSkip(lx, lexKeyEnd) - } - lx.next() - return lexDubQuotedKey -} - -// lexQuotedKey consumes the text of a key between quotes. -func lexQuotedKey(lx *lexer) stateFn { - r := lx.peek() - if r == sqStringEnd { - lx.emit(itemKey) - lx.next() - return lexSkip(lx, lexKeyEnd) - } - lx.next() - return lexQuotedKey -} - -// keyCheckKeyword will check for reserved keywords as the key value when the key is -// separated with a space. -func (lx *lexer) keyCheckKeyword(fallThrough, push stateFn) stateFn { - key := strings.ToLower(lx.input[lx.start:lx.pos]) - switch key { - case "include": - lx.ignore() - if push != nil { - lx.push(push) - } - return lexIncludeStart - } - lx.emit(itemKey) - return fallThrough -} - -// lexIncludeStart will consume the whitespace til the start of the value. -func lexIncludeStart(lx *lexer) stateFn { - r := lx.next() - if isWhitespace(r) { - return lexSkip(lx, lexIncludeStart) - } - lx.backup() - return lexInclude -} - -// lexIncludeQuotedString consumes the inner contents of a string. It assumes that the -// beginning '"' has already been consumed and ignored. It will not interpret any -// internal contents. -func lexIncludeQuotedString(lx *lexer) stateFn { - r := lx.next() - switch { - case r == sqStringEnd: - lx.backup() - lx.emit(itemInclude) - lx.next() - lx.ignore() - return lx.pop() - } - return lexIncludeQuotedString -} - -// lexIncludeDubQuotedString consumes the inner contents of a string. It assumes that the -// beginning '"' has already been consumed and ignored. It will not interpret any -// internal contents. -func lexIncludeDubQuotedString(lx *lexer) stateFn { - r := lx.next() - switch { - case r == dqStringEnd: - lx.backup() - lx.emit(itemInclude) - lx.next() - lx.ignore() - return lx.pop() - } - return lexIncludeDubQuotedString -} - -// lexIncludeString consumes the inner contents of a raw string. -func lexIncludeString(lx *lexer) stateFn { - r := lx.next() - switch { - case isNL(r) || r == eof || r == optValTerm || r == mapEnd || isWhitespace(r): - lx.backup() - lx.emit(itemInclude) - return lx.pop() - case r == sqStringEnd: - lx.backup() - lx.emit(itemInclude) - lx.next() - lx.ignore() - return lx.pop() - } - return lexIncludeString -} - -// lexInclude will consume the include value. -func lexInclude(lx *lexer) stateFn { - r := lx.next() - switch { - case r == sqStringStart: - lx.ignore() // ignore the " or ' - return lexIncludeQuotedString - case r == dqStringStart: - lx.ignore() // ignore the " or ' - return lexIncludeDubQuotedString - case r == arrayStart: - return lx.errorf("Expected include value but found start of an array") - case r == mapStart: - return lx.errorf("Expected include value but found start of a map") - case r == blockStart: - return lx.errorf("Expected include value but found start of a block") - case unicode.IsDigit(r), r == '-': - return lx.errorf("Expected include value but found start of a number") - case r == '\\': - return lx.errorf("Expected include value but found escape sequence") - case isNL(r): - return lx.errorf("Expected include value but found new line") - } - lx.backup() - return lexIncludeString -} - -// lexKey consumes the text of a key. Assumes that the first character (which -// is not whitespace) has already been consumed. -func lexKey(lx *lexer) stateFn { - r := lx.peek() - if unicode.IsSpace(r) { - // Spaces signal we could be looking at a keyword, e.g. include. - // Keywords will eat the keyword and set the appropriate return stateFn. - return lx.keyCheckKeyword(lexKeyEnd, nil) - } else if isKeySeparator(r) || r == eof { - lx.emit(itemKey) - return lexKeyEnd - } - lx.next() - return lexKey -} - -// lexKeyEnd consumes the end of a key (up to the key separator). -// Assumes that the first whitespace character after a key (or the '=' or ':' -// separator) has NOT been consumed. -func lexKeyEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case unicode.IsSpace(r): - return lexSkip(lx, lexKeyEnd) - case isKeySeparator(r): - return lexSkip(lx, lexValue) - case r == eof: - lx.emit(itemEOF) - return nil - } - // We start the value here - lx.backup() - return lexValue -} - -// lexValue starts the consumption of a value anywhere a value is expected. -// lexValue will ignore whitespace. -// After a value is lexed, the last state on the next is popped and returned. -func lexValue(lx *lexer) stateFn { - // We allow whitespace to precede a value, but NOT new lines. - // In array syntax, the array states are responsible for ignoring new lines. - r := lx.next() - if isWhitespace(r) { - return lexSkip(lx, lexValue) - } - - switch { - case r == arrayStart: - lx.ignore() - lx.emit(itemArrayStart) - return lexArrayValue - case r == mapStart: - lx.ignore() - lx.emit(itemMapStart) - return lexMapKeyStart - case r == sqStringStart: - lx.ignore() // ignore the " or ' - return lexQuotedString - case r == dqStringStart: - lx.ignore() // ignore the " or ' - lx.stringStateFn = lexDubQuotedString - return lexDubQuotedString - case r == '-': - return lexNegNumberStart - case r == blockStart: - lx.ignore() - return lexBlock - case unicode.IsDigit(r): - lx.backup() // avoid an extra state and use the same as above - return lexNumberOrDateOrIPStart - case r == '.': // special error case, be kind to users - return lx.errorf("Floats must start with a digit") - case isNL(r): - return lx.errorf("Expected value but found new line") - } - lx.backup() - lx.stringStateFn = lexString - return lexString -} - -// lexArrayValue consumes one value in an array. It assumes that '[' or ',' -// have already been consumed. All whitespace and new lines are ignored. -func lexArrayValue(lx *lexer) stateFn { - r := lx.next() - switch { - case unicode.IsSpace(r): - return lexSkip(lx, lexArrayValue) - case r == commentHashStart: - lx.push(lexArrayValue) - return lexCommentStart - case r == commentSlashStart: - rn := lx.next() - if rn == commentSlashStart { - lx.push(lexArrayValue) - return lexCommentStart - } - lx.backup() - fallthrough - case r == arrayValTerm: - return lx.errorf("Unexpected array value terminator '%v'.", arrayValTerm) - case r == arrayEnd: - return lexArrayEnd - } - - lx.backup() - lx.push(lexArrayValueEnd) - return lexValue -} - -// lexArrayValueEnd consumes the cruft between values of an array. Namely, -// it ignores whitespace and expects either a ',' or a ']'. -func lexArrayValueEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case isWhitespace(r): - return lexSkip(lx, lexArrayValueEnd) - case r == commentHashStart: - lx.push(lexArrayValueEnd) - return lexCommentStart - case r == commentSlashStart: - rn := lx.next() - if rn == commentSlashStart { - lx.push(lexArrayValueEnd) - return lexCommentStart - } - lx.backup() - fallthrough - case r == arrayValTerm || isNL(r): - return lexSkip(lx, lexArrayValue) // Move onto next - case r == arrayEnd: - return lexArrayEnd - } - return lx.errorf("Expected an array value terminator %q or an array "+ - "terminator %q, but got '%v' instead.", arrayValTerm, arrayEnd, r) -} - -// lexArrayEnd finishes the lexing of an array. It assumes that a ']' has -// just been consumed. -func lexArrayEnd(lx *lexer) stateFn { - lx.ignore() - lx.emit(itemArrayEnd) - return lx.pop() -} - -// lexMapKeyStart consumes a key name up until the first non-whitespace -// character. -// lexMapKeyStart will ignore whitespace. -func lexMapKeyStart(lx *lexer) stateFn { - r := lx.peek() - switch { - case isKeySeparator(r): - return lx.errorf("Unexpected key separator '%v'.", r) - case unicode.IsSpace(r): - lx.next() - return lexSkip(lx, lexMapKeyStart) - case r == mapEnd: - lx.next() - return lexSkip(lx, lexMapEnd) - case r == commentHashStart: - lx.next() - lx.push(lexMapKeyStart) - return lexCommentStart - case r == commentSlashStart: - lx.next() - rn := lx.next() - if rn == commentSlashStart { - lx.push(lexMapKeyStart) - return lexCommentStart - } - lx.backup() - case r == sqStringStart: - lx.next() - return lexSkip(lx, lexMapQuotedKey) - case r == dqStringStart: - lx.next() - return lexSkip(lx, lexMapDubQuotedKey) - } - lx.ignore() - lx.next() - return lexMapKey -} - -// lexMapQuotedKey consumes the text of a key between quotes. -func lexMapQuotedKey(lx *lexer) stateFn { - r := lx.peek() - if r == sqStringEnd { - lx.emit(itemKey) - lx.next() - return lexSkip(lx, lexMapKeyEnd) - } - lx.next() - return lexMapQuotedKey -} - -// lexMapQuotedKey consumes the text of a key between quotes. -func lexMapDubQuotedKey(lx *lexer) stateFn { - r := lx.peek() - if r == dqStringEnd { - lx.emit(itemKey) - lx.next() - return lexSkip(lx, lexMapKeyEnd) - } - lx.next() - return lexMapDubQuotedKey -} - -// lexMapKey consumes the text of a key. Assumes that the first character (which -// is not whitespace) has already been consumed. -func lexMapKey(lx *lexer) stateFn { - r := lx.peek() - if unicode.IsSpace(r) { - // Spaces signal we could be looking at a keyword, e.g. include. - // Keywords will eat the keyword and set the appropriate return stateFn. - return lx.keyCheckKeyword(lexMapKeyEnd, lexMapValueEnd) - } else if isKeySeparator(r) { - lx.emit(itemKey) - return lexMapKeyEnd - } - lx.next() - return lexMapKey -} - -// lexMapKeyEnd consumes the end of a key (up to the key separator). -// Assumes that the first whitespace character after a key (or the '=' -// separator) has NOT been consumed. -func lexMapKeyEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case unicode.IsSpace(r): - return lexSkip(lx, lexMapKeyEnd) - case isKeySeparator(r): - return lexSkip(lx, lexMapValue) - } - // We start the value here - lx.backup() - return lexMapValue -} - -// lexMapValue consumes one value in a map. It assumes that '{' or ',' -// have already been consumed. All whitespace and new lines are ignored. -// Map values can be separated by ',' or simple NLs. -func lexMapValue(lx *lexer) stateFn { - r := lx.next() - switch { - case unicode.IsSpace(r): - return lexSkip(lx, lexMapValue) - case r == mapValTerm: - return lx.errorf("Unexpected map value terminator %q.", mapValTerm) - case r == mapEnd: - return lexSkip(lx, lexMapEnd) - } - lx.backup() - lx.push(lexMapValueEnd) - return lexValue -} - -// lexMapValueEnd consumes the cruft between values of a map. Namely, -// it ignores whitespace and expects either a ',' or a '}'. -func lexMapValueEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case isWhitespace(r): - return lexSkip(lx, lexMapValueEnd) - case r == commentHashStart: - lx.push(lexMapValueEnd) - return lexCommentStart - case r == commentSlashStart: - rn := lx.next() - if rn == commentSlashStart { - lx.push(lexMapValueEnd) - return lexCommentStart - } - lx.backup() - fallthrough - case r == optValTerm || r == mapValTerm || isNL(r): - return lexSkip(lx, lexMapKeyStart) // Move onto next - case r == mapEnd: - return lexSkip(lx, lexMapEnd) - } - return lx.errorf("Expected a map value terminator %q or a map "+ - "terminator %q, but got '%v' instead.", mapValTerm, mapEnd, r) -} - -// lexMapEnd finishes the lexing of a map. It assumes that a '}' has -// just been consumed. -func lexMapEnd(lx *lexer) stateFn { - lx.ignore() - lx.emit(itemMapEnd) - return lx.pop() -} - -// Checks if the unquoted string was actually a boolean -func (lx *lexer) isBool() bool { - str := strings.ToLower(lx.input[lx.start:lx.pos]) - return str == "true" || str == "false" || - str == "on" || str == "off" || - str == "yes" || str == "no" -} - -// Check if the unquoted string is a variable reference, starting with $. -func (lx *lexer) isVariable() bool { - if lx.input[lx.start] == '$' { - lx.start += 1 - return true - } - return false -} - -// lexQuotedString consumes the inner contents of a string. It assumes that the -// beginning '"' has already been consumed and ignored. It will not interpret any -// internal contents. -func lexQuotedString(lx *lexer) stateFn { - r := lx.next() - switch { - case r == sqStringEnd: - lx.backup() - lx.emit(itemString) - lx.next() - lx.ignore() - return lx.pop() - } - return lexQuotedString -} - -// lexDubQuotedString consumes the inner contents of a string. It assumes that the -// beginning '"' has already been consumed and ignored. It will not interpret any -// internal contents. -func lexDubQuotedString(lx *lexer) stateFn { - r := lx.next() - switch { - case r == '\\': - lx.addCurrentStringPart(1) - return lexStringEscape - case r == dqStringEnd: - lx.backup() - lx.emitString() - lx.next() - lx.ignore() - return lx.pop() - } - return lexDubQuotedString -} - -// lexString consumes the inner contents of a raw string. -func lexString(lx *lexer) stateFn { - r := lx.next() - switch { - case r == '\\': - lx.addCurrentStringPart(1) - return lexStringEscape - // Termination of non-quoted strings - case isNL(r) || r == eof || r == optValTerm || - r == arrayValTerm || r == arrayEnd || r == mapEnd || - isWhitespace(r): - - lx.backup() - if lx.hasEscapedParts() { - lx.emitString() - } else if lx.isBool() { - lx.emit(itemBool) - } else if lx.isVariable() { - lx.emit(itemVariable) - } else { - lx.emitString() - } - return lx.pop() - case r == sqStringEnd: - lx.backup() - lx.emitString() - lx.next() - lx.ignore() - return lx.pop() - } - return lexString -} - -// lexBlock consumes the inner contents as a string. It assumes that the -// beginning '(' has already been consumed and ignored. It will continue -// processing until it finds a ')' on a new line by itself. -func lexBlock(lx *lexer) stateFn { - r := lx.next() - switch { - case r == blockEnd: - lx.backup() - lx.backup() - - // Looking for a ')' character on a line by itself, if the previous - // character isn't a new line, then break so we keep processing the block. - if lx.next() != '\n' { - lx.next() - break - } - lx.next() - - // Make sure the next character is a new line or an eof. We want a ')' on a - // bare line by itself. - switch lx.next() { - case '\n', eof: - lx.backup() - lx.backup() - lx.emit(itemString) - lx.next() - lx.ignore() - return lx.pop() - } - lx.backup() - } - return lexBlock -} - -// lexStringEscape consumes an escaped character. It assumes that the preceding -// '\\' has already been consumed. -func lexStringEscape(lx *lexer) stateFn { - r := lx.next() - switch r { - case 'x': - return lexStringBinary - case 't': - return lx.addStringPart("\t") - case 'n': - return lx.addStringPart("\n") - case 'r': - return lx.addStringPart("\r") - case '"': - return lx.addStringPart("\"") - case '\\': - return lx.addStringPart("\\") - } - return lx.errorf("Invalid escape character '%v'. Only the following "+ - "escape characters are allowed: \\xXX, \\t, \\n, \\r, \\\", \\\\.", r) -} - -// lexStringBinary consumes two hexadecimal digits following '\x'. It assumes -// that the '\x' has already been consumed. -func lexStringBinary(lx *lexer) stateFn { - r := lx.next() - if isNL(r) { - return lx.errorf("Expected two hexadecimal digits after '\\x', but hit end of line") - } - r = lx.next() - if isNL(r) { - return lx.errorf("Expected two hexadecimal digits after '\\x', but hit end of line") - } - offset := lx.pos - 2 - byteString, err := hex.DecodeString(lx.input[offset:lx.pos]) - if err != nil { - return lx.errorf("Expected two hexadecimal digits after '\\x', but got '%s'", lx.input[offset:lx.pos]) - } - lx.addStringPart(string(byteString)) - return lx.stringStateFn -} - -// lexNumberOrDateStart consumes either a (positive) integer, a float, a datetime, or IP. -// It assumes that NO negative sign has been consumed, that is triggered above. -func lexNumberOrDateOrIPStart(lx *lexer) stateFn { - r := lx.next() - if !unicode.IsDigit(r) { - if r == '.' { - return lx.errorf("Floats must start with a digit, not '.'.") - } - return lx.errorf("Expected a digit but got '%v'.", r) - } - return lexNumberOrDateOrIP -} - -// lexNumberOrDateOrIP consumes either a (positive) integer, float, datetime or IP. -func lexNumberOrDateOrIP(lx *lexer) stateFn { - r := lx.next() - switch { - case r == '-': - if lx.pos-lx.start != 5 { - return lx.errorf("All ISO8601 dates must be in full Zulu form.") - } - return lexDateAfterYear - case unicode.IsDigit(r): - return lexNumberOrDateOrIP - case r == '.': - return lexFloatStart // Assume float at first, but could be IP - case isNumberSuffix(r): - return lexConvenientNumber - } - - lx.backup() - lx.emit(itemInteger) - return lx.pop() -} - -// lexConvenientNumber is when we have a suffix, e.g. 1k or 1Mb -func lexConvenientNumber(lx *lexer) stateFn { - r := lx.next() - switch { - case r == 'b' || r == 'B': - return lexConvenientNumber - } - lx.backup() - lx.emit(itemInteger) - return lx.pop() -} - -// lexDateAfterYear consumes a full Zulu Datetime in ISO8601 format. -// It assumes that "YYYY-" has already been consumed. -func lexDateAfterYear(lx *lexer) stateFn { - formats := []rune{ - // digits are '0'. - // everything else is direct equality. - '0', '0', '-', '0', '0', - 'T', - '0', '0', ':', '0', '0', ':', '0', '0', - 'Z', - } - for _, f := range formats { - r := lx.next() - if f == '0' { - if !unicode.IsDigit(r) { - return lx.errorf("Expected digit in ISO8601 datetime, "+ - "but found '%v' instead.", r) - } - } else if f != r { - return lx.errorf("Expected '%v' in ISO8601 datetime, "+ - "but found '%v' instead.", f, r) - } - } - lx.emit(itemDatetime) - return lx.pop() -} - -// lexNegNumberStart consumes either an integer or a float. It assumes that a -// negative sign has already been read, but that *no* digits have been consumed. -// lexNegNumberStart will move to the appropriate integer or float states. -func lexNegNumberStart(lx *lexer) stateFn { - // we MUST see a digit. Even floats have to start with a digit. - r := lx.next() - if !unicode.IsDigit(r) { - if r == '.' { - return lx.errorf("Floats must start with a digit, not '.'.") - } - return lx.errorf("Expected a digit but got '%v'.", r) - } - return lexNegNumber -} - -// lexNumber consumes a negative integer or a float after seeing the first digit. -func lexNegNumber(lx *lexer) stateFn { - r := lx.next() - switch { - case unicode.IsDigit(r): - return lexNegNumber - case r == '.': - return lexFloatStart - case isNumberSuffix(r): - return lexConvenientNumber - } - lx.backup() - lx.emit(itemInteger) - return lx.pop() -} - -// lexFloatStart starts the consumption of digits of a float after a '.'. -// Namely, at least one digit is required. -func lexFloatStart(lx *lexer) stateFn { - r := lx.next() - if !unicode.IsDigit(r) { - return lx.errorf("Floats must have a digit after the '.', but got "+ - "'%v' instead.", r) - } - return lexFloat -} - -// lexFloat consumes the digits of a float after a '.'. -// Assumes that one digit has been consumed after a '.' already. -func lexFloat(lx *lexer) stateFn { - r := lx.next() - if unicode.IsDigit(r) { - return lexFloat - } - - // Not a digit, if its another '.', need to see if we falsely assumed a float. - if r == '.' { - return lexIPAddr - } - - lx.backup() - lx.emit(itemFloat) - return lx.pop() -} - -// lexIPAddr consumes IP addrs, like 127.0.0.1:4222 -func lexIPAddr(lx *lexer) stateFn { - r := lx.next() - if unicode.IsDigit(r) || r == '.' || r == ':' || r == '-' { - return lexIPAddr - } - lx.backup() - lx.emit(itemString) - return lx.pop() -} - -// lexCommentStart begins the lexing of a comment. It will emit -// itemCommentStart and consume no characters, passing control to lexComment. -func lexCommentStart(lx *lexer) stateFn { - lx.ignore() - lx.emit(itemCommentStart) - return lexComment -} - -// lexComment lexes an entire comment. It assumes that '#' has been consumed. -// It will consume *up to* the first new line character, and pass control -// back to the last state on the stack. -func lexComment(lx *lexer) stateFn { - r := lx.peek() - if isNL(r) || r == eof { - lx.emit(itemText) - return lx.pop() - } - lx.next() - return lexComment -} - -// lexSkip ignores all slurped input and moves on to the next state. -func lexSkip(lx *lexer, nextState stateFn) stateFn { - return func(lx *lexer) stateFn { - lx.ignore() - return nextState - } -} - -// Tests to see if we have a number suffix -func isNumberSuffix(r rune) bool { - return r == 'k' || r == 'K' || r == 'm' || r == 'M' || r == 'g' || r == 'G' -} - -// Tests for both key separators -func isKeySeparator(r rune) bool { - return r == keySepEqual || r == keySepColon -} - -// isWhitespace returns true if `r` is a whitespace character according -// to the spec. -func isWhitespace(r rune) bool { - return r == '\t' || r == ' ' -} - -func isNL(r rune) bool { - return r == '\n' || r == '\r' -} - -func (itype itemType) String() string { - switch itype { - case itemError: - return "Error" - case itemNIL: - return "NIL" - case itemEOF: - return "EOF" - case itemText: - return "Text" - case itemString: - return "String" - case itemBool: - return "Bool" - case itemInteger: - return "Integer" - case itemFloat: - return "Float" - case itemDatetime: - return "DateTime" - case itemKey: - return "Key" - case itemArrayStart: - return "ArrayStart" - case itemArrayEnd: - return "ArrayEnd" - case itemMapStart: - return "MapStart" - case itemMapEnd: - return "MapEnd" - case itemCommentStart: - return "CommentStart" - case itemVariable: - return "Variable" - case itemInclude: - return "Include" - } - panic(fmt.Sprintf("BUG: Unknown type '%s'.", itype.String())) -} - -func (item item) String() string { - return fmt.Sprintf("(%s, '%s', %d)", item.typ.String(), item.val, item.line) -} - -func escapeSpecial(c rune) string { - switch c { - case '\n': - return "\\n" - } - return string(c) -} diff --git a/vendor/github.com/nats-io/gnatsd/conf/lex_test.go b/vendor/github.com/nats-io/gnatsd/conf/lex_test.go deleted file mode 100644 index 28cce804..00000000 --- a/vendor/github.com/nats-io/gnatsd/conf/lex_test.go +++ /dev/null @@ -1,1224 +0,0 @@ -package conf - -import "testing" - -// Test to make sure we get what we expect. -func expect(t *testing.T, lx *lexer, items []item) { - for i := 0; i < len(items); i++ { - item := lx.nextItem() - _ = item.String() - if item.typ == itemEOF { - break - } - if item != items[i] { - t.Fatalf("Testing: '%s'\nExpected %q, received %q\n", - lx.input, items[i], item) - } - if item.typ == itemError { - break - } - } -} - -func TestPlainValue(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo") - expect(t, lx, expectedItems) -} - -func TestSimpleKeyStringValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "bar", 1}, - {itemEOF, "", 1}, - } - // Double quotes - lx := lex("foo = \"bar\"") - expect(t, lx, expectedItems) - // Single quotes - lx = lex("foo = 'bar'") - expect(t, lx, expectedItems) - // No spaces - lx = lex("foo='bar'") - expect(t, lx, expectedItems) - // NL - lx = lex("foo='bar'\r\n") - expect(t, lx, expectedItems) - lx = lex("foo=\t'bar'\t") - expect(t, lx, expectedItems) -} - -func TestComplexStringValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "bar\\r\\n \\t", 1}, - {itemEOF, "", 2}, - } - - lx := lex("foo = 'bar\\r\\n \\t'") - expect(t, lx, expectedItems) -} - -func TestBinaryString(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "e", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = \\x65") - expect(t, lx, expectedItems) -} - -func TestBinaryStringLatin1(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "\xe9", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = \\xe9") - expect(t, lx, expectedItems) -} - -func TestSimpleKeyIntegerValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemInteger, "123", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = 123") - expect(t, lx, expectedItems) - lx = lex("foo=123") - expect(t, lx, expectedItems) - lx = lex("foo=123\r\n") - expect(t, lx, expectedItems) -} - -func TestSimpleKeyNegativeIntegerValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemInteger, "-123", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = -123") - expect(t, lx, expectedItems) - lx = lex("foo=-123") - expect(t, lx, expectedItems) - lx = lex("foo=-123\r\n") - expect(t, lx, expectedItems) -} - -func TestConvenientIntegerValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemInteger, "1k", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = 1k") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "1K", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = 1K") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "1m", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = 1m") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "1M", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = 1M") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "1g", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = 1g") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "1G", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = 1G") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "1MB", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = 1MB") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "1Gb", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = 1Gb") - expect(t, lx, expectedItems) - - // Negative versions - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "-1m", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = -1m") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "foo", 1}, - {itemInteger, "-1GB", 1}, - {itemEOF, "", 1}, - } - lx = lex("foo = -1GB ") - expect(t, lx, expectedItems) -} - -func TestSimpleKeyFloatValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemFloat, "22.2", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = 22.2") - expect(t, lx, expectedItems) - lx = lex("foo=22.2") - expect(t, lx, expectedItems) - lx = lex("foo=22.2\r\n") - expect(t, lx, expectedItems) -} - -func TestBadBinaryStringEndingAfterZeroHexChars(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemError, "Expected two hexadecimal digits after '\\x', but hit end of line", 2}, - {itemEOF, "", 1}, - } - lx := lex("foo = xyz\\x\n") - expect(t, lx, expectedItems) -} - -func TestBadBinaryStringEndingAfterOneHexChar(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemError, "Expected two hexadecimal digits after '\\x', but hit end of line", 2}, - {itemEOF, "", 1}, - } - lx := lex("foo = xyz\\xF\n") - expect(t, lx, expectedItems) -} - -func TestBadBinaryStringWithZeroHexChars(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemError, "Expected two hexadecimal digits after '\\x', but got ']\"'", 1}, - {itemEOF, "", 1}, - } - lx := lex(`foo = "[\x]"`) - expect(t, lx, expectedItems) -} - -func TestBadBinaryStringWithOneHexChar(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemError, "Expected two hexadecimal digits after '\\x', but got 'e]'", 1}, - {itemEOF, "", 1}, - } - lx := lex(`foo = "[\xe]"`) - expect(t, lx, expectedItems) -} - -func TestBadFloatValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemError, "Floats must start with a digit", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = .2") - expect(t, lx, expectedItems) -} - -func TestBadKey(t *testing.T) { - expectedItems := []item{ - {itemError, "Unexpected key separator ':'", 1}, - {itemEOF, "", 1}, - } - lx := lex(" :foo = 22") - expect(t, lx, expectedItems) -} - -func TestSimpleKeyBoolValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemBool, "true", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = true") - expect(t, lx, expectedItems) - lx = lex("foo=true") - expect(t, lx, expectedItems) - lx = lex("foo=true\r\n") - expect(t, lx, expectedItems) -} - -func TestComments(t *testing.T) { - expectedItems := []item{ - {itemCommentStart, "", 1}, - {itemText, " This is a comment", 1}, - {itemEOF, "", 1}, - } - lx := lex("# This is a comment") - expect(t, lx, expectedItems) - lx = lex("# This is a comment\r\n") - expect(t, lx, expectedItems) - lx = lex("// This is a comment\r\n") - expect(t, lx, expectedItems) -} - -func TestTopValuesWithComments(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemInteger, "123", 1}, - {itemCommentStart, "", 1}, - {itemText, " This is a comment", 1}, - {itemEOF, "", 1}, - } - - lx := lex("foo = 123 // This is a comment") - expect(t, lx, expectedItems) - lx = lex("foo=123 # This is a comment") - expect(t, lx, expectedItems) -} - -func TestRawString(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "bar", 1}, - {itemEOF, "", 1}, - } - - lx := lex("foo = bar") - expect(t, lx, expectedItems) - - lx = lex(`foo = bar' `) //'single-quote for emacs TODO: Remove me - expect(t, lx, expectedItems) -} - -func TestDateValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemDatetime, "2016-05-04T18:53:41Z", 1}, - {itemEOF, "", 1}, - } - - lx := lex("foo = 2016-05-04T18:53:41Z") - expect(t, lx, expectedItems) -} - -func TestVariableValues(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemVariable, "bar", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = $bar") - expect(t, lx, expectedItems) - lx = lex("foo =$bar") - expect(t, lx, expectedItems) - lx = lex("foo $bar") - expect(t, lx, expectedItems) -} - -func TestArrays(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemArrayStart, "", 1}, - {itemInteger, "1", 1}, - {itemInteger, "2", 1}, - {itemInteger, "3", 1}, - {itemString, "bar", 1}, - {itemArrayEnd, "", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = [1, 2, 3, 'bar']") - expect(t, lx, expectedItems) - lx = lex("foo = [1,2,3,'bar']") - expect(t, lx, expectedItems) - lx = lex("foo = [1, 2,3,'bar']") - expect(t, lx, expectedItems) -} - -var mlArray = ` -# top level comment -foo = [ - 1, # One - 2, // Two - 3 # Three - 'bar' , - "bar" -] -` - -func TestMultilineArrays(t *testing.T) { - expectedItems := []item{ - {itemCommentStart, "", 2}, - {itemText, " top level comment", 2}, - {itemKey, "foo", 3}, - {itemArrayStart, "", 3}, - {itemInteger, "1", 4}, - {itemCommentStart, "", 4}, - {itemText, " One", 4}, - {itemInteger, "2", 5}, - {itemCommentStart, "", 5}, - {itemText, " Two", 5}, - {itemInteger, "3", 6}, - {itemCommentStart, "", 6}, - {itemText, " Three", 6}, - {itemString, "bar", 7}, - {itemString, "bar", 8}, - {itemArrayEnd, "", 9}, - {itemEOF, "", 9}, - } - lx := lex(mlArray) - expect(t, lx, expectedItems) -} - -var mlArrayNoSep = ` -# top level comment -foo = [ - 1 // foo - 2 - 3 - 'bar' - "bar" -] -` - -func TestMultilineArraysNoSep(t *testing.T) { - expectedItems := []item{ - {itemCommentStart, "", 2}, - {itemText, " top level comment", 2}, - {itemKey, "foo", 3}, - {itemArrayStart, "", 3}, - {itemInteger, "1", 4}, - {itemCommentStart, "", 4}, - {itemText, " foo", 4}, - {itemInteger, "2", 5}, - {itemInteger, "3", 6}, - {itemString, "bar", 7}, - {itemString, "bar", 8}, - {itemArrayEnd, "", 9}, - {itemEOF, "", 9}, - } - lx := lex(mlArrayNoSep) - expect(t, lx, expectedItems) -} - -func TestSimpleMap(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemMapStart, "", 1}, - {itemKey, "ip", 1}, - {itemString, "127.0.0.1", 1}, - {itemKey, "port", 1}, - {itemInteger, "4242", 1}, - {itemMapEnd, "", 1}, - {itemEOF, "", 1}, - } - - lx := lex("foo = {ip='127.0.0.1', port = 4242}") - expect(t, lx, expectedItems) -} - -var mlMap = ` -foo = { - ip = '127.0.0.1' # the IP - port= 4242 // the port -} -` - -func TestMultilineMap(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 2}, - {itemMapStart, "", 2}, - {itemKey, "ip", 3}, - {itemString, "127.0.0.1", 3}, - {itemCommentStart, "", 3}, - {itemText, " the IP", 3}, - {itemKey, "port", 4}, - {itemInteger, "4242", 4}, - {itemCommentStart, "", 4}, - {itemText, " the port", 4}, - {itemMapEnd, "", 5}, - {itemEOF, "", 5}, - } - - lx := lex(mlMap) - expect(t, lx, expectedItems) -} - -var nestedMap = ` -foo = { - host = { - ip = '127.0.0.1' - port= 4242 - } -} -` - -func TestNestedMaps(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 2}, - {itemMapStart, "", 2}, - {itemKey, "host", 3}, - {itemMapStart, "", 3}, - {itemKey, "ip", 4}, - {itemString, "127.0.0.1", 4}, - {itemKey, "port", 5}, - {itemInteger, "4242", 5}, - {itemMapEnd, "", 6}, - {itemMapEnd, "", 7}, - {itemEOF, "", 5}, - } - - lx := lex(nestedMap) - expect(t, lx, expectedItems) -} - -func TestQuotedKeys(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemInteger, "123", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo : 123") - expect(t, lx, expectedItems) - lx = lex("'foo' : 123") - expect(t, lx, expectedItems) - lx = lex("\"foo\" : 123") - expect(t, lx, expectedItems) -} - -func TestQuotedKeysWithSpace(t *testing.T) { - expectedItems := []item{ - {itemKey, " foo", 1}, - {itemInteger, "123", 1}, - {itemEOF, "", 1}, - } - lx := lex("' foo' : 123") - expect(t, lx, expectedItems) - lx = lex("\" foo\" : 123") - expect(t, lx, expectedItems) -} - -func TestColonKeySep(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemInteger, "123", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo : 123") - expect(t, lx, expectedItems) - lx = lex("foo:123") - expect(t, lx, expectedItems) - lx = lex("foo: 123") - expect(t, lx, expectedItems) - lx = lex("foo: 123\r\n") - expect(t, lx, expectedItems) -} - -func TestWhitespaceKeySep(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemInteger, "123", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo 123") - expect(t, lx, expectedItems) - lx = lex("foo 123") - expect(t, lx, expectedItems) - lx = lex("foo\t123") - expect(t, lx, expectedItems) - lx = lex("foo\t\t123\r\n") - expect(t, lx, expectedItems) -} - -var escString = ` -foo = \t -bar = \r -baz = \n -q = \" -bs = \\ -` - -func TestEscapedString(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 2}, - {itemString, "\t", 2}, - {itemKey, "bar", 3}, - {itemString, "\r", 3}, - {itemKey, "baz", 4}, - {itemString, "\n", 4}, - {itemKey, "q", 5}, - {itemString, "\"", 5}, - {itemKey, "bs", 6}, - {itemString, "\\", 6}, - {itemEOF, "", 6}, - } - lx := lex(escString) - expect(t, lx, expectedItems) -} - -func TestCompoundStringES(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "\\end", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = "\\end"`) - expect(t, lx, expectedItems) -} - -func TestCompoundStringSE(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "start\\", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = "start\\"`) - expect(t, lx, expectedItems) -} - -func TestCompoundStringEE(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "Eq", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = \x45\x71`) - expect(t, lx, expectedItems) -} - -func TestCompoundStringSEE(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "startEq", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = start\x45\x71`) - expect(t, lx, expectedItems) -} - -func TestCompoundStringSES(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "start|end", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = start\x7Cend`) - expect(t, lx, expectedItems) -} - -func TestCompoundStringEES(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "<>end", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = \x3c\x3eend`) - expect(t, lx, expectedItems) -} - -func TestCompoundStringESE(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = \x3cmiddle\x3E`) - expect(t, lx, expectedItems) -} - -func TestBadStringEscape(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemError, "Invalid escape character 'y'. Only the following escape characters are allowed: \\xXX, \\t, \\n, \\r, \\\", \\\\.", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = \y`) - expect(t, lx, expectedItems) -} - -func TestNonBool(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "\\true", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = \\true`) - expect(t, lx, expectedItems) -} - -func TestNonVariable(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "\\$var", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = \\$var`) - expect(t, lx, expectedItems) -} - -func TestEmptyStringDQ(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = ""`) - expect(t, lx, expectedItems) -} - -func TestEmptyStringSQ(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "", 1}, - {itemEOF, "", 2}, - } - lx := lex(`foo = ''`) - expect(t, lx, expectedItems) -} - -var nestedWhitespaceMap = ` -foo { - host { - ip = '127.0.0.1' - port= 4242 - } -} -` - -func TestNestedWhitespaceMaps(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 2}, - {itemMapStart, "", 2}, - {itemKey, "host", 3}, - {itemMapStart, "", 3}, - {itemKey, "ip", 4}, - {itemString, "127.0.0.1", 4}, - {itemKey, "port", 5}, - {itemInteger, "4242", 5}, - {itemMapEnd, "", 6}, - {itemMapEnd, "", 7}, - {itemEOF, "", 5}, - } - - lx := lex(nestedWhitespaceMap) - expect(t, lx, expectedItems) -} - -var semicolons = ` -foo = 123; -bar = 'baz'; -baz = 'boo' -map { - id = 1; -} -` - -func TestOptionalSemicolons(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 2}, - {itemInteger, "123", 2}, - {itemKey, "bar", 3}, - {itemString, "baz", 3}, - {itemKey, "baz", 4}, - {itemString, "boo", 4}, - {itemKey, "map", 5}, - {itemMapStart, "", 5}, - {itemKey, "id", 6}, - {itemInteger, "1", 6}, - {itemMapEnd, "", 7}, - {itemEOF, "", 5}, - } - - lx := lex(semicolons) - expect(t, lx, expectedItems) -} - -func TestSemicolonChaining(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemString, "1", 1}, - {itemKey, "bar", 1}, - {itemFloat, "2.2", 1}, - {itemKey, "baz", 1}, - {itemBool, "true", 1}, - {itemEOF, "", 1}, - } - - lx := lex("foo='1'; bar=2.2; baz=true;") - expect(t, lx, expectedItems) -} - -var noquotes = ` -foo = 123 -bar = baz -baz=boo -map { - id:one - id2 : onetwo -} -t true -f false -tstr "true" -tkey = two -fkey = five # This should be a string -` - -func TestNonQuotedStrings(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 2}, - {itemInteger, "123", 2}, - {itemKey, "bar", 3}, - {itemString, "baz", 3}, - {itemKey, "baz", 4}, - {itemString, "boo", 4}, - {itemKey, "map", 5}, - {itemMapStart, "", 5}, - {itemKey, "id", 6}, - {itemString, "one", 6}, - {itemKey, "id2", 7}, - {itemString, "onetwo", 7}, - {itemMapEnd, "", 8}, - {itemKey, "t", 9}, - {itemBool, "true", 9}, - {itemKey, "f", 10}, - {itemBool, "false", 10}, - {itemKey, "tstr", 11}, - {itemString, "true", 11}, - {itemKey, "tkey", 12}, - {itemString, "two", 12}, - {itemKey, "fkey", 13}, - {itemString, "five", 13}, - {itemCommentStart, "", 13}, - {itemText, " This should be a string", 13}, - - {itemEOF, "", 14}, - } - lx := lex(noquotes) - expect(t, lx, expectedItems) -} - -func TestMapQuotedKeys(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemMapStart, "", 1}, - {itemKey, "bar", 1}, - {itemInteger, "4242", 1}, - {itemMapEnd, "", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = {'bar' = 4242}") - expect(t, lx, expectedItems) - lx = lex("foo = {\"bar\" = 4242}") - expect(t, lx, expectedItems) -} - -func TestSpecialCharsMapQuotedKeys(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemMapStart, "", 1}, - {itemKey, "bar-1.2.3", 1}, - {itemMapStart, "", 1}, - {itemKey, "port", 1}, - {itemInteger, "4242", 1}, - {itemMapEnd, "", 1}, - {itemMapEnd, "", 1}, - {itemEOF, "", 1}, - } - lx := lex("foo = {'bar-1.2.3' = { port:4242 }}") - expect(t, lx, expectedItems) - lx = lex("foo = {\"bar-1.2.3\" = { port:4242 }}") - expect(t, lx, expectedItems) -} - -var mlnestedmap = ` -systems { - allinone { - description: "This is a description." - } -} -` - -func TestDoubleNestedMapsNewLines(t *testing.T) { - expectedItems := []item{ - {itemKey, "systems", 2}, - {itemMapStart, "", 2}, - {itemKey, "allinone", 3}, - {itemMapStart, "", 3}, - {itemKey, "description", 4}, - {itemString, "This is a description.", 4}, - {itemMapEnd, "", 5}, - {itemMapEnd, "", 6}, - {itemEOF, "", 7}, - } - lx := lex(mlnestedmap) - expect(t, lx, expectedItems) -} - -var blockexample = ` -numbers ( -1234567890 -) -` - -func TestBlockString(t *testing.T) { - expectedItems := []item{ - {itemKey, "numbers", 2}, - {itemString, "\n1234567890\n", 4}, - } - lx := lex(blockexample) - expect(t, lx, expectedItems) -} - -func TestBlockStringEOF(t *testing.T) { - expectedItems := []item{ - {itemKey, "numbers", 2}, - {itemString, "\n1234567890\n", 4}, - } - blockbytes := []byte(blockexample[0 : len(blockexample)-1]) - blockbytes = append(blockbytes, 0) - lx := lex(string(blockbytes)) - expect(t, lx, expectedItems) -} - -var mlblockexample = ` -numbers ( - 12(34)56 - ( - 7890 - ) -) -` - -func TestBlockStringMultiLine(t *testing.T) { - expectedItems := []item{ - {itemKey, "numbers", 2}, - {itemString, "\n 12(34)56\n (\n 7890\n )\n", 7}, - } - lx := lex(mlblockexample) - expect(t, lx, expectedItems) -} - -func TestUnquotedIPAddr(t *testing.T) { - expectedItems := []item{ - {itemKey, "listen", 1}, - {itemString, "127.0.0.1:4222", 1}, - {itemEOF, "", 1}, - } - lx := lex("listen: 127.0.0.1:4222") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "listen", 1}, - {itemString, "127.0.0.1", 1}, - {itemEOF, "", 1}, - } - lx = lex("listen: 127.0.0.1") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "listen", 1}, - {itemString, "apcera.me:80", 1}, - {itemEOF, "", 1}, - } - lx = lex("listen: apcera.me:80") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "listen", 1}, - {itemString, "nats.io:-1", 1}, - {itemEOF, "", 1}, - } - lx = lex("listen: nats.io:-1") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "listen", 1}, - {itemInteger, "-1", 1}, - {itemEOF, "", 1}, - } - lx = lex("listen: -1") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "listen", 1}, - {itemString, ":-1", 1}, - {itemEOF, "", 1}, - } - lx = lex("listen: :-1") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "listen", 1}, - {itemString, ":80", 1}, - {itemEOF, "", 1}, - } - lx = lex("listen = :80") - expect(t, lx, expectedItems) - - expectedItems = []item{ - {itemKey, "listen", 1}, - {itemArrayStart, "", 1}, - {itemString, "localhost:4222", 1}, - {itemString, "localhost:4333", 1}, - {itemArrayEnd, "", 1}, - {itemEOF, "", 1}, - } - lx = lex("listen = [localhost:4222, localhost:4333]") - expect(t, lx, expectedItems) -} - -var arrayOfMaps = ` -authorization { - users = [ - {user: alice, password: foo} - {user: bob, password: bar} - ] - timeout: 0.5 -} -` - -func TestArrayOfMaps(t *testing.T) { - expectedItems := []item{ - {itemKey, "authorization", 2}, - {itemMapStart, "", 2}, - {itemKey, "users", 3}, - {itemArrayStart, "", 3}, - {itemMapStart, "", 4}, - {itemKey, "user", 4}, - {itemString, "alice", 4}, - {itemKey, "password", 4}, - {itemString, "foo", 4}, - {itemMapEnd, "", 4}, - {itemMapStart, "", 5}, - {itemKey, "user", 5}, - {itemString, "bob", 5}, - {itemKey, "password", 5}, - {itemString, "bar", 5}, - {itemMapEnd, "", 5}, - {itemArrayEnd, "", 6}, - {itemKey, "timeout", 7}, - {itemFloat, "0.5", 7}, - {itemMapEnd, "", 8}, - {itemEOF, "", 9}, - } - lx := lex(arrayOfMaps) - expect(t, lx, expectedItems) -} - -func TestInclude(t *testing.T) { - expectedItems := []item{ - {itemInclude, "users.conf", 1}, - {itemEOF, "", 1}, - } - lx := lex("include \"users.conf\"") - expect(t, lx, expectedItems) - - lx = lex("include 'users.conf'") - expect(t, lx, expectedItems) - - lx = lex("include users.conf") - expect(t, lx, expectedItems) -} - -func TestMapInclude(t *testing.T) { - expectedItems := []item{ - {itemKey, "foo", 1}, - {itemMapStart, "", 1}, - {itemInclude, "users.conf", 1}, - {itemMapEnd, "", 1}, - {itemEOF, "", 1}, - } - - lx := lex("foo { include users.conf }") - expect(t, lx, expectedItems) - - lx = lex("foo {include users.conf}") - expect(t, lx, expectedItems) - - lx = lex("foo { include 'users.conf' }") - expect(t, lx, expectedItems) - - lx = lex("foo { include \"users.conf\"}") - expect(t, lx, expectedItems) -} - -func TestJSONCompat(t *testing.T) { - for _, test := range []struct { - name string - input string - expected []item - }{ - { - name: "should omit initial and final brackets at top level with a single item", - input: ` - { - "http_port": 8223 - } - `, - expected: []item{ - {itemKey, "http_port", 3}, - {itemInteger, "8223", 3}, - }, - }, - { - name: "should omit trailing commas at top level with two items", - input: ` - { - "http_port": 8223, - "port": 4223 - } - `, - expected: []item{ - {itemKey, "http_port", 3}, - {itemInteger, "8223", 3}, - {itemKey, "port", 4}, - {itemInteger, "4223", 4}, - }, - }, - { - name: "should omit trailing commas at top level with multiple items", - input: ` - { - "http_port": 8223, - "port": 4223, - "max_payload": "5MB", - "debug": true, - "max_control_line": 1024 - } - `, - expected: []item{ - {itemKey, "http_port", 3}, - {itemInteger, "8223", 3}, - {itemKey, "port", 4}, - {itemInteger, "4223", 4}, - {itemKey, "max_payload", 5}, - {itemString, "5MB", 5}, - {itemKey, "debug", 6}, - {itemBool, "true", 6}, - {itemKey, "max_control_line", 7}, - {itemInteger, "1024", 7}, - }, - }, - { - name: "should support JSON not prettified", - input: `{"http_port": 8224,"port": 4224} - `, - expected: []item{ - {itemKey, "http_port", 1}, - {itemInteger, "8224", 1}, - {itemKey, "port", 1}, - {itemInteger, "4224", 1}, - }, - }, - { - name: "should support JSON not prettified with final bracket after newline", - input: `{"http_port": 8225,"port": 4225 - } - `, - expected: []item{ - {itemKey, "http_port", 1}, - {itemInteger, "8225", 1}, - {itemKey, "port", 1}, - {itemInteger, "4225", 1}, - }, - }, - { - name: "should support uglified JSON with inner blocks", - input: `{"http_port": 8227,"port": 4227,"write_deadline": "1h","cluster": {"port": 6222,"routes": ["nats://127.0.0.1:4222","nats://127.0.0.1:4223","nats://127.0.0.1:4224"]}} - `, - expected: []item{ - {itemKey, "http_port", 1}, - {itemInteger, "8227", 1}, - {itemKey, "port", 1}, - {itemInteger, "4227", 1}, - {itemKey, "write_deadline", 1}, - {itemString, "1h", 1}, - {itemKey, "cluster", 1}, - {itemMapStart, "", 1}, - {itemKey, "port", 1}, - {itemInteger, "6222", 1}, - {itemKey, "routes", 1}, - {itemArrayStart, "", 1}, - {itemString, "nats://127.0.0.1:4222", 1}, - {itemString, "nats://127.0.0.1:4223", 1}, - {itemString, "nats://127.0.0.1:4224", 1}, - {itemArrayEnd, "", 1}, - {itemMapEnd, "", 1}, - }, - }, - { - name: "should support prettified JSON with inner blocks", - input: ` - { - "http_port": 8227, - "port": 4227, - "write_deadline": "1h", - "cluster": { - "port": 6222, - "routes": [ - "nats://127.0.0.1:4222", - "nats://127.0.0.1:4223", - "nats://127.0.0.1:4224" - ] - } - } - `, - expected: []item{ - {itemKey, "http_port", 3}, - {itemInteger, "8227", 3}, - {itemKey, "port", 4}, - {itemInteger, "4227", 4}, - {itemKey, "write_deadline", 5}, - {itemString, "1h", 5}, - {itemKey, "cluster", 6}, - {itemMapStart, "", 6}, - {itemKey, "port", 7}, - {itemInteger, "6222", 7}, - {itemKey, "routes", 8}, - {itemArrayStart, "", 8}, - {itemString, "nats://127.0.0.1:4222", 9}, - {itemString, "nats://127.0.0.1:4223", 10}, - {itemString, "nats://127.0.0.1:4224", 11}, - {itemArrayEnd, "", 12}, - {itemMapEnd, "", 13}, - }, - }, - } { - t.Run(test.name, func(t *testing.T) { - lx := lex(test.input) - expect(t, lx, test.expected) - }) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/conf/parse.go b/vendor/github.com/nats-io/gnatsd/conf/parse.go deleted file mode 100644 index 09205ae0..00000000 --- a/vendor/github.com/nats-io/gnatsd/conf/parse.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package conf supports a configuration file format used by gnatsd. It is -// a flexible format that combines the best of traditional -// configuration formats and newer styles such as JSON and YAML. -package conf - -// The format supported is less restrictive than today's formats. -// Supports mixed Arrays [], nested Maps {}, multiple comment types (# and //) -// Also supports key value assigments using '=' or ':' or whiteSpace() -// e.g. foo = 2, foo : 2, foo 2 -// maps can be assigned with no key separator as well -// semicolons as value terminators in key/value assignments are optional -// -// see parse_test.go for more examples. - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "time" - "unicode" -) - -type parser struct { - mapping map[string]interface{} - lx *lexer - - // The current scoped context, can be array or map - ctx interface{} - - // stack of contexts, either map or array/slice stack - ctxs []interface{} - - // Keys stack - keys []string - - // The config file path, empty by default. - fp string -} - -// Parse will return a map of keys to interface{}, although concrete types -// underly them. The values supported are string, bool, int64, float64, DateTime. -// Arrays and nested Maps are also supported. -func Parse(data string) (map[string]interface{}, error) { - p, err := parse(data, "") - if err != nil { - return nil, err - } - return p.mapping, nil -} - -// ParseFile is a helper to open file, etc. and parse the contents. -func ParseFile(fp string) (map[string]interface{}, error) { - data, err := ioutil.ReadFile(fp) - if err != nil { - return nil, fmt.Errorf("error opening config file: %v", err) - } - - p, err := parse(string(data), filepath.Dir(fp)) - if err != nil { - return nil, err - } - return p.mapping, nil -} - -func parse(data, fp string) (p *parser, err error) { - p = &parser{ - mapping: make(map[string]interface{}), - lx: lex(data), - ctxs: make([]interface{}, 0, 4), - keys: make([]string, 0, 4), - fp: fp, - } - p.pushContext(p.mapping) - - for { - it := p.next() - if it.typ == itemEOF { - break - } - if err := p.processItem(it); err != nil { - return nil, err - } - } - - return p, nil -} - -func (p *parser) next() item { - return p.lx.nextItem() -} - -func (p *parser) pushContext(ctx interface{}) { - p.ctxs = append(p.ctxs, ctx) - p.ctx = ctx -} - -func (p *parser) popContext() interface{} { - if len(p.ctxs) == 0 { - panic("BUG in parser, context stack empty") - } - li := len(p.ctxs) - 1 - last := p.ctxs[li] - p.ctxs = p.ctxs[0:li] - p.ctx = p.ctxs[len(p.ctxs)-1] - return last -} - -func (p *parser) pushKey(key string) { - p.keys = append(p.keys, key) -} - -func (p *parser) popKey() string { - if len(p.keys) == 0 { - panic("BUG in parser, keys stack empty") - } - li := len(p.keys) - 1 - last := p.keys[li] - p.keys = p.keys[0:li] - return last -} - -func (p *parser) processItem(it item) error { - switch it.typ { - case itemError: - return fmt.Errorf("Parse error on line %d: '%s'", it.line, it.val) - case itemKey: - p.pushKey(it.val) - case itemMapStart: - newCtx := make(map[string]interface{}) - p.pushContext(newCtx) - case itemMapEnd: - p.setValue(p.popContext()) - case itemString: - p.setValue(it.val) // FIXME(dlc) sanitize string? - case itemInteger: - lastDigit := 0 - for _, r := range it.val { - if !unicode.IsDigit(r) && r != '-' { - break - } - lastDigit++ - } - numStr := it.val[:lastDigit] - num, err := strconv.ParseInt(numStr, 10, 64) - if err != nil { - if e, ok := err.(*strconv.NumError); ok && - e.Err == strconv.ErrRange { - return fmt.Errorf("Integer '%s' is out of the range.", it.val) - } - return fmt.Errorf("Expected integer, but got '%s'.", it.val) - } - // Process a suffix - suffix := strings.ToLower(strings.TrimSpace(it.val[lastDigit:])) - switch suffix { - case "": - p.setValue(num) - case "k": - p.setValue(num * 1000) - case "kb": - p.setValue(num * 1024) - case "m": - p.setValue(num * 1000 * 1000) - case "mb": - p.setValue(num * 1024 * 1024) - case "g": - p.setValue(num * 1000 * 1000 * 1000) - case "gb": - p.setValue(num * 1024 * 1024 * 1024) - } - case itemFloat: - num, err := strconv.ParseFloat(it.val, 64) - if err != nil { - if e, ok := err.(*strconv.NumError); ok && - e.Err == strconv.ErrRange { - return fmt.Errorf("Float '%s' is out of the range.", it.val) - } - return fmt.Errorf("Expected float, but got '%s'.", it.val) - } - p.setValue(num) - case itemBool: - switch strings.ToLower(it.val) { - case "true", "yes", "on": - p.setValue(true) - case "false", "no", "off": - p.setValue(false) - default: - return fmt.Errorf("Expected boolean value, but got '%s'.", it.val) - } - case itemDatetime: - dt, err := time.Parse("2006-01-02T15:04:05Z", it.val) - if err != nil { - return fmt.Errorf( - "Expected Zulu formatted DateTime, but got '%s'.", it.val) - } - p.setValue(dt) - case itemArrayStart: - var array = make([]interface{}, 0) - p.pushContext(array) - case itemArrayEnd: - array := p.ctx - p.popContext() - p.setValue(array) - case itemVariable: - if value, ok := p.lookupVariable(it.val); ok { - p.setValue(value) - } else { - return fmt.Errorf("Variable reference for '%s' on line %d can not be found.", - it.val, it.line) - } - case itemInclude: - m, err := ParseFile(filepath.Join(p.fp, it.val)) - if err != nil { - return fmt.Errorf("Error parsing include file '%s', %v.", it.val, err) - } - for k, v := range m { - p.pushKey(k) - p.setValue(v) - } - } - - return nil -} - -// Used to map an environment value into a temporary map to pass to secondary Parse call. -const pkey = "pk" - -// We special case raw strings here that are bcrypt'd. This allows us not to force quoting the strings -const bcryptPrefix = "2a$" - -// lookupVariable will lookup a variable reference. It will use block scoping on keys -// it has seen before, with the top level scoping being the environment variables. We -// ignore array contexts and only process the map contexts.. -// -// Returns true for ok if it finds something, similar to map. -func (p *parser) lookupVariable(varReference string) (interface{}, bool) { - // Do special check to see if it is a raw bcrypt string. - if strings.HasPrefix(varReference, bcryptPrefix) { - return "$" + varReference, true - } - - // Loop through contexts currently on the stack. - for i := len(p.ctxs) - 1; i >= 0; i -= 1 { - ctx := p.ctxs[i] - // Process if it is a map context - if m, ok := ctx.(map[string]interface{}); ok { - if v, ok := m[varReference]; ok { - return v, ok - } - } - } - - // If we are here, we have exhausted our context maps and still not found anything. - // Parse from the environment. - if vStr, ok := os.LookupEnv(varReference); ok { - // Everything we get here will be a string value, so we need to process as a parser would. - if vmap, err := Parse(fmt.Sprintf("%s=%s", pkey, vStr)); err == nil { - v, ok := vmap[pkey] - return v, ok - } - } - return nil, false -} - -func (p *parser) setValue(val interface{}) { - // Test to see if we are on an array or a map - - // Array processing - if ctx, ok := p.ctx.([]interface{}); ok { - p.ctx = append(ctx, val) - p.ctxs[len(p.ctxs)-1] = p.ctx - } - - // Map processing - if ctx, ok := p.ctx.(map[string]interface{}); ok { - key := p.popKey() - // FIXME(dlc), make sure to error if redefining same key? - ctx[key] = val - } -} diff --git a/vendor/github.com/nats-io/gnatsd/conf/parse_test.go b/vendor/github.com/nats-io/gnatsd/conf/parse_test.go deleted file mode 100644 index 6992c113..00000000 --- a/vendor/github.com/nats-io/gnatsd/conf/parse_test.go +++ /dev/null @@ -1,275 +0,0 @@ -package conf - -import ( - "fmt" - "os" - "reflect" - "strings" - "testing" - "time" -) - -// Test to make sure we get what we expect. - -func test(t *testing.T, data string, ex map[string]interface{}) { - m, err := Parse(data) - if err != nil { - t.Fatalf("Received err: %v\n", err) - } - if m == nil { - t.Fatal("Received nil map") - } - - if !reflect.DeepEqual(m, ex) { - t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex) - } -} - -func TestSimpleTopLevel(t *testing.T) { - ex := map[string]interface{}{ - "foo": "1", - "bar": float64(2.2), - "baz": true, - "boo": int64(22), - } - test(t, "foo='1'; bar=2.2; baz=true; boo=22", ex) -} - -func TestBools(t *testing.T) { - ex := map[string]interface{}{ - "foo": true, - } - test(t, "foo=true", ex) - test(t, "foo=TRUE", ex) - test(t, "foo=true", ex) - test(t, "foo=yes", ex) - test(t, "foo=on", ex) -} - -var varSample = ` - index = 22 - foo = $index -` - -func TestSimpleVariable(t *testing.T) { - ex := map[string]interface{}{ - "index": int64(22), - "foo": int64(22), - } - test(t, varSample, ex) -} - -var varNestedSample = ` - index = 22 - nest { - index = 11 - foo = $index - } - bar = $index -` - -func TestNestedVariable(t *testing.T) { - ex := map[string]interface{}{ - "index": int64(22), - "nest": map[string]interface{}{ - "index": int64(11), - "foo": int64(11), - }, - "bar": int64(22), - } - test(t, varNestedSample, ex) -} - -func TestMissingVariable(t *testing.T) { - _, err := Parse("foo=$index") - if err == nil { - t.Fatalf("Expected an error for a missing variable, got none") - } - if !strings.HasPrefix(err.Error(), "Variable reference") { - t.Fatalf("Wanted a variable reference err, got %q\n", err) - } -} - -func TestEnvVariable(t *testing.T) { - ex := map[string]interface{}{ - "foo": int64(22), - } - evar := "__UNIQ22__" - os.Setenv(evar, "22") - defer os.Unsetenv(evar) - test(t, fmt.Sprintf("foo = $%s", evar), ex) -} - -func TestBcryptVariable(t *testing.T) { - ex := map[string]interface{}{ - "password": "$2a$11$ooo", - } - test(t, "password: $2a$11$ooo", ex) -} - -var easynum = ` -k = 8k -kb = 4kb -m = 1m -mb = 2MB -g = 2g -gb = 22GB -` - -func TestConvenientNumbers(t *testing.T) { - ex := map[string]interface{}{ - "k": int64(8 * 1000), - "kb": int64(4 * 1024), - "m": int64(1000 * 1000), - "mb": int64(2 * 1024 * 1024), - "g": int64(2 * 1000 * 1000 * 1000), - "gb": int64(22 * 1024 * 1024 * 1024), - } - test(t, easynum, ex) -} - -var sample1 = ` -foo { - host { - ip = '127.0.0.1' - port = 4242 - } - servers = [ "a.com", "b.com", "c.com"] -} -` - -func TestSample1(t *testing.T) { - ex := map[string]interface{}{ - "foo": map[string]interface{}{ - "host": map[string]interface{}{ - "ip": "127.0.0.1", - "port": int64(4242), - }, - "servers": []interface{}{"a.com", "b.com", "c.com"}, - }, - } - test(t, sample1, ex) -} - -var cluster = ` -cluster { - port: 4244 - - authorization { - user: route_user - password: top_secret - timeout: 1 - } - - # Routes are actively solicited and connected to from this server. - # Other servers can connect to us if they supply the correct credentials - # in their routes definitions from above. - - // Test both styles of comments - - routes = [ - nats-route://foo:bar@apcera.me:4245 - nats-route://foo:bar@apcera.me:4246 - ] -} -` - -func TestSample2(t *testing.T) { - ex := map[string]interface{}{ - "cluster": map[string]interface{}{ - "port": int64(4244), - "authorization": map[string]interface{}{ - "user": "route_user", - "password": "top_secret", - "timeout": int64(1), - }, - "routes": []interface{}{ - "nats-route://foo:bar@apcera.me:4245", - "nats-route://foo:bar@apcera.me:4246", - }, - }, - } - - test(t, cluster, ex) -} - -var sample3 = ` -foo { - expr = '(true == "false")' - text = 'This is a multi-line -text block.' -} -` - -func TestSample3(t *testing.T) { - ex := map[string]interface{}{ - "foo": map[string]interface{}{ - "expr": "(true == \"false\")", - "text": "This is a multi-line\ntext block.", - }, - } - test(t, sample3, ex) -} - -var sample4 = ` - array [ - { abc: 123 } - { xyz: "word" } - ] -` - -func TestSample4(t *testing.T) { - ex := map[string]interface{}{ - "array": []interface{}{ - map[string]interface{}{"abc": int64(123)}, - map[string]interface{}{"xyz": "word"}, - }, - } - test(t, sample4, ex) -} - -var sample5 = ` - now = 2016-05-04T18:53:41Z - gmt = false - -` - -func TestSample5(t *testing.T) { - dt, _ := time.Parse("2006-01-02T15:04:05Z", "2016-05-04T18:53:41Z") - ex := map[string]interface{}{ - "now": dt, - "gmt": false, - } - test(t, sample5, ex) -} - -func TestIncludes(t *testing.T) { - ex := map[string]interface{}{ - "listen": "127.0.0.1:4222", - "authorization": map[string]interface{}{ - "ALICE_PASS": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q", - "BOB_PASS": "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly", - "users": []interface{}{ - map[string]interface{}{ - "user": "alice", - "password": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q"}, - map[string]interface{}{ - "user": "bob", - "password": "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly"}, - }, - "timeout": float64(0.5), - }, - } - - m, err := ParseFile("simple.conf") - if err != nil { - t.Fatalf("Received err: %v\n", err) - } - if m == nil { - t.Fatal("Received nil map") - } - - if !reflect.DeepEqual(m, ex) { - t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/conf/simple.conf b/vendor/github.com/nats-io/gnatsd/conf/simple.conf deleted file mode 100644 index 8f75d73a..00000000 --- a/vendor/github.com/nats-io/gnatsd/conf/simple.conf +++ /dev/null @@ -1,6 +0,0 @@ -listen: 127.0.0.1:4222 - -authorization { - include 'includes/users.conf' # Pull in from file - timeout: 0.5 -} diff --git a/vendor/github.com/nats-io/gnatsd/logger/log.go b/vendor/github.com/nats-io/gnatsd/logger/log.go deleted file mode 100644 index 132cb42a..00000000 --- a/vendor/github.com/nats-io/gnatsd/logger/log.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//Package logger provides logging facilities for the NATS server -package logger - -import ( - "fmt" - "log" - "os" -) - -// Logger is the server logger -type Logger struct { - logger *log.Logger - debug bool - trace bool - infoLabel string - errorLabel string - fatalLabel string - debugLabel string - traceLabel string - logFile *os.File // file pointer for the file logger. -} - -// NewStdLogger creates a logger with output directed to Stderr -func NewStdLogger(time, debug, trace, colors, pid bool) *Logger { - flags := 0 - if time { - flags = log.LstdFlags | log.Lmicroseconds - } - - pre := "" - if pid { - pre = pidPrefix() - } - - l := &Logger{ - logger: log.New(os.Stderr, pre, flags), - debug: debug, - trace: trace, - } - - if colors { - setColoredLabelFormats(l) - } else { - setPlainLabelFormats(l) - } - - return l -} - -// NewFileLogger creates a logger with output directed to a file -func NewFileLogger(filename string, time, debug, trace, pid bool) *Logger { - fileflags := os.O_WRONLY | os.O_APPEND | os.O_CREATE - f, err := os.OpenFile(filename, fileflags, 0660) - if err != nil { - log.Fatalf("error opening file: %v", err) - } - - flags := 0 - if time { - flags = log.LstdFlags | log.Lmicroseconds - } - - pre := "" - if pid { - pre = pidPrefix() - } - - l := &Logger{ - logger: log.New(f, pre, flags), - debug: debug, - trace: trace, - logFile: f, - } - - setPlainLabelFormats(l) - return l -} - -// Close implements the io.Closer interface to clean up -// resources in the server's logger implementation. -// Caller must ensure threadsafety. -func (l *Logger) Close() error { - if f := l.logFile; f != nil { - l.logFile = nil - return f.Close() - } - return nil -} - -// Generate the pid prefix string -func pidPrefix() string { - return fmt.Sprintf("[%d] ", os.Getpid()) -} - -func setPlainLabelFormats(l *Logger) { - l.infoLabel = "[INF] " - l.debugLabel = "[DBG] " - l.errorLabel = "[ERR] " - l.fatalLabel = "[FTL] " - l.traceLabel = "[TRC] " -} - -func setColoredLabelFormats(l *Logger) { - colorFormat := "[\x1b[%dm%s\x1b[0m] " - l.infoLabel = fmt.Sprintf(colorFormat, 32, "INF") - l.debugLabel = fmt.Sprintf(colorFormat, 36, "DBG") - l.errorLabel = fmt.Sprintf(colorFormat, 31, "ERR") - l.fatalLabel = fmt.Sprintf(colorFormat, 31, "FTL") - l.traceLabel = fmt.Sprintf(colorFormat, 33, "TRC") -} - -// Noticef logs a notice statement -func (l *Logger) Noticef(format string, v ...interface{}) { - l.logger.Printf(l.infoLabel+format, v...) -} - -// Errorf logs an error statement -func (l *Logger) Errorf(format string, v ...interface{}) { - l.logger.Printf(l.errorLabel+format, v...) -} - -// Fatalf logs a fatal error -func (l *Logger) Fatalf(format string, v ...interface{}) { - l.logger.Fatalf(l.fatalLabel+format, v...) -} - -// Debugf logs a debug statement -func (l *Logger) Debugf(format string, v ...interface{}) { - if l.debug { - l.logger.Printf(l.debugLabel+format, v...) - } -} - -// Tracef logs a trace statement -func (l *Logger) Tracef(format string, v ...interface{}) { - if l.trace { - l.logger.Printf(l.traceLabel+format, v...) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/logger/log_test.go b/vendor/github.com/nats-io/gnatsd/logger/log_test.go deleted file mode 100644 index d53728e7..00000000 --- a/vendor/github.com/nats-io/gnatsd/logger/log_test.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package logger - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "strings" - "testing" -) - -func TestStdLogger(t *testing.T) { - logger := NewStdLogger(false, false, false, false, false) - - flags := logger.logger.Flags() - if flags != 0 { - t.Fatalf("Expected %q, received %q\n", 0, flags) - } - - if logger.debug { - t.Fatalf("Expected %t, received %t\n", false, logger.debug) - } - - if logger.trace { - t.Fatalf("Expected %t, received %t\n", false, logger.trace) - } -} - -func TestStdLoggerWithDebugTraceAndTime(t *testing.T) { - logger := NewStdLogger(true, true, true, false, false) - - flags := logger.logger.Flags() - if flags != log.LstdFlags|log.Lmicroseconds { - t.Fatalf("Expected %d, received %d\n", log.LstdFlags, flags) - } - - if !logger.debug { - t.Fatalf("Expected %t, received %t\n", true, logger.debug) - } - - if !logger.trace { - t.Fatalf("Expected %t, received %t\n", true, logger.trace) - } -} - -func TestStdLoggerNotice(t *testing.T) { - expectOutput(t, func() { - logger := NewStdLogger(false, false, false, false, false) - logger.Noticef("foo") - }, "[INF] foo\n") -} - -func TestStdLoggerNoticeWithColor(t *testing.T) { - expectOutput(t, func() { - logger := NewStdLogger(false, false, false, true, false) - logger.Noticef("foo") - }, "[\x1b[32mINF\x1b[0m] foo\n") -} - -func TestStdLoggerDebug(t *testing.T) { - expectOutput(t, func() { - logger := NewStdLogger(false, true, false, false, false) - logger.Debugf("foo %s", "bar") - }, "[DBG] foo bar\n") -} - -func TestStdLoggerDebugWithOutDebug(t *testing.T) { - expectOutput(t, func() { - logger := NewStdLogger(false, false, false, false, false) - logger.Debugf("foo") - }, "") -} - -func TestStdLoggerTrace(t *testing.T) { - expectOutput(t, func() { - logger := NewStdLogger(false, false, true, false, false) - logger.Tracef("foo") - }, "[TRC] foo\n") -} - -func TestStdLoggerTraceWithOutDebug(t *testing.T) { - expectOutput(t, func() { - logger := NewStdLogger(false, false, false, false, false) - logger.Tracef("foo") - }, "") -} - -func TestFileLogger(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "_gnatsd") - if err != nil { - t.Fatal("Could not create tmp dir") - } - defer os.RemoveAll(tmpDir) - - file, err := ioutil.TempFile(tmpDir, "gnatsd:log_") - if err != nil { - t.Fatalf("Could not create the temp file: %v", err) - } - file.Close() - - logger := NewFileLogger(file.Name(), false, false, false, false) - logger.Noticef("foo") - - buf, err := ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatalf("Could not read logfile: %v", err) - } - if len(buf) <= 0 { - t.Fatal("Expected a non-zero length logfile") - } - - if string(buf) != "[INF] foo\n" { - t.Fatalf("Expected '%s', received '%s'\n", "[INFO] foo", string(buf)) - } - - file, err = ioutil.TempFile(tmpDir, "gnatsd:log_") - if err != nil { - t.Fatalf("Could not create the temp file: %v", err) - } - file.Close() - - logger = NewFileLogger(file.Name(), true, true, true, true) - logger.Errorf("foo") - - buf, err = ioutil.ReadFile(file.Name()) - if err != nil { - t.Fatalf("Could not read logfile: %v", err) - } - if len(buf) <= 0 { - t.Fatal("Expected a non-zero length logfile") - } - str := string(buf) - errMsg := fmt.Sprintf("Expected '%s', received '%s'\n", "[pid] [ERR] foo", str) - pidEnd := strings.Index(str, " ") - infoStart := strings.LastIndex(str, "[ERR]") - if pidEnd == -1 || infoStart == -1 { - t.Fatalf("%v", errMsg) - } - pid := str[0:pidEnd] - if pid[0] != '[' || pid[len(pid)-1] != ']' { - t.Fatalf("%v", errMsg) - } - //TODO: Parse date. - if !strings.HasSuffix(str, "[ERR] foo\n") { - t.Fatalf("%v", errMsg) - } -} - -func expectOutput(t *testing.T, f func(), expected string) { - old := os.Stderr // keep backup of the real stdout - r, w, _ := os.Pipe() - os.Stderr = w - - f() - - outC := make(chan string) - // copy the output in a separate goroutine so printing can't block indefinitely - go func() { - var buf bytes.Buffer - io.Copy(&buf, r) - outC <- buf.String() - }() - - os.Stderr.Close() - os.Stderr = old // restoring the real stdout - out := <-outC - if out != expected { - t.Fatalf("Expected '%s', received '%s'\n", expected, out) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/logger/syslog.go b/vendor/github.com/nats-io/gnatsd/logger/syslog.go deleted file mode 100644 index 96d65ca6..00000000 --- a/vendor/github.com/nats-io/gnatsd/logger/syslog.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -package logger - -import ( - "fmt" - "log" - "log/syslog" - "net/url" - "os" - "strings" -) - -// SysLogger provides a system logger facility -type SysLogger struct { - writer *syslog.Writer - debug bool - trace bool -} - -// GetSysLoggerTag generates the tag name for use in syslog statements. If -// the executable is linked, the name of the link will be used as the tag, -// otherwise, the name of the executable is used. "gnatsd" is the default -// for the NATS server. -func GetSysLoggerTag() string { - procName := os.Args[0] - if strings.ContainsRune(procName, os.PathSeparator) { - parts := strings.FieldsFunc(procName, func(c rune) bool { - return c == os.PathSeparator - }) - procName = parts[len(parts)-1] - } - return procName -} - -// NewSysLogger creates a new system logger -func NewSysLogger(debug, trace bool) *SysLogger { - w, err := syslog.New(syslog.LOG_DAEMON|syslog.LOG_NOTICE, GetSysLoggerTag()) - if err != nil { - log.Fatalf("error connecting to syslog: %q", err.Error()) - } - - return &SysLogger{ - writer: w, - debug: debug, - trace: trace, - } -} - -// NewRemoteSysLogger creates a new remote system logger -func NewRemoteSysLogger(fqn string, debug, trace bool) *SysLogger { - network, addr := getNetworkAndAddr(fqn) - w, err := syslog.Dial(network, addr, syslog.LOG_DEBUG, GetSysLoggerTag()) - if err != nil { - log.Fatalf("error connecting to syslog: %q", err.Error()) - } - - return &SysLogger{ - writer: w, - debug: debug, - trace: trace, - } -} - -func getNetworkAndAddr(fqn string) (network, addr string) { - u, err := url.Parse(fqn) - if err != nil { - log.Fatal(err) - } - - network = u.Scheme - if network == "udp" || network == "tcp" { - addr = u.Host - } else if network == "unix" { - addr = u.Path - } else { - log.Fatalf("error invalid network type: %q", u.Scheme) - } - - return -} - -// Noticef logs a notice statement -func (l *SysLogger) Noticef(format string, v ...interface{}) { - l.writer.Notice(fmt.Sprintf(format, v...)) -} - -// Fatalf logs a fatal error -func (l *SysLogger) Fatalf(format string, v ...interface{}) { - l.writer.Crit(fmt.Sprintf(format, v...)) -} - -// Errorf logs an error statement -func (l *SysLogger) Errorf(format string, v ...interface{}) { - l.writer.Err(fmt.Sprintf(format, v...)) -} - -// Debugf logs a debug statement -func (l *SysLogger) Debugf(format string, v ...interface{}) { - if l.debug { - l.writer.Debug(fmt.Sprintf(format, v...)) - } -} - -// Tracef logs a trace statement -func (l *SysLogger) Tracef(format string, v ...interface{}) { - if l.trace { - l.writer.Notice(fmt.Sprintf(format, v...)) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/logger/syslog_test.go b/vendor/github.com/nats-io/gnatsd/logger/syslog_test.go deleted file mode 100644 index 604ccd16..00000000 --- a/vendor/github.com/nats-io/gnatsd/logger/syslog_test.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -package logger - -import ( - "fmt" - "log" - "net" - "os" - "path/filepath" - "strings" - "testing" - "time" -) - -var serverFQN string - -func TestSysLogger(t *testing.T) { - logger := NewSysLogger(false, false) - - if logger.debug { - t.Fatalf("Expected %t, received %t\n", false, logger.debug) - } - - if logger.trace { - t.Fatalf("Expected %t, received %t\n", false, logger.trace) - } -} - -func TestSysLoggerWithDebugAndTrace(t *testing.T) { - logger := NewSysLogger(true, true) - - if !logger.debug { - t.Fatalf("Expected %t, received %t\n", true, logger.debug) - } - - if !logger.trace { - t.Fatalf("Expected %t, received %t\n", true, logger.trace) - } -} - -func testTag(t *testing.T, exePath, expected string) { - os.Args[0] = exePath - if result := GetSysLoggerTag(); result != expected { - t.Fatalf("Expected %s, received %s", expected, result) - } -} - -func restoreArg(orig string) { - os.Args[0] = orig -} - -func TestSysLoggerTagGen(t *testing.T) { - origArg := os.Args[0] - defer restoreArg(origArg) - - testTag(t, "gnatsd", "gnatsd") - testTag(t, filepath.Join(".", "gnatsd"), "gnatsd") - testTag(t, filepath.Join("home", "bin", "gnatsd"), "gnatsd") - testTag(t, filepath.Join("..", "..", "gnatsd"), "gnatsd") - testTag(t, "gnatsd.service1", "gnatsd.service1") - testTag(t, "gnatsd_service1", "gnatsd_service1") - testTag(t, "gnatsd-service1", "gnatsd-service1") - testTag(t, "gnatsd service1", "gnatsd service1") -} - -func TestSysLoggerTag(t *testing.T) { - origArg := os.Args[0] - defer restoreArg(origArg) - - os.Args[0] = "ServerLoggerTag" - - done := make(chan string) - startServer(done) - logger := NewRemoteSysLogger(serverFQN, true, true) - logger.Noticef("foo") - - line := <-done - data := strings.Split(line, "[") - if len(data) != 2 { - t.Fatalf("Unexpected syslog line %s\n", line) - } - - if !strings.Contains(data[0], os.Args[0]) { - t.Fatalf("Expected '%s', received '%s'\n", os.Args[0], data[0]) - } -} - -func TestRemoteSysLogger(t *testing.T) { - done := make(chan string) - startServer(done) - logger := NewRemoteSysLogger(serverFQN, true, true) - - if !logger.debug { - t.Fatalf("Expected %t, received %t\n", true, logger.debug) - } - - if !logger.trace { - t.Fatalf("Expected %t, received %t\n", true, logger.trace) - } -} - -func TestRemoteSysLoggerNotice(t *testing.T) { - done := make(chan string) - startServer(done) - logger := NewRemoteSysLogger(serverFQN, true, true) - - logger.Noticef("foo %s", "bar") - expectSyslogOutput(t, <-done, "foo bar\n") -} - -func TestRemoteSysLoggerDebug(t *testing.T) { - done := make(chan string) - startServer(done) - logger := NewRemoteSysLogger(serverFQN, true, true) - - logger.Debugf("foo %s", "qux") - expectSyslogOutput(t, <-done, "foo qux\n") -} - -func TestRemoteSysLoggerDebugDisabled(t *testing.T) { - done := make(chan string) - startServer(done) - logger := NewRemoteSysLogger(serverFQN, false, false) - - logger.Debugf("foo %s", "qux") - rcvd := <-done - if rcvd != "" { - t.Fatalf("Unexpected syslog response %s\n", rcvd) - } -} - -func TestRemoteSysLoggerTrace(t *testing.T) { - done := make(chan string) - startServer(done) - logger := NewRemoteSysLogger(serverFQN, true, true) - - logger.Tracef("foo %s", "qux") - expectSyslogOutput(t, <-done, "foo qux\n") -} - -func TestRemoteSysLoggerTraceDisabled(t *testing.T) { - done := make(chan string) - startServer(done) - logger := NewRemoteSysLogger(serverFQN, true, false) - - logger.Tracef("foo %s", "qux") - rcvd := <-done - if rcvd != "" { - t.Fatalf("Unexpected syslog response %s\n", rcvd) - } -} - -func TestGetNetworkAndAddrUDP(t *testing.T) { - n, a := getNetworkAndAddr("udp://foo.com:1000") - - if n != "udp" { - t.Fatalf("Unexpected network %s\n", n) - } - - if a != "foo.com:1000" { - t.Fatalf("Unexpected addr %s\n", a) - } -} - -func TestGetNetworkAndAddrTCP(t *testing.T) { - n, a := getNetworkAndAddr("tcp://foo.com:1000") - - if n != "tcp" { - t.Fatalf("Unexpected network %s\n", n) - } - - if a != "foo.com:1000" { - t.Fatalf("Unexpected addr %s\n", a) - } -} - -func TestGetNetworkAndAddrUnix(t *testing.T) { - n, a := getNetworkAndAddr("unix:///foo.sock") - - if n != "unix" { - t.Fatalf("Unexpected network %s\n", n) - } - - if a != "/foo.sock" { - t.Fatalf("Unexpected addr %s\n", a) - } -} -func expectSyslogOutput(t *testing.T, line string, expected string) { - data := strings.Split(line, "]: ") - if len(data) != 2 { - t.Fatalf("Unexpected syslog line %s\n", line) - } - - if data[1] != expected { - t.Fatalf("Expected '%s', received '%s'\n", expected, data[1]) - } -} - -func runSyslog(c net.PacketConn, done chan<- string) { - var buf [4096]byte - var rcvd string - for { - n, _, err := c.ReadFrom(buf[:]) - if err != nil || n == 0 { - break - } - rcvd += string(buf[:n]) - } - done <- rcvd -} - -func startServer(done chan<- string) { - c, e := net.ListenPacket("udp", "127.0.0.1:0") - if e != nil { - log.Fatalf("net.ListenPacket failed udp :0 %v", e) - } - - serverFQN = fmt.Sprintf("udp://%s", c.LocalAddr().String()) - c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) - go runSyslog(c, done) -} diff --git a/vendor/github.com/nats-io/gnatsd/logger/syslog_windows.go b/vendor/github.com/nats-io/gnatsd/logger/syslog_windows.go deleted file mode 100644 index 7e780812..00000000 --- a/vendor/github.com/nats-io/gnatsd/logger/syslog_windows.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package logger logs to the windows event log -package logger - -import ( - "fmt" - "os" - "strings" - - "golang.org/x/sys/windows/svc/eventlog" -) - -const ( - natsEventSource = "NATS-Server" -) - -// SysLogger logs to the windows event logger -type SysLogger struct { - writer *eventlog.Log - debug bool - trace bool -} - -// NewSysLogger creates a log using the windows event logger -func NewSysLogger(debug, trace bool) *SysLogger { - if err := eventlog.InstallAsEventCreate(natsEventSource, eventlog.Info|eventlog.Error|eventlog.Warning); err != nil { - if !strings.Contains(err.Error(), "registry key already exists") { - panic(fmt.Sprintf("could not access event log: %v", err)) - } - } - - w, err := eventlog.Open(natsEventSource) - if err != nil { - panic(fmt.Sprintf("could not open event log: %v", err)) - } - - return &SysLogger{ - writer: w, - debug: debug, - trace: trace, - } -} - -// NewRemoteSysLogger creates a remote event logger -func NewRemoteSysLogger(fqn string, debug, trace bool) *SysLogger { - w, err := eventlog.OpenRemote(fqn, natsEventSource) - if err != nil { - panic(fmt.Sprintf("could not open event log: %v", err)) - } - - return &SysLogger{ - writer: w, - debug: debug, - trace: trace, - } -} - -func formatMsg(tag, format string, v ...interface{}) string { - orig := fmt.Sprintf(format, v...) - return fmt.Sprintf("pid[%d][%s]: %s", os.Getpid(), tag, orig) -} - -// Noticef logs a notice statement -func (l *SysLogger) Noticef(format string, v ...interface{}) { - l.writer.Info(1, formatMsg("NOTICE", format, v...)) -} - -// Fatalf logs a fatal error -func (l *SysLogger) Fatalf(format string, v ...interface{}) { - msg := formatMsg("FATAL", format, v...) - l.writer.Error(5, msg) - panic(msg) -} - -// Errorf logs an error statement -func (l *SysLogger) Errorf(format string, v ...interface{}) { - l.writer.Error(2, formatMsg("ERROR", format, v...)) -} - -// Debugf logs a debug statement -func (l *SysLogger) Debugf(format string, v ...interface{}) { - if l.debug { - l.writer.Info(3, formatMsg("DEBUG", format, v...)) - } -} - -// Tracef logs a trace statement -func (l *SysLogger) Tracef(format string, v ...interface{}) { - if l.trace { - l.writer.Info(4, formatMsg("TRACE", format, v...)) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/logger/syslog_windows_test.go b/vendor/github.com/nats-io/gnatsd/logger/syslog_windows_test.go deleted file mode 100755 index 8dc06464..00000000 --- a/vendor/github.com/nats-io/gnatsd/logger/syslog_windows_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build windows - -package logger - -import ( - "os/exec" - "strings" - "testing" - - "golang.org/x/sys/windows/svc/eventlog" -) - -// Skips testing if we do not have privledges to run this test. -// This lets us skip the tests for general (non admin/system) users. -func checkPrivledges(t *testing.T) { - src := "NATS-eventlog-testsource" - defer eventlog.Remove(src) - if err := eventlog.InstallAsEventCreate(src, eventlog.Info|eventlog.Error|eventlog.Warning); err != nil { - if strings.Contains(err.Error(), "Access is denied") { - t.Skip("skipping: elevated privledges are required.") - } - // let the tests report other types of errors - } -} - -// lastLogEntryContains reads the last entry (/c:1 /rd:true) written -// to the event log by the NATS-Server source, returning true if the -// passed text was found, false otherwise. -func lastLogEntryContains(t *testing.T, text string) bool { - var output []byte - var err error - - cmd := exec.Command("wevtutil.exe", "qe", "Application", "/q:*[System[Provider[@Name='NATS-Server']]]", - "/rd:true", "/c:1") - if output, err = cmd.Output(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - return strings.Contains(string(output), text) -} - -// TestSysLogger tests event logging on windows -func TestSysLogger(t *testing.T) { - checkPrivledges(t) - logger := NewSysLogger(false, false) - if logger.debug { - t.Fatalf("Expected %t, received %t\n", false, logger.debug) - } - - if logger.trace { - t.Fatalf("Expected %t, received %t\n", false, logger.trace) - } - logger.Noticef("%s", "Noticef") - if !lastLogEntryContains(t, "[NOTICE]: Noticef") { - t.Fatalf("missing log entry") - } - - logger.Errorf("%s", "Errorf") - if !lastLogEntryContains(t, "[ERROR]: Errorf") { - t.Fatalf("missing log entry") - } - - logger.Tracef("%s", "Tracef") - if lastLogEntryContains(t, "Tracef") { - t.Fatalf("should not contain log entry") - } - - logger.Debugf("%s", "Debugf") - if lastLogEntryContains(t, "Debugf") { - t.Fatalf("should not contain log entry") - } -} - -// TestSysLoggerWithDebugAndTrace tests event logging -func TestSysLoggerWithDebugAndTrace(t *testing.T) { - checkPrivledges(t) - logger := NewSysLogger(true, true) - if !logger.debug { - t.Fatalf("Expected %t, received %t\n", true, logger.debug) - } - - if !logger.trace { - t.Fatalf("Expected %t, received %t\n", true, logger.trace) - } - - logger.Tracef("%s", "Tracef") - if !lastLogEntryContains(t, "[TRACE]: Tracef") { - t.Fatalf("missing log entry") - } - - logger.Debugf("%s", "Debugf") - if !lastLogEntryContains(t, "[DEBUG]: Debugf") { - t.Fatalf("missing log entry") - } -} - -// TestSysLoggerWithDebugAndTrace tests remote event logging -func TestRemoteSysLoggerWithDebugAndTrace(t *testing.T) { - checkPrivledges(t) - logger := NewRemoteSysLogger("", true, true) - if !logger.debug { - t.Fatalf("Expected %t, received %t\n", true, logger.debug) - } - - if !logger.trace { - t.Fatalf("Expected %t, received %t\n", true, logger.trace) - } - logger.Tracef("NATS %s", "[TRACE]: Remote Noticef") - if !lastLogEntryContains(t, "Remote Noticef") { - t.Fatalf("missing log entry") - } -} - -func TestSysLoggerFatalf(t *testing.T) { - defer func() { - if r := recover(); r != nil { - if !lastLogEntryContains(t, "[FATAL]: Fatalf") { - t.Fatalf("missing log entry") - } - } - }() - - checkPrivledges(t) - logger := NewSysLogger(true, true) - logger.Fatalf("%s", "Fatalf") - t.Fatalf("did not panic when expected to") -} diff --git a/vendor/github.com/nats-io/gnatsd/server/auth.go b/vendor/github.com/nats-io/gnatsd/server/auth.go deleted file mode 100644 index 25b5ab7a..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/auth.go +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "crypto/tls" - "fmt" - "strings" - - "golang.org/x/crypto/bcrypt" -) - -// Authentication is an interface for implementing authentication -type Authentication interface { - // Check if a client is authorized to connect - Check(c ClientAuthentication) bool -} - -// ClientAuthentication is an interface for client authentication -type ClientAuthentication interface { - // Get options associated with a client - GetOpts() *clientOpts - // If TLS is enabled, TLS ConnectionState, nil otherwise - GetTLSConnectionState() *tls.ConnectionState - // Optionally map a user after auth. - RegisterUser(*User) -} - -// User is for multiple accounts/users. -type User struct { - Username string `json:"user"` - Password string `json:"password"` - Permissions *Permissions `json:"permissions"` -} - -// clone performs a deep copy of the User struct, returning a new clone with -// all values copied. -func (u *User) clone() *User { - if u == nil { - return nil - } - clone := &User{} - *clone = *u - clone.Permissions = u.Permissions.clone() - return clone -} - -// Permissions are the allowed subjects on a per -// publish or subscribe basis. -type Permissions struct { - Publish []string `json:"publish"` - Subscribe []string `json:"subscribe"` -} - -// RoutePermissions are similar to user permissions -// but describe what a server can import/export from and to -// another server. -type RoutePermissions struct { - Import []string `json:"import"` - Export []string `json:"export"` -} - -// clone performs a deep copy of the Permissions struct, returning a new clone -// with all values copied. -func (p *Permissions) clone() *Permissions { - if p == nil { - return nil - } - clone := &Permissions{} - if p.Publish != nil { - clone.Publish = make([]string, len(p.Publish)) - copy(clone.Publish, p.Publish) - } - if p.Subscribe != nil { - clone.Subscribe = make([]string, len(p.Subscribe)) - copy(clone.Subscribe, p.Subscribe) - } - return clone -} - -// configureAuthorization will do any setup needed for authorization. -// Lock is assumed held. -func (s *Server) configureAuthorization() { - if s.opts == nil { - return - } - - // Snapshot server options. - opts := s.getOpts() - - // Check for multiple users first - // This just checks and sets up the user map if we have multiple users. - if opts.CustomClientAuthentication != nil { - s.info.AuthRequired = true - } else if opts.Users != nil { - s.users = make(map[string]*User) - for _, u := range opts.Users { - s.users[u.Username] = u - } - s.info.AuthRequired = true - } else if opts.Username != "" || opts.Authorization != "" { - s.info.AuthRequired = true - } else { - s.users = nil - s.info.AuthRequired = false - } -} - -// checkAuthorization will check authorization based on client type and -// return boolean indicating if client is authorized. -func (s *Server) checkAuthorization(c *client) bool { - switch c.typ { - case CLIENT: - return s.isClientAuthorized(c) - case ROUTER: - return s.isRouterAuthorized(c) - default: - return false - } -} - -// hasUsers leyt's us know if we have a users array. -func (s *Server) hasUsers() bool { - s.mu.Lock() - hu := s.users != nil - s.mu.Unlock() - return hu -} - -// isClientAuthorized will check the client against the proper authorization method and data. -// This could be token or username/password based. -func (s *Server) isClientAuthorized(c *client) bool { - // Snapshot server options. - opts := s.getOpts() - - // Check custom auth first, then multiple users, then token, then single user/pass. - if opts.CustomClientAuthentication != nil { - return opts.CustomClientAuthentication.Check(c) - } else if s.hasUsers() { - s.mu.Lock() - user, ok := s.users[c.opts.Username] - s.mu.Unlock() - - if !ok { - return false - } - ok = comparePasswords(user.Password, c.opts.Password) - // If we are authorized, register the user which will properly setup any permissions - // for pub/sub authorizations. - if ok { - c.RegisterUser(user) - } - return ok - - } else if opts.Authorization != "" { - return comparePasswords(opts.Authorization, c.opts.Authorization) - - } else if opts.Username != "" { - if opts.Username != c.opts.Username { - return false - } - return comparePasswords(opts.Password, c.opts.Password) - } - - return true -} - -// checkRouterAuth checks optional router authorization which can be nil or username/password. -func (s *Server) isRouterAuthorized(c *client) bool { - // Snapshot server options. - opts := s.getOpts() - - if s.opts.CustomRouterAuthentication != nil { - return s.opts.CustomRouterAuthentication.Check(c) - } - - if opts.Cluster.Username == "" { - return true - } - - if opts.Cluster.Username != c.opts.Username { - return false - } - if !comparePasswords(opts.Cluster.Password, c.opts.Password) { - return false - } - c.setRoutePermissions(opts.Cluster.Permissions) - return true -} - -// removeUnauthorizedSubs removes any subscriptions the client has that are no -// longer authorized, e.g. due to a config reload. -func (s *Server) removeUnauthorizedSubs(c *client) { - c.mu.Lock() - if c.perms == nil { - c.mu.Unlock() - return - } - - subs := make(map[string]*subscription, len(c.subs)) - for sid, sub := range c.subs { - subs[sid] = sub - } - c.mu.Unlock() - - for sid, sub := range subs { - if !c.canSubscribe(sub.subject) { - _ = s.sl.Remove(sub) - c.mu.Lock() - delete(c.subs, sid) - c.mu.Unlock() - c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q (sid %s)", - sub.subject, sub.sid)) - s.Noticef("Removed sub %q for user %q - not authorized", - string(sub.subject), c.opts.Username) - } - } -} - -// Support for bcrypt stored passwords and tokens. -const bcryptPrefix = "$2a$" - -// isBcrypt checks whether the given password or token is bcrypted. -func isBcrypt(password string) bool { - return strings.HasPrefix(password, bcryptPrefix) -} - -func comparePasswords(serverPassword, clientPassword string) bool { - // Check to see if the server password is a bcrypt hash - if isBcrypt(serverPassword) { - if err := bcrypt.CompareHashAndPassword([]byte(serverPassword), []byte(clientPassword)); err != nil { - return false - } - } else if serverPassword != clientPassword { - return false - } - return true -} diff --git a/vendor/github.com/nats-io/gnatsd/server/auth_test.go b/vendor/github.com/nats-io/gnatsd/server/auth_test.go deleted file mode 100644 index 787f5993..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/auth_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "reflect" - "testing" -) - -func TestUserCloneNilPermissions(t *testing.T) { - user := &User{ - Username: "foo", - Password: "bar", - } - - clone := user.clone() - - if !reflect.DeepEqual(user, clone) { - t.Fatalf("Cloned Users are incorrect.\nexpected: %+v\ngot: %+v", - user, clone) - } - - clone.Password = "baz" - if reflect.DeepEqual(user, clone) { - t.Fatal("Expected Users to be different") - } -} - -func TestUserClone(t *testing.T) { - user := &User{ - Username: "foo", - Password: "bar", - Permissions: &Permissions{ - Publish: []string{"foo"}, - Subscribe: []string{"bar"}, - }, - } - - clone := user.clone() - - if !reflect.DeepEqual(user, clone) { - t.Fatalf("Cloned Users are incorrect.\nexpected: %+v\ngot: %+v", - user, clone) - } - - clone.Permissions.Subscribe = []string{"baz"} - if reflect.DeepEqual(user, clone) { - t.Fatal("Expected Users to be different") - } -} - -func TestUserClonePermissionsNoLists(t *testing.T) { - user := &User{ - Username: "foo", - Password: "bar", - Permissions: &Permissions{}, - } - - clone := user.clone() - - if clone.Permissions.Publish != nil { - t.Fatalf("Expected Publish to be nil, got: %v", clone.Permissions.Publish) - } - if clone.Permissions.Subscribe != nil { - t.Fatalf("Expected Subscribe to be nil, got: %v", clone.Permissions.Subscribe) - } -} - -func TestUserCloneNoPermissions(t *testing.T) { - user := &User{ - Username: "foo", - Password: "bar", - } - - clone := user.clone() - - if clone.Permissions != nil { - t.Fatalf("Expected Permissions to be nil, got: %v", clone.Permissions) - } -} - -func TestUserCloneNil(t *testing.T) { - user := (*User)(nil) - clone := user.clone() - if clone != nil { - t.Fatalf("Expected nil, got: %+v", clone) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/ciphersuites.go b/vendor/github.com/nats-io/gnatsd/server/ciphersuites.go deleted file mode 100644 index cbc5a2ff..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/ciphersuites.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "crypto/tls" -) - -// Where we maintain all of the available ciphers -var cipherMap = map[string]uint16{ - "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, - "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, - "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, -} - -var cipherMapByID = map[uint16]string{ - tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA", - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA", - tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA", - tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256", - tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA", - tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384", - tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA", - tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", -} - -func defaultCipherSuites() []uint16 { - return []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - } -} - -// Where we maintain available curve preferences -var curvePreferenceMap = map[string]tls.CurveID{ - "CurveP256": tls.CurveP256, - "CurveP384": tls.CurveP384, - "CurveP521": tls.CurveP521, - "X25519": tls.X25519, -} - -// reorder to default to the highest level of security. See: -// https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go -func defaultCurvePreferences() []tls.CurveID { - return []tls.CurveID{ - tls.CurveP521, - tls.CurveP384, - tls.X25519, // faster than P256, arguably more secure - tls.CurveP256, - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/client.go b/vendor/github.com/nats-io/gnatsd/server/client.go deleted file mode 100644 index 1f2cdb38..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/client.go +++ /dev/null @@ -1,1810 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "crypto/tls" - "encoding/json" - "fmt" - "io" - "math/rand" - "net" - "sync" - "sync/atomic" - "time" -) - -// Type of client connection. -const ( - // CLIENT is an end user. - CLIENT = iota - // ROUTER is another router in the cluster. - ROUTER -) - -const ( - // Original Client protocol from 2009. - // http://nats.io/documentation/internals/nats-protocol/ - ClientProtoZero = iota - // This signals a client can receive more then the original INFO block. - // This can be used to update clients on other cluster members, etc. - ClientProtoInfo -) - -func init() { - rand.Seed(time.Now().UnixNano()) -} - -const ( - // Scratch buffer size for the processMsg() calls. - msgScratchSize = 512 - msgHeadProto = "MSG " - msgHeadProtoLen = len(msgHeadProto) -) - -// For controlling dynamic buffer sizes. -const ( - startBufSize = 512 // For INFO/CONNECT block - minBufSize = 64 // Smallest to shrink to for PING/PONG - maxBufSize = 65536 // 64k - shortsToShrink = 2 -) - -// Represent client booleans with a bitmask -type clientFlag byte - -// Some client state represented as flags -const ( - connectReceived clientFlag = 1 << iota // The CONNECT proto has been received - firstPongSent // The first PONG has been sent - handshakeComplete // For TLS clients, indicate that the handshake is complete - clearConnection // Marks that clearConnection has already been called. - flushOutbound // Marks client as having a flushOutbound call in progress. -) - -// set the flag (would be equivalent to set the boolean to true) -func (cf *clientFlag) set(c clientFlag) { - *cf |= c -} - -// clear the flag (would be equivalent to set the boolean to false) -func (cf *clientFlag) clear(c clientFlag) { - *cf &= ^c -} - -// isSet returns true if the flag is set, false otherwise -func (cf clientFlag) isSet(c clientFlag) bool { - return cf&c != 0 -} - -// setIfNotSet will set the flag `c` only if that flag was not already -// set and return true to indicate that the flag has been set. Returns -// false otherwise. -func (cf *clientFlag) setIfNotSet(c clientFlag) bool { - if *cf&c == 0 { - *cf |= c - return true - } - return false -} - -// Reason client was closed. This will be passed into -// calls to clearConnection, but will only be stored -// in ConnInfo for monitoring. -type ClosedState int - -const ( - ClientClosed = ClosedState(iota + 1) - AuthenticationTimeout - AuthenticationViolation - TLSHandshakeError - SlowConsumerPendingBytes - SlowConsumerWriteDeadline - WriteError - ReadError - ParseError - StaleConnection - ProtocolViolation - BadClientProtocolVersion - WrongPort - MaxConnectionsExceeded - MaxPayloadExceeded - MaxControlLineExceeded - DuplicateRoute - RouteRemoved - ServerShutdown -) - -type client struct { - // Here first because of use of atomics, and memory alignment. - stats - mpay int64 - msubs int - mu sync.Mutex - typ int - cid uint64 - opts clientOpts - start time.Time - nc net.Conn - ncs string - out outbound - srv *Server - subs map[string]*subscription - perms *permissions - in readCache - pcd map[*client]struct{} - atmr *time.Timer - ping pinfo - msgb [msgScratchSize]byte - last time.Time - parseState - - rtt time.Duration - rttStart time.Time - - route *route - - debug bool - trace bool - echo bool - - flags clientFlag // Compact booleans into a single field. Size will be increased when needed. -} - -// Struct for PING initiation from the server. -type pinfo struct { - tmr *time.Timer - out int -} - -// outbound holds pending data for a socket. -type outbound struct { - p []byte // Primary write buffer - s []byte // Secondary for use post flush - nb net.Buffers // net.Buffers for writev IO - sz int // limit size per []byte, uses variable BufSize constants, start, min, max. - sws int // Number of short writes, used for dyanmic resizing. - pb int64 // Total pending/queued bytes. - pm int64 // Total pending/queued messages. - sg *sync.Cond // Flusher conditional for signaling. - fsp int // Flush signals that are pending from readLoop's pcd. - mp int64 // snapshot of max pending. - wdl time.Duration // Snapshot fo write deadline. - lft time.Duration // Last flush time. -} - -type permissions struct { - sub *Sublist - pub *Sublist - pcache map[string]bool -} - -const ( - maxResultCacheSize = 512 - maxPermCacheSize = 32 - pruneSize = 16 -) - -// Used in readloop to cache hot subject lookups and group statistics. -type readCache struct { - genid uint64 - results map[string]*SublistResult - prand *rand.Rand - msgs int - bytes int - subs int - rsz int // Read buffer size - srs int // Short reads, used for dynamic buffer resizing. -} - -func (c *client) String() (id string) { - return c.ncs -} - -func (c *client) GetOpts() *clientOpts { - return &c.opts -} - -// GetTLSConnectionState returns the TLS ConnectionState if TLS is enabled, nil -// otherwise. Implements the ClientAuth interface. -func (c *client) GetTLSConnectionState() *tls.ConnectionState { - tc, ok := c.nc.(*tls.Conn) - if !ok { - return nil - } - state := tc.ConnectionState() - return &state -} - -type subscription struct { - client *client - subject []byte - queue []byte - sid []byte - nm int64 - max int64 -} - -type clientOpts struct { - Echo bool `json:"echo"` - Verbose bool `json:"verbose"` - Pedantic bool `json:"pedantic"` - TLSRequired bool `json:"tls_required"` - Authorization string `json:"auth_token"` - Username string `json:"user"` - Password string `json:"pass"` - Name string `json:"name"` - Lang string `json:"lang"` - Version string `json:"version"` - Protocol int `json:"protocol"` -} - -var defaultOpts = clientOpts{Verbose: true, Pedantic: true, Echo: true} - -func init() { - rand.Seed(time.Now().UnixNano()) -} - -// Lock should be held -func (c *client) initClient() { - s := c.srv - c.cid = atomic.AddUint64(&s.gcid, 1) - - // Outbound data structure setup - c.out.sz = startBufSize - c.out.sg = sync.NewCond(&c.mu) - opts := s.getOpts() - // Snapshots to avoid mutex access in fast paths. - c.out.wdl = opts.WriteDeadline - c.out.mp = opts.MaxPending - - c.subs = make(map[string]*subscription) - c.echo = true - - c.debug = (atomic.LoadInt32(&c.srv.logging.debug) != 0) - c.trace = (atomic.LoadInt32(&c.srv.logging.trace) != 0) - - // This is a scratch buffer used for processMsg() - // The msg header starts with "MSG ", - // in bytes that is [77 83 71 32]. - c.msgb = [msgScratchSize]byte{77, 83, 71, 32} - - // This is to track pending clients that have data to be flushed - // after we process inbound msgs from our own connection. - c.pcd = make(map[*client]struct{}) - - // snapshot the string version of the connection - conn := "-" - if ip, ok := c.nc.(*net.TCPConn); ok { - addr := ip.RemoteAddr().(*net.TCPAddr) - conn = fmt.Sprintf("%s:%d", addr.IP, addr.Port) - } - - switch c.typ { - case CLIENT: - c.ncs = fmt.Sprintf("%s - cid:%d", conn, c.cid) - case ROUTER: - c.ncs = fmt.Sprintf("%s - rid:%d", conn, c.cid) - } -} - -// RegisterUser allows auth to call back into a new client -// with the authenticated user. This is used to map any permissions -// into the client. -func (c *client) RegisterUser(user *User) { - if user.Permissions == nil { - // Reset perms to nil in case client previously had them. - c.mu.Lock() - c.perms = nil - c.mu.Unlock() - return - } - - // Process Permissions and map into client connection structures. - c.mu.Lock() - defer c.mu.Unlock() - - c.setPermissions(user.Permissions) -} - -// Initializes client.perms structure. -// Lock is held on entry. -func (c *client) setPermissions(perms *Permissions) { - // Pre-allocate all to simplify checks later. - c.perms = &permissions{} - c.perms.sub = NewSublist() - c.perms.pub = NewSublist() - c.perms.pcache = make(map[string]bool) - - // Loop over publish permissions - for _, pubSubject := range perms.Publish { - sub := &subscription{subject: []byte(pubSubject)} - c.perms.pub.Insert(sub) - } - - // Loop over subscribe permissions - for _, subSubject := range perms.Subscribe { - sub := &subscription{subject: []byte(subSubject)} - c.perms.sub.Insert(sub) - } -} - -// writeLoop is the main socket write functionality. -// Runs in its own Go routine. -func (c *client) writeLoop() { - defer c.srv.grWG.Done() - - // Used to check that we did flush from last wake up. - waitOk := true - - // Main loop. Will wait to be signaled and then will use - // buffered outbound structure for efficient writev to the underlying socket. - for { - c.mu.Lock() - if waitOk && (c.out.pb == 0 || c.out.fsp > 0) && len(c.out.nb) == 0 && !c.flags.isSet(clearConnection) { - // Wait on pending data. - c.out.sg.Wait() - } - // Flush data - waitOk = c.flushOutbound() - isClosed := c.flags.isSet(clearConnection) - c.mu.Unlock() - - if isClosed { - return - } - } -} - -// readLoop is the main socket read functionality. -// Runs in its own Go routine. -func (c *client) readLoop() { - // Grab the connection off the client, it will be cleared on a close. - // We check for that after the loop, but want to avoid a nil dereference - c.mu.Lock() - nc := c.nc - s := c.srv - c.in.rsz = startBufSize - defer s.grWG.Done() - c.mu.Unlock() - - if nc == nil { - return - } - - // Start read buffer. - - b := make([]byte, c.in.rsz) - - for { - n, err := nc.Read(b) - if err != nil { - if err == io.EOF { - c.closeConnection(ClientClosed) - } else { - c.closeConnection(ReadError) - } - return - } - - // Grab for updates for last activity. - last := time.Now() - - // Clear inbound stats cache - c.in.msgs = 0 - c.in.bytes = 0 - c.in.subs = 0 - - // Main call into parser for inbound data. This will generate callouts - // to process messages, etc. - if err := c.parse(b[:n]); err != nil { - // handled inline - if err != ErrMaxPayload && err != ErrAuthorization { - c.Errorf("%s", err.Error()) - c.closeConnection(ProtocolViolation) - } - return - } - - // Updates stats for client and server that were collected - // from parsing through the buffer. - if c.in.msgs > 0 { - atomic.AddInt64(&c.inMsgs, int64(c.in.msgs)) - atomic.AddInt64(&c.inBytes, int64(c.in.bytes)) - atomic.AddInt64(&s.inMsgs, int64(c.in.msgs)) - atomic.AddInt64(&s.inBytes, int64(c.in.bytes)) - } - - // Budget to spend in place flushing outbound data. - // Client will be checked on several fronts to see - // if applicable. Routes will never wait in place. - budget := 500 * time.Microsecond - if c.typ == ROUTER { - budget = 0 - } - - // Check pending clients for flush. - for cp := range c.pcd { - // Queue up a flush for those in the set - cp.mu.Lock() - // Update last activity for message delivery - cp.last = last - cp.out.fsp-- - if budget > 0 && cp.flushOutbound() { - budget -= cp.out.lft - } else { - cp.flushSignal() - } - cp.mu.Unlock() - delete(c.pcd, cp) - } - - // Update activity, check read buffer size. - c.mu.Lock() - nc := c.nc - - // Activity based on interest changes or data/msgs. - if c.in.msgs > 0 || c.in.subs > 0 { - c.last = last - } - - if n >= cap(b) { - c.in.srs = 0 - } else if n < cap(b)/2 { // divide by 2 b/c we want less than what we would shrink to. - c.in.srs++ - } - - // Update read buffer size as/if needed. - if n >= cap(b) && cap(b) < maxBufSize { - // Grow - c.in.rsz = cap(b) * 2 - b = make([]byte, c.in.rsz) - } else if n < cap(b) && cap(b) > minBufSize && c.in.srs > shortsToShrink { - // Shrink, for now don't accelerate, ping/pong will eventually sort it out. - c.in.rsz = cap(b) / 2 - b = make([]byte, c.in.rsz) - } - c.mu.Unlock() - - // Check to see if we got closed, e.g. slow consumer - if nc == nil { - return - } - } -} - -// collapsePtoNB will place primary onto nb buffer as needed in prep for WriteTo. -// This will return a copy on purpose. -func (c *client) collapsePtoNB() net.Buffers { - if c.out.p != nil { - p := c.out.p - c.out.p = nil - return append(c.out.nb, p) - } - return c.out.nb -} - -// This will handle the fixup needed on a partial write. -// Assume pending has been already calculated correctly. -func (c *client) handlePartialWrite(pnb net.Buffers) { - nb := c.collapsePtoNB() - // The partial needs to be first, so append nb to pnb - c.out.nb = append(pnb, nb...) -} - -// flushOutbound will flush outbound buffer to a client. -// Will return if data was attempted to be written. -// Lock must be held -func (c *client) flushOutbound() bool { - if c.flags.isSet(flushOutbound) { - return false - } - c.flags.set(flushOutbound) - defer c.flags.clear(flushOutbound) - - // Check for nothing to do. - if c.nc == nil || c.srv == nil || c.out.pb == 0 { - return true // true because no need to queue a signal. - } - - // Snapshot opts - srv := c.srv - - // Place primary on nb, assign primary to secondary, nil out nb and secondary. - nb := c.collapsePtoNB() - c.out.p, c.out.nb, c.out.s = c.out.s, nil, nil - - // For selecting primary replacement. - cnb := nb - - // In case it goes away after releasing the lock. - nc := c.nc - attempted := c.out.pb - apm := c.out.pm - - // Do NOT hold lock during actual IO - c.mu.Unlock() - - // flush here - now := time.Now() - // FIXME(dlc) - writev will do multiple IOs past 1024 on - // most platforms, need to account for that with deadline? - nc.SetWriteDeadline(now.Add(c.out.wdl)) - // Actual write to the socket. - n, err := nb.WriteTo(nc) - nc.SetWriteDeadline(time.Time{}) - lft := time.Since(now) - - // Re-acquire client lock - c.mu.Lock() - - // Update flush time statistics - c.out.lft = lft - - // Subtract from pending bytes and messages. - c.out.pb -= n - c.out.pm -= apm // FIXME(dlc) - this will not be accurate. - - // Check for partial writes - if n != attempted && n > 0 { - c.handlePartialWrite(nb) - } else if n >= int64(c.out.sz) { - c.out.sws = 0 - } - - if err != nil { - if n == 0 { - c.out.pb -= attempted - } - if ne, ok := err.(net.Error); ok && ne.Timeout() { - atomic.AddInt64(&srv.slowConsumers, 1) - c.clearConnection(SlowConsumerWriteDeadline) - c.Noticef("Slow Consumer Detected: WriteDeadline of %v Exceeded", c.out.wdl) - } else { - c.clearConnection(WriteError) - c.Debugf("Error flushing: %v", err) - } - return true - } - - // Adjust based on what we wrote plus any pending. - pt := int(n + c.out.pb) - - // Adjust sz as needed downward, keeping power of 2. - // We do this at a slower rate, hence the pt*4. - if pt < c.out.sz && c.out.sz > minBufSize { - c.out.sws++ - if c.out.sws > shortsToShrink { - c.out.sz >>= 1 - } - } - // Adjust sz as needed upward, keeping power of 2. - if pt > c.out.sz && c.out.sz < maxBufSize { - c.out.sz <<= 1 - } - - // Check to see if we can reuse buffers. - if len(cnb) > 0 { - oldp := cnb[0][:0] - if cap(oldp) >= c.out.sz { - // Replace primary or secondary if they are nil, reusing same buffer. - if c.out.p == nil { - c.out.p = oldp - } else if c.out.s == nil || cap(c.out.s) < c.out.sz { - c.out.s = oldp - } - } - } - return true -} - -// flushSignal will use server to queue the flush IO operation to a pool of flushers. -// Lock must be held. -func (c *client) flushSignal() { - c.out.sg.Signal() -} - -func (c *client) traceMsg(msg []byte) { - if !c.trace { - return - } - // FIXME(dlc), allow limits to printable payload - c.Tracef("->> MSG_PAYLOAD: [%s]", string(msg[:len(msg)-LEN_CR_LF])) -} - -func (c *client) traceInOp(op string, arg []byte) { - c.traceOp("->> %s", op, arg) -} - -func (c *client) traceOutOp(op string, arg []byte) { - c.traceOp("<<- %s", op, arg) -} - -func (c *client) traceOp(format, op string, arg []byte) { - if !c.trace { - return - } - - opa := []interface{}{} - if op != "" { - opa = append(opa, op) - } - if arg != nil { - opa = append(opa, string(arg)) - } - c.Tracef(format, opa) -} - -// Process the information messages from Clients and other Routes. -func (c *client) processInfo(arg []byte) error { - info := Info{} - if err := json.Unmarshal(arg, &info); err != nil { - return err - } - if c.typ == ROUTER { - c.processRouteInfo(&info) - } - return nil -} - -func (c *client) processErr(errStr string) { - switch c.typ { - case CLIENT: - c.Errorf("Client Error %s", errStr) - case ROUTER: - c.Errorf("Route Error %s", errStr) - } - c.closeConnection(ParseError) -} - -func (c *client) processConnect(arg []byte) error { - c.traceInOp("CONNECT", arg) - - c.mu.Lock() - // If we can't stop the timer because the callback is in progress... - if !c.clearAuthTimer() { - // wait for it to finish and handle sending the failure back to - // the client. - for c.nc != nil { - c.mu.Unlock() - time.Sleep(25 * time.Millisecond) - c.mu.Lock() - } - c.mu.Unlock() - return nil - } - c.last = time.Now() - typ := c.typ - r := c.route - srv := c.srv - // Moved unmarshalling of clients' Options under the lock. - // The client has already been added to the server map, so it is possible - // that other routines lookup the client, and access its options under - // the client's lock, so unmarshalling the options outside of the lock - // would cause data RACEs. - if err := json.Unmarshal(arg, &c.opts); err != nil { - c.mu.Unlock() - return err - } - // Indicate that the CONNECT protocol has been received, and that the - // server now knows which protocol this client supports. - c.flags.set(connectReceived) - // Capture these under lock - c.echo = c.opts.Echo - proto := c.opts.Protocol - verbose := c.opts.Verbose - lang := c.opts.Lang - c.mu.Unlock() - - if srv != nil { - // As soon as c.opts is unmarshalled and if the proto is at - // least ClientProtoInfo, we need to increment the following counter. - // This is decremented when client is removed from the server's - // clients map. - if proto >= ClientProtoInfo { - srv.mu.Lock() - srv.cproto++ - srv.mu.Unlock() - } - - // Check for Auth - if ok := srv.checkAuthorization(c); !ok { - c.authViolation() - return ErrAuthorization - } - } - - // Check client protocol request if it exists. - if typ == CLIENT && (proto < ClientProtoZero || proto > ClientProtoInfo) { - c.sendErr(ErrBadClientProtocol.Error()) - c.closeConnection(BadClientProtocolVersion) - return ErrBadClientProtocol - } else if typ == ROUTER && lang != "" { - // Way to detect clients that incorrectly connect to the route listen - // port. Client provide Lang in the CONNECT protocol while ROUTEs don't. - c.sendErr(ErrClientConnectedToRoutePort.Error()) - c.closeConnection(WrongPort) - return ErrClientConnectedToRoutePort - } - - // Grab connection name of remote route. - if typ == ROUTER && r != nil { - c.mu.Lock() - c.route.remoteID = c.opts.Name - c.mu.Unlock() - } - - if verbose { - c.sendOK() - } - return nil -} - -func (c *client) authTimeout() { - c.sendErr(ErrAuthTimeout.Error()) - c.Debugf("Authorization Timeout") - c.closeConnection(AuthenticationTimeout) -} - -func (c *client) authViolation() { - if c.srv != nil && c.srv.getOpts().Users != nil { - c.Errorf("%s - User %q", - ErrAuthorization.Error(), - c.opts.Username) - } else { - c.Errorf(ErrAuthorization.Error()) - } - c.sendErr("Authorization Violation") - c.closeConnection(AuthenticationViolation) -} - -func (c *client) maxConnExceeded() { - c.Errorf(ErrTooManyConnections.Error()) - c.sendErr(ErrTooManyConnections.Error()) - c.closeConnection(MaxConnectionsExceeded) -} - -func (c *client) maxSubsExceeded() { - c.Errorf(ErrTooManySubs.Error()) - c.sendErr(ErrTooManySubs.Error()) -} - -func (c *client) maxPayloadViolation(sz int, max int64) { - c.Errorf("%s: %d vs %d", ErrMaxPayload.Error(), sz, max) - c.sendErr("Maximum Payload Violation") - c.closeConnection(MaxPayloadExceeded) -} - -// queueOutbound queues data for client/route connections. -// Return pending length. -// Lock should be held. -func (c *client) queueOutbound(data []byte) { - // Add to pending bytes total. - c.out.pb += int64(len(data)) - - // Check for slow consumer via pending bytes limit. - // ok to return here, client is going away. - if c.out.pb > c.out.mp { - c.clearConnection(SlowConsumerPendingBytes) - atomic.AddInt64(&c.srv.slowConsumers, 1) - c.Noticef("Slow Consumer Detected: MaxPending of %d Exceeded", c.out.mp) - return - } - - if c.out.p == nil && len(data) < maxBufSize { - if c.out.sz == 0 { - c.out.sz = startBufSize - } - if c.out.s != nil && cap(c.out.s) >= c.out.sz { - c.out.p = c.out.s - c.out.s = nil - } else { - // FIXME(dlc) - make power of 2 if less than maxBufSize? - c.out.p = make([]byte, 0, c.out.sz) - } - } - // Determine if we copy or reference - available := cap(c.out.p) - len(c.out.p) - if len(data) > available { - // We can fit into existing primary, but message will fit in next one - // we allocate or utilize from the secondary. So copy what we can. - if available > 0 && len(data) < c.out.sz { - c.out.p = append(c.out.p, data[:available]...) - data = data[available:] - } - // Put the primary on the nb if it has a payload - if len(c.out.p) > 0 { - c.out.nb = append(c.out.nb, c.out.p) - c.out.p = nil - } - // Check for a big message, and if found place directly on nb - // FIXME(dlc) - do we need signaling of ownership here if we want len(data) < - if len(data) > maxBufSize { - c.out.nb = append(c.out.nb, data) - } else { - // We will copy to primary. - if c.out.p == nil { - // Grow here - if (c.out.sz << 1) <= maxBufSize { - c.out.sz <<= 1 - } - if len(data) > c.out.sz { - c.out.p = make([]byte, 0, len(data)) - } else { - if c.out.s != nil && cap(c.out.s) >= c.out.sz { // TODO(dlc) - Size mismatch? - c.out.p = c.out.s - c.out.s = nil - } else { - c.out.p = make([]byte, 0, c.out.sz) - } - } - } - c.out.p = append(c.out.p, data...) - } - } else { - c.out.p = append(c.out.p, data...) - } -} - -// Assume the lock is held upon entry. -func (c *client) sendProto(info []byte, doFlush bool) { - if c.nc == nil { - return - } - c.queueOutbound(info) - if !(doFlush && c.flushOutbound()) { - c.flushSignal() - } -} - -// Assume the lock is held upon entry. -func (c *client) sendPong() { - c.traceOutOp("PONG", nil) - c.sendProto([]byte("PONG\r\n"), true) -} - -// Assume the lock is held upon entry. -func (c *client) sendPing() { - c.rttStart = time.Now() - c.ping.out++ - c.traceOutOp("PING", nil) - c.sendProto([]byte("PING\r\n"), true) -} - -// Generates the INFO to be sent to the client with the client ID included. -// info arg will be copied since passed by value. -// Assume lock is held. -func (c *client) generateClientInfoJSON(info Info) []byte { - info.CID = c.cid - // Generate the info json - b, _ := json.Marshal(info) - pcs := [][]byte{[]byte("INFO"), b, []byte(CR_LF)} - return bytes.Join(pcs, []byte(" ")) -} - -// Assume the lock is held upon entry. -func (c *client) sendInfo(info []byte) { - c.sendProto(info, true) -} - -func (c *client) sendErr(err string) { - c.mu.Lock() - c.traceOutOp("-ERR", []byte(err)) - c.sendProto([]byte(fmt.Sprintf("-ERR '%s'\r\n", err)), true) - c.mu.Unlock() -} - -func (c *client) sendOK() { - c.mu.Lock() - c.traceOutOp("OK", nil) - // Can not autoflush this one, needs to be async. - c.sendProto([]byte("+OK\r\n"), false) - // FIXME(dlc) - ?? - c.pcd[c] = needFlush - c.mu.Unlock() -} - -func (c *client) processPing() { - c.mu.Lock() - c.traceInOp("PING", nil) - if c.nc == nil { - c.mu.Unlock() - return - } - c.sendPong() - - // The CONNECT should have been received, but make sure it - // is so before proceeding - if !c.flags.isSet(connectReceived) { - c.mu.Unlock() - return - } - // If we are here, the CONNECT has been received so we know - // if this client supports async INFO or not. - var ( - checkClusterChange bool - srv = c.srv - ) - // For older clients, just flip the firstPongSent flag if not already - // set and we are done. - if c.opts.Protocol < ClientProtoInfo || srv == nil { - c.flags.setIfNotSet(firstPongSent) - } else { - // This is a client that supports async INFO protocols. - // If this is the first PING (so firstPongSent is not set yet), - // we will need to check if there was a change in cluster topology. - checkClusterChange = !c.flags.isSet(firstPongSent) - } - c.mu.Unlock() - - if checkClusterChange { - srv.mu.Lock() - c.mu.Lock() - // Now that we are under both locks, we can flip the flag. - // This prevents sendAsyncInfoToClients() and and code here - // to send a double INFO protocol. - c.flags.set(firstPongSent) - // If there was a cluster update since this client was created, - // send an updated INFO protocol now. - if srv.lastCURLsUpdate >= c.start.UnixNano() { - c.sendInfo(c.generateClientInfoJSON(srv.copyInfo())) - } - c.mu.Unlock() - srv.mu.Unlock() - } -} - -func (c *client) processPong() { - c.traceInOp("PONG", nil) - c.mu.Lock() - c.ping.out = 0 - c.rtt = time.Since(c.rttStart) - c.mu.Unlock() -} - -func (c *client) processMsgArgs(arg []byte) error { - if c.trace { - c.traceInOp("MSG", arg) - } - - // Unroll splitArgs to avoid runtime/heap issues - a := [MAX_MSG_ARGS][]byte{} - args := a[:0] - start := -1 - for i, b := range arg { - switch b { - case ' ', '\t', '\r', '\n': - if start >= 0 { - args = append(args, arg[start:i]) - start = -1 - } - default: - if start < 0 { - start = i - } - } - } - if start >= 0 { - args = append(args, arg[start:]) - } - - switch len(args) { - case 3: - c.pa.reply = nil - c.pa.szb = args[2] - c.pa.size = parseSize(args[2]) - case 4: - c.pa.reply = args[2] - c.pa.szb = args[3] - c.pa.size = parseSize(args[3]) - default: - return fmt.Errorf("processMsgArgs Parse Error: '%s'", arg) - } - if c.pa.size < 0 { - return fmt.Errorf("processMsgArgs Bad or Missing Size: '%s'", arg) - } - - // Common ones processed after check for arg length - c.pa.subject = args[0] - c.pa.sid = args[1] - - return nil -} - -func (c *client) processPub(arg []byte) error { - if c.trace { - c.traceInOp("PUB", arg) - } - - // Unroll splitArgs to avoid runtime/heap issues - a := [MAX_PUB_ARGS][]byte{} - args := a[:0] - start := -1 - for i, b := range arg { - switch b { - case ' ', '\t': - if start >= 0 { - args = append(args, arg[start:i]) - start = -1 - } - default: - if start < 0 { - start = i - } - } - } - if start >= 0 { - args = append(args, arg[start:]) - } - - switch len(args) { - case 2: - c.pa.subject = args[0] - c.pa.reply = nil - c.pa.size = parseSize(args[1]) - c.pa.szb = args[1] - case 3: - c.pa.subject = args[0] - c.pa.reply = args[1] - c.pa.size = parseSize(args[2]) - c.pa.szb = args[2] - default: - return fmt.Errorf("processPub Parse Error: '%s'", arg) - } - if c.pa.size < 0 { - return fmt.Errorf("processPub Bad or Missing Size: '%s'", arg) - } - maxPayload := atomic.LoadInt64(&c.mpay) - if maxPayload > 0 && int64(c.pa.size) > maxPayload { - c.maxPayloadViolation(c.pa.size, maxPayload) - return ErrMaxPayload - } - - if c.opts.Pedantic && !IsValidLiteralSubject(string(c.pa.subject)) { - c.sendErr("Invalid Publish Subject") - } - return nil -} - -func splitArg(arg []byte) [][]byte { - a := [MAX_MSG_ARGS][]byte{} - args := a[:0] - start := -1 - for i, b := range arg { - switch b { - case ' ', '\t', '\r', '\n': - if start >= 0 { - args = append(args, arg[start:i]) - start = -1 - } - default: - if start < 0 { - start = i - } - } - } - if start >= 0 { - args = append(args, arg[start:]) - } - return args -} - -func (c *client) processSub(argo []byte) (err error) { - c.traceInOp("SUB", argo) - - // Indicate activity. - c.in.subs++ - - // Copy so we do not reference a potentially large buffer - arg := make([]byte, len(argo)) - copy(arg, argo) - args := splitArg(arg) - sub := &subscription{client: c} - switch len(args) { - case 2: - sub.subject = args[0] - sub.queue = nil - sub.sid = args[1] - case 3: - sub.subject = args[0] - sub.queue = args[1] - sub.sid = args[2] - default: - return fmt.Errorf("processSub Parse Error: '%s'", arg) - } - - shouldForward := false - - c.mu.Lock() - if c.nc == nil { - c.mu.Unlock() - return nil - } - - // Check permissions if applicable. - if c.typ == ROUTER { - if !c.canExport(sub.subject) { - c.mu.Unlock() - return nil - } - } else if !c.canSubscribe(sub.subject) { - c.mu.Unlock() - c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q", sub.subject)) - c.Errorf("Subscription Violation - User %q, Subject %q, SID %s", - c.opts.Username, sub.subject, sub.sid) - return nil - } - - if c.msubs > 0 && len(c.subs) >= c.msubs { - c.mu.Unlock() - c.maxSubsExceeded() - return nil - } - - // Check if we have a maximum on the number of subscriptions. - // We can have two SUB protocols coming from a route due to some - // race conditions. We should make sure that we process only one. - sid := string(sub.sid) - if c.subs[sid] == nil { - c.subs[sid] = sub - if c.srv != nil { - err = c.srv.sl.Insert(sub) - if err != nil { - delete(c.subs, sid) - } else { - shouldForward = c.typ != ROUTER - } - } - } - c.mu.Unlock() - if err != nil { - c.sendErr("Invalid Subject") - return nil - } else if c.opts.Verbose { - c.sendOK() - } - if shouldForward { - c.srv.broadcastSubscribe(sub) - } - - return nil -} - -// canSubscribe determines if the client is authorized to subscribe to the -// given subject. Assumes caller is holding lock. -func (c *client) canSubscribe(sub []byte) bool { - if c.perms == nil { - return true - } - return len(c.perms.sub.Match(string(sub)).psubs) > 0 -} - -// Low level unsubscribe for a given client. -func (c *client) unsubscribe(sub *subscription) { - c.mu.Lock() - defer c.mu.Unlock() - if sub.max > 0 && sub.nm < sub.max { - c.Debugf( - "Deferring actual UNSUB(%s): %d max, %d received\n", - string(sub.subject), sub.max, sub.nm) - return - } - c.traceOp("<-> %s", "DELSUB", sub.sid) - - delete(c.subs, string(sub.sid)) - if c.srv != nil { - c.srv.sl.Remove(sub) - } - - // If we are a queue subscriber on a client connection and we have routes, - // we will remember the remote sid and the queue group in case a route - // tries to deliver us a message. Remote queue subscribers are directed - // so we need to know what to do to avoid unnecessary message drops - // from [auto-]unsubscribe. - if c.typ == CLIENT && c.srv != nil && len(sub.queue) > 0 { - c.srv.holdRemoteQSub(sub) - } -} - -func (c *client) processUnsub(arg []byte) error { - c.traceInOp("UNSUB", arg) - args := splitArg(arg) - var sid []byte - max := -1 - - switch len(args) { - case 1: - sid = args[0] - case 2: - sid = args[0] - max = parseSize(args[1]) - default: - return fmt.Errorf("processUnsub Parse Error: '%s'", arg) - } - - // Indicate activity. - c.in.subs += 1 - - var sub *subscription - - unsub := false - shouldForward := false - ok := false - - c.mu.Lock() - if sub, ok = c.subs[string(sid)]; ok { - if max > 0 { - sub.max = int64(max) - } else { - // Clear it here to override - sub.max = 0 - } - unsub = true - shouldForward = c.typ != ROUTER && c.srv != nil - } - c.mu.Unlock() - - if unsub { - c.unsubscribe(sub) - } - if shouldForward { - c.srv.broadcastUnSubscribe(sub) - } - if c.opts.Verbose { - c.sendOK() - } - - return nil -} - -func (c *client) msgHeader(mh []byte, sub *subscription) []byte { - mh = append(mh, sub.sid...) - mh = append(mh, ' ') - if c.pa.reply != nil { - mh = append(mh, c.pa.reply...) - mh = append(mh, ' ') - } - mh = append(mh, c.pa.szb...) - mh = append(mh, "\r\n"...) - return mh -} - -// Used to treat maps as efficient set -var needFlush = struct{}{} -var routeSeen = struct{}{} - -func (c *client) deliverMsg(sub *subscription, mh, msg []byte) bool { - if sub.client == nil { - return false - } - client := sub.client - client.mu.Lock() - - // Check echo - if c == client && !client.echo { - client.mu.Unlock() - return false - } - - srv := client.srv - - sub.nm++ - // Check if we should auto-unsubscribe. - if sub.max > 0 { - // For routing.. - shouldForward := client.typ != ROUTER && client.srv != nil - // If we are at the exact number, unsubscribe but - // still process the message in hand, otherwise - // unsubscribe and drop message on the floor. - if sub.nm == sub.max { - c.Debugf("Auto-unsubscribe limit of %d reached for sid '%s'\n", sub.max, string(sub.sid)) - // Due to defer, reverse the code order so that execution - // is consistent with other cases where we unsubscribe. - if shouldForward { - defer srv.broadcastUnSubscribe(sub) - } - defer client.unsubscribe(sub) - } else if sub.nm > sub.max { - c.Debugf("Auto-unsubscribe limit [%d] exceeded\n", sub.max) - client.mu.Unlock() - client.unsubscribe(sub) - if shouldForward { - srv.broadcastUnSubscribe(sub) - } - return false - } - } - - // Check for closed connection - if client.nc == nil { - client.mu.Unlock() - return false - } - - // Update statistics - - // The msg includes the CR_LF, so pull back out for accounting. - msgSize := int64(len(msg) - LEN_CR_LF) - - // No atomic needed since accessed under client lock. - // Monitor is reading those also under client's lock. - client.outMsgs++ - client.outBytes += msgSize - - atomic.AddInt64(&srv.outMsgs, 1) - atomic.AddInt64(&srv.outBytes, msgSize) - - // Queue to outbound buffer - client.queueOutbound(mh) - client.queueOutbound(msg) - - client.out.pm++ - - // Check outbound threshold and queue IO flush if needed. - if client.out.pm > 1 && client.out.pb > maxBufSize*2 { - client.flushSignal() - } - - if c.trace { - client.traceOutOp(string(mh[:len(mh)-LEN_CR_LF]), nil) - } - - // Increment the flush pending signals if we are setting for the first time. - if _, ok := c.pcd[client]; !ok { - client.out.fsp++ - } - client.mu.Unlock() - - // Remember for when we return to the top of the loop. - c.pcd[client] = needFlush - - return true -} - -// pruneCache will prune the cache via randomly -// deleting items. Doing so pruneSize items at a time. -func (c *client) prunePubPermsCache() { - r := 0 - for subject := range c.perms.pcache { - delete(c.perms.pcache, subject) - if r++; r > pruneSize { - break - } - } -} - -// pubAllowed checks on publish permissioning. -func (c *client) pubAllowed(subject []byte) bool { - // Disallow publish to _SYS.>, these are reserved for internals. - if len(subject) > 4 && string(subject[:5]) == "_SYS." { - return false - } - if c.perms == nil { - return true - } - - // Check if published subject is allowed if we have permissions in place. - allowed, ok := c.perms.pcache[string(subject)] - if ok { - return allowed - } - // Cache miss - r := c.perms.pub.Match(string(subject)) - allowed = len(r.psubs) != 0 - c.perms.pcache[string(subject)] = allowed - // Prune if needed. - if len(c.perms.pcache) > maxPermCacheSize { - c.prunePubPermsCache() - } - return allowed -} - -// prepMsgHeader will prepare the message header prefix -func (c *client) prepMsgHeader() []byte { - // Use the scratch buffer.. - msgh := c.msgb[:msgHeadProtoLen] - - // msg header - msgh = append(msgh, c.pa.subject...) - return append(msgh, ' ') -} - -// processMsg is called to process an inbound msg from a client. -func (c *client) processMsg(msg []byte) { - // Snapshot server. - srv := c.srv - - // Update statistics - // The msg includes the CR_LF, so pull back out for accounting. - c.in.msgs += 1 - c.in.bytes += len(msg) - LEN_CR_LF - - if c.trace { - c.traceMsg(msg) - } - - // Check pub permissions (don't do this for routes) - if c.typ == CLIENT && !c.pubAllowed(c.pa.subject) { - c.pubPermissionViolation(c.pa.subject) - return - } - - if c.opts.Verbose { - c.sendOK() - } - - // Mostly under testing scenarios. - if srv == nil { - return - } - - // Match the subscriptions. We will use our own L1 map if - // it's still valid, avoiding contention on the shared sublist. - var r *SublistResult - var ok bool - - genid := atomic.LoadUint64(&srv.sl.genid) - - if genid == c.in.genid && c.in.results != nil { - r, ok = c.in.results[string(c.pa.subject)] - } else { - // reset our L1 completely. - c.in.results = make(map[string]*SublistResult) - c.in.genid = genid - } - - if !ok { - subject := string(c.pa.subject) - r = srv.sl.Match(subject) - c.in.results[subject] = r - // Prune the results cache. Keeps us from unbounded growth. - if len(c.in.results) > maxResultCacheSize { - n := 0 - for subject := range c.in.results { - delete(c.in.results, subject) - if n++; n > pruneSize { - break - } - } - } - } - - // This is the fanout scale. - fanout := len(r.psubs) + len(r.qsubs) - - // Check for no interest, short circuit if so. - if fanout == 0 { - return - } - - if c.typ == ROUTER { - c.processRoutedMsg(r, msg) - return - } - - // Client connection processing here. - msgh := c.prepMsgHeader() - si := len(msgh) - - // Used to only send messages once across any given route. - var rmap map[string]struct{} - - // Loop over all normal subscriptions that match. - for _, sub := range r.psubs { - // Check if this is a send to a ROUTER, make sure we only send it - // once. The other side will handle the appropriate re-processing - // and fan-out. Also enforce 1-Hop semantics, so no routing to another. - if sub.client.typ == ROUTER { - // Check to see if we have already sent it here. - if rmap == nil { - rmap = make(map[string]struct{}, srv.numRoutes()) - } - sub.client.mu.Lock() - if sub.client.nc == nil || - sub.client.route == nil || - sub.client.route.remoteID == "" { - c.Debugf("Bad or Missing ROUTER Identity, not processing msg") - sub.client.mu.Unlock() - continue - } - if _, ok := rmap[sub.client.route.remoteID]; ok { - c.Debugf("Ignoring route, already processed and sent msg") - sub.client.mu.Unlock() - continue - } - rmap[sub.client.route.remoteID] = routeSeen - sub.client.mu.Unlock() - } - // Normal delivery - mh := c.msgHeader(msgh[:si], sub) - c.deliverMsg(sub, mh, msg) - } - - // Check to see if we have our own rand yet. Global rand - // has contention with lots of clients, etc. - if c.in.prand == nil { - c.in.prand = rand.New(rand.NewSource(time.Now().UnixNano())) - } - // Process queue subs - for i := 0; i < len(r.qsubs); i++ { - qsubs := r.qsubs[i] - // Find a subscription that is able to deliver this message - // starting at a random index. - startIndex := c.in.prand.Intn(len(qsubs)) - for i := 0; i < len(qsubs); i++ { - index := (startIndex + i) % len(qsubs) - sub := qsubs[index] - if sub != nil { - mh := c.msgHeader(msgh[:si], sub) - if c.deliverMsg(sub, mh, msg) { - break - } - } - } - } -} - -func (c *client) pubPermissionViolation(subject []byte) { - c.sendErr(fmt.Sprintf("Permissions Violation for Publish to %q", subject)) - c.Errorf("Publish Violation - User %q, Subject %q", c.opts.Username, subject) -} - -func (c *client) processPingTimer() { - c.mu.Lock() - defer c.mu.Unlock() - c.ping.tmr = nil - // Check if connection is still opened - if c.nc == nil { - return - } - - c.Debugf("%s Ping Timer", c.typeString()) - - // Check for violation - if c.ping.out+1 > c.srv.getOpts().MaxPingsOut { - c.Debugf("Stale Client Connection - Closing") - c.sendProto([]byte(fmt.Sprintf("-ERR '%s'\r\n", "Stale Connection")), true) - c.clearConnection(StaleConnection) - return - } - - // If we have had activity within the PingInterval no - // need to send a ping. - if delta := time.Since(c.last); delta < c.srv.getOpts().PingInterval { - c.Debugf("Delaying PING due to activity %v ago", delta.Round(time.Second)) - } else { - // Send PING - c.sendPing() - } - - // Reset to fire again. - c.setPingTimer() -} - -// Lock should be held -func (c *client) setPingTimer() { - if c.srv == nil { - return - } - d := c.srv.getOpts().PingInterval - c.ping.tmr = time.AfterFunc(d, c.processPingTimer) -} - -// Lock should be held -func (c *client) clearPingTimer() { - if c.ping.tmr == nil { - return - } - c.ping.tmr.Stop() - c.ping.tmr = nil -} - -// Lock should be held -func (c *client) setAuthTimer(d time.Duration) { - c.atmr = time.AfterFunc(d, func() { c.authTimeout() }) -} - -// Lock should be held -func (c *client) clearAuthTimer() bool { - if c.atmr == nil { - return true - } - stopped := c.atmr.Stop() - c.atmr = nil - return stopped -} - -func (c *client) isAuthTimerSet() bool { - c.mu.Lock() - isSet := c.atmr != nil - c.mu.Unlock() - return isSet -} - -// Lock should be held -func (c *client) clearConnection(reason ClosedState) { - if c.flags.isSet(clearConnection) { - return - } - c.flags.set(clearConnection) - - nc := c.nc - if nc == nil || c.srv == nil { - return - } - // Flush any pending. - c.flushOutbound() - - // Clear outbound here. - c.out.sg.Broadcast() - - // With TLS, Close() is sending an alert (that is doing a write). - // Need to set a deadline otherwise the server could block there - // if the peer is not reading from socket. - if c.flags.isSet(handshakeComplete) { - nc.SetWriteDeadline(time.Now().Add(c.out.wdl)) - } - nc.Close() - // Do this always to also kick out any IO writes. - nc.SetWriteDeadline(time.Time{}) - - // Save off the connection if its a client. - if c.typ == CLIENT && c.srv != nil { - go c.srv.saveClosedClient(c, nc, reason) - } -} - -func (c *client) typeString() string { - switch c.typ { - case CLIENT: - return "Client" - case ROUTER: - return "Router" - } - return "Unknown Type" -} - -func (c *client) closeConnection(reason ClosedState) { - c.mu.Lock() - if c.nc == nil { - c.mu.Unlock() - return - } - - c.Debugf("%s connection closed", c.typeString()) - - c.clearAuthTimer() - c.clearPingTimer() - c.clearConnection(reason) - c.nc = nil - - // Snapshot for use. - subs := make([]*subscription, 0, len(c.subs)) - for _, sub := range c.subs { - // Auto-unsubscribe subscriptions must be unsubscribed forcibly. - sub.max = 0 - subs = append(subs, sub) - } - srv := c.srv - - var ( - routeClosed bool - retryImplicit bool - connectURLs []string - ) - if c.route != nil { - routeClosed = c.route.closed - if !routeClosed { - retryImplicit = c.route.retry - } - connectURLs = c.route.connectURLs - } - - c.mu.Unlock() - - if srv != nil { - // This is a route that disconnected... - if len(connectURLs) > 0 { - // Unless disabled, possibly update the server's INFO protcol - // and send to clients that know how to handle async INFOs. - if !srv.getOpts().Cluster.NoAdvertise { - srv.removeClientConnectURLsAndSendINFOToClients(connectURLs) - } - } - - // Unregister - srv.removeClient(c) - - // Remove clients subscriptions. - srv.sl.RemoveBatch(subs) - if c.typ != ROUTER { - for _, sub := range subs { - // Forward on unsubscribes if we are not - // a router ourselves. - srv.broadcastUnSubscribe(sub) - } - } - } - - // Don't reconnect routes that are being closed. - if routeClosed { - return - } - - // Check for a solicited route. If it was, start up a reconnect unless - // we are already connected to the other end. - if c.isSolicitedRoute() || retryImplicit { - // Capture these under lock - c.mu.Lock() - rid := c.route.remoteID - rtype := c.route.routeType - rurl := c.route.url - c.mu.Unlock() - - srv.mu.Lock() - defer srv.mu.Unlock() - - // It is possible that the server is being shutdown. - // If so, don't try to reconnect - if !srv.running { - return - } - - if rid != "" && srv.remotes[rid] != nil { - c.srv.Debugf("Not attempting reconnect for solicited route, already connected to \"%s\"", rid) - return - } else if rid == srv.info.ID { - c.srv.Debugf("Detected route to self, ignoring \"%s\"", rurl) - return - } else if rtype != Implicit || retryImplicit { - c.srv.Debugf("Attempting reconnect for solicited route \"%s\"", rurl) - // Keep track of this go-routine so we can wait for it on - // server shutdown. - srv.startGoRoutine(func() { srv.reConnectToRoute(rurl, rtype) }) - } - } -} - -// If the client is a route connection, sets the `closed` flag to true -// to prevent any reconnecting attempt when c.closeConnection() is called. -func (c *client) setRouteNoReconnectOnClose() { - c.mu.Lock() - if c.route != nil { - c.route.closed = true - } - c.mu.Unlock() -} - -// Logging functionality scoped to a client or route. - -func (c *client) Errorf(format string, v ...interface{}) { - format = fmt.Sprintf("%s - %s", c, format) - c.srv.Errorf(format, v...) -} - -func (c *client) Debugf(format string, v ...interface{}) { - format = fmt.Sprintf("%s - %s", c, format) - c.srv.Debugf(format, v...) -} - -func (c *client) Noticef(format string, v ...interface{}) { - format = fmt.Sprintf("%s - %s", c, format) - c.srv.Noticef(format, v...) -} - -func (c *client) Tracef(format string, v ...interface{}) { - format = fmt.Sprintf("%s - %s", c, format) - c.srv.Tracef(format, v...) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/client_test.go b/vendor/github.com/nats-io/gnatsd/server/client_test.go deleted file mode 100644 index 29271a34..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/client_test.go +++ /dev/null @@ -1,1096 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "net" - "reflect" - "regexp" - "strings" - "sync" - "sync/atomic" - "testing" - "time" - - "crypto/rand" - "crypto/tls" - - "github.com/nats-io/go-nats" -) - -type serverInfo struct { - Id string `json:"server_id"` - Host string `json:"host"` - Port uint `json:"port"` - Version string `json:"version"` - AuthRequired bool `json:"auth_required"` - TLSRequired bool `json:"tls_required"` - MaxPayload int64 `json:"max_payload"` -} - -func createClientAsync(ch chan *client, s *Server, cli net.Conn) { - go func() { - c := s.createClient(cli) - // Must be here to suppress +OK - c.opts.Verbose = false - ch <- c - }() -} - -var defaultServerOptions = Options{ - Trace: false, - Debug: false, - NoLog: true, - NoSigs: true, -} - -func rawSetup(serverOptions Options) (*Server, *client, *bufio.Reader, string) { - cli, srv := net.Pipe() - cr := bufio.NewReaderSize(cli, maxBufSize) - s := New(&serverOptions) - - ch := make(chan *client) - createClientAsync(ch, s, srv) - - l, _ := cr.ReadString('\n') - - // Grab client - c := <-ch - return s, c, cr, l -} - -func setUpClientWithResponse() (*client, string) { - _, c, _, l := rawSetup(defaultServerOptions) - return c, l -} - -func setupClient() (*Server, *client, *bufio.Reader) { - s, c, cr, _ := rawSetup(defaultServerOptions) - return s, c, cr -} - -func checkClientsCount(t *testing.T, s *Server, expected int) { - t.Helper() - checkFor(t, 2*time.Second, 15*time.Millisecond, func() error { - if nc := s.NumClients(); nc != expected { - return fmt.Errorf("The number of expected connections was %v, got %v", expected, nc) - } - return nil - }) -} - -func TestClientCreateAndInfo(t *testing.T) { - c, l := setUpClientWithResponse() - - if c.cid != 1 { - t.Fatalf("Expected cid of 1 vs %d\n", c.cid) - } - if c.state != OP_START { - t.Fatal("Expected state to be OP_START") - } - - if !strings.HasPrefix(l, "INFO ") { - t.Fatalf("INFO response incorrect: %s\n", l) - } - // Make sure payload is proper json - var info serverInfo - err := json.Unmarshal([]byte(l[5:]), &info) - if err != nil { - t.Fatalf("Could not parse INFO json: %v\n", err) - } - // Sanity checks - if info.MaxPayload != MAX_PAYLOAD_SIZE || - info.AuthRequired || info.TLSRequired || - info.Port != DEFAULT_PORT { - t.Fatalf("INFO inconsistent: %+v\n", info) - } -} - -func TestNonTLSConnectionState(t *testing.T) { - _, c, _ := setupClient() - state := c.GetTLSConnectionState() - if state != nil { - t.Error("GetTLSConnectionState() returned non-nil") - } -} - -func TestClientConnect(t *testing.T) { - _, c, _ := setupClient() - - // Basic Connect setting flags - connectOp := []byte("CONNECT {\"verbose\":true,\"pedantic\":true,\"tls_required\":false,\"echo\":false}\r\n") - err := c.parse(connectOp) - if err != nil { - t.Fatalf("Received error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected state of OP_START vs %d\n", c.state) - } - if !reflect.DeepEqual(c.opts, clientOpts{Verbose: true, Pedantic: true, Echo: false}) { - t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts) - } - - // Test that we can capture user/pass - connectOp = []byte("CONNECT {\"user\":\"derek\",\"pass\":\"foo\"}\r\n") - c.opts = defaultOpts - err = c.parse(connectOp) - if err != nil { - t.Fatalf("Received error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected state of OP_START vs %d\n", c.state) - } - if !reflect.DeepEqual(c.opts, clientOpts{Echo: true, Verbose: true, Pedantic: true, Username: "derek", Password: "foo"}) { - t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts) - } - - // Test that we can capture client name - connectOp = []byte("CONNECT {\"user\":\"derek\",\"pass\":\"foo\",\"name\":\"router\"}\r\n") - c.opts = defaultOpts - err = c.parse(connectOp) - if err != nil { - t.Fatalf("Received error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected state of OP_START vs %d\n", c.state) - } - - if !reflect.DeepEqual(c.opts, clientOpts{Echo: true, Verbose: true, Pedantic: true, Username: "derek", Password: "foo", Name: "router"}) { - t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts) - } - - // Test that we correctly capture auth tokens - connectOp = []byte("CONNECT {\"auth_token\":\"YZZ222\",\"name\":\"router\"}\r\n") - c.opts = defaultOpts - err = c.parse(connectOp) - if err != nil { - t.Fatalf("Received error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected state of OP_START vs %d\n", c.state) - } - - if !reflect.DeepEqual(c.opts, clientOpts{Echo: true, Verbose: true, Pedantic: true, Authorization: "YZZ222", Name: "router"}) { - t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts) - } -} - -func TestClientConnectProto(t *testing.T) { - _, c, r := setupClient() - - // Basic Connect setting flags, proto should be zero (original proto) - connectOp := []byte("CONNECT {\"verbose\":true,\"pedantic\":true,\"tls_required\":false}\r\n") - err := c.parse(connectOp) - if err != nil { - t.Fatalf("Received error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected state of OP_START vs %d\n", c.state) - } - if !reflect.DeepEqual(c.opts, clientOpts{Echo: true, Verbose: true, Pedantic: true, Protocol: ClientProtoZero}) { - t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts) - } - - // ProtoInfo - connectOp = []byte(fmt.Sprintf("CONNECT {\"verbose\":true,\"pedantic\":true,\"tls_required\":false,\"protocol\":%d}\r\n", ClientProtoInfo)) - err = c.parse(connectOp) - if err != nil { - t.Fatalf("Received error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected state of OP_START vs %d\n", c.state) - } - if !reflect.DeepEqual(c.opts, clientOpts{Echo: true, Verbose: true, Pedantic: true, Protocol: ClientProtoInfo}) { - t.Fatalf("Did not parse connect options correctly: %+v\n", c.opts) - } - if c.opts.Protocol != ClientProtoInfo { - t.Fatalf("Protocol should have been set to %v, but is set to %v", ClientProtoInfo, c.opts.Protocol) - } - - // Illegal Option - connectOp = []byte("CONNECT {\"protocol\":22}\r\n") - wg := sync.WaitGroup{} - wg.Add(1) - // The client here is using a pipe, we need to be dequeuing - // data otherwise the server would be blocked trying to send - // the error back to it. - go func() { - defer wg.Done() - for { - if _, _, err := r.ReadLine(); err != nil { - return - } - } - }() - err = c.parse(connectOp) - if err == nil { - t.Fatalf("Expected to receive an error\n") - } - if err != ErrBadClientProtocol { - t.Fatalf("Expected err of %q, got %q\n", ErrBadClientProtocol, err) - } - wg.Wait() -} - -func TestClientPing(t *testing.T) { - _, c, cr := setupClient() - - // PING - pingOp := []byte("PING\r\n") - go c.parse(pingOp) - l, err := cr.ReadString('\n') - if err != nil { - t.Fatalf("Error receiving info from server: %v\n", err) - } - if !strings.HasPrefix(l, "PONG\r\n") { - t.Fatalf("PONG response incorrect: %s\n", l) - } -} - -var msgPat = regexp.MustCompile(`\AMSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\r\n`) - -const ( - SUB_INDEX = 1 - SID_INDEX = 2 - REPLY_INDEX = 4 - LEN_INDEX = 5 -) - -func checkPayload(cr *bufio.Reader, expected []byte, t *testing.T) { - // Read in payload - d := make([]byte, len(expected)) - n, err := cr.Read(d) - if err != nil { - t.Fatalf("Error receiving msg payload from server: %v\n", err) - } - if n != len(expected) { - t.Fatalf("Did not read correct amount of bytes: %d vs %d\n", n, len(expected)) - } - if !bytes.Equal(d, expected) { - t.Fatalf("Did not read correct payload:: <%s>\n", d) - } -} - -func TestClientSimplePubSub(t *testing.T) { - _, c, cr := setupClient() - // SUB/PUB - go c.parse([]byte("SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n")) - l, err := cr.ReadString('\n') - if err != nil { - t.Fatalf("Error receiving msg from server: %v\n", err) - } - matches := msgPat.FindAllStringSubmatch(l, -1)[0] - if len(matches) != 6 { - t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6) - } - if matches[SUB_INDEX] != "foo" { - t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX]) - } - if matches[SID_INDEX] != "1" { - t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX]) - } - if matches[LEN_INDEX] != "5" { - t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX]) - } - checkPayload(cr, []byte("hello\r\n"), t) -} - -func TestClientPubSubNoEcho(t *testing.T) { - _, c, cr := setupClient() - // Specify no echo - connectOp := []byte("CONNECT {\"echo\":false}\r\n") - err := c.parse(connectOp) - if err != nil { - t.Fatalf("Received error: %v\n", err) - } - // SUB/PUB - go c.parse([]byte("SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n")) - l, err := cr.ReadString('\n') - if err != nil { - t.Fatalf("Error receiving msg from server: %v\n", err) - } - // We should not receive anything but a PONG since we specified no echo. - if !strings.HasPrefix(l, "PONG\r\n") { - t.Fatalf("PONG response incorrect: %q\n", l) - } -} - -func TestClientSimplePubSubWithReply(t *testing.T) { - _, c, cr := setupClient() - - // SUB/PUB - go c.parse([]byte("SUB foo 1\r\nPUB foo bar 5\r\nhello\r\nPING\r\n")) - l, err := cr.ReadString('\n') - if err != nil { - t.Fatalf("Error receiving msg from server: %v\n", err) - } - matches := msgPat.FindAllStringSubmatch(l, -1)[0] - if len(matches) != 6 { - t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6) - } - if matches[SUB_INDEX] != "foo" { - t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX]) - } - if matches[SID_INDEX] != "1" { - t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX]) - } - if matches[REPLY_INDEX] != "bar" { - t.Fatalf("Did not get correct reply subject: '%s'\n", matches[REPLY_INDEX]) - } - if matches[LEN_INDEX] != "5" { - t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX]) - } -} - -func TestClientNoBodyPubSubWithReply(t *testing.T) { - _, c, cr := setupClient() - - // SUB/PUB - go c.parse([]byte("SUB foo 1\r\nPUB foo bar 0\r\n\r\nPING\r\n")) - l, err := cr.ReadString('\n') - if err != nil { - t.Fatalf("Error receiving msg from server: %v\n", err) - } - matches := msgPat.FindAllStringSubmatch(l, -1)[0] - if len(matches) != 6 { - t.Fatalf("Did not get correct # matches: %d vs %d\n", len(matches), 6) - } - if matches[SUB_INDEX] != "foo" { - t.Fatalf("Did not get correct subject: '%s'\n", matches[SUB_INDEX]) - } - if matches[SID_INDEX] != "1" { - t.Fatalf("Did not get correct sid: '%s'\n", matches[SID_INDEX]) - } - if matches[REPLY_INDEX] != "bar" { - t.Fatalf("Did not get correct reply subject: '%s'\n", matches[REPLY_INDEX]) - } - if matches[LEN_INDEX] != "0" { - t.Fatalf("Did not get correct msg length: '%s'\n", matches[LEN_INDEX]) - } -} - -func (c *client) parseFlushAndClose(op []byte) { - c.parse(op) - for cp := range c.pcd { - cp.mu.Lock() - cp.flushOutbound() - cp.mu.Unlock() - } - c.nc.Close() -} - -func TestClientPubWithQueueSub(t *testing.T) { - _, c, cr := setupClient() - - num := 100 - - // Queue SUB/PUB - subs := []byte("SUB foo g1 1\r\nSUB foo g1 2\r\n") - pubs := []byte("PUB foo bar 5\r\nhello\r\n") - op := []byte{} - op = append(op, subs...) - for i := 0; i < num; i++ { - op = append(op, pubs...) - } - - go c.parseFlushAndClose(op) - - var n1, n2, received int - for ; ; received++ { - l, err := cr.ReadString('\n') - if err != nil { - break - } - matches := msgPat.FindAllStringSubmatch(l, -1)[0] - - // Count which sub - switch matches[SID_INDEX] { - case "1": - n1++ - case "2": - n2++ - } - checkPayload(cr, []byte("hello\r\n"), t) - } - if received != num { - t.Fatalf("Received wrong # of msgs: %d vs %d\n", received, num) - } - // Threshold for randomness for now - if n1 < 20 || n2 < 20 { - t.Fatalf("Received wrong # of msgs per subscriber: %d - %d\n", n1, n2) - } -} - -func TestClientPubWithQueueSubNoEcho(t *testing.T) { - opts := DefaultOptions() - s := RunServer(opts) - defer s.Shutdown() - - nc1, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc1.Close() - - // Grab the client from server and set no echo by hand. - s.mu.Lock() - lc := len(s.clients) - c := s.clients[s.gcid] - s.mu.Unlock() - - if lc != 1 { - t.Fatalf("Expected only 1 client but got %d\n", lc) - } - if c == nil { - t.Fatal("Expected to retrieve client\n") - } - c.mu.Lock() - c.echo = false - c.mu.Unlock() - - // Queue sub on nc1. - _, err = nc1.QueueSubscribe("foo", "bar", func(*nats.Msg) {}) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - nc1.Flush() - - nc2, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc2.Close() - - n := int32(0) - cb := func(m *nats.Msg) { - atomic.AddInt32(&n, 1) - } - - _, err = nc2.QueueSubscribe("foo", "bar", cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - nc2.Flush() - - // Now publish 100 messages on nc1 which does not allow echo. - for i := 0; i < 100; i++ { - nc1.Publish("foo", []byte("Hello")) - } - nc1.Flush() - nc2.Flush() - - checkFor(t, 5*time.Second, 10*time.Millisecond, func() error { - num := atomic.LoadInt32(&n) - if num != int32(100) { - return fmt.Errorf("Expected all the msgs to be received by nc2, got %d\n", num) - } - return nil - }) -} - -func TestClientUnSub(t *testing.T) { - _, c, cr := setupClient() - - num := 1 - - // SUB/PUB - subs := []byte("SUB foo 1\r\nSUB foo 2\r\n") - unsub := []byte("UNSUB 1\r\n") - pub := []byte("PUB foo bar 5\r\nhello\r\n") - - op := []byte{} - op = append(op, subs...) - op = append(op, unsub...) - op = append(op, pub...) - - go c.parseFlushAndClose(op) - - var received int - for ; ; received++ { - l, err := cr.ReadString('\n') - if err != nil { - break - } - matches := msgPat.FindAllStringSubmatch(l, -1)[0] - if matches[SID_INDEX] != "2" { - t.Fatalf("Received msg on unsubscribed subscription!\n") - } - checkPayload(cr, []byte("hello\r\n"), t) - } - if received != num { - t.Fatalf("Received wrong # of msgs: %d vs %d\n", received, num) - } -} - -func TestClientUnSubMax(t *testing.T) { - _, c, cr := setupClient() - - num := 10 - exp := 5 - - // SUB/PUB - subs := []byte("SUB foo 1\r\n") - unsub := []byte("UNSUB 1 5\r\n") - pub := []byte("PUB foo bar 5\r\nhello\r\n") - - op := []byte{} - op = append(op, subs...) - op = append(op, unsub...) - for i := 0; i < num; i++ { - op = append(op, pub...) - } - - go c.parseFlushAndClose(op) - - var received int - for ; ; received++ { - l, err := cr.ReadString('\n') - if err != nil { - break - } - matches := msgPat.FindAllStringSubmatch(l, -1)[0] - if matches[SID_INDEX] != "1" { - t.Fatalf("Received msg on unsubscribed subscription!\n") - } - checkPayload(cr, []byte("hello\r\n"), t) - } - if received != exp { - t.Fatalf("Received wrong # of msgs: %d vs %d\n", received, exp) - } -} - -func TestClientAutoUnsubExactReceived(t *testing.T) { - _, c, _ := setupClient() - defer c.nc.Close() - - // SUB/PUB - subs := []byte("SUB foo 1\r\n") - unsub := []byte("UNSUB 1 1\r\n") - pub := []byte("PUB foo bar 2\r\nok\r\n") - - op := []byte{} - op = append(op, subs...) - op = append(op, unsub...) - op = append(op, pub...) - - ch := make(chan bool) - go func() { - c.parse(op) - ch <- true - }() - - // Wait for processing - <-ch - - // We should not have any subscriptions in place here. - if len(c.subs) != 0 { - t.Fatalf("Wrong number of subscriptions: expected 0, got %d\n", len(c.subs)) - } -} - -func TestClientUnsubAfterAutoUnsub(t *testing.T) { - _, c, _ := setupClient() - defer c.nc.Close() - - // SUB/UNSUB/UNSUB - subs := []byte("SUB foo 1\r\n") - asub := []byte("UNSUB 1 1\r\n") - unsub := []byte("UNSUB 1\r\n") - - op := []byte{} - op = append(op, subs...) - op = append(op, asub...) - op = append(op, unsub...) - - ch := make(chan bool) - go func() { - c.parse(op) - ch <- true - }() - - // Wait for processing - <-ch - - // We should not have any subscriptions in place here. - if len(c.subs) != 0 { - t.Fatalf("Wrong number of subscriptions: expected 0, got %d\n", len(c.subs)) - } -} - -func TestClientRemoveSubsOnDisconnect(t *testing.T) { - s, c, _ := setupClient() - subs := []byte("SUB foo 1\r\nSUB bar 2\r\n") - - ch := make(chan bool) - go func() { - c.parse(subs) - ch <- true - }() - <-ch - - if s.sl.Count() != 2 { - t.Fatalf("Should have 2 subscriptions, got %d\n", s.sl.Count()) - } - c.closeConnection(ClientClosed) - if s.sl.Count() != 0 { - t.Fatalf("Should have no subscriptions after close, got %d\n", s.sl.Count()) - } -} - -func TestClientDoesNotAddSubscriptionsWhenConnectionClosed(t *testing.T) { - s, c, _ := setupClient() - c.closeConnection(ClientClosed) - subs := []byte("SUB foo 1\r\nSUB bar 2\r\n") - - ch := make(chan bool) - go func() { - c.parse(subs) - ch <- true - }() - <-ch - - if s.sl.Count() != 0 { - t.Fatalf("Should have no subscriptions after close, got %d\n", s.sl.Count()) - } -} - -func TestClientMapRemoval(t *testing.T) { - s, c, _ := setupClient() - c.nc.Close() - - checkClientsCount(t, s, 0) -} - -func TestAuthorizationTimeout(t *testing.T) { - serverOptions := DefaultOptions() - serverOptions.Authorization = "my_token" - serverOptions.AuthTimeout = 0.4 - s := RunServer(serverOptions) - defer s.Shutdown() - - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverOptions.Host, serverOptions.Port)) - if err != nil { - t.Fatalf("Error dialing server: %v\n", err) - } - defer conn.Close() - client := bufio.NewReaderSize(conn, maxBufSize) - if _, err := client.ReadString('\n'); err != nil { - t.Fatalf("Error receiving info from server: %v\n", err) - } - time.Sleep(3 * secondsToDuration(serverOptions.AuthTimeout)) - l, err := client.ReadString('\n') - if err != nil { - t.Fatalf("Error receiving info from server: %v\n", err) - } - if !strings.Contains(l, "Authorization Timeout") { - t.Fatalf("Authorization Timeout response incorrect: %q\n", l) - } -} - -// This is from bug report #18 -func TestTwoTokenPubMatchSingleTokenSub(t *testing.T) { - _, c, cr := setupClient() - test := []byte("PUB foo.bar 5\r\nhello\r\nSUB foo 1\r\nPING\r\nPUB foo.bar 5\r\nhello\r\nPING\r\n") - go c.parse(test) - l, err := cr.ReadString('\n') - if err != nil { - t.Fatalf("Error receiving info from server: %v\n", err) - } - if !strings.HasPrefix(l, "PONG\r\n") { - t.Fatalf("PONG response incorrect: %q\n", l) - } - // Expect just a pong, no match should exist here.. - l, _ = cr.ReadString('\n') - if !strings.HasPrefix(l, "PONG\r\n") { - t.Fatalf("PONG response was expected, got: %q\n", l) - } -} - -func TestUnsubRace(t *testing.T) { - opts := DefaultOptions() - s := RunServer(opts) - defer s.Shutdown() - - url := fmt.Sprintf("nats://%s:%d", - s.getOpts().Host, - s.Addr().(*net.TCPAddr).Port, - ) - nc, err := nats.Connect(url) - if err != nil { - t.Fatalf("Error creating client to %s: %v\n", url, err) - } - defer nc.Close() - - ncp, err := nats.Connect(url) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer ncp.Close() - - sub, _ := nc.Subscribe("foo", func(m *nats.Msg) { - // Just eat it.. - }) - nc.Flush() - - var wg sync.WaitGroup - - wg.Add(1) - - go func() { - for i := 0; i < 10000; i++ { - ncp.Publish("foo", []byte("hello")) - } - wg.Done() - }() - - time.Sleep(5 * time.Millisecond) - - sub.Unsubscribe() - - wg.Wait() -} - -func TestTLSCloseClientConnection(t *testing.T) { - opts, err := ProcessConfigFile("./configs/tls.conf") - if err != nil { - t.Fatalf("Error processing config file: %v", err) - } - opts.TLSTimeout = 100 - opts.NoLog = true - opts.NoSigs = true - s := RunServer(opts) - defer s.Shutdown() - - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - conn, err := net.DialTimeout("tcp", endpoint, 2*time.Second) - if err != nil { - t.Fatalf("Unexpected error on dial: %v", err) - } - defer conn.Close() - br := bufio.NewReaderSize(conn, 100) - if _, err := br.ReadString('\n'); err != nil { - t.Fatalf("Unexpected error reading INFO: %v", err) - } - - tlsConn := tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) - defer tlsConn.Close() - if err := tlsConn.Handshake(); err != nil { - t.Fatalf("Unexpected error during handshake: %v", err) - } - br = bufio.NewReaderSize(tlsConn, 100) - connectOp := []byte("CONNECT {\"user\":\"derek\",\"pass\":\"foo\",\"verbose\":false,\"pedantic\":false,\"tls_required\":true}\r\n") - if _, err := tlsConn.Write(connectOp); err != nil { - t.Fatalf("Unexpected error writing CONNECT: %v", err) - } - if _, err := tlsConn.Write([]byte("PING\r\n")); err != nil { - t.Fatalf("Unexpected error writing PING: %v", err) - } - if _, err := br.ReadString('\n'); err != nil { - t.Fatalf("Unexpected error reading PONG: %v", err) - } - - // Check that client is registered. - checkClientsCount(t, s, 1) - var cli *client - s.mu.Lock() - for _, c := range s.clients { - cli = c - break - } - s.mu.Unlock() - if cli == nil { - t.Fatal("Did not register client on time") - } - // Test GetTLSConnectionState - state := cli.GetTLSConnectionState() - if state == nil { - t.Error("GetTLSConnectionState() returned nil") - } - // Fill the buffer. Need to send 1 byte at a time so that we timeout here - // the nc.Close() would block due to a write that can not complete. - done := false - for !done { - cli.nc.SetWriteDeadline(time.Now().Add(time.Second)) - if _, err := cli.nc.Write([]byte("a")); err != nil { - done = true - } - cli.nc.SetWriteDeadline(time.Time{}) - } - ch := make(chan bool) - go func() { - select { - case <-ch: - return - case <-time.After(3 * time.Second): - fmt.Println("!!!! closeConnection is blocked, test will hang !!!") - return - } - }() - // Close the client - cli.closeConnection(ClientClosed) - ch <- true -} - -// This tests issue #558 -func TestWildcardCharsInLiteralSubjectWorks(t *testing.T) { - opts := DefaultOptions() - s := RunServer(opts) - defer s.Shutdown() - - nc, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc.Close() - - ch := make(chan bool, 1) - // This subject is a literal even though it contains `*` and `>`, - // they are not treated as wildcards. - subj := "foo.bar,*,>,baz" - cb := func(_ *nats.Msg) { - ch <- true - } - for i := 0; i < 2; i++ { - sub, err := nc.Subscribe(subj, cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - if err := nc.Flush(); err != nil { - t.Fatalf("Error on flush: %v", err) - } - if err := nc.LastError(); err != nil { - t.Fatalf("Server reported error: %v", err) - } - if err := nc.Publish(subj, []byte("msg")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - select { - case <-ch: - case <-time.After(time.Second): - t.Fatalf("Should have received the message") - } - if err := sub.Unsubscribe(); err != nil { - t.Fatalf("Error on unsubscribe: %v", err) - } - } -} - -func TestDynamicBuffers(t *testing.T) { - opts := DefaultOptions() - s := RunServer(opts) - defer s.Shutdown() - - nc, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc.Close() - - // Grab the client from server. - s.mu.Lock() - lc := len(s.clients) - c := s.clients[s.gcid] - s.mu.Unlock() - - if lc != 1 { - t.Fatalf("Expected only 1 client but got %d\n", lc) - } - if c == nil { - t.Fatal("Expected to retrieve client\n") - } - - // Create some helper functions and data structures. - done := make(chan bool) // Used to stop recording. - type maxv struct{ rsz, wsz int } // Used to hold max values. - results := make(chan maxv) - - // stopRecording stops the recording ticker and releases go routine. - stopRecording := func() maxv { - done <- true - return <-results - } - // max just grabs max values. - max := func(a, b int) int { - if a > b { - return a - } - return b - } - // Returns current value of the buffer sizes. - getBufferSizes := func() (int, int) { - c.mu.Lock() - defer c.mu.Unlock() - return c.in.rsz, c.out.sz - } - // Record the max values seen. - recordMaxBufferSizes := func() { - ticker := time.NewTicker(10 * time.Microsecond) - defer ticker.Stop() - - var m maxv - - recordMax := func() { - rsz, wsz := getBufferSizes() - m.rsz = max(m.rsz, rsz) - m.wsz = max(m.wsz, wsz) - } - - for { - select { - case <-done: - recordMax() - results <- m - return - case <-ticker.C: - recordMax() - } - } - } - // Check that the current value is what we expected. - checkBuffers := func(ers, ews int) { - t.Helper() - rsz, wsz := getBufferSizes() - if rsz != ers { - t.Fatalf("Expected read buffer of %d, but got %d\n", ers, rsz) - } - if wsz != ews { - t.Fatalf("Expected write buffer of %d, but got %d\n", ews, wsz) - } - } - - // Check that the max was as expected. - checkResults := func(m maxv, rsz, wsz int) { - t.Helper() - if rsz != m.rsz { - t.Fatalf("Expected read buffer of %d, but got %d\n", rsz, m.rsz) - } - if wsz != m.wsz { - t.Fatalf("Expected write buffer of %d, but got %d\n", wsz, m.wsz) - } - } - - // Here is where testing begins.. - - // Should be at or below the startBufSize for both. - rsz, wsz := getBufferSizes() - if rsz > startBufSize { - t.Fatalf("Expected read buffer of <= %d, but got %d\n", startBufSize, rsz) - } - if wsz > startBufSize { - t.Fatalf("Expected write buffer of <= %d, but got %d\n", startBufSize, wsz) - } - - // Send some data. - data := make([]byte, 2048) - rand.Read(data) - - go recordMaxBufferSizes() - for i := 0; i < 200; i++ { - nc.Publish("foo", data) - } - nc.Flush() - m := stopRecording() - - if m.rsz != maxBufSize && m.rsz != maxBufSize/2 { - t.Fatalf("Expected read buffer of %d or %d, but got %d\n", maxBufSize, maxBufSize/2, m.rsz) - } - if m.wsz > startBufSize { - t.Fatalf("Expected write buffer of <= %d, but got %d\n", startBufSize, m.wsz) - } - - // Create Subscription to test outbound buffer from server. - nc.Subscribe("foo", func(m *nats.Msg) { - // Just eat it.. - }) - go recordMaxBufferSizes() - - for i := 0; i < 200; i++ { - nc.Publish("foo", data) - } - nc.Flush() - - m = stopRecording() - checkResults(m, maxBufSize, maxBufSize) - - // Now test that we shrink correctly. - - // Should go to minimum for both.. - for i := 0; i < 20; i++ { - nc.Flush() - } - checkBuffers(minBufSize, minBufSize) -} - -// Similar to the routed version. Make sure we receive all of the -// messages with auto-unsubscribe enabled. -func TestQueueAutoUnsubscribe(t *testing.T) { - opts := DefaultOptions() - s := RunServer(opts) - defer s.Shutdown() - - nc, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc.Close() - - rbar := int32(0) - barCb := func(m *nats.Msg) { - atomic.AddInt32(&rbar, 1) - } - rbaz := int32(0) - bazCb := func(m *nats.Msg) { - atomic.AddInt32(&rbaz, 1) - } - - // Create 1000 subscriptions with auto-unsubscribe of 1. - // Do two groups, one bar and one baz. - for i := 0; i < 1000; i++ { - qsub, err := nc.QueueSubscribe("foo", "bar", barCb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - if err := qsub.AutoUnsubscribe(1); err != nil { - t.Fatalf("Error on auto-unsubscribe: %v", err) - } - qsub, err = nc.QueueSubscribe("foo", "baz", bazCb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - if err := qsub.AutoUnsubscribe(1); err != nil { - t.Fatalf("Error on auto-unsubscribe: %v", err) - } - } - nc.Flush() - - expected := int32(1000) - for i := int32(0); i < expected; i++ { - nc.Publish("foo", []byte("Don't Drop Me!")) - } - nc.Flush() - - checkFor(t, 5*time.Second, 10*time.Millisecond, func() error { - nbar := atomic.LoadInt32(&rbar) - nbaz := atomic.LoadInt32(&rbaz) - if nbar == expected && nbaz == expected { - return nil - } - return fmt.Errorf("Did not receive all %d queue messages, received %d for 'bar' and %d for 'baz'", - expected, atomic.LoadInt32(&rbar), atomic.LoadInt32(&rbaz)) - }) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/closed_conns_test.go b/vendor/github.com/nats-io/gnatsd/server/closed_conns_test.go deleted file mode 100644 index e3a86a64..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/closed_conns_test.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "fmt" - "net" - "strings" - "testing" - "time" - - nats "github.com/nats-io/go-nats" -) - -func checkClosedConns(t *testing.T, s *Server, num int, wait time.Duration) { - t.Helper() - checkFor(t, wait, 5*time.Millisecond, func() error { - if nc := s.numClosedConns(); nc != num { - return fmt.Errorf("Closed conns expected to be %v, got %v", num, nc) - } - return nil - }) -} - -func checkTotalClosedConns(t *testing.T, s *Server, num uint64, wait time.Duration) { - t.Helper() - checkFor(t, wait, 5*time.Millisecond, func() error { - if nc := s.totalClosedConns(); nc != num { - return fmt.Errorf("Total closed conns expected to be %v, got %v", num, nc) - } - return nil - }) -} - -func TestClosedConnsAccounting(t *testing.T) { - opts := DefaultOptions() - opts.MaxClosedClients = 10 - - s := RunServer(opts) - defer s.Shutdown() - - wait := 20 * time.Millisecond - - nc, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - nc.Close() - - checkClosedConns(t, s, 1, wait) - - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 1, lc) - } - if conns[0].Cid != 1 { - t.Fatalf("Expected CID to be 1, got %d\n", conns[0].Cid) - } - - // Now create 21 more - for i := 0; i < 21; i++ { - nc, err = nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - nc.Close() - checkTotalClosedConns(t, s, uint64(i+2), wait) - } - - checkClosedConns(t, s, opts.MaxClosedClients, wait) - checkTotalClosedConns(t, s, 22, wait) - - conns = s.closedClients() - if lc := len(conns); lc != opts.MaxClosedClients { - t.Fatalf("len(conns) expected to be %d, got %d\n", - opts.MaxClosedClients, lc) - } - - // Set it to the start after overflow. - cid := uint64(22 - opts.MaxClosedClients) - for _, ci := range conns { - cid++ - if ci.Cid != cid { - t.Fatalf("Expected cid of %d, got %d\n", cid, ci.Cid) - } - } -} - -func TestClosedConnsSubsAccounting(t *testing.T) { - opts := DefaultOptions() - s := RunServer(opts) - defer s.Shutdown() - - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - nc, err := nats.Connect(url) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - - // Now create some subscriptions - numSubs := 10 - for i := 0; i < numSubs; i++ { - subj := fmt.Sprintf("foo.%d", i) - nc.Subscribe(subj, func(m *nats.Msg) {}) - } - nc.Flush() - nc.Close() - - checkClosedConns(t, s, 1, 20*time.Millisecond) - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be 1, got %d\n", lc) - } - ci := conns[0] - - if len(ci.subs) != numSubs { - t.Fatalf("Expected number of Subs to be %d, got %d\n", numSubs, len(ci.subs)) - } -} - -func checkReason(t *testing.T, reason string, expected ClosedState) { - if !strings.Contains(reason, expected.String()) { - t.Fatalf("Expected closed connection with `%s` state, got `%s`\n", - expected, reason) - } -} - -func TestClosedAuthorizationTimeout(t *testing.T) { - serverOptions := DefaultOptions() - serverOptions.Authorization = "my_token" - serverOptions.AuthTimeout = 0.4 - s := RunServer(serverOptions) - defer s.Shutdown() - - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverOptions.Host, serverOptions.Port)) - if err != nil { - t.Fatalf("Error dialing server: %v\n", err) - } - defer conn.Close() - - checkClosedConns(t, s, 1, 2*time.Second) - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 1, lc) - } - checkReason(t, conns[0].Reason, AuthenticationTimeout) -} - -func TestClosedAuthorizationViolation(t *testing.T) { - serverOptions := DefaultOptions() - serverOptions.Authorization = "my_token" - s := RunServer(serverOptions) - defer s.Shutdown() - - opts := s.getOpts() - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - nc, err := nats.Connect(url) - if err == nil { - nc.Close() - t.Fatal("Expected failure for connection") - } - - checkClosedConns(t, s, 1, 2*time.Second) - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 1, lc) - } - checkReason(t, conns[0].Reason, AuthenticationViolation) -} - -func TestClosedUPAuthorizationViolation(t *testing.T) { - serverOptions := DefaultOptions() - serverOptions.Username = "my_user" - serverOptions.Password = "my_secret" - s := RunServer(serverOptions) - defer s.Shutdown() - - opts := s.getOpts() - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - nc, err := nats.Connect(url) - if err == nil { - nc.Close() - t.Fatal("Expected failure for connection") - } - - url2 := fmt.Sprintf("nats://my_user:wrong_pass@%s:%d", opts.Host, opts.Port) - nc, err = nats.Connect(url2) - if err == nil { - nc.Close() - t.Fatal("Expected failure for connection") - } - - checkClosedConns(t, s, 2, 2*time.Second) - conns := s.closedClients() - if lc := len(conns); lc != 2 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 2, lc) - } - checkReason(t, conns[0].Reason, AuthenticationViolation) - checkReason(t, conns[1].Reason, AuthenticationViolation) -} - -func TestClosedMaxPayload(t *testing.T) { - serverOptions := DefaultOptions() - serverOptions.MaxPayload = 100 - - s := RunServer(serverOptions) - defer s.Shutdown() - - opts := s.getOpts() - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - - conn, err := net.DialTimeout("tcp", endpoint, time.Second) - if err != nil { - t.Fatalf("Could not make a raw connection to the server: %v", err) - } - defer conn.Close() - - // This should trigger it. - pub := fmt.Sprintf("PUB foo.bar 1024\r\n") - conn.Write([]byte(pub)) - - checkClosedConns(t, s, 1, 2*time.Second) - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 1, lc) - } - checkReason(t, conns[0].Reason, MaxPayloadExceeded) -} - -func TestClosedSlowConsumerWriteDeadline(t *testing.T) { - opts := DefaultOptions() - opts.WriteDeadline = 10 * time.Millisecond // Make very small to trip. - opts.MaxPending = 500 * 1024 * 1024 // Set high so it will not trip here. - s := RunServer(opts) - defer s.Shutdown() - - c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", opts.Host, opts.Port), 3*time.Second) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer c.Close() - if _, err := c.Write([]byte("CONNECT {}\r\nPING\r\nSUB foo 1\r\n")); err != nil { - t.Fatalf("Error sending protocols to server: %v", err) - } - // Reduce socket buffer to increase reliability of data backing up in the server destined - // for our subscribed client. - c.(*net.TCPConn).SetReadBuffer(128) - - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - sender, err := nats.Connect(url) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer sender.Close() - - payload := make([]byte, 1024*1024) - for i := 0; i < 100; i++ { - if err := sender.Publish("foo", payload); err != nil { - t.Fatalf("Error on publish: %v", err) - } - } - - // Flush sender connection to ensure that all data has been sent. - if err := sender.Flush(); err != nil { - t.Fatalf("Error on flush: %v", err) - } - - // At this point server should have closed connection c. - checkClosedConns(t, s, 1, 2*time.Second) - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 1, lc) - } - checkReason(t, conns[0].Reason, SlowConsumerWriteDeadline) -} - -func TestClosedSlowConsumerPendingBytes(t *testing.T) { - opts := DefaultOptions() - opts.WriteDeadline = 30 * time.Second // Wait for long time so write deadline does not trigger slow consumer. - opts.MaxPending = 1 * 1024 * 1024 // Set to low value (1MB) to allow SC to trip. - s := RunServer(opts) - defer s.Shutdown() - - c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", opts.Host, opts.Port), 3*time.Second) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer c.Close() - if _, err := c.Write([]byte("CONNECT {}\r\nPING\r\nSUB foo 1\r\n")); err != nil { - t.Fatalf("Error sending protocols to server: %v", err) - } - // Reduce socket buffer to increase reliability of data backing up in the server destined - // for our subscribed client. - c.(*net.TCPConn).SetReadBuffer(128) - - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - sender, err := nats.Connect(url) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer sender.Close() - - payload := make([]byte, 1024*1024) - for i := 0; i < 100; i++ { - if err := sender.Publish("foo", payload); err != nil { - t.Fatalf("Error on publish: %v", err) - } - } - - // Flush sender connection to ensure that all data has been sent. - if err := sender.Flush(); err != nil { - t.Fatalf("Error on flush: %v", err) - } - - // At this point server should have closed connection c. - checkClosedConns(t, s, 1, 2*time.Second) - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 1, lc) - } - checkReason(t, conns[0].Reason, SlowConsumerPendingBytes) -} - -func TestClosedTLSHandshake(t *testing.T) { - opts, err := ProcessConfigFile("./configs/tls.conf") - if err != nil { - t.Fatalf("Error processing config file: %v", err) - } - opts.TLSVerify = true - opts.NoLog = true - opts.NoSigs = true - s := RunServer(opts) - defer s.Shutdown() - - nc, err := nats.Connect(fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port)) - if err == nil { - nc.Close() - t.Fatal("Expected failure for connection") - } - - checkClosedConns(t, s, 1, 2*time.Second) - conns := s.closedClients() - if lc := len(conns); lc != 1 { - t.Fatalf("len(conns) expected to be %d, got %d\n", 1, lc) - } - checkReason(t, conns[0].Reason, TLSHandshakeError) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/const.go b/vendor/github.com/nats-io/gnatsd/server/const.go deleted file mode 100644 index b27ae5d9..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/const.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "time" -) - -// Command is a signal used to control a running gnatsd process. -type Command string - -// Valid Command values. -const ( - CommandStop = Command("stop") - CommandQuit = Command("quit") - CommandReopen = Command("reopen") - CommandReload = Command("reload") -) - -var ( - // gitCommit injected at build - gitCommit string -) - -const ( - // VERSION is the current version for the server. - VERSION = "1.2.0" - - // PROTO is the currently supported protocol. - // 0 was the original - // 1 maintains proto 0, adds echo abilities for CONNECT from the client. Clients - // should not send echo unless proto in INFO is >= 1. - PROTO = 1 - - // DEFAULT_PORT is the default port for client connections. - DEFAULT_PORT = 4222 - - // RANDOM_PORT is the value for port that, when supplied, will cause the - // server to listen on a randomly-chosen available port. The resolved port - // is available via the Addr() method. - RANDOM_PORT = -1 - - // DEFAULT_HOST defaults to all interfaces. - DEFAULT_HOST = "0.0.0.0" - - // MAX_CONTROL_LINE_SIZE is the maximum allowed protocol control line size. - // 1k should be plenty since payloads sans connect string are separate - MAX_CONTROL_LINE_SIZE = 1024 - - // MAX_PAYLOAD_SIZE is the maximum allowed payload size. Should be using - // something different if > 1MB payloads are needed. - MAX_PAYLOAD_SIZE = (1024 * 1024) - - // MAX_PENDING_SIZE is the maximum outbound pending bytes per client. - MAX_PENDING_SIZE = (256 * 1024 * 1024) - - // DEFAULT_MAX_CONNECTIONS is the default maximum connections allowed. - DEFAULT_MAX_CONNECTIONS = (64 * 1024) - - // TLS_TIMEOUT is the TLS wait time. - TLS_TIMEOUT = 500 * time.Millisecond - - // AUTH_TIMEOUT is the authorization wait time. - AUTH_TIMEOUT = 2 * TLS_TIMEOUT - - // DEFAULT_PING_INTERVAL is how often pings are sent to clients and routes. - DEFAULT_PING_INTERVAL = 2 * time.Minute - - // DEFAULT_PING_MAX_OUT is maximum allowed pings outstanding before disconnect. - DEFAULT_PING_MAX_OUT = 2 - - // CR_LF string - CR_LF = "\r\n" - - // LEN_CR_LF hold onto the computed size. - LEN_CR_LF = len(CR_LF) - - // DEFAULT_FLUSH_DEADLINE is the write/flush deadlines. - DEFAULT_FLUSH_DEADLINE = 2 * time.Second - - // DEFAULT_HTTP_PORT is the default monitoring port. - DEFAULT_HTTP_PORT = 8222 - - // ACCEPT_MIN_SLEEP is the minimum acceptable sleep times on temporary errors. - ACCEPT_MIN_SLEEP = 10 * time.Millisecond - - // ACCEPT_MAX_SLEEP is the maximum acceptable sleep times on temporary errors - ACCEPT_MAX_SLEEP = 1 * time.Second - - // DEFAULT_ROUTE_CONNECT Route solicitation intervals. - DEFAULT_ROUTE_CONNECT = 1 * time.Second - - // DEFAULT_ROUTE_RECONNECT Route reconnect intervals. - DEFAULT_ROUTE_RECONNECT = 1 * time.Second - - // DEFAULT_ROUTE_DIAL Route dial timeout. - DEFAULT_ROUTE_DIAL = 1 * time.Second - - // PROTO_SNIPPET_SIZE is the default size of proto to print on parse errors. - PROTO_SNIPPET_SIZE = 32 - - // MAX_MSG_ARGS Maximum possible number of arguments from MSG proto. - MAX_MSG_ARGS = 4 - - // MAX_PUB_ARGS Maximum possible number of arguments from PUB proto. - MAX_PUB_ARGS = 3 - - // DEFAULT_REMOTE_QSUBS_SWEEPER - DEFAULT_REMOTE_QSUBS_SWEEPER = 30 * time.Second - - // DEFAULT_MAX_CLOSED_CLIENTS - DEFAULT_MAX_CLOSED_CLIENTS = 10000 -) diff --git a/vendor/github.com/nats-io/gnatsd/server/errors.go b/vendor/github.com/nats-io/gnatsd/server/errors.go deleted file mode 100644 index c722bfc4..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/errors.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import "errors" - -var ( - // ErrConnectionClosed represents an error condition on a closed connection. - ErrConnectionClosed = errors.New("Connection Closed") - - // ErrAuthorization represents an error condition on failed authorization. - ErrAuthorization = errors.New("Authorization Error") - - // ErrAuthTimeout represents an error condition on failed authorization due to timeout. - ErrAuthTimeout = errors.New("Authorization Timeout") - - // ErrMaxPayload represents an error condition when the payload is too big. - ErrMaxPayload = errors.New("Maximum Payload Exceeded") - - // ErrMaxControlLine represents an error condition when the control line is too big. - ErrMaxControlLine = errors.New("Maximum Control Line Exceeded") - - // ErrReservedPublishSubject represents an error condition when sending to a reserved subject, e.g. _SYS.> - ErrReservedPublishSubject = errors.New("Reserved Internal Subject") - - // ErrBadClientProtocol signals a client requested an invalud client protocol. - ErrBadClientProtocol = errors.New("Invalid Client Protocol") - - // ErrTooManyConnections signals a client that the maximum number of connections supported by the - // server has been reached. - ErrTooManyConnections = errors.New("Maximum Connections Exceeded") - - // ErrTooManySubs signals a client that the maximum number of subscriptions per connection - // has been reached. - ErrTooManySubs = errors.New("Maximum Subscriptions Exceeded") - - // ErrClientConnectedToRoutePort represents an error condition when a client - // attempted to connect to the route listen port. - ErrClientConnectedToRoutePort = errors.New("Attempted To Connect To Route Port") -) diff --git a/vendor/github.com/nats-io/gnatsd/server/log.go b/vendor/github.com/nats-io/gnatsd/server/log.go deleted file mode 100644 index 8c2be370..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/log.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "io" - "os" - "sync/atomic" - - srvlog "github.com/nats-io/gnatsd/logger" -) - -// Logger interface of the NATS Server -type Logger interface { - - // Log a notice statement - Noticef(format string, v ...interface{}) - - // Log a fatal error - Fatalf(format string, v ...interface{}) - - // Log an error - Errorf(format string, v ...interface{}) - - // Log a debug statement - Debugf(format string, v ...interface{}) - - // Log a trace statement - Tracef(format string, v ...interface{}) -} - -// ConfigureLogger configures and sets the logger for the server. -func (s *Server) ConfigureLogger() { - var ( - log Logger - - // Snapshot server options. - opts = s.getOpts() - ) - - syslog := opts.Syslog - if isWindowsService() && opts.LogFile == "" { - // Enable syslog if no log file is specified and we're running as a - // Windows service so that logs are written to the Windows event log. - syslog = true - } - - if opts.LogFile != "" { - log = srvlog.NewFileLogger(opts.LogFile, opts.Logtime, opts.Debug, opts.Trace, true) - } else if opts.RemoteSyslog != "" { - log = srvlog.NewRemoteSysLogger(opts.RemoteSyslog, opts.Debug, opts.Trace) - } else if syslog { - log = srvlog.NewSysLogger(opts.Debug, opts.Trace) - } else { - colors := true - // Check to see if stderr is being redirected and if so turn off color - // Also turn off colors if we're running on Windows where os.Stderr.Stat() returns an invalid handle-error - stat, err := os.Stderr.Stat() - if err != nil || (stat.Mode()&os.ModeCharDevice) == 0 { - colors = false - } - log = srvlog.NewStdLogger(opts.Logtime, opts.Debug, opts.Trace, colors, true) - } - - s.SetLogger(log, opts.Debug, opts.Trace) -} - -// SetLogger sets the logger of the server -func (s *Server) SetLogger(logger Logger, debugFlag, traceFlag bool) { - if debugFlag { - atomic.StoreInt32(&s.logging.debug, 1) - } else { - atomic.StoreInt32(&s.logging.debug, 0) - } - if traceFlag { - atomic.StoreInt32(&s.logging.trace, 1) - } else { - atomic.StoreInt32(&s.logging.trace, 0) - } - s.logging.Lock() - if s.logging.logger != nil { - // Check to see if the logger implements io.Closer. This could be a - // logger from another process embedding the NATS server or a dummy - // test logger that may not implement that interface. - if l, ok := s.logging.logger.(io.Closer); ok { - if err := l.Close(); err != nil { - s.Errorf("Error closing logger: %v", err) - } - } - } - s.logging.logger = logger - s.logging.Unlock() -} - -// If the logger is a file based logger, close and re-open the file. -// This allows for file rotation by 'mv'ing the file then signaling -// the process to trigger this function. -func (s *Server) ReOpenLogFile() { - // Check to make sure this is a file logger. - s.logging.RLock() - ll := s.logging.logger - s.logging.RUnlock() - - if ll == nil { - s.Noticef("File log re-open ignored, no logger") - return - } - - // Snapshot server options. - opts := s.getOpts() - - if opts.LogFile == "" { - s.Noticef("File log re-open ignored, not a file logger") - } else { - fileLog := srvlog.NewFileLogger(opts.LogFile, - opts.Logtime, opts.Debug, opts.Trace, true) - s.SetLogger(fileLog, opts.Debug, opts.Trace) - s.Noticef("File log re-opened") - } -} - -// Noticef logs a notice statement -func (s *Server) Noticef(format string, v ...interface{}) { - s.executeLogCall(func(logger Logger, format string, v ...interface{}) { - logger.Noticef(format, v...) - }, format, v...) -} - -// Errorf logs an error -func (s *Server) Errorf(format string, v ...interface{}) { - s.executeLogCall(func(logger Logger, format string, v ...interface{}) { - logger.Errorf(format, v...) - }, format, v...) -} - -// Fatalf logs a fatal error -func (s *Server) Fatalf(format string, v ...interface{}) { - s.executeLogCall(func(logger Logger, format string, v ...interface{}) { - logger.Fatalf(format, v...) - }, format, v...) -} - -// Debugf logs a debug statement -func (s *Server) Debugf(format string, v ...interface{}) { - if atomic.LoadInt32(&s.logging.debug) == 0 { - return - } - - s.executeLogCall(func(logger Logger, format string, v ...interface{}) { - logger.Debugf(format, v...) - }, format, v...) -} - -// Tracef logs a trace statement -func (s *Server) Tracef(format string, v ...interface{}) { - if atomic.LoadInt32(&s.logging.trace) == 0 { - return - } - - s.executeLogCall(func(logger Logger, format string, v ...interface{}) { - logger.Tracef(format, v...) - }, format, v...) -} - -func (s *Server) executeLogCall(f func(logger Logger, format string, v ...interface{}), format string, args ...interface{}) { - s.logging.RLock() - defer s.logging.RUnlock() - if s.logging.logger == nil { - return - } - - f(s.logging.logger, format, args...) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/log_test.go b/vendor/github.com/nats-io/gnatsd/server/log_test.go deleted file mode 100644 index 78aea10f..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/log_test.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "fmt" - "io/ioutil" - "os" - "runtime" - "strings" - "sync" - "testing" - - "github.com/nats-io/gnatsd/logger" -) - -func TestSetLogger(t *testing.T) { - server := &Server{} - defer server.SetLogger(nil, false, false) - dl := &DummyLogger{} - server.SetLogger(dl, true, true) - - // We assert that the logger has change to the DummyLogger - _ = server.logging.logger.(*DummyLogger) - - if server.logging.debug != 1 { - t.Fatalf("Expected debug 1, received value %d\n", server.logging.debug) - } - - if server.logging.trace != 1 { - t.Fatalf("Expected trace 1, received value %d\n", server.logging.trace) - } - - // Check traces - expectedStr := "This is a Notice" - server.Noticef(expectedStr) - dl.checkContent(t, expectedStr) - expectedStr = "This is an Error" - server.Errorf(expectedStr) - dl.checkContent(t, expectedStr) - expectedStr = "This is a Fatal" - server.Fatalf(expectedStr) - dl.checkContent(t, expectedStr) - expectedStr = "This is a Debug" - server.Debugf(expectedStr) - dl.checkContent(t, expectedStr) - expectedStr = "This is a Trace" - server.Tracef(expectedStr) - dl.checkContent(t, expectedStr) - - // Make sure that we can reset to fal - server.SetLogger(dl, false, false) - if server.logging.debug != 0 { - t.Fatalf("Expected debug 0, got %v", server.logging.debug) - } - if server.logging.trace != 0 { - t.Fatalf("Expected trace 0, got %v", server.logging.trace) - } - // Now, Debug and Trace should not produce anything - dl.msg = "" - server.Debugf("This Debug should not be traced") - dl.checkContent(t, "") - server.Tracef("This Trace should not be traced") - dl.checkContent(t, "") -} - -type DummyLogger struct { - sync.Mutex - msg string -} - -func (l *DummyLogger) checkContent(t *testing.T, expectedStr string) { - l.Lock() - defer l.Unlock() - if l.msg != expectedStr { - stackFatalf(t, "Expected log to be: %v, got %v", expectedStr, l.msg) - } -} - -func (l *DummyLogger) Noticef(format string, v ...interface{}) { - l.Lock() - defer l.Unlock() - l.msg = fmt.Sprintf(format, v...) -} -func (l *DummyLogger) Errorf(format string, v ...interface{}) { - l.Lock() - defer l.Unlock() - l.msg = fmt.Sprintf(format, v...) -} -func (l *DummyLogger) Fatalf(format string, v ...interface{}) { - l.Lock() - defer l.Unlock() - l.msg = fmt.Sprintf(format, v...) -} -func (l *DummyLogger) Debugf(format string, v ...interface{}) { - l.Lock() - defer l.Unlock() - l.msg = fmt.Sprintf(format, v...) -} -func (l *DummyLogger) Tracef(format string, v ...interface{}) { - l.Lock() - defer l.Unlock() - l.msg = fmt.Sprintf(format, v...) -} - -func TestReOpenLogFile(t *testing.T) { - // We can't rename the file log when still opened on Windows, so skip - if runtime.GOOS == "windows" { - t.SkipNow() - } - s := &Server{opts: &Options{}} - defer s.SetLogger(nil, false, false) - - // First check with no logger - s.SetLogger(nil, false, false) - s.ReOpenLogFile() - - // Then when LogFile is not provided. - dl := &DummyLogger{} - s.SetLogger(dl, false, false) - s.ReOpenLogFile() - dl.checkContent(t, "File log re-open ignored, not a file logger") - - // Set a File log - s.opts.LogFile = "test.log" - defer os.Remove(s.opts.LogFile) - defer os.Remove(s.opts.LogFile + ".bak") - fileLog := logger.NewFileLogger(s.opts.LogFile, s.opts.Logtime, s.opts.Debug, s.opts.Trace, true) - s.SetLogger(fileLog, false, false) - // Add some log - expectedStr := "This is a Notice" - s.Noticef(expectedStr) - // Check content of log - buf, err := ioutil.ReadFile(s.opts.LogFile) - if err != nil { - t.Fatalf("Error reading file: %v", err) - } - if !strings.Contains(string(buf), expectedStr) { - t.Fatalf("Expected log to contain: %q, got %q", expectedStr, string(buf)) - } - // Close the file and rename it - if err := os.Rename(s.opts.LogFile, s.opts.LogFile+".bak"); err != nil { - t.Fatalf("Unable to rename log file: %v", err) - } - // Now re-open LogFile - s.ReOpenLogFile() - // Content should indicate that we have re-opened the log - buf, err = ioutil.ReadFile(s.opts.LogFile) - if err != nil { - t.Fatalf("Error reading file: %v", err) - } - if strings.HasSuffix(string(buf), "File log-reopened") { - t.Fatalf("File should indicate that file log was re-opened, got: %v", string(buf)) - } - // Make sure we can append to the log - s.Noticef("New message") - buf, err = ioutil.ReadFile(s.opts.LogFile) - if err != nil { - t.Fatalf("Error reading file: %v", err) - } - if strings.HasSuffix(string(buf), "New message") { - t.Fatalf("New message was not appended after file was re-opened, got: %v", string(buf)) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/monitor.go b/vendor/github.com/nats-io/gnatsd/server/monitor.go deleted file mode 100644 index 550b8083..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/monitor.go +++ /dev/null @@ -1,1029 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "crypto/tls" - "encoding/json" - "fmt" - "net" - "net/http" - "runtime" - "sort" - "strconv" - "strings" - "sync/atomic" - "time" - - "github.com/nats-io/gnatsd/server/pse" -) - -// Snapshot this -var numCores int - -func init() { - numCores = runtime.NumCPU() -} - -// Connz represents detailed information on current client connections. -type Connz struct { - ID string `json:"server_id"` - Now time.Time `json:"now"` - NumConns int `json:"num_connections"` - Total int `json:"total"` - Offset int `json:"offset"` - Limit int `json:"limit"` - Conns []*ConnInfo `json:"connections"` -} - -// ConnzOptions are the options passed to Connz() -type ConnzOptions struct { - // Sort indicates how the results will be sorted. Check SortOpt for possible values. - // Only the sort by connection ID (ByCid) is ascending, all others are descending. - Sort SortOpt `json:"sort"` - - // Username indicates if user names should be included in the results. - Username bool `json:"auth"` - - // Subscriptions indicates if subscriptions should be included in the results. - Subscriptions bool `json:"subscriptions"` - - // Offset is used for pagination. Connz() only returns connections starting at this - // offset from the global results. - Offset int `json:"offset"` - - // Limit is the maximum number of connections that should be returned by Connz(). - Limit int `json:"limit"` - - // Filter for this explicit client connection. - CID uint64 `json:"cid"` - - // Filter by connection state. - State ConnState `json:"state"` -} - -// For filtering states of connections. We will only have two, open and closed. -type ConnState int - -const ( - ConnOpen = ConnState(iota) - ConnClosed - ConnAll -) - -// ConnInfo has detailed information on a per connection basis. -type ConnInfo struct { - Cid uint64 `json:"cid"` - IP string `json:"ip"` - Port int `json:"port"` - Start time.Time `json:"start"` - LastActivity time.Time `json:"last_activity"` - Stop *time.Time `json:"stop,omitempty"` - Reason string `json:"reason,omitempty"` - RTT string `json:"rtt,omitempty"` - Uptime string `json:"uptime"` - Idle string `json:"idle"` - Pending int `json:"pending_bytes"` - InMsgs int64 `json:"in_msgs"` - OutMsgs int64 `json:"out_msgs"` - InBytes int64 `json:"in_bytes"` - OutBytes int64 `json:"out_bytes"` - NumSubs uint32 `json:"subscriptions"` - Name string `json:"name,omitempty"` - Lang string `json:"lang,omitempty"` - Version string `json:"version,omitempty"` - TLSVersion string `json:"tls_version,omitempty"` - TLSCipher string `json:"tls_cipher_suite,omitempty"` - AuthorizedUser string `json:"authorized_user,omitempty"` - Subs []string `json:"subscriptions_list,omitempty"` -} - -// DefaultConnListSize is the default size of the connection list. -const DefaultConnListSize = 1024 - -// DefaultSubListSize is the default size of the subscriptions list. -const DefaultSubListSize = 1024 - -const defaultStackBufSize = 10000 - -// Connz returns a Connz struct containing inormation about connections. -func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) { - var ( - sortOpt = ByCid - auth bool - subs bool - offset int - limit = DefaultConnListSize - cid = uint64(0) - state = ConnOpen - ) - - if opts != nil { - // If no sort option given or sort is by uptime, then sort by cid - if opts.Sort == "" { - sortOpt = ByCid - } else { - sortOpt = opts.Sort - if !sortOpt.IsValid() { - return nil, fmt.Errorf("Invalid sorting option: %s", sortOpt) - } - } - auth = opts.Username - subs = opts.Subscriptions - offset = opts.Offset - if offset < 0 { - offset = 0 - } - limit = opts.Limit - if limit <= 0 { - limit = DefaultConnListSize - } - // state - state = opts.State - - // ByStop only makes sense on closed connections - if sortOpt == ByStop && state != ConnClosed { - return nil, fmt.Errorf("Sort by stop only valid on closed connections") - } - // ByReason is the same. - if sortOpt == ByReason && state != ConnClosed { - return nil, fmt.Errorf("Sort by reason only valid on closed connections") - } - - // If searching by CID - if opts.CID > 0 { - cid = opts.CID - limit = 1 - } - } - - c := &Connz{ - Offset: offset, - Limit: limit, - Now: time.Now(), - } - - // Open clients - var openClients []*client - // Hold for closed clients if requested. - var closedClients []*closedClient - - // Walk the open client list with server lock held. - s.mu.Lock() - - // copy the server id for monitoring - c.ID = s.info.ID - - // Number of total clients. The resulting ConnInfo array - // may be smaller if pagination is used. - switch state { - case ConnOpen: - c.Total = len(s.clients) - case ConnClosed: - c.Total = s.closed.len() - closedClients = s.closed.closedClients() - case ConnAll: - c.Total = len(s.clients) + s.closed.len() - closedClients = s.closed.closedClients() - } - - totalClients := c.Total - if cid > 0 { // Meaning we only want 1. - totalClients = 1 - } - if state == ConnOpen || state == ConnAll { - openClients = make([]*client, 0, totalClients) - } - - // Data structures for results. - var conns []ConnInfo // Limits allocs for actual ConnInfos. - var pconns ConnInfos - - switch state { - case ConnOpen: - conns = make([]ConnInfo, totalClients) - pconns = make(ConnInfos, totalClients) - case ConnClosed: - pconns = make(ConnInfos, totalClients) - case ConnAll: - conns = make([]ConnInfo, cap(openClients)) - pconns = make(ConnInfos, totalClients) - } - - // Search by individual CID. - if cid > 0 { - if state == ConnClosed || state == ConnAll { - copyClosed := closedClients - closedClients = nil - for _, cc := range copyClosed { - if cc.Cid == cid { - closedClients = []*closedClient{cc} - break - } - } - } else if state == ConnOpen || state == ConnAll { - client := s.clients[cid] - if client != nil { - openClients = append(openClients, client) - } - } - } else { - // Gather all open clients. - if state == ConnOpen || state == ConnAll { - for _, client := range s.clients { - openClients = append(openClients, client) - } - } - } - s.mu.Unlock() - - // Just return with empty array if nothing here. - if len(openClients) == 0 && len(closedClients) == 0 { - c.Conns = ConnInfos{} - return c, nil - } - - // Now whip through and generate ConnInfo entries - - // Open Clients - i := 0 - for _, client := range openClients { - client.mu.Lock() - ci := &conns[i] - ci.fill(client, client.nc, c.Now) - // Fill in subscription data if requested. - if subs && len(client.subs) > 0 { - ci.Subs = make([]string, 0, len(client.subs)) - for _, sub := range client.subs { - ci.Subs = append(ci.Subs, string(sub.subject)) - } - } - // Fill in user if auth requested. - if auth { - ci.AuthorizedUser = client.opts.Username - } - client.mu.Unlock() - pconns[i] = ci - i++ - } - // Closed Clients - var needCopy bool - if subs || auth { - needCopy = true - } - for _, cc := range closedClients { - // Copy if needed for any changes to the ConnInfo - if needCopy { - cx := *cc - cc = &cx - } - // Fill in subscription data if requested. - if subs && len(cc.subs) > 0 { - cc.Subs = cc.subs - } - // Fill in user if auth requested. - if auth { - cc.AuthorizedUser = cc.user - } - pconns[i] = &cc.ConnInfo - i++ - } - - switch sortOpt { - case ByCid, ByStart: - sort.Sort(byCid{pconns}) - case BySubs: - sort.Sort(sort.Reverse(bySubs{pconns})) - case ByPending: - sort.Sort(sort.Reverse(byPending{pconns})) - case ByOutMsgs: - sort.Sort(sort.Reverse(byOutMsgs{pconns})) - case ByInMsgs: - sort.Sort(sort.Reverse(byInMsgs{pconns})) - case ByOutBytes: - sort.Sort(sort.Reverse(byOutBytes{pconns})) - case ByInBytes: - sort.Sort(sort.Reverse(byInBytes{pconns})) - case ByLast: - sort.Sort(sort.Reverse(byLast{pconns})) - case ByIdle: - sort.Sort(sort.Reverse(byIdle{pconns})) - case ByUptime: - sort.Sort(byUptime{pconns, time.Now()}) - case ByStop: - sort.Sort(sort.Reverse(byStop{pconns})) - case ByReason: - sort.Sort(byReason{pconns}) - } - - minoff := c.Offset - maxoff := c.Offset + c.Limit - - maxIndex := totalClients - - // Make sure these are sane. - if minoff > maxIndex { - minoff = maxIndex - } - if maxoff > maxIndex { - maxoff = maxIndex - } - - // Now pare down to the requested size. - // TODO(dlc) - for very large number of connections we - // could save the whole list in a hash, send hash on first - // request and allow users to use has for subsequent pages. - // Low TTL, say < 1sec. - c.Conns = pconns[minoff:maxoff] - c.NumConns = len(c.Conns) - - return c, nil -} - -// Fills in the ConnInfo from the client. -// client should be locked. -func (ci *ConnInfo) fill(client *client, nc net.Conn, now time.Time) { - ci.Cid = client.cid - ci.Start = client.start - ci.LastActivity = client.last - ci.Uptime = myUptime(now.Sub(client.start)) - ci.Idle = myUptime(now.Sub(client.last)) - ci.RTT = client.getRTT() - ci.OutMsgs = client.outMsgs - ci.OutBytes = client.outBytes - ci.NumSubs = uint32(len(client.subs)) - ci.Pending = int(client.out.pb) - ci.Name = client.opts.Name - ci.Lang = client.opts.Lang - ci.Version = client.opts.Version - // inMsgs and inBytes are updated outside of the client's lock, so - // we need to use atomic here. - ci.InMsgs = atomic.LoadInt64(&client.inMsgs) - ci.InBytes = atomic.LoadInt64(&client.inBytes) - - // If the connection is gone, too bad, we won't set TLSVersion and TLSCipher. - // Exclude clients that are still doing handshake so we don't block in - // ConnectionState(). - if client.flags.isSet(handshakeComplete) && nc != nil { - conn := nc.(*tls.Conn) - cs := conn.ConnectionState() - ci.TLSVersion = tlsVersion(cs.Version) - ci.TLSCipher = tlsCipher(cs.CipherSuite) - } - - switch conn := nc.(type) { - case *net.TCPConn, *tls.Conn: - addr := conn.RemoteAddr().(*net.TCPAddr) - ci.Port = addr.Port - ci.IP = addr.IP.String() - } -} - -// Assume lock is held -func (c *client) getRTT() string { - if c.rtt == 0 { - // If a real client, go ahead and send ping now to get a value - // for RTT. For tests and telnet, etc skip. - if c.flags.isSet(connectReceived) && c.opts.Lang != "" { - c.sendPing() - } - return "" - } - var rtt time.Duration - if c.rtt > time.Microsecond && c.rtt < time.Millisecond { - rtt = c.rtt.Truncate(time.Microsecond) - } else { - rtt = c.rtt.Truncate(time.Millisecond) - } - return rtt.String() -} - -func decodeBool(w http.ResponseWriter, r *http.Request, param string) (bool, error) { - str := r.URL.Query().Get(param) - if str == "" { - return false, nil - } - val, err := strconv.ParseBool(str) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("Error decoding boolean for '%s': %v", param, err))) - return false, err - } - return val, nil -} - -func decodeUint64(w http.ResponseWriter, r *http.Request, param string) (uint64, error) { - str := r.URL.Query().Get(param) - if str == "" { - return 0, nil - } - val, err := strconv.ParseUint(str, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("Error decoding uint64 for '%s': %v", param, err))) - return 0, err - } - return val, nil -} - -func decodeInt(w http.ResponseWriter, r *http.Request, param string) (int, error) { - str := r.URL.Query().Get(param) - if str == "" { - return 0, nil - } - val, err := strconv.Atoi(str) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("Error decoding int for '%s': %v", param, err))) - return 0, err - } - return val, nil -} - -func decodeState(w http.ResponseWriter, r *http.Request) (ConnState, error) { - str := r.URL.Query().Get("state") - if str == "" { - return ConnOpen, nil - } - switch strings.ToLower(str) { - case "open": - return ConnOpen, nil - case "closed": - return ConnClosed, nil - case "any", "all": - return ConnAll, nil - } - // We do not understand intended state here. - w.WriteHeader(http.StatusBadRequest) - err := fmt.Errorf("Error decoding state for %s", str) - w.Write([]byte(err.Error())) - return 0, err -} - -// HandleConnz process HTTP requests for connection information. -func (s *Server) HandleConnz(w http.ResponseWriter, r *http.Request) { - sortOpt := SortOpt(r.URL.Query().Get("sort")) - auth, err := decodeBool(w, r, "auth") - if err != nil { - return - } - subs, err := decodeBool(w, r, "subs") - if err != nil { - return - } - offset, err := decodeInt(w, r, "offset") - if err != nil { - return - } - limit, err := decodeInt(w, r, "limit") - if err != nil { - return - } - cid, err := decodeUint64(w, r, "cid") - if err != nil { - return - } - state, err := decodeState(w, r) - if err != nil { - return - } - - connzOpts := &ConnzOptions{ - Sort: sortOpt, - Username: auth, - Subscriptions: subs, - Offset: offset, - Limit: limit, - CID: cid, - State: state, - } - - s.mu.Lock() - s.httpReqStats[ConnzPath]++ - s.mu.Unlock() - - c, err := s.Connz(connzOpts) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - b, err := json.MarshalIndent(c, "", " ") - if err != nil { - s.Errorf("Error marshaling response to /connz request: %v", err) - } - - // Handle response - ResponseHandler(w, r, b) -} - -// Routez represents detailed information on current client connections. -type Routez struct { - ID string `json:"server_id"` - Now time.Time `json:"now"` - NumRoutes int `json:"num_routes"` - Routes []*RouteInfo `json:"routes"` -} - -// RoutezOptions are options passed to Routez -type RoutezOptions struct { - // Subscriptions indicates that Routez will return a route's subscriptions - Subscriptions bool `json:"subscriptions"` -} - -// RouteInfo has detailed information on a per connection basis. -type RouteInfo struct { - Rid uint64 `json:"rid"` - RemoteID string `json:"remote_id"` - DidSolicit bool `json:"did_solicit"` - IsConfigured bool `json:"is_configured"` - IP string `json:"ip"` - Port int `json:"port"` - Pending int `json:"pending_size"` - InMsgs int64 `json:"in_msgs"` - OutMsgs int64 `json:"out_msgs"` - InBytes int64 `json:"in_bytes"` - OutBytes int64 `json:"out_bytes"` - NumSubs uint32 `json:"subscriptions"` - Subs []string `json:"subscriptions_list,omitempty"` -} - -// Routez returns a Routez struct containing inormation about routes. -func (s *Server) Routez(routezOpts *RoutezOptions) (*Routez, error) { - rs := &Routez{Routes: []*RouteInfo{}} - rs.Now = time.Now() - - subs := routezOpts != nil && routezOpts.Subscriptions - - // Walk the list - s.mu.Lock() - rs.NumRoutes = len(s.routes) - - // copy the server id for monitoring - rs.ID = s.info.ID - - for _, r := range s.routes { - r.mu.Lock() - ri := &RouteInfo{ - Rid: r.cid, - RemoteID: r.route.remoteID, - DidSolicit: r.route.didSolicit, - IsConfigured: r.route.routeType == Explicit, - InMsgs: atomic.LoadInt64(&r.inMsgs), - OutMsgs: r.outMsgs, - InBytes: atomic.LoadInt64(&r.inBytes), - OutBytes: r.outBytes, - NumSubs: uint32(len(r.subs)), - } - - if subs && len(r.subs) > 0 { - ri.Subs = make([]string, 0, len(r.subs)) - for _, sub := range r.subs { - ri.Subs = append(ri.Subs, string(sub.subject)) - } - } - switch conn := r.nc.(type) { - case *net.TCPConn, *tls.Conn: - addr := conn.RemoteAddr().(*net.TCPAddr) - ri.Port = addr.Port - ri.IP = addr.IP.String() - } - r.mu.Unlock() - rs.Routes = append(rs.Routes, ri) - } - s.mu.Unlock() - return rs, nil -} - -// HandleRoutez process HTTP requests for route information. -func (s *Server) HandleRoutez(w http.ResponseWriter, r *http.Request) { - subs, err := decodeBool(w, r, "subs") - if err != nil { - return - } - var opts *RoutezOptions - if subs { - opts = &RoutezOptions{Subscriptions: true} - } - - s.mu.Lock() - s.httpReqStats[RoutezPath]++ - s.mu.Unlock() - - // As of now, no error is ever returned. - rs, _ := s.Routez(opts) - b, err := json.MarshalIndent(rs, "", " ") - if err != nil { - s.Errorf("Error marshaling response to /routez request: %v", err) - } - - // Handle response - ResponseHandler(w, r, b) -} - -// Subsz represents detail information on current connections. -type Subsz struct { - *SublistStats - Total int `json:"total"` - Offset int `json:"offset"` - Limit int `json:"limit"` - Subs []SubDetail `json:"subscriptions_list,omitempty"` -} - -// SubszOptions are the options passed to Subsz. -// As of now, there are no options defined. -type SubszOptions struct { - // Offset is used for pagination. Subsz() only returns connections starting at this - // offset from the global results. - Offset int `json:"offset"` - - // Limit is the maximum number of subscriptions that should be returned by Subsz(). - Limit int `json:"limit"` - - // Subscriptions indicates if subscriptions should be included in the results. - Subscriptions bool `json:"subscriptions"` - - // Test the list against this subject. Needs to be literal since it signifies a publish subject. - // We will only return subscriptions that would match if a message was sent to this subject. - Test string `json:"test,omitempty"` -} - -type SubDetail struct { - Subject string `json:"subject"` - Queue string `json:"qgroup,omitempty"` - Sid string `json:"sid"` - Msgs int64 `json:"msgs"` - Max int64 `json:"max,omitempty"` - Cid uint64 `json:"cid"` -} - -// Subsz returns a Subsz struct containing subjects statistics -func (s *Server) Subsz(opts *SubszOptions) (*Subsz, error) { - var ( - subdetail bool - test bool - offset int - limit = DefaultSubListSize - testSub = "" - ) - - if opts != nil { - subdetail = opts.Subscriptions - offset = opts.Offset - if offset < 0 { - offset = 0 - } - limit = opts.Limit - if limit <= 0 { - limit = DefaultSubListSize - } - if opts.Test != "" { - testSub = opts.Test - test = true - if !IsValidLiteralSubject(testSub) { - return nil, fmt.Errorf("Invalid test subject, must be valid publish subject: %s", testSub) - } - } - } - - sz := &Subsz{s.sl.Stats(), 0, offset, limit, nil} - - if subdetail { - // Now add in subscription's details - var raw [4096]*subscription - subs := raw[:0] - - s.sl.localSubs(&subs) - details := make([]SubDetail, len(subs)) - i := 0 - // TODO(dlc) - may be inefficient and could just do normal match when total subs is large and filtering. - for _, sub := range subs { - // Check for filter - if test && !matchLiteral(testSub, string(sub.subject)) { - continue - } - if sub.client == nil { - continue - } - sub.client.mu.Lock() - details[i] = SubDetail{ - Subject: string(sub.subject), - Queue: string(sub.queue), - Sid: string(sub.sid), - Msgs: sub.nm, - Max: sub.max, - Cid: sub.client.cid, - } - sub.client.mu.Unlock() - i++ - } - minoff := sz.Offset - maxoff := sz.Offset + sz.Limit - - maxIndex := i - - // Make sure these are sane. - if minoff > maxIndex { - minoff = maxIndex - } - if maxoff > maxIndex { - maxoff = maxIndex - } - sz.Subs = details[minoff:maxoff] - sz.Total = len(sz.Subs) - } - - return sz, nil -} - -// HandleSubsz processes HTTP requests for subjects stats. -func (s *Server) HandleSubsz(w http.ResponseWriter, r *http.Request) { - s.mu.Lock() - s.httpReqStats[SubszPath]++ - s.mu.Unlock() - - subs, err := decodeBool(w, r, "subs") - if err != nil { - return - } - offset, err := decodeInt(w, r, "offset") - if err != nil { - return - } - limit, err := decodeInt(w, r, "limit") - if err != nil { - return - } - testSub := r.URL.Query().Get("test") - - subszOpts := &SubszOptions{ - Subscriptions: subs, - Offset: offset, - Limit: limit, - Test: testSub, - } - - st, err := s.Subsz(subszOpts) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - - var b []byte - - if len(st.Subs) == 0 { - b, err = json.MarshalIndent(st.SublistStats, "", " ") - } else { - b, err = json.MarshalIndent(st, "", " ") - } - if err != nil { - s.Errorf("Error marshaling response to /subscriptionsz request: %v", err) - } - - // Handle response - ResponseHandler(w, r, b) -} - -// HandleStacksz processes HTTP requests for getting stacks -func (s *Server) HandleStacksz(w http.ResponseWriter, r *http.Request) { - // Do not get any lock here that would prevent getting the stacks - // if we were to have a deadlock somewhere. - var defaultBuf [defaultStackBufSize]byte - size := defaultStackBufSize - buf := defaultBuf[:size] - n := 0 - for { - n = runtime.Stack(buf, true) - if n < size { - break - } - size *= 2 - buf = make([]byte, size) - } - // Handle response - ResponseHandler(w, r, buf[:n]) -} - -// Varz will output server information on the monitoring port at /varz. -type Varz struct { - *Info - *Options - Port int `json:"port"` - MaxPayload int `json:"max_payload"` - Start time.Time `json:"start"` - Now time.Time `json:"now"` - Uptime string `json:"uptime"` - Mem int64 `json:"mem"` - Cores int `json:"cores"` - CPU float64 `json:"cpu"` - Connections int `json:"connections"` - TotalConnections uint64 `json:"total_connections"` - Routes int `json:"routes"` - Remotes int `json:"remotes"` - InMsgs int64 `json:"in_msgs"` - OutMsgs int64 `json:"out_msgs"` - InBytes int64 `json:"in_bytes"` - OutBytes int64 `json:"out_bytes"` - SlowConsumers int64 `json:"slow_consumers"` - MaxPending int64 `json:"max_pending"` - WriteDeadline time.Duration `json:"write_deadline"` - Subscriptions uint32 `json:"subscriptions"` - HTTPReqStats map[string]uint64 `json:"http_req_stats"` - ConfigLoadTime time.Time `json:"config_load_time"` -} - -// VarzOptions are the options passed to Varz(). -// Currently, there are no options defined. -type VarzOptions struct{} - -func myUptime(d time.Duration) string { - // Just use total seconds for uptime, and display days / years - tsecs := d / time.Second - tmins := tsecs / 60 - thrs := tmins / 60 - tdays := thrs / 24 - tyrs := tdays / 365 - - if tyrs > 0 { - return fmt.Sprintf("%dy%dd%dh%dm%ds", tyrs, tdays%365, thrs%24, tmins%60, tsecs%60) - } - if tdays > 0 { - return fmt.Sprintf("%dd%dh%dm%ds", tdays, thrs%24, tmins%60, tsecs%60) - } - if thrs > 0 { - return fmt.Sprintf("%dh%dm%ds", thrs, tmins%60, tsecs%60) - } - if tmins > 0 { - return fmt.Sprintf("%dm%ds", tmins, tsecs%60) - } - return fmt.Sprintf("%ds", tsecs) -} - -// HandleRoot will show basic info and links to others handlers. -func (s *Server) HandleRoot(w http.ResponseWriter, r *http.Request) { - // This feels dumb to me, but is required: https://code.google.com/p/go/issues/detail?id=4799 - if r.URL.Path != "/" { - http.NotFound(w, r) - return - } - s.mu.Lock() - s.httpReqStats[RootPath]++ - s.mu.Unlock() - fmt.Fprintf(w, ` - - - - - - NATS -
- varz
- connz
- routez
- subsz
-
- help - -`) -} - -// Varz returns a Varz struct containing the server information. -func (s *Server) Varz(varzOpts *VarzOptions) (*Varz, error) { - // Snapshot server options. - opts := s.getOpts() - - v := &Varz{Info: &s.info, Options: opts, MaxPayload: opts.MaxPayload, Start: s.start} - v.Now = time.Now() - v.Uptime = myUptime(time.Since(s.start)) - v.Port = v.Info.Port - - updateUsage(v) - - s.mu.Lock() - v.Connections = len(s.clients) - v.TotalConnections = s.totalClients - v.Routes = len(s.routes) - v.Remotes = len(s.remotes) - v.InMsgs = atomic.LoadInt64(&s.inMsgs) - v.InBytes = atomic.LoadInt64(&s.inBytes) - v.OutMsgs = atomic.LoadInt64(&s.outMsgs) - v.OutBytes = atomic.LoadInt64(&s.outBytes) - v.SlowConsumers = atomic.LoadInt64(&s.slowConsumers) - v.MaxPending = opts.MaxPending - v.WriteDeadline = opts.WriteDeadline - v.Subscriptions = s.sl.Count() - v.ConfigLoadTime = s.configTime - // Need a copy here since s.httpReqStats can change while doing - // the marshaling down below. - v.HTTPReqStats = make(map[string]uint64, len(s.httpReqStats)) - for key, val := range s.httpReqStats { - v.HTTPReqStats[key] = val - } - s.mu.Unlock() - - return v, nil -} - -// HandleVarz will process HTTP requests for server information. -func (s *Server) HandleVarz(w http.ResponseWriter, r *http.Request) { - s.mu.Lock() - s.httpReqStats[VarzPath]++ - s.mu.Unlock() - - // As of now, no error is ever returned - v, _ := s.Varz(nil) - b, err := json.MarshalIndent(v, "", " ") - if err != nil { - s.Errorf("Error marshaling response to /varz request: %v", err) - } - - // Handle response - ResponseHandler(w, r, b) -} - -// Grab RSS and PCPU -func updateUsage(v *Varz) { - var rss, vss int64 - var pcpu float64 - - pse.ProcUsage(&pcpu, &rss, &vss) - - v.Mem = rss - v.CPU = pcpu - v.Cores = numCores -} - -// ResponseHandler handles responses for monitoring routes -func ResponseHandler(w http.ResponseWriter, r *http.Request, data []byte) { - // Get callback from request - callback := r.URL.Query().Get("callback") - // If callback is not empty then - if callback != "" { - // Response for JSONP - w.Header().Set("Content-Type", "application/javascript") - fmt.Fprintf(w, "%s(%s)", callback, data) - } else { - // Otherwise JSON - w.Header().Set("Content-Type", "application/json") - w.Write(data) - } -} - -func (reason ClosedState) String() string { - switch reason { - case ClientClosed: - return "Client" - case AuthenticationTimeout: - return "Authentication Timeout" - case AuthenticationViolation: - return "Authentication Failure" - case TLSHandshakeError: - return "TLS Handshake Failure" - case SlowConsumerPendingBytes: - return "Slow Consumer (Pending Bytes)" - case SlowConsumerWriteDeadline: - return "Slow Consumer (Write Deadline)" - case WriteError: - return "Write Error" - case ReadError: - return "Read Error" - case ParseError: - return "Parse Error" - case StaleConnection: - return "Stale Connection" - case ProtocolViolation: - return "Protocol Violation" - case BadClientProtocolVersion: - return "Bad Client Protocol Version" - case WrongPort: - return "Incorrect Port" - case MaxConnectionsExceeded: - return "Maximum Connections Exceeded" - case MaxPayloadExceeded: - return "Maximum Message Payload Exceeded" - case MaxControlLineExceeded: - return "Maximum Control Line Exceeded" - case DuplicateRoute: - return "Duplicate Route" - case RouteRemoved: - return "Route Removed" - case ServerShutdown: - return "Server Shutdown" - } - return "Unknown State" -} diff --git a/vendor/github.com/nats-io/gnatsd/server/monitor_sort_opts.go b/vendor/github.com/nats-io/gnatsd/server/monitor_sort_opts.go deleted file mode 100644 index 926ceb26..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/monitor_sort_opts.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "time" -) - -// Represents a connection info list. We use pointers since it will be sorted. -type ConnInfos []*ConnInfo - -// For sorting -func (cl ConnInfos) Len() int { return len(cl) } -func (cl ConnInfos) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] } - -// SortOpt is a helper type to sort clients -type SortOpt string - -// Possible sort options -const ( - ByCid SortOpt = "cid" // By connection ID - ByStart SortOpt = "start" // By connection start time, same as CID - BySubs SortOpt = "subs" // By number of subscriptions - ByPending SortOpt = "pending" // By amount of data in bytes waiting to be sent to client - ByOutMsgs SortOpt = "msgs_to" // By number of messages sent - ByInMsgs SortOpt = "msgs_from" // By number of messages received - ByOutBytes SortOpt = "bytes_to" // By amount of bytes sent - ByInBytes SortOpt = "bytes_from" // By amount of bytes received - ByLast SortOpt = "last" // By the last activity - ByIdle SortOpt = "idle" // By the amount of inactivity - ByUptime SortOpt = "uptime" // By the amount of time connections exist - ByStop SortOpt = "stop" // By the stop time for a closed connection - ByReason SortOpt = "reason" // By the reason for a closed connection - -) - -// Individual sort options provide the Less for sort.Interface. Len and Swap are on cList. -// CID -type byCid struct{ ConnInfos } - -func (l byCid) Less(i, j int) bool { return l.ConnInfos[i].Cid < l.ConnInfos[j].Cid } - -// Number of Subscriptions -type bySubs struct{ ConnInfos } - -func (l bySubs) Less(i, j int) bool { return l.ConnInfos[i].NumSubs < l.ConnInfos[j].NumSubs } - -// Pending Bytes -type byPending struct{ ConnInfos } - -func (l byPending) Less(i, j int) bool { return l.ConnInfos[i].Pending < l.ConnInfos[j].Pending } - -// Outbound Msgs -type byOutMsgs struct{ ConnInfos } - -func (l byOutMsgs) Less(i, j int) bool { return l.ConnInfos[i].OutMsgs < l.ConnInfos[j].OutMsgs } - -// Inbound Msgs -type byInMsgs struct{ ConnInfos } - -func (l byInMsgs) Less(i, j int) bool { return l.ConnInfos[i].InMsgs < l.ConnInfos[j].InMsgs } - -// Outbound Bytes -type byOutBytes struct{ ConnInfos } - -func (l byOutBytes) Less(i, j int) bool { return l.ConnInfos[i].OutBytes < l.ConnInfos[j].OutBytes } - -// Inbound Bytes -type byInBytes struct{ ConnInfos } - -func (l byInBytes) Less(i, j int) bool { return l.ConnInfos[i].InBytes < l.ConnInfos[j].InBytes } - -// Last Activity -type byLast struct{ ConnInfos } - -func (l byLast) Less(i, j int) bool { - return l.ConnInfos[i].LastActivity.UnixNano() < l.ConnInfos[j].LastActivity.UnixNano() -} - -// Idle time -type byIdle struct{ ConnInfos } - -func (l byIdle) Less(i, j int) bool { - ii := l.ConnInfos[i].LastActivity.Sub(l.ConnInfos[i].Start) - ij := l.ConnInfos[j].LastActivity.Sub(l.ConnInfos[j].Start) - return ii < ij -} - -// Uptime -type byUptime struct { - ConnInfos - now time.Time -} - -func (l byUptime) Less(i, j int) bool { - ci := l.ConnInfos[i] - cj := l.ConnInfos[j] - var upi, upj time.Duration - if ci.Stop == nil || ci.Stop.IsZero() { - upi = l.now.Sub(ci.Start) - } else { - upi = ci.Stop.Sub(ci.Start) - } - if cj.Stop == nil || cj.Stop.IsZero() { - upj = l.now.Sub(cj.Start) - } else { - upj = cj.Stop.Sub(cj.Start) - } - return upi < upj -} - -// Stop -type byStop struct{ ConnInfos } - -func (l byStop) Less(i, j int) bool { - ciStop := l.ConnInfos[i].Stop - cjStop := l.ConnInfos[j].Stop - return ciStop.Before(*cjStop) -} - -// Reason -type byReason struct{ ConnInfos } - -func (l byReason) Less(i, j int) bool { - return l.ConnInfos[i].Reason < l.ConnInfos[j].Reason -} - -// IsValid determines if a sort option is valid -func (s SortOpt) IsValid() bool { - switch s { - case "", ByCid, ByStart, BySubs, ByPending, ByOutMsgs, ByInMsgs, ByOutBytes, ByInBytes, ByLast, ByIdle, ByUptime, ByStop, ByReason: - return true - default: - return false - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/monitor_test.go b/vendor/github.com/nats-io/gnatsd/server/monitor_test.go deleted file mode 100644 index f23bff8c..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/monitor_test.go +++ /dev/null @@ -1,1933 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "math/rand" - "net/http" - "net/url" - "runtime" - "sort" - "strings" - "sync" - "testing" - "time" - "unicode" - - "net" - - "github.com/nats-io/go-nats" -) - -const CLIENT_PORT = -1 -const MONITOR_PORT = -1 -const CLUSTER_PORT = -1 - -func DefaultMonitorOptions() *Options { - return &Options{ - Host: "127.0.0.1", - Port: CLIENT_PORT, - HTTPHost: "127.0.0.1", - HTTPPort: MONITOR_PORT, - NoLog: true, - NoSigs: true, - } -} - -func runMonitorServer() *Server { - resetPreviousHTTPConnections() - opts := DefaultMonitorOptions() - return RunServer(opts) -} - -func runMonitorServerNoHTTPPort() *Server { - resetPreviousHTTPConnections() - opts := DefaultMonitorOptions() - opts.HTTPPort = 0 - return RunServer(opts) -} - -func resetPreviousHTTPConnections() { - http.DefaultTransport = &http.Transport{} -} - -func TestMyUptime(t *testing.T) { - // Make sure we print this stuff right. - var d time.Duration - var s string - - d = 22 * time.Second - s = myUptime(d) - if s != "22s" { - t.Fatalf("Expected `22s`, go ``%s`", s) - } - d = 4*time.Minute + d - s = myUptime(d) - if s != "4m22s" { - t.Fatalf("Expected `4m22s`, go ``%s`", s) - } - d = 4*time.Hour + d - s = myUptime(d) - if s != "4h4m22s" { - t.Fatalf("Expected `4h4m22s`, go ``%s`", s) - } - d = 32*24*time.Hour + d - s = myUptime(d) - if s != "32d4h4m22s" { - t.Fatalf("Expected `32d4h4m22s`, go ``%s`", s) - } - d = 22*365*24*time.Hour + d - s = myUptime(d) - if s != "22y32d4h4m22s" { - t.Fatalf("Expected `22y32d4h4m22s`, go ``%s`", s) - } -} - -// Make sure that we do not run the http server for monitoring unless asked. -func TestNoMonitorPort(t *testing.T) { - s := runMonitorServerNoHTTPPort() - defer s.Shutdown() - - // this test might be meaningless now that we're testing with random ports? - url := fmt.Sprintf("http://127.0.0.1:%d/", 11245) - if resp, err := http.Get(url + "varz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } - if resp, err := http.Get(url + "healthz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } - if resp, err := http.Get(url + "connz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } -} - -var ( - appJSONContent = "application/json" - appJSContent = "application/javascript" - textPlain = "text/plain; charset=utf-8" -) - -func readBodyEx(t *testing.T, url string, status int, content string) []byte { - resp, err := http.Get(url) - if err != nil { - stackFatalf(t, "Expected no error: Got %v\n", err) - } - defer resp.Body.Close() - if resp.StatusCode != status { - stackFatalf(t, "Expected a %d response, got %d\n", status, resp.StatusCode) - } - ct := resp.Header.Get("Content-Type") - if ct != content { - stackFatalf(t, "Expected %s content-type, got %s\n", content, ct) - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - stackFatalf(t, "Got an error reading the body: %v\n", err) - } - return body -} - -func readBody(t *testing.T, url string) []byte { - return readBodyEx(t, url, http.StatusOK, appJSONContent) -} - -func pollVarz(t *testing.T, s *Server, mode int, url string, opts *VarzOptions) *Varz { - if mode == 0 { - v := &Varz{} - body := readBody(t, url) - if err := json.Unmarshal(body, v); err != nil { - stackFatalf(t, "Got an error unmarshalling the body: %v\n", err) - } - return v - } - v, _ := s.Varz(opts) - return v -} - -func TestHandleVarz(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - for mode := 0; mode < 2; mode++ { - v := pollVarz(t, s, mode, url+"varz", nil) - - // Do some sanity checks on values - if time.Since(v.Start) > 10*time.Second { - t.Fatal("Expected start time to be within 10 seconds.") - } - } - - time.Sleep(100 * time.Millisecond) - - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - for mode := 0; mode < 2; mode++ { - v := pollVarz(t, s, mode, url+"varz", nil) - - if v.Connections != 1 { - t.Fatalf("Expected Connections of 1, got %v\n", v.Connections) - } - if v.TotalConnections < 1 { - t.Fatalf("Expected Total Connections of at least 1, got %v\n", v.TotalConnections) - } - if v.InMsgs != 1 { - t.Fatalf("Expected InMsgs of 1, got %v\n", v.InMsgs) - } - if v.OutMsgs != 1 { - t.Fatalf("Expected OutMsgs of 1, got %v\n", v.OutMsgs) - } - if v.InBytes != 5 { - t.Fatalf("Expected InBytes of 5, got %v\n", v.InBytes) - } - if v.OutBytes != 5 { - t.Fatalf("Expected OutBytes of 5, got %v\n", v.OutBytes) - } - if v.Subscriptions != 0 { - t.Fatalf("Expected Subscriptions of 0, got %v\n", v.Subscriptions) - } - } - - // Test JSONP - readBodyEx(t, url+"varz?callback=callback", http.StatusOK, appJSContent) -} - -func pollConz(t *testing.T, s *Server, mode int, url string, opts *ConnzOptions) *Connz { - if mode == 0 { - body := readBody(t, url) - c := &Connz{} - if err := json.Unmarshal(body, &c); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - return c - } - c, err := s.Connz(opts) - if err != nil { - stackFatalf(t, "Error on Connz(): %v", err) - } - return c -} - -func TestConnz(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - testConnz := func(mode int) { - c := pollConz(t, s, mode, url+"connz", nil) - - // Test contents.. - if c.NumConns != 0 { - t.Fatalf("Expected 0 connections, got %d\n", c.NumConns) - } - if c.Total != 0 { - t.Fatalf("Expected 0 live connections, got %d\n", c.Total) - } - if c.Conns == nil || len(c.Conns) != 0 { - t.Fatalf("Expected 0 connections in array, got %p\n", c.Conns) - } - - // Test with connections. - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - time.Sleep(50 * time.Millisecond) - - c = pollConz(t, s, mode, url+"connz", nil) - - if c.NumConns != 1 { - t.Fatalf("Expected 1 connection, got %d\n", c.NumConns) - } - if c.Total != 1 { - t.Fatalf("Expected 1 live connection, got %d\n", c.Total) - } - if c.Conns == nil || len(c.Conns) != 1 { - t.Fatalf("Expected 1 connection in array, got %d\n", len(c.Conns)) - } - - if c.Limit != DefaultConnListSize { - t.Fatalf("Expected limit of %d, got %v\n", DefaultConnListSize, c.Limit) - } - - if c.Offset != 0 { - t.Fatalf("Expected offset of 0, got %v\n", c.Offset) - } - - // Test inside details of each connection - ci := c.Conns[0] - - if ci.Cid == 0 { - t.Fatalf("Expected non-zero cid, got %v\n", ci.Cid) - } - if ci.IP != "127.0.0.1" { - t.Fatalf("Expected \"127.0.0.1\" for IP, got %v\n", ci.IP) - } - if ci.Port == 0 { - t.Fatalf("Expected non-zero port, got %v\n", ci.Port) - } - if ci.NumSubs != 0 { - t.Fatalf("Expected num_subs of 0, got %v\n", ci.NumSubs) - } - if len(ci.Subs) != 0 { - t.Fatalf("Expected subs of 0, got %v\n", ci.Subs) - } - if ci.InMsgs != 1 { - t.Fatalf("Expected InMsgs of 1, got %v\n", ci.InMsgs) - } - if ci.OutMsgs != 1 { - t.Fatalf("Expected OutMsgs of 1, got %v\n", ci.OutMsgs) - } - if ci.InBytes != 5 { - t.Fatalf("Expected InBytes of 1, got %v\n", ci.InBytes) - } - if ci.OutBytes != 5 { - t.Fatalf("Expected OutBytes of 1, got %v\n", ci.OutBytes) - } - if ci.Start.IsZero() { - t.Fatal("Expected Start to be valid\n") - } - if ci.Uptime == "" { - t.Fatal("Expected Uptime to be valid\n") - } - if ci.LastActivity.IsZero() { - t.Fatal("Expected LastActivity to be valid\n") - } - if ci.LastActivity.UnixNano() < ci.Start.UnixNano() { - t.Fatalf("Expected LastActivity [%v] to be > Start [%v]\n", ci.LastActivity, ci.Start) - } - if ci.Idle == "" { - t.Fatal("Expected Idle to be valid\n") - } - if ci.RTT != "" { - t.Fatal("Expected RTT to NOT be set for new connection\n") - } - } - - for mode := 0; mode < 2; mode++ { - testConnz(mode) - checkClientsCount(t, s, 0) - } - - // Test JSONP - readBodyEx(t, url+"connz?callback=callback", http.StatusOK, appJSContent) -} - -func TestConnzBadParams(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/connz?", s.MonitorAddr().Port) - readBodyEx(t, url+"auth=xxx", http.StatusBadRequest, textPlain) - readBodyEx(t, url+"subs=xxx", http.StatusBadRequest, textPlain) - readBodyEx(t, url+"offset=xxx", http.StatusBadRequest, textPlain) - readBodyEx(t, url+"limit=xxx", http.StatusBadRequest, textPlain) - readBodyEx(t, url+"state=xxx", http.StatusBadRequest, textPlain) -} - -func TestConnzWithSubs(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - nc.Subscribe("hello.foo", func(m *nats.Msg) {}) - ensureServerActivityRecorded(t, nc) - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?subs=1", &ConnzOptions{Subscriptions: true}) - // Test inside details of each connection - ci := c.Conns[0] - if len(ci.Subs) != 1 || ci.Subs[0] != "hello.foo" { - t.Fatalf("Expected subs of 1, got %v\n", ci.Subs) - } - } -} - -func TestConnzWithCID(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - // The one we will request - cid := 5 - total := 10 - - // Create 10 - for i := 1; i <= total; i++ { - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - if i == cid { - nc.Subscribe("hello.foo", func(m *nats.Msg) {}) - nc.Subscribe("hello.bar", func(m *nats.Msg) {}) - ensureServerActivityRecorded(t, nc) - } - } - - url := fmt.Sprintf("http://127.0.0.1:%d/connz?cid=%d", s.MonitorAddr().Port, cid) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url, &ConnzOptions{CID: uint64(cid)}) - // Test inside details of each connection - if len(c.Conns) != 1 { - t.Fatalf("Expected only one connection, but got %d\n", len(c.Conns)) - } - if c.NumConns != 1 { - t.Fatalf("Expected NumConns to be 1, but got %d\n", c.NumConns) - } - ci := c.Conns[0] - if ci.Cid != uint64(cid) { - t.Fatalf("Expected to receive connection %v, but received %v\n", cid, ci.Cid) - } - if ci.NumSubs != 2 { - t.Fatalf("Expected to receive connection with %d subs, but received %d\n", 2, ci.NumSubs) - } - // Now test a miss - badUrl := fmt.Sprintf("http://127.0.0.1:%d/connz?cid=%d", s.MonitorAddr().Port, 100) - c = pollConz(t, s, mode, badUrl, &ConnzOptions{CID: uint64(100)}) - if len(c.Conns) != 0 { - t.Fatalf("Expected no connections, got %d\n", len(c.Conns)) - } - if c.NumConns != 0 { - t.Fatalf("Expected NumConns of 0, got %d\n", c.NumConns) - } - } -} - -// Helper to map to connection name -func createConnMap(t *testing.T, cz *Connz) map[string]*ConnInfo { - cm := make(map[string]*ConnInfo) - for _, c := range cz.Conns { - cm[c.Name] = c - } - return cm -} - -func getFooAndBar(t *testing.T, cm map[string]*ConnInfo) (*ConnInfo, *ConnInfo) { - return cm["foo"], cm["bar"] -} - -func ensureServerActivityRecorded(t *testing.T, nc *nats.Conn) { - nc.Flush() - err := nc.Flush() - if err != nil { - t.Fatalf("Error flushing: %v\n", err) - } -} - -func TestConnzRTT(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - testRTT := func(mode int) { - // Test with connections. - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - c := pollConz(t, s, mode, url+"connz", nil) - - if c.NumConns != 1 { - t.Fatalf("Expected 1 connection, got %d\n", c.NumConns) - } - - // Send a server side PING to record RTT - s.mu.Lock() - ci := c.Conns[0] - sc := s.clients[ci.Cid] - if sc == nil { - t.Fatalf("Error looking up client %v\n", ci.Cid) - } - s.mu.Unlock() - sc.mu.Lock() - sc.sendPing() - sc.mu.Unlock() - - // Wait for client to respond with PONG - time.Sleep(20 * time.Millisecond) - - // Repoll for updated information. - c = pollConz(t, s, mode, url+"connz", nil) - ci = c.Conns[0] - - rtt, err := time.ParseDuration(ci.RTT) - if err != nil { - t.Fatalf("Could not parse RTT properly, %v (ci.RTT=%v)", err, ci.RTT) - } - if rtt <= 0 { - t.Fatal("Expected RTT to be valid and non-zero\n") - } - if rtt > 20*time.Millisecond || rtt < 100*time.Nanosecond { - t.Fatalf("Invalid RTT of %s\n", ci.RTT) - } - } - - for mode := 0; mode < 2; mode++ { - testRTT(mode) - checkClientsCount(t, s, 0) - } -} - -func TestConnzLastActivity(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - url += "connz?subs=1" - opts := &ConnzOptions{Subscriptions: true} - - testActivity := func(mode int) { - ncFoo := createClientConnWithName(t, "foo", s) - defer ncFoo.Close() - - ncBar := createClientConnWithName(t, "bar", s) - defer ncBar.Close() - - // Test inside details of each connection - ciFoo, ciBar := getFooAndBar(t, createConnMap(t, pollConz(t, s, mode, url, opts))) - - // Test that LastActivity is non-zero - if ciFoo.LastActivity.IsZero() { - t.Fatalf("Expected LastActivity for connection '%s'to be valid\n", ciFoo.Name) - } - if ciBar.LastActivity.IsZero() { - t.Fatalf("Expected LastActivity for connection '%s'to be valid\n", ciBar.Name) - } - // Foo should be older than Bar - if ciFoo.LastActivity.After(ciBar.LastActivity) { - t.Fatal("Expected connection 'foo' to be older than 'bar'\n") - } - - fooLA := ciFoo.LastActivity - barLA := ciBar.LastActivity - - ensureServerActivityRecorded(t, ncFoo) - ensureServerActivityRecorded(t, ncBar) - - // Sub should trigger update. - sub, _ := ncFoo.Subscribe("hello.world", func(m *nats.Msg) {}) - ensureServerActivityRecorded(t, ncFoo) - - ciFoo, _ = getFooAndBar(t, createConnMap(t, pollConz(t, s, mode, url, opts))) - nextLA := ciFoo.LastActivity - if fooLA.Equal(nextLA) { - t.Fatalf("Subscribe should have triggered update to LastActivity %+v\n", ciFoo) - } - fooLA = nextLA - - // Publish and Message Delivery should trigger as well. So both connections - // should have updates. - ncBar.Publish("hello.world", []byte("Hello")) - - ensureServerActivityRecorded(t, ncFoo) - ensureServerActivityRecorded(t, ncBar) - - ciFoo, ciBar = getFooAndBar(t, createConnMap(t, pollConz(t, s, mode, url, opts))) - nextLA = ciBar.LastActivity - if barLA.Equal(nextLA) { - t.Fatalf("Publish should have triggered update to LastActivity\n") - } - barLA = nextLA - - // Message delivery on ncFoo should have triggered as well. - nextLA = ciFoo.LastActivity - if fooLA.Equal(nextLA) { - t.Fatalf("Message delivery should have triggered update to LastActivity\n") - } - fooLA = nextLA - - // Unsub should trigger as well - sub.Unsubscribe() - ensureServerActivityRecorded(t, ncFoo) - - ciFoo, _ = getFooAndBar(t, createConnMap(t, pollConz(t, s, mode, url, opts))) - nextLA = ciFoo.LastActivity - if fooLA.Equal(nextLA) { - t.Fatalf("Message delivery should have triggered update to LastActivity\n") - } - } - - for mode := 0; mode < 2; mode++ { - testActivity(mode) - } -} - -func TestConnzWithOffsetAndLimit(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?offset=1&limit=1", &ConnzOptions{Offset: 1, Limit: 1}) - if c.Conns == nil || len(c.Conns) != 0 { - t.Fatalf("Expected 0 connections in array, got %p\n", c.Conns) - } - - // Test that when given negative values, 0 or default is used - c = pollConz(t, s, mode, url+"connz?offset=-1&limit=-1", &ConnzOptions{Offset: -11, Limit: -11}) - if c.Conns == nil || len(c.Conns) != 0 { - t.Fatalf("Expected 0 connections in array, got %p\n", c.Conns) - } - if c.Offset != 0 { - t.Fatalf("Expected offset to be 0, and limit to be %v, got %v and %v", - DefaultConnListSize, c.Offset, c.Limit) - } - } - - cl1 := createClientConnSubscribeAndPublish(t, s) - defer cl1.Close() - - cl2 := createClientConnSubscribeAndPublish(t, s) - defer cl2.Close() - - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?offset=1&limit=1", &ConnzOptions{Offset: 1, Limit: 1}) - if c.Limit != 1 { - t.Fatalf("Expected limit of 1, got %v\n", c.Limit) - } - - if c.Offset != 1 { - t.Fatalf("Expected offset of 1, got %v\n", c.Offset) - } - - if len(c.Conns) != 1 { - t.Fatalf("Expected conns of 1, got %v\n", len(c.Conns)) - } - - if c.NumConns != 1 { - t.Fatalf("Expected NumConns to be 1, got %v\n", c.NumConns) - } - - if c.Total != 2 { - t.Fatalf("Expected Total to be at least 2, got %v", c.Total) - } - - c = pollConz(t, s, mode, url+"connz?offset=2&limit=1", &ConnzOptions{Offset: 2, Limit: 1}) - if c.Limit != 1 { - t.Fatalf("Expected limit of 1, got %v\n", c.Limit) - } - - if c.Offset != 2 { - t.Fatalf("Expected offset of 2, got %v\n", c.Offset) - } - - if len(c.Conns) != 0 { - t.Fatalf("Expected conns of 0, got %v\n", len(c.Conns)) - } - - if c.NumConns != 0 { - t.Fatalf("Expected NumConns to be 0, got %v\n", c.NumConns) - } - - if c.Total != 2 { - t.Fatalf("Expected Total to be 2, got %v", c.Total) - } - } -} - -func TestConnzDefaultSorted(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - clients := make([]*nats.Conn, 4) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz", nil) - if c.Conns[0].Cid > c.Conns[1].Cid || - c.Conns[1].Cid > c.Conns[2].Cid || - c.Conns[2].Cid > c.Conns[3].Cid { - t.Fatalf("Expected conns sorted in ascending order by cid, got %v < %v\n", c.Conns[0].Cid, c.Conns[3].Cid) - } - } -} - -func TestConnzSortedByCid(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - clients := make([]*nats.Conn, 4) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?sort=cid", &ConnzOptions{Sort: ByCid}) - if c.Conns[0].Cid > c.Conns[1].Cid || - c.Conns[1].Cid > c.Conns[2].Cid || - c.Conns[2].Cid > c.Conns[3].Cid { - t.Fatalf("Expected conns sorted in ascending order by cid, got [%v, %v, %v, %v]\n", - c.Conns[0].Cid, c.Conns[1].Cid, c.Conns[2].Cid, c.Conns[3].Cid) - } - } -} - -func TestConnzSortedByStart(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - clients := make([]*nats.Conn, 4) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?sort=start", &ConnzOptions{Sort: ByStart}) - if c.Conns[0].Start.After(c.Conns[1].Start) || - c.Conns[1].Start.After(c.Conns[2].Start) || - c.Conns[2].Start.After(c.Conns[3].Start) { - t.Fatalf("Expected conns sorted in ascending order by startime, got [%v, %v, %v, %v]\n", - c.Conns[0].Start, c.Conns[1].Start, c.Conns[2].Start, c.Conns[3].Start) - } - } -} - -func TestConnzSortedByBytesAndMsgs(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - // Create a connection and make it send more messages than others - firstClient := createClientConnSubscribeAndPublish(t, s) - for i := 0; i < 100; i++ { - firstClient.Publish("foo", []byte("Hello World")) - } - defer firstClient.Close() - firstClient.Flush() - - clients := make([]*nats.Conn, 3) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?sort=bytes_to", &ConnzOptions{Sort: ByOutBytes}) - if c.Conns[0].OutBytes < c.Conns[1].OutBytes || - c.Conns[0].OutBytes < c.Conns[2].OutBytes || - c.Conns[0].OutBytes < c.Conns[3].OutBytes { - t.Fatalf("Expected conns sorted in descending order by bytes to, got %v < one of [%v, %v, %v]\n", - c.Conns[0].OutBytes, c.Conns[1].OutBytes, c.Conns[2].OutBytes, c.Conns[3].OutBytes) - } - - c = pollConz(t, s, mode, url+"connz?sort=msgs_to", &ConnzOptions{Sort: ByOutMsgs}) - if c.Conns[0].OutMsgs < c.Conns[1].OutMsgs || - c.Conns[0].OutMsgs < c.Conns[2].OutMsgs || - c.Conns[0].OutMsgs < c.Conns[3].OutMsgs { - t.Fatalf("Expected conns sorted in descending order by msgs from, got %v < one of [%v, %v, %v]\n", - c.Conns[0].OutMsgs, c.Conns[1].OutMsgs, c.Conns[2].OutMsgs, c.Conns[3].OutMsgs) - } - - c = pollConz(t, s, mode, url+"connz?sort=bytes_from", &ConnzOptions{Sort: ByInBytes}) - if c.Conns[0].InBytes < c.Conns[1].InBytes || - c.Conns[0].InBytes < c.Conns[2].InBytes || - c.Conns[0].InBytes < c.Conns[3].InBytes { - t.Fatalf("Expected conns sorted in descending order by bytes from, got %v < one of [%v, %v, %v]\n", - c.Conns[0].InBytes, c.Conns[1].InBytes, c.Conns[2].InBytes, c.Conns[3].InBytes) - } - - c = pollConz(t, s, mode, url+"connz?sort=msgs_from", &ConnzOptions{Sort: ByInMsgs}) - if c.Conns[0].InMsgs < c.Conns[1].InMsgs || - c.Conns[0].InMsgs < c.Conns[2].InMsgs || - c.Conns[0].InMsgs < c.Conns[3].InMsgs { - t.Fatalf("Expected conns sorted in descending order by msgs from, got %v < one of [%v, %v, %v]\n", - c.Conns[0].InMsgs, c.Conns[1].InMsgs, c.Conns[2].InMsgs, c.Conns[3].InMsgs) - } - } -} - -func TestConnzSortedByPending(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - firstClient := createClientConnSubscribeAndPublish(t, s) - firstClient.Subscribe("hello.world", func(m *nats.Msg) {}) - clients := make([]*nats.Conn, 3) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - } - defer firstClient.Close() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?sort=pending", &ConnzOptions{Sort: ByPending}) - if c.Conns[0].Pending < c.Conns[1].Pending || - c.Conns[0].Pending < c.Conns[2].Pending || - c.Conns[0].Pending < c.Conns[3].Pending { - t.Fatalf("Expected conns sorted in descending order by number of pending, got %v < one of [%v, %v, %v]\n", - c.Conns[0].Pending, c.Conns[1].Pending, c.Conns[2].Pending, c.Conns[3].Pending) - } - } -} - -func TestConnzSortedBySubs(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - firstClient := createClientConnSubscribeAndPublish(t, s) - firstClient.Subscribe("hello.world", func(m *nats.Msg) {}) - defer firstClient.Close() - - clients := make([]*nats.Conn, 3) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?sort=subs", &ConnzOptions{Sort: BySubs}) - if c.Conns[0].NumSubs < c.Conns[1].NumSubs || - c.Conns[0].NumSubs < c.Conns[2].NumSubs || - c.Conns[0].NumSubs < c.Conns[3].NumSubs { - t.Fatalf("Expected conns sorted in descending order by number of subs, got %v < one of [%v, %v, %v]\n", - c.Conns[0].NumSubs, c.Conns[1].NumSubs, c.Conns[2].NumSubs, c.Conns[3].NumSubs) - } - } -} - -func TestConnzSortedByLast(t *testing.T) { - opts := DefaultMonitorOptions() - s := RunServer(opts) - defer s.Shutdown() - - firstClient := createClientConnSubscribeAndPublish(t, s) - defer firstClient.Close() - firstClient.Subscribe("hello.world", func(m *nats.Msg) {}) - firstClient.Flush() - - clients := make([]*nats.Conn, 3) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - clients[i].Flush() - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?sort=last", &ConnzOptions{Sort: ByLast}) - if c.Conns[0].LastActivity.UnixNano() < c.Conns[1].LastActivity.UnixNano() || - c.Conns[1].LastActivity.UnixNano() < c.Conns[2].LastActivity.UnixNano() || - c.Conns[2].LastActivity.UnixNano() < c.Conns[3].LastActivity.UnixNano() { - t.Fatalf("Expected conns sorted in descending order by lastActivity, got %v < one of [%v, %v, %v]\n", - c.Conns[0].LastActivity, c.Conns[1].LastActivity, c.Conns[2].LastActivity, c.Conns[3].LastActivity) - } - } -} - -func TestConnzSortedByUptime(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - for i := 0; i < 4; i++ { - client := createClientConnSubscribeAndPublish(t, s) - defer client.Close() - // Since we check times (now-start) does not have to be big. - time.Sleep(50 * time.Millisecond) - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?sort=uptime", &ConnzOptions{Sort: ByUptime}) - now := time.Now() - ups := make([]int, 4) - for i := 0; i < 4; i++ { - ups[i] = int(now.Sub(c.Conns[i].Start)) - } - if !sort.IntsAreSorted(ups) { - d := make([]time.Duration, 4) - for i := 0; i < 4; i++ { - d[i] = time.Duration(ups[i]) - } - t.Fatalf("Expected conns sorted in ascending order by uptime (now-Start), got %+v\n", d) - } - } -} - -func TestConnzSortedByUptimeClosedConn(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - for i := time.Duration(1); i <= 4; i++ { - c := createClientConnSubscribeAndPublish(t, s) - - // Grab client and asjust start time such that - client := s.getClient(uint64(i)) - if client == nil { - t.Fatalf("Could nopt retrieve client for %d\n", i) - } - client.mu.Lock() - client.start = client.start.Add(-10 * (4 - i) * time.Second) - client.mu.Unlock() - - c.Close() - } - - checkClosedConns(t, s, 4, time.Second) - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?state=closed&sort=uptime", &ConnzOptions{State: ConnClosed, Sort: ByUptime}) - ups := make([]int, 4) - for i := 0; i < 4; i++ { - ups[i] = int(c.Conns[i].Stop.Sub(c.Conns[i].Start)) - } - if !sort.IntsAreSorted(ups) { - d := make([]time.Duration, 4) - for i := 0; i < 4; i++ { - d[i] = time.Duration(ups[i]) - } - t.Fatalf("Expected conns sorted in ascending order by uptime, got %+v\n", d) - } - } -} - -func TestConnzSortedByStopOnOpen(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - opts := s.getOpts() - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - // 4 clients - for i := 0; i < 4; i++ { - c, err := nats.Connect(url) - if err != nil { - t.Fatalf("Could not create client: %v\n", err) - } - defer c.Close() - } - - c, err := s.Connz(&ConnzOptions{Sort: ByStop}) - if err == nil { - t.Fatalf("Expected err to be non-nil, got %+v\n", c) - } -} - -func TestConnzSortedByStopTimeClosedConn(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - opts := s.getOpts() - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - // 4 clients - for i := 0; i < 4; i++ { - c, err := nats.Connect(url) - if err != nil { - t.Fatalf("Could not create client: %v\n", err) - } - c.Close() - } - checkClosedConns(t, s, 4, time.Second) - - //Now adjust the Stop times for these with some random values. - s.mu.Lock() - now := time.Now() - ccs := s.closed.closedClients() - for _, cc := range ccs { - newStop := now.Add(time.Duration(rand.Int()%120) * -time.Minute) - cc.Stop = &newStop - } - s.mu.Unlock() - - url = fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?state=closed&sort=stop", &ConnzOptions{State: ConnClosed, Sort: ByStop}) - ups := make([]int, 4) - nowU := time.Now().UnixNano() - for i := 0; i < 4; i++ { - ups[i] = int(nowU - c.Conns[i].Stop.UnixNano()) - } - if !sort.IntsAreSorted(ups) { - d := make([]time.Duration, 4) - for i := 0; i < 4; i++ { - d[i] = time.Duration(ups[i]) - } - t.Fatalf("Expected conns sorted in ascending order by stop time, got %+v\n", d) - } - } -} - -func TestConnzSortedByReason(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - opts := s.getOpts() - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - // 20 clients - for i := 0; i < 20; i++ { - c, err := nats.Connect(url) - if err != nil { - t.Fatalf("Could not create client: %v\n", err) - } - c.Close() - } - checkClosedConns(t, s, 20, time.Second) - - //Now adjust the Reasons for these with some random values. - s.mu.Lock() - ccs := s.closed.closedClients() - max := int(ServerShutdown) - for _, cc := range ccs { - cc.Reason = ClosedState(rand.Int() % max).String() - } - s.mu.Unlock() - - url = fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz?state=closed&sort=reason", &ConnzOptions{State: ConnClosed, Sort: ByReason}) - rs := make([]string, 20) - for i := 0; i < 20; i++ { - rs[i] = c.Conns[i].Reason - } - if !sort.StringsAreSorted(rs) { - t.Fatalf("Expected conns sorted in order by stop reason, got %#v\n", rs) - } - } -} - -func TestConnzSortedByReasonOnOpen(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - opts := s.getOpts() - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - // 4 clients - for i := 0; i < 4; i++ { - c, err := nats.Connect(url) - if err != nil { - t.Fatalf("Could not create client: %v\n", err) - } - defer c.Close() - } - - c, err := s.Connz(&ConnzOptions{Sort: ByReason}) - if err == nil { - t.Fatalf("Expected err to be non-nil, got %+v\n", c) - } -} - -func TestConnzSortedByIdle(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - testIdle := func(mode int) { - firstClient := createClientConnSubscribeAndPublish(t, s) - defer firstClient.Close() - firstClient.Subscribe("client.1", func(m *nats.Msg) {}) - firstClient.Flush() - - secondClient := createClientConnSubscribeAndPublish(t, s) - defer secondClient.Close() - - // Make it such that the second client started 10 secs ago. 10 is important since bug - // was strcmp, e.g. 1s vs 11s - var cid uint64 - switch mode { - case 0: - cid = uint64(2) - case 1: - cid = uint64(4) - } - client := s.getClient(cid) - if client == nil { - t.Fatalf("Error looking up client %v\n", 2) - } - - client.mu.Lock() - client.start = client.start.Add(-10 * time.Second) - client.last = client.start - client.mu.Unlock() - - // The Idle granularity is a whole second - time.Sleep(time.Second) - firstClient.Publish("client.1", []byte("new message")) - - c := pollConz(t, s, mode, url+"connz?sort=idle", &ConnzOptions{Sort: ByIdle}) - // Make sure we are returned 2 connections... - if len(c.Conns) != 2 { - t.Fatalf("Expected to get two connections, got %v", len(c.Conns)) - } - - // And that the Idle time is valid (even if equal to "0s") - if c.Conns[0].Idle == "" || c.Conns[1].Idle == "" { - t.Fatal("Expected Idle value to be valid") - } - - idle1, err := time.ParseDuration(c.Conns[0].Idle) - if err != nil { - t.Fatalf("Unable to parse duration %v, err=%v", c.Conns[0].Idle, err) - } - idle2, err := time.ParseDuration(c.Conns[1].Idle) - if err != nil { - t.Fatalf("Unable to parse duration %v, err=%v", c.Conns[0].Idle, err) - } - - if idle2 < idle1 { - t.Fatalf("Expected conns sorted in descending order by Idle, got %v < %v\n", - idle2, idle1) - } - } - for mode := 0; mode < 2; mode++ { - testIdle(mode) - } -} - -func TestConnzSortBadRequest(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - firstClient := createClientConnSubscribeAndPublish(t, s) - firstClient.Subscribe("hello.world", func(m *nats.Msg) {}) - clients := make([]*nats.Conn, 3) - for i := range clients { - clients[i] = createClientConnSubscribeAndPublish(t, s) - defer clients[i].Close() - } - defer firstClient.Close() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - readBodyEx(t, url+"connz?sort=foo", http.StatusBadRequest, textPlain) - - if _, err := s.Connz(&ConnzOptions{Sort: "foo"}); err == nil { - t.Fatal("Expected error, got none") - } -} - -func pollRoutez(t *testing.T, s *Server, mode int, url string, opts *RoutezOptions) *Routez { - if mode == 0 { - rz := &Routez{} - body := readBody(t, url) - if err := json.Unmarshal(body, rz); err != nil { - stackFatalf(t, "Got an error unmarshalling the body: %v\n", err) - } - return rz - } - rz, _ := s.Routez(opts) - return rz -} - -func TestConnzWithRoutes(t *testing.T) { - - opts := DefaultMonitorOptions() - opts.Cluster.Host = "127.0.0.1" - opts.Cluster.Port = CLUSTER_PORT - - s := RunServer(opts) - defer s.Shutdown() - - opts = &Options{ - Host: "127.0.0.1", - Port: -1, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: -1, - }, - NoLog: true, - NoSigs: true, - } - routeURL, _ := url.Parse(fmt.Sprintf("nats-route://127.0.0.1:%d", s.ClusterAddr().Port)) - opts.Routes = []*url.URL{routeURL} - - sc := RunServer(opts) - defer sc.Shutdown() - - checkClusterFormed(t, s, sc) - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - c := pollConz(t, s, mode, url+"connz", nil) - // Test contents.. - // Make sure routes don't show up under connz, but do under routez - if c.NumConns != 0 { - t.Fatalf("Expected 0 connections, got %d\n", c.NumConns) - } - if c.Conns == nil || len(c.Conns) != 0 { - t.Fatalf("Expected 0 connections in array, got %p\n", c.Conns) - } - } - - nc := createClientConnSubscribeAndPublish(t, sc) - defer nc.Close() - - nc.Subscribe("hello.bar", func(m *nats.Msg) {}) - nc.Flush() - checkExpectedSubs(t, 1, s, sc) - - // Now check routez - urls := []string{"routez", "routez?subs=1"} - for subs, urlSuffix := range urls { - for mode := 0; mode < 2; mode++ { - rz := pollRoutez(t, s, mode, url+urlSuffix, &RoutezOptions{Subscriptions: subs == 1}) - - if rz.NumRoutes != 1 { - t.Fatalf("Expected 1 route, got %d\n", rz.NumRoutes) - } - - if len(rz.Routes) != 1 { - t.Fatalf("Expected route array of 1, got %v\n", len(rz.Routes)) - } - - route := rz.Routes[0] - - if route.DidSolicit { - t.Fatalf("Expected unsolicited route, got %v\n", route.DidSolicit) - } - - // Don't ask for subs, so there should not be any - if subs == 0 { - if len(route.Subs) != 0 { - t.Fatalf("There should not be subs, got %v", len(route.Subs)) - } - } else { - if len(route.Subs) != 1 { - t.Fatalf("There should be 1 sub, got %v", len(route.Subs)) - } - } - } - } - - // Test JSONP - readBodyEx(t, url+"routez?callback=callback", http.StatusOK, appJSContent) -} - -func TestRoutezWithBadParams(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/routez?", s.MonitorAddr().Port) - readBodyEx(t, url+"subs=xxx", http.StatusBadRequest, textPlain) -} - -func pollSubsz(t *testing.T, s *Server, mode int, url string, opts *SubszOptions) *Subsz { - if mode == 0 { - body := readBody(t, url) - sz := &Subsz{} - if err := json.Unmarshal(body, sz); err != nil { - stackFatalf(t, "Got an error unmarshalling the body: %v\n", err) - } - return sz - } - sz, _ := s.Subsz(opts) - return sz -} - -func TestSubsz(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - for mode := 0; mode < 2; mode++ { - sl := pollSubsz(t, s, mode, url+"subsz", nil) - if sl.NumSubs != 0 { - t.Fatalf("Expected NumSubs of 0, got %d\n", sl.NumSubs) - } - if sl.NumInserts != 1 { - t.Fatalf("Expected NumInserts of 1, got %d\n", sl.NumInserts) - } - if sl.NumMatches != 1 { - t.Fatalf("Expected NumMatches of 1, got %d\n", sl.NumMatches) - } - } - - // Test JSONP - readBodyEx(t, url+"subsz?callback=callback", http.StatusOK, appJSContent) -} - -func TestSubszDetails(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - nc.Subscribe("foo.*", func(m *nats.Msg) {}) - nc.Subscribe("foo.bar", func(m *nats.Msg) {}) - nc.Subscribe("foo.foo", func(m *nats.Msg) {}) - - nc.Publish("foo.bar", []byte("Hello")) - nc.Publish("foo.baz", []byte("Hello")) - nc.Publish("foo.foo", []byte("Hello")) - - nc.Flush() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - for mode := 0; mode < 2; mode++ { - sl := pollSubsz(t, s, mode, url+"subsz?subs=1", &SubszOptions{Subscriptions: true}) - if sl.NumSubs != 3 { - t.Fatalf("Expected NumSubs of 3, got %d\n", sl.NumSubs) - } - if sl.Total != 3 { - t.Fatalf("Expected Total of 3, got %d\n", sl.Total) - } - if len(sl.Subs) != 3 { - t.Fatalf("Expected subscription details for 3 subs, got %d\n", len(sl.Subs)) - } - } -} - -func TestSubszWithOffsetAndLimit(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - for i := 0; i < 200; i++ { - nc.Subscribe(fmt.Sprintf("foo.%d", i), func(m *nats.Msg) {}) - } - nc.Flush() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - sl := pollSubsz(t, s, mode, url+"subsz?subs=1&offset=10&limit=100", &SubszOptions{Subscriptions: true, Offset: 10, Limit: 100}) - if sl.NumSubs != 200 { - t.Fatalf("Expected NumSubs of 200, got %d\n", sl.NumSubs) - } - if sl.Total != 100 { - t.Fatalf("Expected Total of 100, got %d\n", sl.Total) - } - if sl.Offset != 10 { - t.Fatalf("Expected Offset of 10, got %d\n", sl.Offset) - } - if sl.Limit != 100 { - t.Fatalf("Expected Total of 100, got %d\n", sl.Limit) - } - if len(sl.Subs) != 100 { - t.Fatalf("Expected subscription details for 100 subs, got %d\n", len(sl.Subs)) - } - } -} - -func TestSubszTestPubSubject(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - nc.Subscribe("foo.*", func(m *nats.Msg) {}) - nc.Subscribe("foo.bar", func(m *nats.Msg) {}) - nc.Subscribe("foo.foo", func(m *nats.Msg) {}) - nc.Flush() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - sl := pollSubsz(t, s, mode, url+"subsz?subs=1&test=foo.foo", &SubszOptions{Subscriptions: true, Test: "foo.foo"}) - if sl.Total != 2 { - t.Fatalf("Expected Total of 2 match, got %d\n", sl.Total) - } - if len(sl.Subs) != 2 { - t.Fatalf("Expected subscription details for 2 matching subs, got %d\n", len(sl.Subs)) - } - sl = pollSubsz(t, s, mode, url+"subsz?subs=1&test=foo", &SubszOptions{Subscriptions: true, Test: "foo"}) - if len(sl.Subs) != 0 { - t.Fatalf("Expected no matching subs, got %d\n", len(sl.Subs)) - } - } - // Make sure we get an error with invalid test subject. - testUrl := url + "subsz?subs=1&" - readBodyEx(t, testUrl+"test=*", http.StatusBadRequest, textPlain) - readBodyEx(t, testUrl+"test=foo.*", http.StatusBadRequest, textPlain) - readBodyEx(t, testUrl+"test=foo.>", http.StatusBadRequest, textPlain) - readBodyEx(t, testUrl+"test=foo..bar", http.StatusBadRequest, textPlain) -} - -// Tests handle root -func TestHandleRoot(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - nc := createClientConnSubscribeAndPublish(t, s) - defer nc.Close() - - resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port)) - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != http.StatusOK { - t.Fatalf("Expected a %d response, got %d\n", http.StatusOK, resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Expected no error reading body: Got %v\n", err) - } - for _, b := range body { - if b > unicode.MaxASCII { - t.Fatalf("Expected body to contain only ASCII characters, but got %v\n", b) - } - } - - ct := resp.Header.Get("Content-Type") - if !strings.Contains(ct, "text/html") { - t.Fatalf("Expected text/html response, got %s\n", ct) - } - defer resp.Body.Close() -} - -func TestConnzWithNamedClient(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - clientName := "test-client" - nc := createClientConnWithName(t, clientName, s) - defer nc.Close() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - // Confirm server is exposing client name in monitoring endpoint. - c := pollConz(t, s, mode, url+"connz", nil) - got := len(c.Conns) - expected := 1 - if got != expected { - t.Fatalf("Expected %d connection in array, got %d\n", expected, got) - } - - conn := c.Conns[0] - if conn.Name != clientName { - t.Fatalf("Expected client to have name %q. got %q", clientName, conn.Name) - } - } -} - -func TestConnzWithStateForClosedConns(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - numEach := 10 - // Create 10 closed, and 10 to leave open. - for i := 0; i < numEach; i++ { - nc := createClientConnSubscribeAndPublish(t, s) - nc.Subscribe("hello.closed.conns", func(m *nats.Msg) {}) - nc.Close() - nc = createClientConnSubscribeAndPublish(t, s) - nc.Subscribe("hello.open.conns", func(m *nats.Msg) {}) - defer nc.Close() - } - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - for mode := 0; mode < 2; mode++ { - // Look at all open - c := pollConz(t, s, mode, url+"connz?state=open", &ConnzOptions{State: ConnOpen}) - if lc := len(c.Conns); lc != numEach { - t.Fatalf("Expected %d connections in array, got %d\n", numEach, lc) - } - // Look at all closed - c = pollConz(t, s, mode, url+"connz?state=closed", &ConnzOptions{State: ConnClosed}) - if lc := len(c.Conns); lc != numEach { - t.Fatalf("Expected %d connections in array, got %d\n", numEach, lc) - } - // Look at all - c = pollConz(t, s, mode, url+"connz?state=ALL", &ConnzOptions{State: ConnAll}) - if lc := len(c.Conns); lc != numEach*2 { - t.Fatalf("Expected %d connections in array, got %d\n", 2*numEach, lc) - } - // Look at CID #1, which is in closed. - c = pollConz(t, s, mode, url+"connz?cid=1&state=open", &ConnzOptions{CID: 1, State: ConnOpen}) - if lc := len(c.Conns); lc != 0 { - t.Fatalf("Expected no connections in open array, got %d\n", lc) - } - c = pollConz(t, s, mode, url+"connz?cid=1&state=closed", &ConnzOptions{CID: 1, State: ConnClosed}) - if lc := len(c.Conns); lc != 1 { - t.Fatalf("Expected a connection in closed array, got %d\n", lc) - } - c = pollConz(t, s, mode, url+"connz?cid=1&state=ALL", &ConnzOptions{CID: 1, State: ConnAll}) - if lc := len(c.Conns); lc != 1 { - t.Fatalf("Expected a connection in closed array, got %d\n", lc) - } - c = pollConz(t, s, mode, url+"connz?cid=1&state=closed&subs=true", - &ConnzOptions{CID: 1, State: ConnClosed, Subscriptions: true}) - if lc := len(c.Conns); lc != 1 { - t.Fatalf("Expected a connection in closed array, got %d\n", lc) - } - ci := c.Conns[0] - if ci.NumSubs != 1 { - t.Fatalf("Expected NumSubs to be 1, got %d\n", ci.NumSubs) - } - if len(ci.Subs) != 1 { - t.Fatalf("Expected len(ci.Subs) to be 1 also, got %d\n", len(ci.Subs)) - } - // Now ask for same thing without subs and make sure they are not returned. - c = pollConz(t, s, mode, url+"connz?cid=1&state=closed&subs=false", - &ConnzOptions{CID: 1, State: ConnClosed, Subscriptions: false}) - if lc := len(c.Conns); lc != 1 { - t.Fatalf("Expected a connection in closed array, got %d\n", lc) - } - ci = c.Conns[0] - if ci.NumSubs != 1 { - t.Fatalf("Expected NumSubs to be 1, got %d\n", ci.NumSubs) - } - if len(ci.Subs) != 0 { - t.Fatalf("Expected len(ci.Subs) to be 0 since subs=false, got %d\n", len(ci.Subs)) - } - - // CID #2 is in open - c = pollConz(t, s, mode, url+"connz?cid=2&state=open", &ConnzOptions{CID: 2, State: ConnOpen}) - if lc := len(c.Conns); lc != 1 { - t.Fatalf("Expected a connection in open array, got %d\n", lc) - } - c = pollConz(t, s, mode, url+"connz?cid=2&state=closed", &ConnzOptions{CID: 2, State: ConnClosed}) - if lc := len(c.Conns); lc != 0 { - t.Fatalf("Expected no connections in closed array, got %d\n", lc) - } - } -} - -// Make sure options for ConnInfo like subs=1, authuser, etc do not cause a race. -func TestConnzClosedConnsRace(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - // Create 100 closed connections. - for i := 0; i < 100; i++ { - nc := createClientConnSubscribeAndPublish(t, s) - nc.Close() - } - - urlWithoutSubs := fmt.Sprintf("http://127.0.0.1:%d/connz?state=closed", s.MonitorAddr().Port) - urlWithSubs := urlWithoutSubs + "&subs=true" - - checkClosedConns(t, s, 100, 2*time.Second) - - wg := &sync.WaitGroup{} - - fn := func(url string) { - deadline := time.Now().Add(1 * time.Second) - for time.Now().Before(deadline) { - c := pollConz(t, s, 0, url, nil) - if len(c.Conns) != 100 { - t.Errorf("Incorrect Results: %+v\n", c) - } - } - wg.Done() - } - - wg.Add(2) - go fn(urlWithSubs) - go fn(urlWithoutSubs) - wg.Wait() -} - -// Make sure a bad client that is disconnected right away has proper values. -func TestConnzClosedConnsBadClient(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - opts := s.getOpts() - - rc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on dial: %v", err) - } - rc.Close() - - checkClosedConns(t, s, 1, 2*time.Second) - - c := pollConz(t, s, 1, "", &ConnzOptions{State: ConnClosed}) - if len(c.Conns) != 1 { - t.Errorf("Incorrect Results: %+v\n", c) - } - ci := c.Conns[0] - - uptime := ci.Stop.Sub(ci.Start) - idle, err := time.ParseDuration(ci.Idle) - if err != nil { - t.Fatalf("Could not parse Idle: %v\n", err) - } - if idle > uptime { - t.Fatalf("Idle can't be larger then uptime, %v vs %v\n", idle, uptime) - } - if ci.LastActivity.IsZero() { - t.Fatalf("LastActivity should not be Zero\n") - } -} - -// Make sure a bad client that tries to connect plain to TLS has proper values. -func TestConnzClosedConnsBadTLSClient(t *testing.T) { - resetPreviousHTTPConnections() - - tc := &TLSConfigOpts{} - tc.CertFile = "configs/certs/server.pem" - tc.KeyFile = "configs/certs/key.pem" - - var err error - opts := DefaultMonitorOptions() - opts.TLSTimeout = 1.5 // 1.5 seconds - opts.TLSConfig, err = GenTLSConfig(tc) - if err != nil { - t.Fatalf("Error creating TSL config: %v", err) - } - - s := RunServer(opts) - defer s.Shutdown() - - opts = s.getOpts() - - rc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on dial: %v", err) - } - rc.Write([]byte("CONNECT {}\r\n")) - rc.Close() - - checkClosedConns(t, s, 1, 2*time.Second) - - c := pollConz(t, s, 1, "", &ConnzOptions{State: ConnClosed}) - if len(c.Conns) != 1 { - t.Errorf("Incorrect Results: %+v\n", c) - } - ci := c.Conns[0] - - uptime := ci.Stop.Sub(ci.Start) - idle, err := time.ParseDuration(ci.Idle) - if err != nil { - t.Fatalf("Could not parse Idle: %v\n", err) - } - if idle > uptime { - t.Fatalf("Idle can't be larger then uptime, %v vs %v\n", idle, uptime) - } - if ci.LastActivity.IsZero() { - t.Fatalf("LastActivity should not be Zero\n") - } -} - -// Create a connection to test ConnInfo -func createClientConnSubscribeAndPublish(t *testing.T, s *Server) *nats.Conn { - natsURL := fmt.Sprintf("nats://127.0.0.1:%d", s.Addr().(*net.TCPAddr).Port) - client := nats.DefaultOptions - client.Servers = []string{natsURL} - nc, err := client.Connect() - if err != nil { - t.Fatalf("Error creating client: %v to: %s\n", err, natsURL) - } - - ch := make(chan bool) - inbox := nats.NewInbox() - sub, err := nc.Subscribe(inbox, func(m *nats.Msg) { ch <- true }) - if err != nil { - t.Fatalf("Error subscribing to `%s`: %v\n", inbox, err) - } - nc.Publish(inbox, []byte("Hello")) - // Wait for message - <-ch - sub.Unsubscribe() - close(ch) - nc.Flush() - return nc -} - -func createClientConnWithName(t *testing.T, name string, s *Server) *nats.Conn { - natsURI := fmt.Sprintf("nats://127.0.0.1:%d", s.Addr().(*net.TCPAddr).Port) - - client := nats.DefaultOptions - client.Servers = []string{natsURI} - client.Name = name - nc, err := client.Connect() - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - return nc -} - -func TestStacksz(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - body := readBody(t, url+"stacksz") - // Check content - str := string(body) - if !strings.Contains(str, "HandleStacksz") { - t.Fatalf("Result does not seem to contain server's stacks:\n%v", str) - } -} - -func TestConcurrentMonitoring(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - // Get some endpoints. Make sure we have at least varz, - // and the more the merrier. - endpoints := []string{"varz", "varz", "varz", "connz", "connz", "subsz", "subsz", "routez", "routez"} - wg := &sync.WaitGroup{} - wg.Add(len(endpoints)) - ech := make(chan string, len(endpoints)) - - for _, e := range endpoints { - go func(endpoint string) { - defer wg.Done() - for i := 0; i < 50; i++ { - resp, err := http.Get(url + endpoint) - if err != nil { - ech <- fmt.Sprintf("Expected no error: Got %v\n", err) - return - } - if resp.StatusCode != http.StatusOK { - ech <- fmt.Sprintf("Expected a %v response, got %d\n", http.StatusOK, resp.StatusCode) - return - } - ct := resp.Header.Get("Content-Type") - if ct != "application/json" { - ech <- fmt.Sprintf("Expected application/json content-type, got %s\n", ct) - return - } - defer resp.Body.Close() - if _, err := ioutil.ReadAll(resp.Body); err != nil { - ech <- fmt.Sprintf("Got an error reading the body: %v\n", err) - return - } - resp.Body.Close() - } - }(e) - } - wg.Wait() - // Check for any errors - select { - case err := <-ech: - t.Fatal(err) - default: - } -} - -func TestMonitorHandler(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - handler := s.HTTPHandler() - if handler == nil { - t.Fatal("HTTP Handler should be set") - } - s.Shutdown() - handler = s.HTTPHandler() - if handler != nil { - t.Fatal("HTTP Handler should be nil") - } -} - -func TestMonitorRoutezRace(t *testing.T) { - resetPreviousHTTPConnections() - srvAOpts := DefaultMonitorOptions() - srvAOpts.Cluster.Port = -1 - srvA := RunServer(srvAOpts) - defer srvA.Shutdown() - - srvBOpts := nextServerOpts(srvAOpts) - srvBOpts.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", srvA.ClusterAddr().Port)) - - url := fmt.Sprintf("http://127.0.0.1:%d/", srvA.MonitorAddr().Port) - doneCh := make(chan struct{}) - go func() { - defer func() { - doneCh <- struct{}{} - }() - for i := 0; i < 10; i++ { - time.Sleep(10 * time.Millisecond) - // Reset ports - srvBOpts.Port = -1 - srvBOpts.Cluster.Port = -1 - srvB := RunServer(srvBOpts) - time.Sleep(20 * time.Millisecond) - srvB.Shutdown() - } - }() - done := false - for !done { - if resp, err := http.Get(url + "routez"); err != nil { - time.Sleep(10 * time.Millisecond) - } else { - resp.Body.Close() - } - select { - case <-doneCh: - done = true - default: - } - } -} - -func TestConnzTLSInHandshake(t *testing.T) { - resetPreviousHTTPConnections() - - tc := &TLSConfigOpts{} - tc.CertFile = "configs/certs/server.pem" - tc.KeyFile = "configs/certs/key.pem" - - var err error - opts := DefaultMonitorOptions() - opts.TLSTimeout = 1.5 // 1.5 seconds - opts.TLSConfig, err = GenTLSConfig(tc) - if err != nil { - t.Fatalf("Error creating TSL config: %v", err) - } - - s := RunServer(opts) - defer s.Shutdown() - - // Create bare TCP connection to delay client TLS handshake - c, err := net.Dial("tcp", fmt.Sprintf("%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on dial: %v", err) - } - defer c.Close() - - // Wait for the connection to be registered - checkClientsCount(t, s, 1) - - start := time.Now() - endpoint := fmt.Sprintf("http://%s:%d/connz", opts.HTTPHost, s.MonitorAddr().Port) - for mode := 0; mode < 2; mode++ { - connz := pollConz(t, s, mode, endpoint, nil) - duration := time.Since(start) - if duration >= 1500*time.Millisecond { - t.Fatalf("Looks like connz blocked on handshake, took %v", duration) - } - if len(connz.Conns) != 1 { - t.Fatalf("Expected 1 conn, got %v", len(connz.Conns)) - } - conn := connz.Conns[0] - // TLS fields should be not set - if conn.TLSVersion != "" || conn.TLSCipher != "" { - t.Fatalf("Expected TLS fields to not be set, got version:%v cipher:%v", conn.TLSVersion, conn.TLSCipher) - } - } -} - -func TestServerIDs(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - murl := fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port) - - for mode := 0; mode < 2; mode++ { - v := pollVarz(t, s, mode, murl+"varz", nil) - if v.ID == "" { - t.Fatal("Varz ID is empty") - } - c := pollConz(t, s, mode, murl+"connz", nil) - if c.ID == "" { - t.Fatal("Connz ID is empty") - } - r := pollRoutez(t, s, mode, murl+"routez", nil) - if r.ID == "" { - t.Fatal("Routez ID is empty") - } - if v.ID != c.ID || v.ID != r.ID { - t.Fatalf("Varz ID [%s] is not equal to Connz ID [%s] or Routez ID [%s]", v.ID, c.ID, r.ID) - } - } -} - -func TestHttpStatsNoUpdatedWhenUsingServerFuncs(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - for i := 0; i < 10; i++ { - s.Varz(nil) - s.Connz(nil) - s.Routez(nil) - s.Subsz(nil) - } - - v, _ := s.Varz(nil) - endpoints := []string{VarzPath, ConnzPath, RoutezPath, SubszPath} - for _, e := range endpoints { - stats := v.HTTPReqStats[e] - if stats != 0 { - t.Fatalf("Expected HTTPReqStats for %q to be 0, got %v", e, stats) - } - } -} - -func TestClusterEmptyWhenNotDefined(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - body := readBody(t, fmt.Sprintf("http://127.0.0.1:%d/varz", s.MonitorAddr().Port)) - var v map[string]interface{} - if err := json.Unmarshal(body, &v); err != nil { - stackFatalf(t, "Got an error unmarshalling the body: %v\n", err) - } - // Cluster can empty, or be defined but that needs to be empty. - c, ok := v["cluster"] - if !ok { - return - } - if len(c.(map[string]interface{})) != 0 { - t.Fatalf("Expected an empty cluster definition, instead got %+v\n", c) - } -} - -// Benchmark our Connz generation. Don't use HTTP here, just measure server endpoint. -func Benchmark_Connz(b *testing.B) { - runtime.MemProfileRate = 0 - - s := runMonitorServerNoHTTPPort() - defer s.Shutdown() - - opts := s.getOpts() - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - // Create 250 connections with 100 subs each. - for i := 0; i < 250; i++ { - nc, err := nats.Connect(url) - if err != nil { - b.Fatalf("Error on connection[%d] to %s: %v", i, url, err) - } - for x := 0; x < 100; x++ { - subj := fmt.Sprintf("foo.%d", x) - nc.Subscribe(subj, func(m *nats.Msg) {}) - } - nc.Flush() - defer nc.Close() - } - - b.ResetTimer() - runtime.MemProfileRate = 1 - - copts := &ConnzOptions{Subscriptions: false} - for i := 0; i < b.N; i++ { - _, err := s.Connz(copts) - if err != nil { - b.Fatalf("Error on Connz(): %v", err) - } - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/norace_test.go b/vendor/github.com/nats-io/gnatsd/server/norace_test.go deleted file mode 100644 index f208be38..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/norace_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !race - -package server - -import ( - "fmt" - "math/rand" - "sync/atomic" - "testing" - "time" - - "github.com/nats-io/go-nats" -) - -// IMPORTANT: Tests in this file are not executed when running with the -race flag. - -func TestAvoidSlowConsumerBigMessages(t *testing.T) { - opts := DefaultOptions() // Use defaults to make sure they avoid pending slow consumer. - s := RunServer(opts) - defer s.Shutdown() - - nc1, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc1.Close() - - nc2, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc2.Close() - - data := make([]byte, 1024*1024) // 1MB payload - rand.Read(data) - - expected := int32(500) - received := int32(0) - - done := make(chan bool) - - // Create Subscription. - nc1.Subscribe("slow.consumer", func(m *nats.Msg) { - // Just eat it so that we are not measuring - // code time, just delivery. - atomic.AddInt32(&received, 1) - if received >= expected { - done <- true - } - }) - - // Create Error handler - nc1.SetErrorHandler(func(c *nats.Conn, s *nats.Subscription, err error) { - t.Fatalf("Received an error on the subscription's connection: %v\n", err) - }) - - nc1.Flush() - - for i := 0; i < int(expected); i++ { - nc2.Publish("slow.consumer", data) - } - nc2.Flush() - - select { - case <-done: - return - case <-time.After(10 * time.Second): - r := atomic.LoadInt32(&received) - if s.NumSlowConsumers() > 0 { - t.Fatalf("Did not receive all large messages due to slow consumer status: %d of %d", r, expected) - } - t.Fatalf("Failed to receive all large messages: %d of %d\n", r, expected) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/opts.go b/vendor/github.com/nats-io/gnatsd/server/opts.go deleted file mode 100644 index ad4f38a3..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/opts.go +++ /dev/null @@ -1,1259 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "flag" - "fmt" - "io/ioutil" - "net" - "net/url" - "os" - "strconv" - "strings" - "time" - - "github.com/nats-io/gnatsd/conf" - "github.com/nats-io/gnatsd/util" -) - -// ClusterOpts are options for clusters. -type ClusterOpts struct { - Host string `json:"addr,omitempty"` - Port int `json:"cluster_port,omitempty"` - Username string `json:"-"` - Password string `json:"-"` - AuthTimeout float64 `json:"auth_timeout,omitempty"` - Permissions *RoutePermissions `json:"-"` - TLSTimeout float64 `json:"-"` - TLSConfig *tls.Config `json:"-"` - ListenStr string `json:"-"` - Advertise string `json:"-"` - NoAdvertise bool `json:"-"` - ConnectRetries int `json:"-"` -} - -// Options block for gnatsd server. -type Options struct { - ConfigFile string `json:"-"` - Host string `json:"addr"` - Port int `json:"port"` - ClientAdvertise string `json:"-"` - Trace bool `json:"-"` - Debug bool `json:"-"` - NoLog bool `json:"-"` - NoSigs bool `json:"-"` - Logtime bool `json:"-"` - MaxConn int `json:"max_connections"` - MaxSubs int `json:"max_subscriptions,omitempty"` - Users []*User `json:"-"` - Username string `json:"-"` - Password string `json:"-"` - Authorization string `json:"-"` - PingInterval time.Duration `json:"ping_interval"` - MaxPingsOut int `json:"ping_max"` - HTTPHost string `json:"http_host"` - HTTPPort int `json:"http_port"` - HTTPSPort int `json:"https_port"` - AuthTimeout float64 `json:"auth_timeout"` - MaxControlLine int `json:"max_control_line"` - MaxPayload int `json:"max_payload"` - MaxPending int64 `json:"max_pending"` - Cluster ClusterOpts `json:"cluster,omitempty"` - ProfPort int `json:"-"` - PidFile string `json:"-"` - PortsFileDir string `json:"-"` - LogFile string `json:"-"` - Syslog bool `json:"-"` - RemoteSyslog string `json:"-"` - Routes []*url.URL `json:"-"` - RoutesStr string `json:"-"` - TLSTimeout float64 `json:"tls_timeout"` - TLS bool `json:"-"` - TLSVerify bool `json:"-"` - TLSCert string `json:"-"` - TLSKey string `json:"-"` - TLSCaCert string `json:"-"` - TLSConfig *tls.Config `json:"-"` - WriteDeadline time.Duration `json:"-"` - RQSubsSweep time.Duration `json:"-"` - MaxClosedClients int `json:"-"` - - CustomClientAuthentication Authentication `json:"-"` - CustomRouterAuthentication Authentication `json:"-"` -} - -// Clone performs a deep copy of the Options struct, returning a new clone -// with all values copied. -func (o *Options) Clone() *Options { - if o == nil { - return nil - } - clone := &Options{} - *clone = *o - if o.Users != nil { - clone.Users = make([]*User, len(o.Users)) - for i, user := range o.Users { - clone.Users[i] = user.clone() - } - } - if o.Routes != nil { - clone.Routes = make([]*url.URL, len(o.Routes)) - for i, route := range o.Routes { - routeCopy := &url.URL{} - *routeCopy = *route - clone.Routes[i] = routeCopy - } - } - if o.TLSConfig != nil { - clone.TLSConfig = util.CloneTLSConfig(o.TLSConfig) - } - if o.Cluster.TLSConfig != nil { - clone.Cluster.TLSConfig = util.CloneTLSConfig(o.Cluster.TLSConfig) - } - return clone -} - -// Configuration file authorization section. -type authorization struct { - // Singles - user string - pass string - token string - // Multiple Users - users []*User - timeout float64 - defaultPermissions *Permissions -} - -// TLSConfigOpts holds the parsed tls config information, -// used with flag parsing -type TLSConfigOpts struct { - CertFile string - KeyFile string - CaFile string - Verify bool - Timeout float64 - Ciphers []uint16 - CurvePreferences []tls.CurveID -} - -var tlsUsage = ` -TLS configuration is specified in the tls section of a configuration file: - -e.g. - - tls { - cert_file: "./certs/server-cert.pem" - key_file: "./certs/server-key.pem" - ca_file: "./certs/ca.pem" - verify: true - - cipher_suites: [ - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" - ] - curve_preferences: [ - "CurveP256", - "CurveP384", - "CurveP521" - ] - } - -Available cipher suites include: -` - -// ProcessConfigFile processes a configuration file. -// FIXME(dlc): Hacky -func ProcessConfigFile(configFile string) (*Options, error) { - opts := &Options{} - if err := opts.ProcessConfigFile(configFile); err != nil { - return nil, err - } - return opts, nil -} - -// ProcessConfigFile updates the Options structure with options -// present in the given configuration file. -// This version is convenient if one wants to set some default -// options and then override them with what is in the config file. -// For instance, this version allows you to do something such as: -// -// opts := &Options{Debug: true} -// opts.ProcessConfigFile(myConfigFile) -// -// If the config file contains "debug: false", after this call, -// opts.Debug would really be false. It would be impossible to -// achieve that with the non receiver ProcessConfigFile() version, -// since one would not know after the call if "debug" was not present -// or was present but set to false. -func (o *Options) ProcessConfigFile(configFile string) error { - o.ConfigFile = configFile - if configFile == "" { - return nil - } - - m, err := conf.ParseFile(configFile) - if err != nil { - return err - } - - for k, v := range m { - switch strings.ToLower(k) { - case "listen": - hp, err := parseListen(v) - if err != nil { - return err - } - o.Host = hp.host - o.Port = hp.port - case "client_advertise": - o.ClientAdvertise = v.(string) - case "port": - o.Port = int(v.(int64)) - case "host", "net": - o.Host = v.(string) - case "debug": - o.Debug = v.(bool) - case "trace": - o.Trace = v.(bool) - case "logtime": - o.Logtime = v.(bool) - case "authorization": - am := v.(map[string]interface{}) - auth, err := parseAuthorization(am) - if err != nil { - return err - } - o.Username = auth.user - o.Password = auth.pass - o.Authorization = auth.token - if (auth.user != "" || auth.pass != "") && auth.token != "" { - return fmt.Errorf("Cannot have a user/pass and token") - } - o.AuthTimeout = auth.timeout - // Check for multiple users defined - if auth.users != nil { - if auth.user != "" { - return fmt.Errorf("Can not have a single user/pass and a users array") - } - if auth.token != "" { - return fmt.Errorf("Can not have a token and a users array") - } - o.Users = auth.users - } - case "http": - hp, err := parseListen(v) - if err != nil { - return err - } - o.HTTPHost = hp.host - o.HTTPPort = hp.port - case "https": - hp, err := parseListen(v) - if err != nil { - return err - } - o.HTTPHost = hp.host - o.HTTPSPort = hp.port - case "http_port", "monitor_port": - o.HTTPPort = int(v.(int64)) - case "https_port": - o.HTTPSPort = int(v.(int64)) - case "cluster": - cm := v.(map[string]interface{}) - if err := parseCluster(cm, o); err != nil { - return err - } - case "logfile", "log_file": - o.LogFile = v.(string) - case "syslog": - o.Syslog = v.(bool) - case "remote_syslog": - o.RemoteSyslog = v.(string) - case "pidfile", "pid_file": - o.PidFile = v.(string) - case "ports_file_dir": - o.PortsFileDir = v.(string) - case "prof_port": - o.ProfPort = int(v.(int64)) - case "max_control_line": - o.MaxControlLine = int(v.(int64)) - case "max_payload": - o.MaxPayload = int(v.(int64)) - case "max_pending": - o.MaxPending = v.(int64) - case "max_connections", "max_conn": - o.MaxConn = int(v.(int64)) - case "max_subscriptions", "max_subs": - o.MaxSubs = int(v.(int64)) - case "ping_interval": - o.PingInterval = time.Duration(int(v.(int64))) * time.Second - case "ping_max": - o.MaxPingsOut = int(v.(int64)) - case "tls": - tlsm := v.(map[string]interface{}) - tc, err := parseTLS(tlsm) - if err != nil { - return err - } - if o.TLSConfig, err = GenTLSConfig(tc); err != nil { - return err - } - o.TLSTimeout = tc.Timeout - case "write_deadline": - wd, ok := v.(string) - if ok { - dur, err := time.ParseDuration(wd) - if err != nil { - return fmt.Errorf("error parsing write_deadline: %v", err) - } - o.WriteDeadline = dur - } else { - // Backward compatible with old type, assume this is the - // number of seconds. - o.WriteDeadline = time.Duration(v.(int64)) * time.Second - fmt.Printf("WARNING: write_deadline should be converted to a duration\n") - } - } - } - return nil -} - -// hostPort is simple struct to hold parsed listen/addr strings. -type hostPort struct { - host string - port int -} - -// parseListen will parse listen option which is replacing host/net and port -func parseListen(v interface{}) (*hostPort, error) { - hp := &hostPort{} - switch v.(type) { - // Only a port - case int64: - hp.port = int(v.(int64)) - case string: - host, port, err := net.SplitHostPort(v.(string)) - if err != nil { - return nil, fmt.Errorf("Could not parse address string %q", v) - } - hp.port, err = strconv.Atoi(port) - if err != nil { - return nil, fmt.Errorf("Could not parse port %q", port) - } - hp.host = host - } - return hp, nil -} - -// parseCluster will parse the cluster config. -func parseCluster(cm map[string]interface{}, opts *Options) error { - for mk, mv := range cm { - switch strings.ToLower(mk) { - case "listen": - hp, err := parseListen(mv) - if err != nil { - return err - } - opts.Cluster.Host = hp.host - opts.Cluster.Port = hp.port - case "port": - opts.Cluster.Port = int(mv.(int64)) - case "host", "net": - opts.Cluster.Host = mv.(string) - case "authorization": - am := mv.(map[string]interface{}) - auth, err := parseAuthorization(am) - if err != nil { - return err - } - if auth.users != nil { - return fmt.Errorf("Cluster authorization does not allow multiple users") - } - opts.Cluster.Username = auth.user - opts.Cluster.Password = auth.pass - opts.Cluster.AuthTimeout = auth.timeout - if auth.defaultPermissions != nil { - // Import is whether or not we will send a SUB for interest to the other side. - // Export is whether or not we will accept a SUB from the remote for a given subject. - // Both only effect interest registration. - // The parsing sets Import into Publish and Export into Subscribe, convert - // accordingly. - opts.Cluster.Permissions = &RoutePermissions{ - Import: auth.defaultPermissions.Publish, - Export: auth.defaultPermissions.Subscribe, - } - } - case "routes": - ra := mv.([]interface{}) - opts.Routes = make([]*url.URL, 0, len(ra)) - for _, r := range ra { - routeURL := r.(string) - url, err := url.Parse(routeURL) - if err != nil { - return fmt.Errorf("error parsing route url [%q]", routeURL) - } - opts.Routes = append(opts.Routes, url) - } - case "tls": - tlsm := mv.(map[string]interface{}) - tc, err := parseTLS(tlsm) - if err != nil { - return err - } - if opts.Cluster.TLSConfig, err = GenTLSConfig(tc); err != nil { - return err - } - // For clusters, we will force strict verification. We also act - // as both client and server, so will mirror the rootCA to the - // clientCA pool. - opts.Cluster.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert - opts.Cluster.TLSConfig.RootCAs = opts.Cluster.TLSConfig.ClientCAs - opts.Cluster.TLSTimeout = tc.Timeout - case "cluster_advertise", "advertise": - opts.Cluster.Advertise = mv.(string) - case "no_advertise": - opts.Cluster.NoAdvertise = mv.(bool) - case "connect_retries": - opts.Cluster.ConnectRetries = int(mv.(int64)) - } - } - return nil -} - -// Helper function to parse Authorization configs. -func parseAuthorization(am map[string]interface{}) (*authorization, error) { - auth := &authorization{} - for mk, mv := range am { - switch strings.ToLower(mk) { - case "user", "username": - auth.user = mv.(string) - case "pass", "password": - auth.pass = mv.(string) - case "token": - auth.token = mv.(string) - case "timeout": - at := float64(1) - switch mv.(type) { - case int64: - at = float64(mv.(int64)) - case float64: - at = mv.(float64) - } - auth.timeout = at - case "users": - users, err := parseUsers(mv) - if err != nil { - return nil, err - } - auth.users = users - case "default_permission", "default_permissions", "permissions": - pm, ok := mv.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("Expected default permissions to be a map/struct, got %+v", mv) - } - permissions, err := parseUserPermissions(pm) - if err != nil { - return nil, err - } - auth.defaultPermissions = permissions - } - - // Now check for permission defaults with multiple users, etc. - if auth.users != nil && auth.defaultPermissions != nil { - for _, user := range auth.users { - if user.Permissions == nil { - user.Permissions = auth.defaultPermissions - } - } - } - - } - return auth, nil -} - -// Helper function to parse multiple users array with optional permissions. -func parseUsers(mv interface{}) ([]*User, error) { - // Make sure we have an array - uv, ok := mv.([]interface{}) - if !ok { - return nil, fmt.Errorf("Expected users field to be an array, got %v", mv) - } - users := []*User{} - for _, u := range uv { - // Check its a map/struct - um, ok := u.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("Expected user entry to be a map/struct, got %v", u) - } - user := &User{} - for k, v := range um { - switch strings.ToLower(k) { - case "user", "username": - user.Username = v.(string) - case "pass", "password": - user.Password = v.(string) - case "permission", "permissions", "authorization": - pm, ok := v.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("Expected user permissions to be a map/struct, got %+v", v) - } - permissions, err := parseUserPermissions(pm) - if err != nil { - return nil, err - } - user.Permissions = permissions - } - } - // Check to make sure we have at least username and password - if user.Username == "" || user.Password == "" { - return nil, fmt.Errorf("User entry requires a user and a password") - } - users = append(users, user) - } - return users, nil -} - -// Helper function to parse user/account permissions -func parseUserPermissions(pm map[string]interface{}) (*Permissions, error) { - p := &Permissions{} - for k, v := range pm { - switch strings.ToLower(k) { - // For routes: - // Import is Publish - // Export is Subscribe - case "pub", "publish", "import": - subjects, err := parseSubjects(v) - if err != nil { - return nil, err - } - p.Publish = subjects - case "sub", "subscribe", "export": - subjects, err := parseSubjects(v) - if err != nil { - return nil, err - } - p.Subscribe = subjects - default: - return nil, fmt.Errorf("Unknown field %s parsing permissions", k) - } - } - return p, nil -} - -// Helper function to parse subject singeltons and/or arrays -func parseSubjects(v interface{}) ([]string, error) { - var subjects []string - switch v.(type) { - case string: - subjects = append(subjects, v.(string)) - case []string: - subjects = v.([]string) - case []interface{}: - for _, i := range v.([]interface{}) { - subject, ok := i.(string) - if !ok { - return nil, fmt.Errorf("Subject in permissions array cannot be cast to string") - } - subjects = append(subjects, subject) - } - default: - return nil, fmt.Errorf("Expected subject permissions to be a subject, or array of subjects, got %T", v) - } - return checkSubjectArray(subjects) -} - -// Helper function to validate subjects, etc for account permissioning. -func checkSubjectArray(sa []string) ([]string, error) { - for _, s := range sa { - if !IsValidSubject(s) { - return nil, fmt.Errorf("Subject %q is not a valid subject", s) - } - } - return sa, nil -} - -// PrintTLSHelpAndDie prints TLS usage and exits. -func PrintTLSHelpAndDie() { - fmt.Printf("%s", tlsUsage) - for k := range cipherMap { - fmt.Printf(" %s\n", k) - } - fmt.Printf("\nAvailable curve preferences include:\n") - for k := range curvePreferenceMap { - fmt.Printf(" %s\n", k) - } - os.Exit(0) -} - -func parseCipher(cipherName string) (uint16, error) { - - cipher, exists := cipherMap[cipherName] - if !exists { - return 0, fmt.Errorf("Unrecognized cipher %s", cipherName) - } - - return cipher, nil -} - -func parseCurvePreferences(curveName string) (tls.CurveID, error) { - curve, exists := curvePreferenceMap[curveName] - if !exists { - return 0, fmt.Errorf("Unrecognized curve preference %s", curveName) - } - return curve, nil -} - -// Helper function to parse TLS configs. -func parseTLS(tlsm map[string]interface{}) (*TLSConfigOpts, error) { - tc := TLSConfigOpts{} - for mk, mv := range tlsm { - switch strings.ToLower(mk) { - case "cert_file": - certFile, ok := mv.(string) - if !ok { - return nil, fmt.Errorf("error parsing tls config, expected 'cert_file' to be filename") - } - tc.CertFile = certFile - case "key_file": - keyFile, ok := mv.(string) - if !ok { - return nil, fmt.Errorf("error parsing tls config, expected 'key_file' to be filename") - } - tc.KeyFile = keyFile - case "ca_file": - caFile, ok := mv.(string) - if !ok { - return nil, fmt.Errorf("error parsing tls config, expected 'ca_file' to be filename") - } - tc.CaFile = caFile - case "verify": - verify, ok := mv.(bool) - if !ok { - return nil, fmt.Errorf("error parsing tls config, expected 'verify' to be a boolean") - } - tc.Verify = verify - case "cipher_suites": - ra := mv.([]interface{}) - if len(ra) == 0 { - return nil, fmt.Errorf("error parsing tls config, 'cipher_suites' cannot be empty") - } - tc.Ciphers = make([]uint16, 0, len(ra)) - for _, r := range ra { - cipher, err := parseCipher(r.(string)) - if err != nil { - return nil, err - } - tc.Ciphers = append(tc.Ciphers, cipher) - } - case "curve_preferences": - ra := mv.([]interface{}) - if len(ra) == 0 { - return nil, fmt.Errorf("error parsing tls config, 'curve_preferences' cannot be empty") - } - tc.CurvePreferences = make([]tls.CurveID, 0, len(ra)) - for _, r := range ra { - cps, err := parseCurvePreferences(r.(string)) - if err != nil { - return nil, err - } - tc.CurvePreferences = append(tc.CurvePreferences, cps) - } - case "timeout": - at := float64(0) - switch mv.(type) { - case int64: - at = float64(mv.(int64)) - case float64: - at = mv.(float64) - } - tc.Timeout = at - default: - return nil, fmt.Errorf("error parsing tls config, unknown field [%q]", mk) - } - } - - // If cipher suites were not specified then use the defaults - if tc.Ciphers == nil { - tc.Ciphers = defaultCipherSuites() - } - - // If curve preferences were not specified, then use the defaults - if tc.CurvePreferences == nil { - tc.CurvePreferences = defaultCurvePreferences() - } - - return &tc, nil -} - -// GenTLSConfig loads TLS related configuration parameters. -func GenTLSConfig(tc *TLSConfigOpts) (*tls.Config, error) { - - // Now load in cert and private key - cert, err := tls.LoadX509KeyPair(tc.CertFile, tc.KeyFile) - if err != nil { - return nil, fmt.Errorf("error parsing X509 certificate/key pair: %v", err) - } - cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]) - if err != nil { - return nil, fmt.Errorf("error parsing certificate: %v", err) - } - - // Create TLSConfig - // We will determine the cipher suites that we prefer. - config := tls.Config{ - CurvePreferences: tc.CurvePreferences, - Certificates: []tls.Certificate{cert}, - PreferServerCipherSuites: true, - MinVersion: tls.VersionTLS12, - CipherSuites: tc.Ciphers, - } - - // Require client certificates as needed - if tc.Verify { - config.ClientAuth = tls.RequireAndVerifyClientCert - } - // Add in CAs if applicable. - if tc.CaFile != "" { - rootPEM, err := ioutil.ReadFile(tc.CaFile) - if err != nil || rootPEM == nil { - return nil, err - } - pool := x509.NewCertPool() - ok := pool.AppendCertsFromPEM(rootPEM) - if !ok { - return nil, fmt.Errorf("failed to parse root ca certificate") - } - config.ClientCAs = pool - } - - return &config, nil -} - -// MergeOptions will merge two options giving preference to the flagOpts -// if the item is present. -func MergeOptions(fileOpts, flagOpts *Options) *Options { - if fileOpts == nil { - return flagOpts - } - if flagOpts == nil { - return fileOpts - } - // Merge the two, flagOpts override - opts := *fileOpts - - if flagOpts.Port != 0 { - opts.Port = flagOpts.Port - } - if flagOpts.Host != "" { - opts.Host = flagOpts.Host - } - if flagOpts.ClientAdvertise != "" { - opts.ClientAdvertise = flagOpts.ClientAdvertise - } - if flagOpts.Username != "" { - opts.Username = flagOpts.Username - } - if flagOpts.Password != "" { - opts.Password = flagOpts.Password - } - if flagOpts.Authorization != "" { - opts.Authorization = flagOpts.Authorization - } - if flagOpts.HTTPPort != 0 { - opts.HTTPPort = flagOpts.HTTPPort - } - if flagOpts.Debug { - opts.Debug = true - } - if flagOpts.Trace { - opts.Trace = true - } - if flagOpts.Logtime { - opts.Logtime = true - } - if flagOpts.LogFile != "" { - opts.LogFile = flagOpts.LogFile - } - if flagOpts.PidFile != "" { - opts.PidFile = flagOpts.PidFile - } - if flagOpts.PortsFileDir != "" { - opts.PortsFileDir = flagOpts.PortsFileDir - } - if flagOpts.ProfPort != 0 { - opts.ProfPort = flagOpts.ProfPort - } - if flagOpts.Cluster.ListenStr != "" { - opts.Cluster.ListenStr = flagOpts.Cluster.ListenStr - } - if flagOpts.Cluster.NoAdvertise { - opts.Cluster.NoAdvertise = true - } - if flagOpts.Cluster.ConnectRetries != 0 { - opts.Cluster.ConnectRetries = flagOpts.Cluster.ConnectRetries - } - if flagOpts.Cluster.Advertise != "" { - opts.Cluster.Advertise = flagOpts.Cluster.Advertise - } - if flagOpts.RoutesStr != "" { - mergeRoutes(&opts, flagOpts) - } - return &opts -} - -// RoutesFromStr parses route URLs from a string -func RoutesFromStr(routesStr string) []*url.URL { - routes := strings.Split(routesStr, ",") - if len(routes) == 0 { - return nil - } - routeUrls := []*url.URL{} - for _, r := range routes { - r = strings.TrimSpace(r) - u, _ := url.Parse(r) - routeUrls = append(routeUrls, u) - } - return routeUrls -} - -// This will merge the flag routes and override anything that was present. -func mergeRoutes(opts, flagOpts *Options) { - routeUrls := RoutesFromStr(flagOpts.RoutesStr) - if routeUrls == nil { - return - } - opts.Routes = routeUrls - opts.RoutesStr = flagOpts.RoutesStr -} - -// RemoveSelfReference removes this server from an array of routes -func RemoveSelfReference(clusterPort int, routes []*url.URL) ([]*url.URL, error) { - var cleanRoutes []*url.URL - cport := strconv.Itoa(clusterPort) - - selfIPs, err := getInterfaceIPs() - if err != nil { - return nil, err - } - for _, r := range routes { - host, port, err := net.SplitHostPort(r.Host) - if err != nil { - return nil, err - } - - ipList, err := getURLIP(host) - if err != nil { - return nil, err - } - if cport == port && isIPInList(selfIPs, ipList) { - continue - } - cleanRoutes = append(cleanRoutes, r) - } - - return cleanRoutes, nil -} - -func isIPInList(list1 []net.IP, list2 []net.IP) bool { - for _, ip1 := range list1 { - for _, ip2 := range list2 { - if ip1.Equal(ip2) { - return true - } - } - } - return false -} - -func getURLIP(ipStr string) ([]net.IP, error) { - ipList := []net.IP{} - - ip := net.ParseIP(ipStr) - if ip != nil { - ipList = append(ipList, ip) - return ipList, nil - } - - hostAddr, err := net.LookupHost(ipStr) - if err != nil { - return nil, fmt.Errorf("Error looking up host with route hostname: %v", err) - } - for _, addr := range hostAddr { - ip = net.ParseIP(addr) - if ip != nil { - ipList = append(ipList, ip) - } - } - return ipList, nil -} - -func getInterfaceIPs() ([]net.IP, error) { - var localIPs []net.IP - - interfaceAddr, err := net.InterfaceAddrs() - if err != nil { - return nil, fmt.Errorf("Error getting self referencing address: %v", err) - } - - for i := 0; i < len(interfaceAddr); i++ { - interfaceIP, _, _ := net.ParseCIDR(interfaceAddr[i].String()) - if net.ParseIP(interfaceIP.String()) != nil { - localIPs = append(localIPs, interfaceIP) - } else { - return nil, fmt.Errorf("Error parsing self referencing address: %v", err) - } - } - return localIPs, nil -} - -func processOptions(opts *Options) { - // Setup non-standard Go defaults - if opts.Host == "" { - opts.Host = DEFAULT_HOST - } - if opts.HTTPHost == "" { - // Default to same bind from server if left undefined - opts.HTTPHost = opts.Host - } - if opts.Port == 0 { - opts.Port = DEFAULT_PORT - } else if opts.Port == RANDOM_PORT { - // Choose randomly inside of net.Listen - opts.Port = 0 - } - if opts.MaxConn == 0 { - opts.MaxConn = DEFAULT_MAX_CONNECTIONS - } - if opts.PingInterval == 0 { - opts.PingInterval = DEFAULT_PING_INTERVAL - } - if opts.MaxPingsOut == 0 { - opts.MaxPingsOut = DEFAULT_PING_MAX_OUT - } - if opts.TLSTimeout == 0 { - opts.TLSTimeout = float64(TLS_TIMEOUT) / float64(time.Second) - } - if opts.AuthTimeout == 0 { - opts.AuthTimeout = float64(AUTH_TIMEOUT) / float64(time.Second) - } - if opts.Cluster.Port != 0 { - if opts.Cluster.Host == "" { - opts.Cluster.Host = DEFAULT_HOST - } - if opts.Cluster.TLSTimeout == 0 { - opts.Cluster.TLSTimeout = float64(TLS_TIMEOUT) / float64(time.Second) - } - if opts.Cluster.AuthTimeout == 0 { - opts.Cluster.AuthTimeout = float64(AUTH_TIMEOUT) / float64(time.Second) - } - } - if opts.MaxControlLine == 0 { - opts.MaxControlLine = MAX_CONTROL_LINE_SIZE - } - if opts.MaxPayload == 0 { - opts.MaxPayload = MAX_PAYLOAD_SIZE - } - if opts.MaxPending == 0 { - opts.MaxPending = MAX_PENDING_SIZE - } - if opts.WriteDeadline == time.Duration(0) { - opts.WriteDeadline = DEFAULT_FLUSH_DEADLINE - } - if opts.RQSubsSweep == time.Duration(0) { - opts.RQSubsSweep = DEFAULT_REMOTE_QSUBS_SWEEPER - } - if opts.MaxClosedClients == 0 { - opts.MaxClosedClients = DEFAULT_MAX_CLOSED_CLIENTS - } -} - -// ConfigureOptions accepts a flag set and augment it with NATS Server -// specific flags. On success, an options structure is returned configured -// based on the selected flags and/or configuration file. -// The command line options take precedence to the ones in the configuration file. -func ConfigureOptions(fs *flag.FlagSet, args []string, printVersion, printHelp, printTLSHelp func()) (*Options, error) { - opts := &Options{} - var ( - showVersion bool - showHelp bool - showTLSHelp bool - signal string - configFile string - err error - ) - - fs.BoolVar(&showHelp, "h", false, "Show this message.") - fs.BoolVar(&showHelp, "help", false, "Show this message.") - fs.IntVar(&opts.Port, "port", 0, "Port to listen on.") - fs.IntVar(&opts.Port, "p", 0, "Port to listen on.") - fs.StringVar(&opts.Host, "addr", "", "Network host to listen on.") - fs.StringVar(&opts.Host, "a", "", "Network host to listen on.") - fs.StringVar(&opts.Host, "net", "", "Network host to listen on.") - fs.StringVar(&opts.ClientAdvertise, "client_advertise", "", "Client URL to advertise to other servers.") - fs.BoolVar(&opts.Debug, "D", false, "Enable Debug logging.") - fs.BoolVar(&opts.Debug, "debug", false, "Enable Debug logging.") - fs.BoolVar(&opts.Trace, "V", false, "Enable Trace logging.") - fs.BoolVar(&opts.Trace, "trace", false, "Enable Trace logging.") - fs.Bool("DV", false, "Enable Debug and Trace logging.") - fs.BoolVar(&opts.Logtime, "T", true, "Timestamp log entries.") - fs.BoolVar(&opts.Logtime, "logtime", true, "Timestamp log entries.") - fs.StringVar(&opts.Username, "user", "", "Username required for connection.") - fs.StringVar(&opts.Password, "pass", "", "Password required for connection.") - fs.StringVar(&opts.Authorization, "auth", "", "Authorization token required for connection.") - fs.IntVar(&opts.HTTPPort, "m", 0, "HTTP Port for /varz, /connz endpoints.") - fs.IntVar(&opts.HTTPPort, "http_port", 0, "HTTP Port for /varz, /connz endpoints.") - fs.IntVar(&opts.HTTPSPort, "ms", 0, "HTTPS Port for /varz, /connz endpoints.") - fs.IntVar(&opts.HTTPSPort, "https_port", 0, "HTTPS Port for /varz, /connz endpoints.") - fs.StringVar(&configFile, "c", "", "Configuration file.") - fs.StringVar(&configFile, "config", "", "Configuration file.") - fs.StringVar(&signal, "sl", "", "Send signal to gnatsd process (stop, quit, reopen, reload)") - fs.StringVar(&signal, "signal", "", "Send signal to gnatsd process (stop, quit, reopen, reload)") - fs.StringVar(&opts.PidFile, "P", "", "File to store process pid.") - fs.StringVar(&opts.PidFile, "pid", "", "File to store process pid.") - fs.StringVar(&opts.PortsFileDir, "ports_file_dir", "", "Creates a ports file in the specified directory (_.ports)") - fs.StringVar(&opts.LogFile, "l", "", "File to store logging output.") - fs.StringVar(&opts.LogFile, "log", "", "File to store logging output.") - fs.BoolVar(&opts.Syslog, "s", false, "Enable syslog as log method.") - fs.BoolVar(&opts.Syslog, "syslog", false, "Enable syslog as log method..") - fs.StringVar(&opts.RemoteSyslog, "r", "", "Syslog server addr (udp://127.0.0.1:514).") - fs.StringVar(&opts.RemoteSyslog, "remote_syslog", "", "Syslog server addr (udp://127.0.0.1:514).") - fs.BoolVar(&showVersion, "version", false, "Print version information.") - fs.BoolVar(&showVersion, "v", false, "Print version information.") - fs.IntVar(&opts.ProfPort, "profile", 0, "Profiling HTTP port") - fs.StringVar(&opts.RoutesStr, "routes", "", "Routes to actively solicit a connection.") - fs.StringVar(&opts.Cluster.ListenStr, "cluster", "", "Cluster url from which members can solicit routes.") - fs.StringVar(&opts.Cluster.ListenStr, "cluster_listen", "", "Cluster url from which members can solicit routes.") - fs.StringVar(&opts.Cluster.Advertise, "cluster_advertise", "", "Cluster URL to advertise to other servers.") - fs.BoolVar(&opts.Cluster.NoAdvertise, "no_advertise", false, "Advertise known cluster IPs to clients.") - fs.IntVar(&opts.Cluster.ConnectRetries, "connect_retries", 0, "For implicit routes, number of connect retries") - fs.BoolVar(&showTLSHelp, "help_tls", false, "TLS help.") - fs.BoolVar(&opts.TLS, "tls", false, "Enable TLS.") - fs.BoolVar(&opts.TLSVerify, "tlsverify", false, "Enable TLS with client verification.") - fs.StringVar(&opts.TLSCert, "tlscert", "", "Server certificate file.") - fs.StringVar(&opts.TLSKey, "tlskey", "", "Private key for server certificate.") - fs.StringVar(&opts.TLSCaCert, "tlscacert", "", "Client certificate CA for verification.") - - // The flags definition above set "default" values to some of the options. - // Calling Parse() here will override the default options with any value - // specified from the command line. This is ok. We will then update the - // options with the content of the configuration file (if present), and then, - // call Parse() again to override the default+config with command line values. - // Calling Parse() before processing config file is necessary since configFile - // itself is a command line argument, and also Parse() is required in order - // to know if user wants simply to show "help" or "version", etc... - if err := fs.Parse(args); err != nil { - return nil, err - } - - if showVersion { - printVersion() - return nil, nil - } - - if showHelp { - printHelp() - return nil, nil - } - - if showTLSHelp { - printTLSHelp() - return nil, nil - } - - // Process args looking for non-flag options, - // 'version' and 'help' only for now - showVersion, showHelp, err = ProcessCommandLineArgs(fs) - if err != nil { - return nil, err - } else if showVersion { - printVersion() - return nil, nil - } else if showHelp { - printHelp() - return nil, nil - } - - // Snapshot flag options. - FlagSnapshot = opts.Clone() - - // Process signal control. - if signal != "" { - if err := processSignal(signal); err != nil { - return nil, err - } - } - - // Parse config if given - if configFile != "" { - // This will update the options with values from the config file. - if err := opts.ProcessConfigFile(configFile); err != nil { - return nil, err - } - // Call this again to override config file options with options from command line. - // Note: We don't need to check error here since if there was an error, it would - // have been caught the first time this function was called (after setting up the - // flags). - fs.Parse(args) - } - - // Special handling of some flags - var ( - flagErr error - tlsDisabled bool - tlsOverride bool - ) - fs.Visit(func(f *flag.Flag) { - // short-circuit if an error was encountered - if flagErr != nil { - return - } - if strings.HasPrefix(f.Name, "tls") { - if f.Name == "tls" { - if !opts.TLS { - // User has specified "-tls=false", we need to disable TLS - opts.TLSConfig = nil - tlsDisabled = true - tlsOverride = false - return - } - tlsOverride = true - } else if !tlsDisabled { - tlsOverride = true - } - } else { - switch f.Name { - case "DV": - // Check value to support -DV=false - boolValue, _ := strconv.ParseBool(f.Value.String()) - opts.Trace, opts.Debug = boolValue, boolValue - case "cluster", "cluster_listen": - // Override cluster config if explicitly set via flags. - flagErr = overrideCluster(opts) - case "routes": - // Keep in mind that the flag has updated opts.RoutesStr at this point. - if opts.RoutesStr == "" { - // Set routes array to nil since routes string is empty - opts.Routes = nil - return - } - routeUrls := RoutesFromStr(opts.RoutesStr) - opts.Routes = routeUrls - } - } - }) - if flagErr != nil { - return nil, flagErr - } - - // This will be true if some of the `-tls` params have been set and - // `-tls=false` has not been set. - if tlsOverride { - if err := overrideTLS(opts); err != nil { - return nil, err - } - } - - // If we don't have cluster defined in the configuration - // file and no cluster listen string override, but we do - // have a routes override, we need to report misconfiguration. - if opts.RoutesStr != "" && opts.Cluster.ListenStr == "" && opts.Cluster.Host == "" && opts.Cluster.Port == 0 { - return nil, errors.New("solicited routes require cluster capabilities, e.g. --cluster") - } - - return opts, nil -} - -// overrideTLS is called when at least "-tls=true" has been set. -func overrideTLS(opts *Options) error { - if opts.TLSCert == "" { - return errors.New("TLS Server certificate must be present and valid") - } - if opts.TLSKey == "" { - return errors.New("TLS Server private key must be present and valid") - } - - tc := TLSConfigOpts{} - tc.CertFile = opts.TLSCert - tc.KeyFile = opts.TLSKey - tc.CaFile = opts.TLSCaCert - tc.Verify = opts.TLSVerify - - var err error - opts.TLSConfig, err = GenTLSConfig(&tc) - return err -} - -// overrideCluster updates Options.Cluster if that flag "cluster" (or "cluster_listen") -// has explicitly be set in the command line. If it is set to empty string, it will -// clear the Cluster options. -func overrideCluster(opts *Options) error { - if opts.Cluster.ListenStr == "" { - // This one is enough to disable clustering. - opts.Cluster.Port = 0 - return nil - } - clusterURL, err := url.Parse(opts.Cluster.ListenStr) - if err != nil { - return err - } - h, p, err := net.SplitHostPort(clusterURL.Host) - if err != nil { - return err - } - opts.Cluster.Host = h - _, err = fmt.Sscan(p, &opts.Cluster.Port) - if err != nil { - return err - } - - if clusterURL.User != nil { - pass, hasPassword := clusterURL.User.Password() - if !hasPassword { - return errors.New("expected cluster password to be set") - } - opts.Cluster.Password = pass - - user := clusterURL.User.Username() - opts.Cluster.Username = user - } else { - // Since we override from flag and there is no user/pwd, make - // sure we clear what we may have gotten from config file. - opts.Cluster.Username = "" - opts.Cluster.Password = "" - } - - return nil -} - -func processSignal(signal string) error { - var ( - pid string - commandAndPid = strings.Split(signal, "=") - ) - if l := len(commandAndPid); l == 2 { - pid = commandAndPid[1] - } else if l > 2 { - return fmt.Errorf("invalid signal parameters: %v", commandAndPid[2:]) - } - if err := ProcessSignal(Command(commandAndPid[0]), pid); err != nil { - return err - } - os.Exit(0) - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/opts_test.go b/vendor/github.com/nats-io/gnatsd/server/opts_test.go deleted file mode 100644 index 9c391c5a..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/opts_test.go +++ /dev/null @@ -1,1037 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "crypto/tls" - "flag" - "io/ioutil" - "net/url" - "os" - "reflect" - "strings" - "testing" - "time" -) - -func TestDefaultOptions(t *testing.T) { - golden := &Options{ - Host: DEFAULT_HOST, - Port: DEFAULT_PORT, - MaxConn: DEFAULT_MAX_CONNECTIONS, - HTTPHost: DEFAULT_HOST, - PingInterval: DEFAULT_PING_INTERVAL, - MaxPingsOut: DEFAULT_PING_MAX_OUT, - TLSTimeout: float64(TLS_TIMEOUT) / float64(time.Second), - AuthTimeout: float64(AUTH_TIMEOUT) / float64(time.Second), - MaxControlLine: MAX_CONTROL_LINE_SIZE, - MaxPayload: MAX_PAYLOAD_SIZE, - MaxPending: MAX_PENDING_SIZE, - WriteDeadline: DEFAULT_FLUSH_DEADLINE, - RQSubsSweep: DEFAULT_REMOTE_QSUBS_SWEEPER, - MaxClosedClients: DEFAULT_MAX_CLOSED_CLIENTS, - } - - opts := &Options{} - processOptions(opts) - - if !reflect.DeepEqual(golden, opts) { - t.Fatalf("Default Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } -} - -func TestOptions_RandomPort(t *testing.T) { - opts := &Options{Port: RANDOM_PORT} - processOptions(opts) - - if opts.Port != 0 { - t.Fatalf("Process of options should have resolved random port to "+ - "zero.\nexpected: %d\ngot: %d\n", 0, opts.Port) - } -} - -func TestConfigFile(t *testing.T) { - golden := &Options{ - ConfigFile: "./configs/test.conf", - Host: "127.0.0.1", - Port: 4242, - Username: "derek", - Password: "porkchop", - AuthTimeout: 1.0, - Debug: false, - Trace: true, - Logtime: false, - HTTPPort: 8222, - PidFile: "/tmp/gnatsd.pid", - ProfPort: 6543, - Syslog: true, - RemoteSyslog: "udp://foo.com:33", - MaxControlLine: 2048, - MaxPayload: 65536, - MaxConn: 100, - MaxSubs: 1000, - MaxPending: 10000000, - PingInterval: 60 * time.Second, - MaxPingsOut: 3, - WriteDeadline: 3 * time.Second, - } - - opts, err := ProcessConfigFile("./configs/test.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - - if !reflect.DeepEqual(golden, opts) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } -} - -func TestTLSConfigFile(t *testing.T) { - golden := &Options{ - ConfigFile: "./configs/tls.conf", - Host: "127.0.0.1", - Port: 4443, - Username: "derek", - Password: "foo", - AuthTimeout: 1.0, - TLSTimeout: 2.0, - } - opts, err := ProcessConfigFile("./configs/tls.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - tlsConfig := opts.TLSConfig - if tlsConfig == nil { - t.Fatal("Expected opts.TLSConfig to be non-nil") - } - opts.TLSConfig = nil - if !reflect.DeepEqual(golden, opts) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } - // Now check TLSConfig a bit more closely - // CipherSuites - ciphers := defaultCipherSuites() - if !reflect.DeepEqual(tlsConfig.CipherSuites, ciphers) { - t.Fatalf("Got incorrect cipher suite list: [%+v]", tlsConfig.CipherSuites) - } - if tlsConfig.MinVersion != tls.VersionTLS12 { - t.Fatalf("Expected MinVersion of 1.2 [%v], got [%v]", tls.VersionTLS12, tlsConfig.MinVersion) - } - if !tlsConfig.PreferServerCipherSuites { - t.Fatal("Expected PreferServerCipherSuites to be true") - } - // Verify hostname is correct in certificate - if len(tlsConfig.Certificates) != 1 { - t.Fatal("Expected 1 certificate") - } - cert := tlsConfig.Certificates[0].Leaf - if err := cert.VerifyHostname("127.0.0.1"); err != nil { - t.Fatalf("Could not verify hostname in certificate: %v\n", err) - } - - // Now test adding cipher suites. - opts, err = ProcessConfigFile("./configs/tls_ciphers.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - tlsConfig = opts.TLSConfig - if tlsConfig == nil { - t.Fatal("Expected opts.TLSConfig to be non-nil") - } - - // CipherSuites listed in the config - test all of them. - ciphers = []uint16{ - tls.TLS_RSA_WITH_RC4_128_SHA, - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - tls.TLS_RSA_WITH_AES_128_CBC_SHA, - tls.TLS_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, - tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - } - - if !reflect.DeepEqual(tlsConfig.CipherSuites, ciphers) { - t.Fatalf("Got incorrect cipher suite list: [%+v]", tlsConfig.CipherSuites) - } - - // Test an unrecognized/bad cipher - if _, err := ProcessConfigFile("./configs/tls_bad_cipher.conf"); err == nil { - t.Fatal("Did not receive an error from a unrecognized cipher") - } - - // Test an empty cipher entry in a config file. - if _, err := ProcessConfigFile("./configs/tls_empty_cipher.conf"); err == nil { - t.Fatal("Did not receive an error from empty cipher_suites") - } - - // Test a curve preference from the config. - curves := []tls.CurveID{ - tls.CurveP256, - } - - // test on a file that will load the curve preference defaults - opts, err = ProcessConfigFile("./configs/tls_ciphers.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - - if !reflect.DeepEqual(opts.TLSConfig.CurvePreferences, defaultCurvePreferences()) { - t.Fatalf("Got incorrect curve preference list: [%+v]", tlsConfig.CurvePreferences) - } - - // Test specifying a single curve preference - opts, err = ProcessConfigFile("./configs/tls_curve_prefs.conf") - if err != nil { - t.Fatal("Did not receive an error from a unrecognized cipher.") - } - - if !reflect.DeepEqual(opts.TLSConfig.CurvePreferences, curves) { - t.Fatalf("Got incorrect cipher suite list: [%+v]", tlsConfig.CurvePreferences) - } - - // Test an unrecognized/bad curve preference - if _, err := ProcessConfigFile("./configs/tls_bad_curve_prefs.conf"); err == nil { - t.Fatal("Did not receive an error from a unrecognized curve preference") - } - // Test an empty curve preference - if _, err := ProcessConfigFile("./configs/tls_empty_curve_prefs.conf"); err == nil { - t.Fatal("Did not receive an error from empty curve preferences") - } -} - -func TestMergeOverrides(t *testing.T) { - golden := &Options{ - ConfigFile: "./configs/test.conf", - Host: "127.0.0.1", - Port: 2222, - Username: "derek", - Password: "porkchop", - AuthTimeout: 1.0, - Debug: true, - Trace: true, - Logtime: false, - HTTPPort: DEFAULT_HTTP_PORT, - PidFile: "/tmp/gnatsd.pid", - ProfPort: 6789, - Syslog: true, - RemoteSyslog: "udp://foo.com:33", - MaxControlLine: 2048, - MaxPayload: 65536, - MaxConn: 100, - MaxSubs: 1000, - MaxPending: 10000000, - PingInterval: 60 * time.Second, - MaxPingsOut: 3, - Cluster: ClusterOpts{ - NoAdvertise: true, - ConnectRetries: 2, - }, - WriteDeadline: 3 * time.Second, - } - fopts, err := ProcessConfigFile("./configs/test.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - - // Overrides via flags - opts := &Options{ - Port: 2222, - Password: "porkchop", - Debug: true, - HTTPPort: DEFAULT_HTTP_PORT, - ProfPort: 6789, - Cluster: ClusterOpts{ - NoAdvertise: true, - ConnectRetries: 2, - }, - } - merged := MergeOptions(fopts, opts) - - if !reflect.DeepEqual(golden, merged) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, merged) - } -} - -func TestRemoveSelfReference(t *testing.T) { - url1, _ := url.Parse("nats-route://user:password@10.4.5.6:4223") - url2, _ := url.Parse("nats-route://user:password@127.0.0.1:4223") - url3, _ := url.Parse("nats-route://user:password@127.0.0.1:4223") - - routes := []*url.URL{url1, url2, url3} - - newroutes, err := RemoveSelfReference(4223, routes) - if err != nil { - t.Fatalf("Error during RemoveSelfReference: %v", err) - } - - if len(newroutes) != 1 { - t.Fatalf("Wrong number of routes: %d", len(newroutes)) - } - - if newroutes[0] != routes[0] { - t.Fatalf("Self reference IP address %s in Routes", routes[0]) - } -} - -func TestAllowRouteWithDifferentPort(t *testing.T) { - url1, _ := url.Parse("nats-route://user:password@127.0.0.1:4224") - routes := []*url.URL{url1} - - newroutes, err := RemoveSelfReference(4223, routes) - if err != nil { - t.Fatalf("Error during RemoveSelfReference: %v", err) - } - - if len(newroutes) != 1 { - t.Fatalf("Wrong number of routes: %d", len(newroutes)) - } -} - -func TestRouteFlagOverride(t *testing.T) { - routeFlag := "nats-route://ruser:top_secret@127.0.0.1:8246" - rurl, _ := url.Parse(routeFlag) - - golden := &Options{ - ConfigFile: "./configs/srv_a.conf", - Host: "127.0.0.1", - Port: 7222, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: 7244, - Username: "ruser", - Password: "top_secret", - AuthTimeout: 0.5, - }, - Routes: []*url.URL{rurl}, - RoutesStr: routeFlag, - } - - fopts, err := ProcessConfigFile("./configs/srv_a.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - - // Overrides via flags - opts := &Options{ - RoutesStr: routeFlag, - } - merged := MergeOptions(fopts, opts) - - if !reflect.DeepEqual(golden, merged) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, merged) - } -} - -func TestClusterFlagsOverride(t *testing.T) { - routeFlag := "nats-route://ruser:top_secret@127.0.0.1:7246" - rurl, _ := url.Parse(routeFlag) - - // In this test, we override the cluster listen string. Note that in - // the golden options, the cluster other infos correspond to what - // is recovered from the configuration file, this explains the - // discrepency between ClusterListenStr and the rest. - // The server would then process the ClusterListenStr override and - // correctly override ClusterHost/ClustherPort/etc.. - golden := &Options{ - ConfigFile: "./configs/srv_a.conf", - Host: "127.0.0.1", - Port: 7222, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: 7244, - ListenStr: "nats://127.0.0.1:8224", - Username: "ruser", - Password: "top_secret", - AuthTimeout: 0.5, - }, - Routes: []*url.URL{rurl}, - } - - fopts, err := ProcessConfigFile("./configs/srv_a.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - - // Overrides via flags - opts := &Options{ - Cluster: ClusterOpts{ - ListenStr: "nats://127.0.0.1:8224", - }, - } - merged := MergeOptions(fopts, opts) - - if !reflect.DeepEqual(golden, merged) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, merged) - } -} - -func TestRouteFlagOverrideWithMultiple(t *testing.T) { - routeFlag := "nats-route://ruser:top_secret@127.0.0.1:8246, nats-route://ruser:top_secret@127.0.0.1:8266" - rurls := RoutesFromStr(routeFlag) - - golden := &Options{ - ConfigFile: "./configs/srv_a.conf", - Host: "127.0.0.1", - Port: 7222, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: 7244, - Username: "ruser", - Password: "top_secret", - AuthTimeout: 0.5, - }, - Routes: rurls, - RoutesStr: routeFlag, - } - - fopts, err := ProcessConfigFile("./configs/srv_a.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - - // Overrides via flags - opts := &Options{ - RoutesStr: routeFlag, - } - merged := MergeOptions(fopts, opts) - - if !reflect.DeepEqual(golden, merged) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, merged) - } -} - -func TestDynamicPortOnListen(t *testing.T) { - opts, err := ProcessConfigFile("./configs/listen-1.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - if opts.Port != -1 { - t.Fatalf("Received incorrect port %v, expected -1\n", opts.Port) - } - if opts.HTTPPort != -1 { - t.Fatalf("Received incorrect monitoring port %v, expected -1\n", opts.HTTPPort) - } - if opts.HTTPSPort != -1 { - t.Fatalf("Received incorrect secure monitoring port %v, expected -1\n", opts.HTTPSPort) - } -} - -func TestListenConfig(t *testing.T) { - opts, err := ProcessConfigFile("./configs/listen.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - processOptions(opts) - - // Normal clients - host := "10.0.1.22" - port := 4422 - monHost := "127.0.0.1" - if opts.Host != host { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.Host, host) - } - if opts.HTTPHost != monHost { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.HTTPHost, monHost) - } - if opts.Port != port { - t.Fatalf("Received incorrect port %v, expected %v\n", opts.Port, port) - } - - // Clustering - clusterHost := "127.0.0.1" - clusterPort := 4244 - - if opts.Cluster.Host != clusterHost { - t.Fatalf("Received incorrect cluster host %q, expected %q\n", opts.Cluster.Host, clusterHost) - } - if opts.Cluster.Port != clusterPort { - t.Fatalf("Received incorrect cluster port %v, expected %v\n", opts.Cluster.Port, clusterPort) - } - - // HTTP - httpHost := "127.0.0.1" - httpPort := 8422 - - if opts.HTTPHost != httpHost { - t.Fatalf("Received incorrect http host %q, expected %q\n", opts.HTTPHost, httpHost) - } - if opts.HTTPPort != httpPort { - t.Fatalf("Received incorrect http port %v, expected %v\n", opts.HTTPPort, httpPort) - } - - // HTTPS - httpsPort := 9443 - if opts.HTTPSPort != httpsPort { - t.Fatalf("Received incorrect https port %v, expected %v\n", opts.HTTPSPort, httpsPort) - } -} - -func TestListenPortOnlyConfig(t *testing.T) { - opts, err := ProcessConfigFile("./configs/listen_port.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - processOptions(opts) - - port := 8922 - - if opts.Host != DEFAULT_HOST { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.Host, DEFAULT_HOST) - } - if opts.HTTPHost != DEFAULT_HOST { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.Host, DEFAULT_HOST) - } - if opts.Port != port { - t.Fatalf("Received incorrect port %v, expected %v\n", opts.Port, port) - } -} - -func TestListenPortWithColonConfig(t *testing.T) { - opts, err := ProcessConfigFile("./configs/listen_port_with_colon.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - processOptions(opts) - - port := 8922 - - if opts.Host != DEFAULT_HOST { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.Host, DEFAULT_HOST) - } - if opts.HTTPHost != DEFAULT_HOST { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.Host, DEFAULT_HOST) - } - if opts.Port != port { - t.Fatalf("Received incorrect port %v, expected %v\n", opts.Port, port) - } -} - -func TestListenMonitoringDefault(t *testing.T) { - opts := &Options{ - Host: "10.0.1.22", - } - processOptions(opts) - - host := "10.0.1.22" - if opts.Host != host { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.Host, host) - } - if opts.HTTPHost != host { - t.Fatalf("Received incorrect host %q, expected %q\n", opts.Host, host) - } - if opts.Port != DEFAULT_PORT { - t.Fatalf("Received incorrect port %v, expected %v\n", opts.Port, DEFAULT_PORT) - } -} - -func TestMultipleUsersConfig(t *testing.T) { - opts, err := ProcessConfigFile("./configs/multiple_users.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - processOptions(opts) -} - -// Test highly depends on contents of the config file listed below. Any changes to that file -// may very well break this test. -func TestAuthorizationConfig(t *testing.T) { - opts, err := ProcessConfigFile("./configs/authorization.conf") - if err != nil { - t.Fatalf("Received an error reading config file: %v\n", err) - } - processOptions(opts) - lu := len(opts.Users) - if lu != 3 { - t.Fatalf("Expected 3 users, got %d\n", lu) - } - // Build a map - mu := make(map[string]*User) - for _, u := range opts.Users { - mu[u.Username] = u - } - - // Alice - alice, ok := mu["alice"] - if !ok { - t.Fatalf("Expected to see user Alice\n") - } - // Check for permissions details - if alice.Permissions == nil { - t.Fatalf("Expected Alice's permissions to be non-nil\n") - } - if alice.Permissions.Publish == nil { - t.Fatalf("Expected Alice's publish permissions to be non-nil\n") - } - if len(alice.Permissions.Publish) != 1 { - t.Fatalf("Expected Alice's publish permissions to have 1 element, got %d\n", - len(alice.Permissions.Publish)) - } - pubPerm := alice.Permissions.Publish[0] - if pubPerm != "*" { - t.Fatalf("Expected Alice's publish permissions to be '*', got %q\n", pubPerm) - } - if alice.Permissions.Subscribe == nil { - t.Fatalf("Expected Alice's subscribe permissions to be non-nil\n") - } - if len(alice.Permissions.Subscribe) != 1 { - t.Fatalf("Expected Alice's subscribe permissions to have 1 element, got %d\n", - len(alice.Permissions.Subscribe)) - } - subPerm := alice.Permissions.Subscribe[0] - if subPerm != ">" { - t.Fatalf("Expected Alice's subscribe permissions to be '>', got %q\n", subPerm) - } - - // Bob - bob, ok := mu["bob"] - if !ok { - t.Fatalf("Expected to see user Bob\n") - } - if bob.Permissions == nil { - t.Fatalf("Expected Bob's permissions to be non-nil\n") - } - - // Susan - susan, ok := mu["susan"] - if !ok { - t.Fatalf("Expected to see user Susan\n") - } - if susan.Permissions == nil { - t.Fatalf("Expected Susan's permissions to be non-nil\n") - } - // Check susan closely since she inherited the default permissions. - if susan.Permissions == nil { - t.Fatalf("Expected Susan's permissions to be non-nil\n") - } - if susan.Permissions.Publish != nil { - t.Fatalf("Expected Susan's publish permissions to be nil\n") - } - if susan.Permissions.Subscribe == nil { - t.Fatalf("Expected Susan's subscribe permissions to be non-nil\n") - } - if len(susan.Permissions.Subscribe) != 1 { - t.Fatalf("Expected Susan's subscribe permissions to have 1 element, got %d\n", - len(susan.Permissions.Subscribe)) - } - subPerm = susan.Permissions.Subscribe[0] - if subPerm != "PUBLIC.>" { - t.Fatalf("Expected Susan's subscribe permissions to be 'PUBLIC.>', got %q\n", subPerm) - } -} - -func TestTokenWithUserPass(t *testing.T) { - confFileName := "test.conf" - defer os.Remove(confFileName) - content := ` - authorization={ - user: user - pass: password - token: $2a$11$whatever - }` - if err := ioutil.WriteFile(confFileName, []byte(content), 0666); err != nil { - t.Fatalf("Error writing config file: %v", err) - } - _, err := ProcessConfigFile(confFileName) - if err == nil { - t.Fatal("Expected error, got none") - } - if !strings.Contains(err.Error(), "token") { - t.Fatalf("Expected error related to token, got %v", err) - } -} - -func TestTokenWithUsers(t *testing.T) { - confFileName := "test.conf" - defer os.Remove(confFileName) - content := ` - authorization={ - token: $2a$11$whatever - users: [ - {user: test, password: test} - ] - }` - if err := ioutil.WriteFile(confFileName, []byte(content), 0666); err != nil { - t.Fatalf("Error writing config file: %v", err) - } - _, err := ProcessConfigFile(confFileName) - if err == nil { - t.Fatal("Expected error, got none") - } - if !strings.Contains(err.Error(), "token") { - t.Fatalf("Expected error related to token, got %v", err) - } -} - -func TestParseWriteDeadline(t *testing.T) { - confFile := "test.conf" - defer os.Remove(confFile) - if err := ioutil.WriteFile(confFile, []byte("write_deadline: \"1x\"\n"), 0666); err != nil { - t.Fatalf("Error writing config file: %v", err) - } - _, err := ProcessConfigFile(confFile) - if err == nil { - t.Fatal("Expected error, got none") - } - if !strings.Contains(err.Error(), "parsing") { - t.Fatalf("Expected error related to parsing, got %v", err) - } - os.Remove(confFile) - if err := ioutil.WriteFile(confFile, []byte("write_deadline: \"1s\"\n"), 0666); err != nil { - t.Fatalf("Error writing config file: %v", err) - } - opts, err := ProcessConfigFile(confFile) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if opts.WriteDeadline != time.Second { - t.Fatalf("Expected write_deadline to be 1s, got %v", opts.WriteDeadline) - } - os.Remove(confFile) - oldStdout := os.Stdout - _, w, _ := os.Pipe() - defer func() { - w.Close() - os.Stdout = oldStdout - }() - os.Stdout = w - if err := ioutil.WriteFile(confFile, []byte("write_deadline: 2\n"), 0666); err != nil { - t.Fatalf("Error writing config file: %v", err) - } - opts, err = ProcessConfigFile(confFile) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if opts.WriteDeadline != 2*time.Second { - t.Fatalf("Expected write_deadline to be 2s, got %v", opts.WriteDeadline) - } -} - -func TestOptionsClone(t *testing.T) { - opts := &Options{ - ConfigFile: "./configs/test.conf", - Host: "127.0.0.1", - Port: 2222, - Username: "derek", - Password: "porkchop", - AuthTimeout: 1.0, - Debug: true, - Trace: true, - Logtime: false, - HTTPPort: DEFAULT_HTTP_PORT, - PidFile: "/tmp/gnatsd.pid", - ProfPort: 6789, - Syslog: true, - RemoteSyslog: "udp://foo.com:33", - MaxControlLine: 2048, - MaxPayload: 65536, - MaxConn: 100, - PingInterval: 60 * time.Second, - MaxPingsOut: 3, - Cluster: ClusterOpts{ - NoAdvertise: true, - ConnectRetries: 2, - }, - WriteDeadline: 3 * time.Second, - Routes: []*url.URL{&url.URL{}}, - Users: []*User{&User{Username: "foo", Password: "bar"}}, - } - - clone := opts.Clone() - - if !reflect.DeepEqual(opts, clone) { - t.Fatalf("Cloned Options are incorrect.\nexpected: %+v\ngot: %+v", - clone, opts) - } - - clone.Users[0].Password = "baz" - if reflect.DeepEqual(opts, clone) { - t.Fatal("Expected Options to be different") - } -} - -func TestOptionsCloneNilLists(t *testing.T) { - opts := &Options{} - - clone := opts.Clone() - - if clone.Routes != nil { - t.Fatalf("Expected Routes to be nil, got: %v", clone.Routes) - } - if clone.Users != nil { - t.Fatalf("Expected Users to be nil, got: %v", clone.Users) - } -} - -func TestOptionsCloneNil(t *testing.T) { - opts := (*Options)(nil) - clone := opts.Clone() - if clone != nil { - t.Fatalf("Expected nil, got: %+v", clone) - } -} - -func TestEmptyConfig(t *testing.T) { - opts, err := ProcessConfigFile("") - - if err != nil { - t.Fatalf("Expected no error from empty config, got: %+v", err) - } - - if opts.ConfigFile != "" { - t.Fatalf("Expected empty config, got: %+v", opts) - } -} - -func TestMalformedListenAddress(t *testing.T) { - opts, err := ProcessConfigFile("./configs/malformed_listen_address.conf") - if err == nil { - t.Fatalf("Expected an error reading config file: got %+v\n", opts) - } -} - -func TestMalformedClusterAddress(t *testing.T) { - opts, err := ProcessConfigFile("./configs/malformed_cluster_address.conf") - if err == nil { - t.Fatalf("Expected an error reading config file: got %+v\n", opts) - } -} - -func TestOptionsProcessConfigFile(t *testing.T) { - // Create options with default values of Debug and Trace - // that are the opposite of what is in the config file. - // Set another option that is not present in the config file. - logFileName := "test.log" - opts := &Options{ - Debug: true, - Trace: false, - LogFile: logFileName, - } - configFileName := "./configs/test.conf" - if err := opts.ProcessConfigFile(configFileName); err != nil { - t.Fatalf("Error processing config file: %v", err) - } - // Verify that values are as expected - if opts.ConfigFile != configFileName { - t.Fatalf("Expected ConfigFile to be set to %q, got %v", configFileName, opts.ConfigFile) - } - if opts.Debug { - t.Fatal("Debug option should have been set to false from config file") - } - if !opts.Trace { - t.Fatal("Trace option should have been set to true from config file") - } - if opts.LogFile != logFileName { - t.Fatalf("Expected LogFile to be %q, got %q", logFileName, opts.LogFile) - } -} - -func TestConfigureOptions(t *testing.T) { - // Options.Configure() will snapshot the flags. This is used by the reload code. - // We need to set it back to nil otherwise it will impact reload tests. - defer func() { FlagSnapshot = nil }() - - ch := make(chan bool, 1) - checkPrintInvoked := func() { - ch <- true - } - usage := func() { panic("should not get there") } - var fs *flag.FlagSet - type testPrint struct { - args []string - version, help, tlsHelp func() - } - testFuncs := []testPrint{ - testPrint{[]string{"-v"}, checkPrintInvoked, usage, PrintTLSHelpAndDie}, - testPrint{[]string{"version"}, checkPrintInvoked, usage, PrintTLSHelpAndDie}, - testPrint{[]string{"-h"}, PrintServerAndExit, checkPrintInvoked, PrintTLSHelpAndDie}, - testPrint{[]string{"help"}, PrintServerAndExit, checkPrintInvoked, PrintTLSHelpAndDie}, - testPrint{[]string{"-help_tls"}, PrintServerAndExit, usage, checkPrintInvoked}, - } - for _, tf := range testFuncs { - fs = flag.NewFlagSet("test", flag.ContinueOnError) - opts, err := ConfigureOptions(fs, tf.args, tf.version, tf.help, tf.tlsHelp) - if err != nil { - t.Fatalf("Error on configure: %v", err) - } - if opts != nil { - t.Fatalf("Expected options to be nil, got %v", opts) - } - select { - case <-ch: - case <-time.After(time.Second): - t.Fatalf("Should have invoked print function for args=%v", tf.args) - } - } - - // Helper function that expect parsing with given args to not produce an error. - mustNotFail := func(args []string) *Options { - fs := flag.NewFlagSet("test", flag.ContinueOnError) - opts, err := ConfigureOptions(fs, args, PrintServerAndExit, fs.Usage, PrintTLSHelpAndDie) - if err != nil { - stackFatalf(t, "Error on configure: %v", err) - } - return opts - } - - // Helper function that expect configuration to fail. - expectToFail := func(args []string, errContent ...string) { - fs := flag.NewFlagSet("test", flag.ContinueOnError) - // Silence the flagSet so that on failure nothing is printed. - // (flagSet would print error message about unknown flags, etc..) - silenceOuput := &bytes.Buffer{} - fs.SetOutput(silenceOuput) - opts, err := ConfigureOptions(fs, args, PrintServerAndExit, fs.Usage, PrintTLSHelpAndDie) - if opts != nil || err == nil { - stackFatalf(t, "Expected no option and an error, got opts=%v and err=%v", opts, err) - } - for _, testErr := range errContent { - if strings.Contains(err.Error(), testErr) { - // We got the error we wanted. - return - } - } - stackFatalf(t, "Expected errors containing any of those %v, got %v", errContent, err) - } - - // Basic test with port number - opts := mustNotFail([]string{"-p", "1234"}) - if opts.Port != 1234 { - t.Fatalf("Expected port to be 1234, got %v", opts.Port) - } - - // Should fail because of unknown parameter - expectToFail([]string{"foo"}, "command") - - // Should fail because unknown flag - expectToFail([]string{"-xxx", "foo"}, "flag") - - // Should fail because of config file missing - expectToFail([]string{"-c", "xxx.cfg"}, "file") - - // Should fail because of too many args for signal command - expectToFail([]string{"-sl", "quit=pid=foo"}, "signal") - - // Should fail because of invalid pid - // On windows, if not running with admin privileges, you would get access denied. - expectToFail([]string{"-sl", "quit=pid"}, "pid", "denied") - - // The config file set Trace to true. - opts = mustNotFail([]string{"-c", "./configs/test.conf"}) - if !opts.Trace { - t.Fatal("Trace should have been set to true") - } - - // The config file set Trace to true, but was overridden by param -V=false - opts = mustNotFail([]string{"-c", "./configs/test.conf", "-V=false"}) - if opts.Trace { - t.Fatal("Trace should have been set to false") - } - - // The config file set Trace to true, but was overridden by param -DV=false - opts = mustNotFail([]string{"-c", "./configs/test.conf", "-DV=false"}) - if opts.Debug || opts.Trace { - t.Fatal("Debug and Trace should have been set to false") - } - - // The config file set Trace to true, but was overridden by param -DV - opts = mustNotFail([]string{"-c", "./configs/test.conf", "-DV"}) - if !opts.Debug || !opts.Trace { - t.Fatal("Debug and Trace should have been set to true") - } - - // This should fail since -cluster is missing - expectedURL, _ := url.Parse("nats://127.0.0.1:6223") - expectToFail([]string{"-routes", expectedURL.String()}, "solicited routes") - - // Ensure that we can set cluster and routes from command line - opts = mustNotFail([]string{"-cluster", "nats://127.0.0.1:6222", "-routes", expectedURL.String()}) - if opts.Cluster.ListenStr != "nats://127.0.0.1:6222" { - t.Fatalf("Unexpected Cluster.ListenStr=%q", opts.Cluster.ListenStr) - } - if opts.RoutesStr != "nats://127.0.0.1:6223" || len(opts.Routes) != 1 || opts.Routes[0].String() != expectedURL.String() { - t.Fatalf("Unexpected RoutesStr: %q and Routes: %v", opts.RoutesStr, opts.Routes) - } - - // Use a config with cluster configuration and explicit route defined. - // Override with empty routes string. - opts = mustNotFail([]string{"-c", "./configs/srv_a.conf", "-routes", ""}) - if opts.RoutesStr != "" || len(opts.Routes) != 0 { - t.Fatalf("Unexpected RoutesStr: %q and Routes: %v", opts.RoutesStr, opts.Routes) - } - - // Use a config with cluster configuration and override cluster listen string - expectedURL, _ = url.Parse("nats-route://ruser:top_secret@127.0.0.1:7246") - opts = mustNotFail([]string{"-c", "./configs/srv_a.conf", "-cluster", "nats://ivan:pwd@127.0.0.1:6222"}) - if opts.Cluster.Username != "ivan" || opts.Cluster.Password != "pwd" || opts.Cluster.Port != 6222 || - len(opts.Routes) != 1 || opts.Routes[0].String() != expectedURL.String() { - t.Fatalf("Unexpected Cluster and/or Routes: %#v - %v", opts.Cluster, opts.Routes) - } - - // Disable clustering from command line - opts = mustNotFail([]string{"-c", "./configs/srv_a.conf", "-cluster", ""}) - if opts.Cluster.Port != 0 { - t.Fatalf("Unexpected Cluster: %v", opts.Cluster) - } - - // Various erros due to malformed cluster listen string. - // (adding -routes to have more than 1 set flag to check - // that Visit() stops when an error is found). - expectToFail([]string{"-cluster", ":", "-routes", ""}, "protocol") - expectToFail([]string{"-cluster", "nats://127.0.0.1", "-routes", ""}, "port") - expectToFail([]string{"-cluster", "nats://127.0.0.1:xxx", "-routes", ""}, "integer") - expectToFail([]string{"-cluster", "nats://ivan:127.0.0.1:6222", "-routes", ""}, "colons") - expectToFail([]string{"-cluster", "nats://ivan@127.0.0.1:6222", "-routes", ""}, "password") - - // Override config file's TLS configuration from command line, and completely disable TLS - opts = mustNotFail([]string{"-c", "./configs/tls.conf", "-tls=false"}) - if opts.TLSConfig != nil || opts.TLS { - t.Fatal("Expected TLS to be disabled") - } - // Override config file's TLS configuration from command line, and force TLS verification. - // However, since TLS config has to be regenerated, user need to provide -tlscert and -tlskey too. - // So this should fail. - expectToFail([]string{"-c", "./configs/tls.conf", "-tlsverify"}, "valid") - - // Now same than above, but with all valid params. - opts = mustNotFail([]string{"-c", "./configs/tls.conf", "-tlsverify", "-tlscert", "./configs/certs/server.pem", "-tlskey", "./configs/certs/key.pem"}) - if opts.TLSConfig == nil || !opts.TLSVerify { - t.Fatal("Expected TLS to be configured and force verification") - } - - // Configure TLS, but some TLS params missing - expectToFail([]string{"-tls"}, "valid") - expectToFail([]string{"-tls", "-tlscert", "./configs/certs/server.pem"}, "valid") - // One of the file does not exist - expectToFail([]string{"-tls", "-tlscert", "./configs/certs/server.pem", "-tlskey", "./configs/certs/notfound.pem"}, "file") - - // Configure TLS and check that this results in a TLSConfig option. - opts = mustNotFail([]string{"-tls", "-tlscert", "./configs/certs/server.pem", "-tlskey", "./configs/certs/key.pem"}) - if opts.TLSConfig == nil || !opts.TLS { - t.Fatal("Expected TLSConfig to be set") - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/parser.go b/vendor/github.com/nats-io/gnatsd/server/parser.go deleted file mode 100644 index 088894fb..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/parser.go +++ /dev/null @@ -1,749 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "fmt" -) - -type pubArg struct { - subject []byte - reply []byte - sid []byte - szb []byte - size int -} - -type parseState struct { - state int - as int - drop int - pa pubArg - argBuf []byte - msgBuf []byte - scratch [MAX_CONTROL_LINE_SIZE]byte -} - -// Parser constants -const ( - OP_START = iota - OP_PLUS - OP_PLUS_O - OP_PLUS_OK - OP_MINUS - OP_MINUS_E - OP_MINUS_ER - OP_MINUS_ERR - OP_MINUS_ERR_SPC - MINUS_ERR_ARG - OP_C - OP_CO - OP_CON - OP_CONN - OP_CONNE - OP_CONNEC - OP_CONNECT - CONNECT_ARG - OP_P - OP_PU - OP_PUB - OP_PUB_SPC - PUB_ARG - OP_PI - OP_PIN - OP_PING - OP_PO - OP_PON - OP_PONG - MSG_PAYLOAD - MSG_END - OP_S - OP_SU - OP_SUB - OP_SUB_SPC - SUB_ARG - OP_U - OP_UN - OP_UNS - OP_UNSU - OP_UNSUB - OP_UNSUB_SPC - UNSUB_ARG - OP_M - OP_MS - OP_MSG - OP_MSG_SPC - MSG_ARG - OP_I - OP_IN - OP_INF - OP_INFO - INFO_ARG -) - -func (c *client) parse(buf []byte) error { - var i int - var b byte - - mcl := MAX_CONTROL_LINE_SIZE - if c.srv != nil && c.srv.getOpts() != nil { - mcl = c.srv.getOpts().MaxControlLine - } - - // snapshot this, and reset when we receive a - // proper CONNECT if needed. - authSet := c.isAuthTimerSet() - - // Move to loop instead of range syntax to allow jumping of i - for i = 0; i < len(buf); i++ { - b = buf[i] - - switch c.state { - case OP_START: - if b != 'C' && b != 'c' && authSet { - goto authErr - } - switch b { - case 'P', 'p': - c.state = OP_P - case 'S', 's': - c.state = OP_S - case 'U', 'u': - c.state = OP_U - case 'M', 'm': - if c.typ == CLIENT { - goto parseErr - } else { - c.state = OP_M - } - case 'C', 'c': - c.state = OP_C - case 'I', 'i': - c.state = OP_I - case '+': - c.state = OP_PLUS - case '-': - c.state = OP_MINUS - default: - goto parseErr - } - case OP_P: - switch b { - case 'U', 'u': - c.state = OP_PU - case 'I', 'i': - c.state = OP_PI - case 'O', 'o': - c.state = OP_PO - default: - goto parseErr - } - case OP_PU: - switch b { - case 'B', 'b': - c.state = OP_PUB - default: - goto parseErr - } - case OP_PUB: - switch b { - case ' ', '\t': - c.state = OP_PUB_SPC - default: - goto parseErr - } - case OP_PUB_SPC: - switch b { - case ' ', '\t': - continue - default: - c.state = PUB_ARG - c.as = i - } - case PUB_ARG: - switch b { - case '\r': - c.drop = 1 - case '\n': - var arg []byte - if c.argBuf != nil { - arg = c.argBuf - } else { - arg = buf[c.as : i-c.drop] - } - if err := c.processPub(arg); err != nil { - return err - } - c.drop, c.as, c.state = OP_START, i+1, MSG_PAYLOAD - // If we don't have a saved buffer then jump ahead with - // the index. If this overruns what is left we fall out - // and process split buffer. - if c.msgBuf == nil { - i = c.as + c.pa.size - LEN_CR_LF - } - default: - if c.argBuf != nil { - c.argBuf = append(c.argBuf, b) - } - } - case MSG_PAYLOAD: - if c.msgBuf != nil { - // copy as much as we can to the buffer and skip ahead. - toCopy := c.pa.size - len(c.msgBuf) - avail := len(buf) - i - if avail < toCopy { - toCopy = avail - } - if toCopy > 0 { - start := len(c.msgBuf) - // This is needed for copy to work. - c.msgBuf = c.msgBuf[:start+toCopy] - copy(c.msgBuf[start:], buf[i:i+toCopy]) - // Update our index - i = (i + toCopy) - 1 - } else { - // Fall back to append if needed. - c.msgBuf = append(c.msgBuf, b) - } - if len(c.msgBuf) >= c.pa.size { - c.state = MSG_END - } - } else if i-c.as >= c.pa.size { - c.state = MSG_END - } - case MSG_END: - switch b { - case '\n': - if c.msgBuf != nil { - c.msgBuf = append(c.msgBuf, b) - } else { - c.msgBuf = buf[c.as : i+1] - } - // strict check for proto - if len(c.msgBuf) != c.pa.size+LEN_CR_LF { - goto parseErr - } - c.processMsg(c.msgBuf) - c.argBuf, c.msgBuf = nil, nil - c.drop, c.as, c.state = 0, i+1, OP_START - default: - if c.msgBuf != nil { - c.msgBuf = append(c.msgBuf, b) - } - continue - } - case OP_S: - switch b { - case 'U', 'u': - c.state = OP_SU - default: - goto parseErr - } - case OP_SU: - switch b { - case 'B', 'b': - c.state = OP_SUB - default: - goto parseErr - } - case OP_SUB: - switch b { - case ' ', '\t': - c.state = OP_SUB_SPC - default: - goto parseErr - } - case OP_SUB_SPC: - switch b { - case ' ', '\t': - continue - default: - c.state = SUB_ARG - c.as = i - } - case SUB_ARG: - switch b { - case '\r': - c.drop = 1 - case '\n': - var arg []byte - if c.argBuf != nil { - arg = c.argBuf - c.argBuf = nil - } else { - arg = buf[c.as : i-c.drop] - } - if err := c.processSub(arg); err != nil { - return err - } - c.drop, c.as, c.state = 0, i+1, OP_START - default: - if c.argBuf != nil { - c.argBuf = append(c.argBuf, b) - } - } - case OP_U: - switch b { - case 'N', 'n': - c.state = OP_UN - default: - goto parseErr - } - case OP_UN: - switch b { - case 'S', 's': - c.state = OP_UNS - default: - goto parseErr - } - case OP_UNS: - switch b { - case 'U', 'u': - c.state = OP_UNSU - default: - goto parseErr - } - case OP_UNSU: - switch b { - case 'B', 'b': - c.state = OP_UNSUB - default: - goto parseErr - } - case OP_UNSUB: - switch b { - case ' ', '\t': - c.state = OP_UNSUB_SPC - default: - goto parseErr - } - case OP_UNSUB_SPC: - switch b { - case ' ', '\t': - continue - default: - c.state = UNSUB_ARG - c.as = i - } - case UNSUB_ARG: - switch b { - case '\r': - c.drop = 1 - case '\n': - var arg []byte - if c.argBuf != nil { - arg = c.argBuf - c.argBuf = nil - } else { - arg = buf[c.as : i-c.drop] - } - if err := c.processUnsub(arg); err != nil { - return err - } - c.drop, c.as, c.state = 0, i+1, OP_START - default: - if c.argBuf != nil { - c.argBuf = append(c.argBuf, b) - } - } - case OP_PI: - switch b { - case 'N', 'n': - c.state = OP_PIN - default: - goto parseErr - } - case OP_PIN: - switch b { - case 'G', 'g': - c.state = OP_PING - default: - goto parseErr - } - case OP_PING: - switch b { - case '\n': - c.processPing() - c.drop, c.state = 0, OP_START - } - case OP_PO: - switch b { - case 'N', 'n': - c.state = OP_PON - default: - goto parseErr - } - case OP_PON: - switch b { - case 'G', 'g': - c.state = OP_PONG - default: - goto parseErr - } - case OP_PONG: - switch b { - case '\n': - c.processPong() - c.drop, c.state = 0, OP_START - } - case OP_C: - switch b { - case 'O', 'o': - c.state = OP_CO - default: - goto parseErr - } - case OP_CO: - switch b { - case 'N', 'n': - c.state = OP_CON - default: - goto parseErr - } - case OP_CON: - switch b { - case 'N', 'n': - c.state = OP_CONN - default: - goto parseErr - } - case OP_CONN: - switch b { - case 'E', 'e': - c.state = OP_CONNE - default: - goto parseErr - } - case OP_CONNE: - switch b { - case 'C', 'c': - c.state = OP_CONNEC - default: - goto parseErr - } - case OP_CONNEC: - switch b { - case 'T', 't': - c.state = OP_CONNECT - default: - goto parseErr - } - case OP_CONNECT: - switch b { - case ' ', '\t': - continue - default: - c.state = CONNECT_ARG - c.as = i - } - case CONNECT_ARG: - switch b { - case '\r': - c.drop = 1 - case '\n': - var arg []byte - if c.argBuf != nil { - arg = c.argBuf - c.argBuf = nil - } else { - arg = buf[c.as : i-c.drop] - } - if err := c.processConnect(arg); err != nil { - return err - } - c.drop, c.state = 0, OP_START - // Reset notion on authSet - authSet = c.isAuthTimerSet() - default: - if c.argBuf != nil { - c.argBuf = append(c.argBuf, b) - } - } - case OP_M: - switch b { - case 'S', 's': - c.state = OP_MS - default: - goto parseErr - } - case OP_MS: - switch b { - case 'G', 'g': - c.state = OP_MSG - default: - goto parseErr - } - case OP_MSG: - switch b { - case ' ', '\t': - c.state = OP_MSG_SPC - default: - goto parseErr - } - case OP_MSG_SPC: - switch b { - case ' ', '\t': - continue - default: - c.state = MSG_ARG - c.as = i - } - case MSG_ARG: - switch b { - case '\r': - c.drop = 1 - case '\n': - var arg []byte - if c.argBuf != nil { - arg = c.argBuf - } else { - arg = buf[c.as : i-c.drop] - } - if err := c.processMsgArgs(arg); err != nil { - return err - } - c.drop, c.as, c.state = 0, i+1, MSG_PAYLOAD - - // jump ahead with the index. If this overruns - // what is left we fall out and process split - // buffer. - i = c.as + c.pa.size - 1 - default: - if c.argBuf != nil { - c.argBuf = append(c.argBuf, b) - } - } - case OP_I: - switch b { - case 'N', 'n': - c.state = OP_IN - default: - goto parseErr - } - case OP_IN: - switch b { - case 'F', 'f': - c.state = OP_INF - default: - goto parseErr - } - case OP_INF: - switch b { - case 'O', 'o': - c.state = OP_INFO - default: - goto parseErr - } - case OP_INFO: - switch b { - case ' ', '\t': - continue - default: - c.state = INFO_ARG - c.as = i - } - case INFO_ARG: - switch b { - case '\r': - c.drop = 1 - case '\n': - var arg []byte - if c.argBuf != nil { - arg = c.argBuf - c.argBuf = nil - } else { - arg = buf[c.as : i-c.drop] - } - if err := c.processInfo(arg); err != nil { - return err - } - c.drop, c.as, c.state = 0, i+1, OP_START - default: - if c.argBuf != nil { - c.argBuf = append(c.argBuf, b) - } - } - case OP_PLUS: - switch b { - case 'O', 'o': - c.state = OP_PLUS_O - default: - goto parseErr - } - case OP_PLUS_O: - switch b { - case 'K', 'k': - c.state = OP_PLUS_OK - default: - goto parseErr - } - case OP_PLUS_OK: - switch b { - case '\n': - c.drop, c.state = 0, OP_START - } - case OP_MINUS: - switch b { - case 'E', 'e': - c.state = OP_MINUS_E - default: - goto parseErr - } - case OP_MINUS_E: - switch b { - case 'R', 'r': - c.state = OP_MINUS_ER - default: - goto parseErr - } - case OP_MINUS_ER: - switch b { - case 'R', 'r': - c.state = OP_MINUS_ERR - default: - goto parseErr - } - case OP_MINUS_ERR: - switch b { - case ' ', '\t': - c.state = OP_MINUS_ERR_SPC - default: - goto parseErr - } - case OP_MINUS_ERR_SPC: - switch b { - case ' ', '\t': - continue - default: - c.state = MINUS_ERR_ARG - c.as = i - } - case MINUS_ERR_ARG: - switch b { - case '\r': - c.drop = 1 - case '\n': - var arg []byte - if c.argBuf != nil { - arg = c.argBuf - c.argBuf = nil - } else { - arg = buf[c.as : i-c.drop] - } - c.processErr(string(arg)) - c.drop, c.as, c.state = 0, i+1, OP_START - default: - if c.argBuf != nil { - c.argBuf = append(c.argBuf, b) - } - } - default: - goto parseErr - } - } - - // Check for split buffer scenarios for any ARG state. - if c.state == SUB_ARG || c.state == UNSUB_ARG || c.state == PUB_ARG || - c.state == MSG_ARG || c.state == MINUS_ERR_ARG || - c.state == CONNECT_ARG || c.state == INFO_ARG { - // Setup a holder buffer to deal with split buffer scenario. - if c.argBuf == nil { - c.argBuf = c.scratch[:0] - c.argBuf = append(c.argBuf, buf[c.as:i-c.drop]...) - } - // Check for violations of control line length here. Note that this is not - // exact at all but the performance hit is too great to be precise, and - // catching here should prevent memory exhaustion attacks. - if len(c.argBuf) > mcl { - c.sendErr("Maximum Control Line Exceeded") - c.closeConnection(MaxControlLineExceeded) - return ErrMaxControlLine - } - } - - // Check for split msg - if (c.state == MSG_PAYLOAD || c.state == MSG_END) && c.msgBuf == nil { - // We need to clone the pubArg if it is still referencing the - // read buffer and we are not able to process the msg. - if c.argBuf == nil { - // Works also for MSG_ARG, when message comes from ROUTE. - c.clonePubArg() - } - - // If we will overflow the scratch buffer, just create a - // new buffer to hold the split message. - if c.pa.size > cap(c.scratch)-len(c.argBuf) { - lrem := len(buf[c.as:]) - - // Consider it a protocol error when the remaining payload - // is larger than the reported size for PUB. It can happen - // when processing incomplete messages from rogue clients. - if lrem > c.pa.size+LEN_CR_LF { - goto parseErr - } - c.msgBuf = make([]byte, lrem, c.pa.size+LEN_CR_LF) - copy(c.msgBuf, buf[c.as:]) - } else { - c.msgBuf = c.scratch[len(c.argBuf):len(c.argBuf)] - c.msgBuf = append(c.msgBuf, (buf[c.as:])...) - } - } - - return nil - -authErr: - c.authViolation() - return ErrAuthorization - -parseErr: - c.sendErr("Unknown Protocol Operation") - snip := protoSnippet(i, buf) - err := fmt.Errorf("%s parser ERROR, state=%d, i=%d: proto='%s...'", - c.typeString(), c.state, i, snip) - return err -} - -func protoSnippet(start int, buf []byte) string { - stop := start + PROTO_SNIPPET_SIZE - bufSize := len(buf) - if start >= bufSize { - return `""` - } - if stop > bufSize { - stop = bufSize - 1 - } - return fmt.Sprintf("%q", buf[start:stop]) -} - -// clonePubArg is used when the split buffer scenario has the pubArg in the existing read buffer, but -// we need to hold onto it into the next read. -func (c *client) clonePubArg() { - c.argBuf = c.scratch[:0] - c.argBuf = append(c.argBuf, c.pa.subject...) - c.argBuf = append(c.argBuf, c.pa.reply...) - c.argBuf = append(c.argBuf, c.pa.sid...) - c.argBuf = append(c.argBuf, c.pa.szb...) - - c.pa.subject = c.argBuf[:len(c.pa.subject)] - - if c.pa.reply != nil { - c.pa.reply = c.argBuf[len(c.pa.subject) : len(c.pa.subject)+len(c.pa.reply)] - } - - if c.pa.sid != nil { - c.pa.sid = c.argBuf[len(c.pa.subject)+len(c.pa.reply) : len(c.pa.subject)+len(c.pa.reply)+len(c.pa.sid)] - } - - c.pa.szb = c.argBuf[len(c.pa.subject)+len(c.pa.reply)+len(c.pa.sid):] -} diff --git a/vendor/github.com/nats-io/gnatsd/server/parser_test.go b/vendor/github.com/nats-io/gnatsd/server/parser_test.go deleted file mode 100644 index 95631b08..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/parser_test.go +++ /dev/null @@ -1,546 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "testing" -) - -func dummyClient() *client { - return &client{srv: New(&defaultServerOptions)} -} - -func dummyRouteClient() *client { - return &client{srv: New(&defaultServerOptions), typ: ROUTER} -} - -func TestParsePing(t *testing.T) { - c := dummyClient() - if c.state != OP_START { - t.Fatalf("Expected OP_START vs %d\n", c.state) - } - ping := []byte("PING\r\n") - err := c.parse(ping[:1]) - if err != nil || c.state != OP_P { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(ping[1:2]) - if err != nil || c.state != OP_PI { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(ping[2:3]) - if err != nil || c.state != OP_PIN { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(ping[3:4]) - if err != nil || c.state != OP_PING { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(ping[4:5]) - if err != nil || c.state != OP_PING { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(ping[5:6]) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(ping) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - // Should tolerate spaces - ping = []byte("PING \r") - err = c.parse(ping) - if err != nil || c.state != OP_PING { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - c.state = OP_START - ping = []byte("PING \r \n") - err = c.parse(ping) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } -} - -func TestParsePong(t *testing.T) { - c := dummyClient() - if c.state != OP_START { - t.Fatalf("Expected OP_START vs %d\n", c.state) - } - pong := []byte("PONG\r\n") - err := c.parse(pong[:1]) - if err != nil || c.state != OP_P { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(pong[1:2]) - if err != nil || c.state != OP_PO { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(pong[2:3]) - if err != nil || c.state != OP_PON { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(pong[3:4]) - if err != nil || c.state != OP_PONG { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(pong[4:5]) - if err != nil || c.state != OP_PONG { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(pong[5:6]) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if c.ping.out != 0 { - t.Fatalf("Unexpected ping.out value: %d vs 0\n", c.ping.out) - } - err = c.parse(pong) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if c.ping.out != 0 { - t.Fatalf("Unexpected ping.out value: %d vs 0\n", c.ping.out) - } - // Should tolerate spaces - pong = []byte("PONG \r") - err = c.parse(pong) - if err != nil || c.state != OP_PONG { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - c.state = OP_START - pong = []byte("PONG \r \n") - err = c.parse(pong) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if c.ping.out != 0 { - t.Fatalf("Unexpected ping.out value: %d vs 0\n", c.ping.out) - } - - // Should be adjusting c.pout (Pings Outstanding): reset to 0 - c.state = OP_START - c.ping.out = 10 - pong = []byte("PONG\r\n") - err = c.parse(pong) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if c.ping.out != 0 { - t.Fatalf("Unexpected ping.out: %d vs 0\n", c.ping.out) - } -} - -func TestParseConnect(t *testing.T) { - c := dummyClient() - connect := []byte("CONNECT {\"verbose\":false,\"pedantic\":true,\"tls_required\":false}\r\n") - err := c.parse(connect) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - // Check saved state - if c.as != 8 { - t.Fatalf("ArgStart state incorrect: 8 vs %d\n", c.as) - } -} - -func TestParseSub(t *testing.T) { - c := dummyClient() - sub := []byte("SUB foo 1\r") - err := c.parse(sub) - if err != nil || c.state != SUB_ARG { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - // Check saved state - if c.as != 4 { - t.Fatalf("ArgStart state incorrect: 4 vs %d\n", c.as) - } - if c.drop != 1 { - t.Fatalf("Drop state incorrect: 1 vs %d\n", c.as) - } - if !bytes.Equal(sub[c.as:], []byte("foo 1\r")) { - t.Fatalf("Arg state incorrect: %s\n", sub[c.as:]) - } -} - -func TestParsePub(t *testing.T) { - c := dummyClient() - - pub := []byte("PUB foo 5\r\nhello\r") - err := c.parse(pub) - if err != nil || c.state != MSG_END { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if !bytes.Equal(c.pa.subject, []byte("foo")) { - t.Fatalf("Did not parse subject correctly: 'foo' vs '%s'\n", string(c.pa.subject)) - } - if c.pa.reply != nil { - t.Fatalf("Did not parse reply correctly: 'nil' vs '%s'\n", string(c.pa.reply)) - } - if c.pa.size != 5 { - t.Fatalf("Did not parse msg size correctly: 5 vs %d\n", c.pa.size) - } - - // Clear snapshots - c.argBuf, c.msgBuf, c.state = nil, nil, OP_START - - pub = []byte("PUB foo.bar INBOX.22 11\r\nhello world\r") - err = c.parse(pub) - if err != nil || c.state != MSG_END { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if !bytes.Equal(c.pa.subject, []byte("foo.bar")) { - t.Fatalf("Did not parse subject correctly: 'foo' vs '%s'\n", string(c.pa.subject)) - } - if !bytes.Equal(c.pa.reply, []byte("INBOX.22")) { - t.Fatalf("Did not parse reply correctly: 'INBOX.22' vs '%s'\n", string(c.pa.reply)) - } - if c.pa.size != 11 { - t.Fatalf("Did not parse msg size correctly: 11 vs %d\n", c.pa.size) - } -} - -func TestParsePubArg(t *testing.T) { - c := dummyClient() - - for _, test := range []struct { - arg string - subject string - reply string - size int - szb string - }{ - {arg: "a 2", - subject: "a", reply: "", size: 2, szb: "2"}, - {arg: "a 222", - subject: "a", reply: "", size: 222, szb: "222"}, - {arg: "foo 22", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: " foo 22", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "foo 22 ", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "foo 22", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: " foo 22 ", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: " foo 22 ", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "foo bar 22", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: " foo bar 22", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "foo bar 22 ", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "foo bar 22", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: " foo bar 22 ", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: " foo bar 22 ", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: " foo bar 2222 ", - subject: "foo", reply: "bar", size: 2222, szb: "2222"}, - {arg: " foo 2222 ", - subject: "foo", reply: "", size: 2222, szb: "2222"}, - {arg: "a\t2", - subject: "a", reply: "", size: 2, szb: "2"}, - {arg: "a\t222", - subject: "a", reply: "", size: 222, szb: "222"}, - {arg: "foo\t22", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "\tfoo\t22", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "foo\t22\t", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "foo\t\t\t22", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "\tfoo\t22\t", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "\tfoo\t\t\t22\t", - subject: "foo", reply: "", size: 22, szb: "22"}, - {arg: "foo\tbar\t22", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "\tfoo\tbar\t22", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "foo\tbar\t22\t", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "foo\t\tbar\t\t22", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "\tfoo\tbar\t22\t", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "\t \tfoo\t \t \tbar\t \t22\t \t", - subject: "foo", reply: "bar", size: 22, szb: "22"}, - {arg: "\t\tfoo\t\t\tbar\t\t2222\t\t", - subject: "foo", reply: "bar", size: 2222, szb: "2222"}, - {arg: "\t \tfoo\t \t \t\t\t2222\t \t", - subject: "foo", reply: "", size: 2222, szb: "2222"}, - } { - t.Run(test.arg, func(t *testing.T) { - if err := c.processPub([]byte(test.arg)); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if !bytes.Equal(c.pa.subject, []byte(test.subject)) { - t.Fatalf("Mismatched subject: '%s'\n", c.pa.subject) - } - if !bytes.Equal(c.pa.reply, []byte(test.reply)) { - t.Fatalf("Mismatched reply subject: '%s'\n", c.pa.reply) - } - if !bytes.Equal(c.pa.szb, []byte(test.szb)) { - t.Fatalf("Bad size buf: '%s'\n", c.pa.szb) - } - if c.pa.size != test.size { - t.Fatalf("Bad size: %d\n", c.pa.size) - } - }) - } -} - -func TestParsePubBadSize(t *testing.T) { - c := dummyClient() - // Setup localized max payload - c.mpay = 32768 - if err := c.processPub([]byte("foo 2222222222222222")); err == nil { - t.Fatalf("Expected parse error for size too large") - } -} - -func TestParseMsg(t *testing.T) { - c := dummyRouteClient() - - pub := []byte("MSG foo RSID:1:2 5\r\nhello\r") - err := c.parse(pub) - if err != nil || c.state != MSG_END { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if !bytes.Equal(c.pa.subject, []byte("foo")) { - t.Fatalf("Did not parse subject correctly: 'foo' vs '%s'\n", c.pa.subject) - } - if c.pa.reply != nil { - t.Fatalf("Did not parse reply correctly: 'nil' vs '%s'\n", c.pa.reply) - } - if c.pa.size != 5 { - t.Fatalf("Did not parse msg size correctly: 5 vs %d\n", c.pa.size) - } - if !bytes.Equal(c.pa.sid, []byte("RSID:1:2")) { - t.Fatalf("Did not parse sid correctly: 'RSID:1:2' vs '%s'\n", c.pa.sid) - } - - // Clear snapshots - c.argBuf, c.msgBuf, c.state = nil, nil, OP_START - - pub = []byte("MSG foo.bar RSID:1:2 INBOX.22 11\r\nhello world\r") - err = c.parse(pub) - if err != nil || c.state != MSG_END { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - if !bytes.Equal(c.pa.subject, []byte("foo.bar")) { - t.Fatalf("Did not parse subject correctly: 'foo' vs '%s'\n", c.pa.subject) - } - if !bytes.Equal(c.pa.reply, []byte("INBOX.22")) { - t.Fatalf("Did not parse reply correctly: 'INBOX.22' vs '%s'\n", c.pa.reply) - } - if c.pa.size != 11 { - t.Fatalf("Did not parse msg size correctly: 11 vs %d\n", c.pa.size) - } -} - -func testMsgArg(c *client, t *testing.T) { - if !bytes.Equal(c.pa.subject, []byte("foobar")) { - t.Fatalf("Mismatched subject: '%s'\n", c.pa.subject) - } - if !bytes.Equal(c.pa.szb, []byte("22")) { - t.Fatalf("Bad size buf: '%s'\n", c.pa.szb) - } - if c.pa.size != 22 { - t.Fatalf("Bad size: %d\n", c.pa.size) - } - if !bytes.Equal(c.pa.sid, []byte("RSID:22:1")) { - t.Fatalf("Bad sid: '%s'\n", c.pa.sid) - } -} - -func TestParseMsgArg(t *testing.T) { - c := dummyClient() - if err := c.processMsgArgs([]byte("foobar RSID:22:1 22")); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - testMsgArg(c, t) - if err := c.processMsgArgs([]byte(" foobar RSID:22:1 22")); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - testMsgArg(c, t) - if err := c.processMsgArgs([]byte(" foobar RSID:22:1 22 ")); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - testMsgArg(c, t) - if err := c.processMsgArgs([]byte("foobar RSID:22:1 \t22")); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if err := c.processMsgArgs([]byte("foobar\t\tRSID:22:1\t22\r")); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - testMsgArg(c, t) -} - -func TestParseMsgSpace(t *testing.T) { - c := dummyRouteClient() - - // Ivan bug he found - if err := c.parse([]byte("MSG \r\n")); err == nil { - t.Fatalf("Expected parse error for MSG ") - } - - c = dummyClient() - - // Anything with an M from a client should parse error - if err := c.parse([]byte("M")); err == nil { - t.Fatalf("Expected parse error for M* from a client") - } -} - -func TestShouldFail(t *testing.T) { - wrongProtos := []string{ - "xxx", - "Px", "PIx", "PINx", " PING", - "POx", "PONx", - "+x", "+Ox", - "-x", "-Ex", "-ERx", "-ERRx", - "Cx", "COx", "CONx", "CONNx", "CONNEx", "CONNECx", "CONNECx", "CONNECT \r\n", - "PUx", "PUB foo\r\n", "PUB \r\n", "PUB foo bar \r\n", - "PUB foo 2\r\nok \r\n", "PUB foo 2\r\nok\r \n", - "Sx", "SUx", "SUB\r\n", "SUB \r\n", "SUB foo\r\n", - "SUB foo bar baz 22\r\n", - "Ux", "UNx", "UNSx", "UNSUx", "UNSUBx", "UNSUBUNSUB 1\r\n", "UNSUB_2\r\n", - "UNSUB_UNSUB_UNSUB 2\r\n", "UNSUB_\t2\r\n", "UNSUB\r\n", "UNSUB \r\n", - "UNSUB \t \r\n", - "Ix", "INx", "INFx", "INFO \r\n", - } - for _, proto := range wrongProtos { - c := dummyClient() - if err := c.parse([]byte(proto)); err == nil { - t.Fatalf("Should have received a parse error for: %v", proto) - } - } - - // Special case for MSG, type needs to not be client. - wrongProtos = []string{"Mx", "MSx", "MSGx", "MSG \r\n"} - for _, proto := range wrongProtos { - c := dummyClient() - c.typ = ROUTER - if err := c.parse([]byte(proto)); err == nil { - t.Fatalf("Should have received a parse error for: %v", proto) - } - } -} - -func TestProtoSnippet(t *testing.T) { - sample := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - - tests := []struct { - input int - expected string - }{ - {0, `"abcdefghijklmnopqrstuvwxyzABCDEF"`}, - {1, `"bcdefghijklmnopqrstuvwxyzABCDEFG"`}, - {2, `"cdefghijklmnopqrstuvwxyzABCDEFGH"`}, - {3, `"defghijklmnopqrstuvwxyzABCDEFGHI"`}, - {4, `"efghijklmnopqrstuvwxyzABCDEFGHIJ"`}, - {5, `"fghijklmnopqrstuvwxyzABCDEFGHIJK"`}, - {6, `"ghijklmnopqrstuvwxyzABCDEFGHIJKL"`}, - {7, `"hijklmnopqrstuvwxyzABCDEFGHIJKLM"`}, - {8, `"ijklmnopqrstuvwxyzABCDEFGHIJKLMN"`}, - {9, `"jklmnopqrstuvwxyzABCDEFGHIJKLMNO"`}, - {10, `"klmnopqrstuvwxyzABCDEFGHIJKLMNOP"`}, - {11, `"lmnopqrstuvwxyzABCDEFGHIJKLMNOPQ"`}, - {12, `"mnopqrstuvwxyzABCDEFGHIJKLMNOPQR"`}, - {13, `"nopqrstuvwxyzABCDEFGHIJKLMNOPQRS"`}, - {14, `"opqrstuvwxyzABCDEFGHIJKLMNOPQRST"`}, - {15, `"pqrstuvwxyzABCDEFGHIJKLMNOPQRSTU"`}, - {16, `"qrstuvwxyzABCDEFGHIJKLMNOPQRSTUV"`}, - {17, `"rstuvwxyzABCDEFGHIJKLMNOPQRSTUVW"`}, - {18, `"stuvwxyzABCDEFGHIJKLMNOPQRSTUVWX"`}, - {19, `"tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"`}, - {20, `"uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"`}, - {21, `"vwxyzABCDEFGHIJKLMNOPQRSTUVWXY"`}, - {22, `"wxyzABCDEFGHIJKLMNOPQRSTUVWXY"`}, - {23, `"xyzABCDEFGHIJKLMNOPQRSTUVWXY"`}, - {24, `"yzABCDEFGHIJKLMNOPQRSTUVWXY"`}, - {25, `"zABCDEFGHIJKLMNOPQRSTUVWXY"`}, - {26, `"ABCDEFGHIJKLMNOPQRSTUVWXY"`}, - {27, `"BCDEFGHIJKLMNOPQRSTUVWXY"`}, - {28, `"CDEFGHIJKLMNOPQRSTUVWXY"`}, - {29, `"DEFGHIJKLMNOPQRSTUVWXY"`}, - {30, `"EFGHIJKLMNOPQRSTUVWXY"`}, - {31, `"FGHIJKLMNOPQRSTUVWXY"`}, - {32, `"GHIJKLMNOPQRSTUVWXY"`}, - {33, `"HIJKLMNOPQRSTUVWXY"`}, - {34, `"IJKLMNOPQRSTUVWXY"`}, - {35, `"JKLMNOPQRSTUVWXY"`}, - {36, `"KLMNOPQRSTUVWXY"`}, - {37, `"LMNOPQRSTUVWXY"`}, - {38, `"MNOPQRSTUVWXY"`}, - {39, `"NOPQRSTUVWXY"`}, - {40, `"OPQRSTUVWXY"`}, - {41, `"PQRSTUVWXY"`}, - {42, `"QRSTUVWXY"`}, - {43, `"RSTUVWXY"`}, - {44, `"STUVWXY"`}, - {45, `"TUVWXY"`}, - {46, `"UVWXY"`}, - {47, `"VWXY"`}, - {48, `"WXY"`}, - {49, `"XY"`}, - {50, `"Y"`}, - {51, `""`}, - {52, `""`}, - {53, `""`}, - {54, `""`}, - } - - for _, tt := range tests { - got := protoSnippet(tt.input, sample) - if tt.expected != got { - t.Errorf("Expected protocol snippet to be %s when start=%d but got %s\n", tt.expected, tt.input, got) - } - } -} - -func TestParseOK(t *testing.T) { - c := dummyClient() - if c.state != OP_START { - t.Fatalf("Expected OP_START vs %d\n", c.state) - } - okProto := []byte("+OK\r\n") - err := c.parse(okProto[:1]) - if err != nil || c.state != OP_PLUS { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(okProto[1:2]) - if err != nil || c.state != OP_PLUS_O { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(okProto[2:3]) - if err != nil || c.state != OP_PLUS_OK { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(okProto[3:4]) - if err != nil || c.state != OP_PLUS_OK { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } - err = c.parse(okProto[4:5]) - if err != nil || c.state != OP_START { - t.Fatalf("Unexpected: %d : %v\n", c.state, err) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/ping_test.go b/vendor/github.com/nats-io/gnatsd/server/ping_test.go deleted file mode 100644 index aa56f6b4..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/ping_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "fmt" - "testing" - "time" - - "github.com/nats-io/go-nats" -) - -const PING_CLIENT_PORT = 11228 - -var DefaultPingOptions = Options{ - Host: "127.0.0.1", - Port: PING_CLIENT_PORT, - NoLog: true, - NoSigs: true, - PingInterval: 5 * time.Millisecond, -} - -func TestPing(t *testing.T) { - s := RunServer(&DefaultPingOptions) - defer s.Shutdown() - - nc, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", PING_CLIENT_PORT)) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc.Close() - time.Sleep(10 * time.Millisecond) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_darwin.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_darwin.go deleted file mode 100644 index b00f1e00..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_darwin.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pse - -import ( - "fmt" - "os" - "os/exec" -) - -// ProcUsage returns CPU usage -func ProcUsage(pcpu *float64, rss, vss *int64) error { - pidStr := fmt.Sprintf("%d", os.Getpid()) - out, err := exec.Command("ps", "o", "pcpu=,rss=,vsz=", "-p", pidStr).Output() - if err != nil { - *rss, *vss = -1, -1 - return fmt.Errorf("ps call failed:%v", err) - } - fmt.Sscanf(string(out), "%f %d %d", pcpu, rss, vss) - *rss *= 1024 // 1k blocks, want bytes. - *vss *= 1024 // 1k blocks, want bytes. - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_freebsd.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_freebsd.go deleted file mode 100644 index 40c52847..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_freebsd.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pse - -/* -#include -#include -#include -#include -#include - -long pagetok(long size) -{ - int pageshift, pagesize; - - pagesize = getpagesize(); - pageshift = 0; - - while (pagesize > 1) { - pageshift++; - pagesize >>= 1; - } - - return (size << pageshift); -} - -int getusage(double *pcpu, unsigned int *rss, unsigned int *vss) -{ - int mib[4], ret; - size_t len; - struct kinfo_proc kp; - - len = 4; - sysctlnametomib("kern.proc.pid", mib, &len); - - mib[3] = getpid(); - len = sizeof(kp); - - ret = sysctl(mib, 4, &kp, &len, NULL, 0); - if (ret != 0) { - return (errno); - } - - *rss = pagetok(kp.ki_rssize); - *vss = kp.ki_size; - *pcpu = kp.ki_pctcpu; - - return 0; -} - -*/ -import "C" - -import ( - "syscall" -) - -// This is a placeholder for now. -func ProcUsage(pcpu *float64, rss, vss *int64) error { - var r, v C.uint - var c C.double - - if ret := C.getusage(&c, &r, &v); ret != 0 { - return syscall.Errno(ret) - } - - *pcpu = float64(c) - *rss = int64(r) - *vss = int64(v) - - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_linux.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_linux.go deleted file mode 100644 index 9fea3e07..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_linux.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pse - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "sync/atomic" - "syscall" - "time" -) - -var ( - procStatFile string - ticks int64 - lastTotal int64 - lastSeconds int64 - ipcpu int64 -) - -const ( - utimePos = 13 - stimePos = 14 - startPos = 21 - vssPos = 22 - rssPos = 23 -) - -func init() { - // Avoiding to generate docker image without CGO - ticks = 100 // int64(C.sysconf(C._SC_CLK_TCK)) - procStatFile = fmt.Sprintf("/proc/%d/stat", os.Getpid()) - periodic() -} - -// Sampling function to keep pcpu relevant. -func periodic() { - contents, err := ioutil.ReadFile(procStatFile) - if err != nil { - return - } - fields := bytes.Fields(contents) - - // PCPU - pstart := parseInt64(fields[startPos]) - utime := parseInt64(fields[utimePos]) - stime := parseInt64(fields[stimePos]) - total := utime + stime - - var sysinfo syscall.Sysinfo_t - if err := syscall.Sysinfo(&sysinfo); err != nil { - return - } - - seconds := int64(sysinfo.Uptime) - (pstart / ticks) - - // Save off temps - lt := lastTotal - ls := lastSeconds - - // Update last sample - lastTotal = total - lastSeconds = seconds - - // Adjust to current time window - total -= lt - seconds -= ls - - if seconds > 0 { - atomic.StoreInt64(&ipcpu, (total*1000/ticks)/seconds) - } - - time.AfterFunc(1*time.Second, periodic) -} - -func ProcUsage(pcpu *float64, rss, vss *int64) error { - contents, err := ioutil.ReadFile(procStatFile) - if err != nil { - return err - } - fields := bytes.Fields(contents) - - // Memory - *rss = (parseInt64(fields[rssPos])) << 12 - *vss = parseInt64(fields[vssPos]) - - // PCPU - // We track this with periodic sampling, so just load and go. - *pcpu = float64(atomic.LoadInt64(&ipcpu)) / 10.0 - - return nil -} - -// Ascii numbers 0-9 -const ( - asciiZero = 48 - asciiNine = 57 -) - -// parseInt64 expects decimal positive numbers. We -// return -1 to signal error -func parseInt64(d []byte) (n int64) { - if len(d) == 0 { - return -1 - } - for _, dec := range d { - if dec < asciiZero || dec > asciiNine { - return -1 - } - n = n*10 + (int64(dec) - asciiZero) - } - return n -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_openbsd.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_openbsd.go deleted file mode 100644 index 260f1a7c..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_openbsd.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Copied from pse_darwin.go - -package pse - -import ( - "fmt" - "os" - "os/exec" -) - -// ProcUsage returns CPU usage -func ProcUsage(pcpu *float64, rss, vss *int64) error { - pidStr := fmt.Sprintf("%d", os.Getpid()) - out, err := exec.Command("ps", "o", "pcpu=,rss=,vsz=", "-p", pidStr).Output() - if err != nil { - *rss, *vss = -1, -1 - return fmt.Errorf("ps call failed:%v", err) - } - fmt.Sscanf(string(out), "%f %d %d", pcpu, rss, vss) - *rss *= 1024 // 1k blocks, want bytes. - *vss *= 1024 // 1k blocks, want bytes. - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_rumprun.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_rumprun.go deleted file mode 100644 index 48e80fca..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_rumprun.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build rumprun - -package pse - -// This is a placeholder for now. -func ProcUsage(pcpu *float64, rss, vss *int64) error { - *pcpu = 0.0 - *rss = 0 - *vss = 0 - - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_solaris.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_solaris.go deleted file mode 100644 index 8e40d2ed..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_solaris.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pse - -// This is a placeholder for now. -func ProcUsage(pcpu *float64, rss, vss *int64) error { - *pcpu = 0.0 - *rss = 0 - *vss = 0 - - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_test.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_test.go deleted file mode 100644 index 890f9645..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package pse - -import ( - "fmt" - "os" - "os/exec" - "runtime" - "testing" -) - -func TestPSEmulation(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skipf("Skipping this test on Windows") - } - var rss, vss, psRss, psVss int64 - var pcpu, psPcpu float64 - - runtime.GC() - - // PS version first - pidStr := fmt.Sprintf("%d", os.Getpid()) - out, err := exec.Command("ps", "o", "pcpu=,rss=,vsz=", "-p", pidStr).Output() - if err != nil { - t.Fatalf("Failed to execute ps command: %v\n", err) - } - - fmt.Sscanf(string(out), "%f %d %d", &psPcpu, &psRss, &psVss) - psRss *= 1024 // 1k blocks, want bytes. - psVss *= 1024 // 1k blocks, want bytes. - - runtime.GC() - - // Our internal version - ProcUsage(&pcpu, &rss, &vss) - - if pcpu != psPcpu { - delta := int64(pcpu - psPcpu) - if delta < 0 { - delta = -delta - } - if delta > 30 { // 30%? - t.Fatalf("CPUs did not match close enough: %f vs %f", pcpu, psPcpu) - } - } - if rss != psRss { - delta := rss - psRss - if delta < 0 { - delta = -delta - } - if delta > 1024*1024 { // 1MB - t.Fatalf("RSSs did not match close enough: %d vs %d", rss, psRss) - } - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_windows.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_windows.go deleted file mode 100644 index a8b11070..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_windows.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build windows - -package pse - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "syscall" - "time" - "unsafe" -) - -var ( - pdh = syscall.NewLazyDLL("pdh.dll") - winPdhOpenQuery = pdh.NewProc("PdhOpenQuery") - winPdhAddCounter = pdh.NewProc("PdhAddCounterW") - winPdhCollectQueryData = pdh.NewProc("PdhCollectQueryData") - winPdhGetFormattedCounterValue = pdh.NewProc("PdhGetFormattedCounterValue") - winPdhGetFormattedCounterArray = pdh.NewProc("PdhGetFormattedCounterArrayW") -) - -// global performance counter query handle and counters -var ( - pcHandle PDH_HQUERY - pidCounter, cpuCounter, rssCounter, vssCounter PDH_HCOUNTER - prevCPU float64 - prevRss int64 - prevVss int64 - lastSampleTime time.Time - processPid int - pcQueryLock sync.Mutex - initialSample = true -) - -// maxQuerySize is the number of values to return from a query. -// It represents the maximum # of servers that can be queried -// simultaneously running on a machine. -const maxQuerySize = 512 - -// Keep static memory around to reuse; this works best for passing -// into the pdh API. -var counterResults [maxQuerySize]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE - -// PDH Types -type ( - PDH_HQUERY syscall.Handle - PDH_HCOUNTER syscall.Handle -) - -// PDH constants used here -const ( - PDH_FMT_DOUBLE = 0x00000200 - PDH_INVALID_DATA = 0xC0000BC6 - PDH_MORE_DATA = 0x800007D2 -) - -// PDH_FMT_COUNTERVALUE_DOUBLE - double value -type PDH_FMT_COUNTERVALUE_DOUBLE struct { - CStatus uint32 - DoubleValue float64 -} - -// PDH_FMT_COUNTERVALUE_ITEM_DOUBLE is an array -// element of a double value -type PDH_FMT_COUNTERVALUE_ITEM_DOUBLE struct { - SzName *uint16 // pointer to a string - FmtValue PDH_FMT_COUNTERVALUE_DOUBLE -} - -func pdhAddCounter(hQuery PDH_HQUERY, szFullCounterPath string, dwUserData uintptr, phCounter *PDH_HCOUNTER) error { - ptxt, _ := syscall.UTF16PtrFromString(szFullCounterPath) - r0, _, _ := winPdhAddCounter.Call( - uintptr(hQuery), - uintptr(unsafe.Pointer(ptxt)), - dwUserData, - uintptr(unsafe.Pointer(phCounter))) - - if r0 != 0 { - return fmt.Errorf("pdhAddCounter failed. %d", r0) - } - return nil -} - -func pdhOpenQuery(datasrc *uint16, userdata uint32, query *PDH_HQUERY) error { - r0, _, _ := syscall.Syscall(winPdhOpenQuery.Addr(), 3, 0, uintptr(userdata), uintptr(unsafe.Pointer(query))) - if r0 != 0 { - return fmt.Errorf("pdhOpenQuery failed - %d", r0) - } - return nil -} - -func pdhCollectQueryData(hQuery PDH_HQUERY) error { - r0, _, _ := winPdhCollectQueryData.Call(uintptr(hQuery)) - if r0 != 0 { - return fmt.Errorf("pdhCollectQueryData failed - %d", r0) - } - return nil -} - -// pdhGetFormattedCounterArrayDouble returns the value of return code -// rather than error, to easily check return codes -func pdhGetFormattedCounterArrayDouble(hCounter PDH_HCOUNTER, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *PDH_FMT_COUNTERVALUE_ITEM_DOUBLE) uint32 { - ret, _, _ := winPdhGetFormattedCounterArray.Call( - uintptr(hCounter), - uintptr(PDH_FMT_DOUBLE), - uintptr(unsafe.Pointer(lpdwBufferSize)), - uintptr(unsafe.Pointer(lpdwBufferCount)), - uintptr(unsafe.Pointer(itemBuffer))) - - return uint32(ret) -} - -func getCounterArrayData(counter PDH_HCOUNTER) ([]float64, error) { - var bufSize uint32 - var bufCount uint32 - - // Retrieving array data requires two calls, the first which - // requires an addressable empty buffer, and sets size fields. - // The second call returns the data. - initialBuf := make([]PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, 1) - ret := pdhGetFormattedCounterArrayDouble(counter, &bufSize, &bufCount, &initialBuf[0]) - if ret == PDH_MORE_DATA { - // we'll likely never get here, but be safe. - if bufCount > maxQuerySize { - bufCount = maxQuerySize - } - ret = pdhGetFormattedCounterArrayDouble(counter, &bufSize, &bufCount, &counterResults[0]) - if ret == 0 { - rv := make([]float64, bufCount) - for i := 0; i < int(bufCount); i++ { - rv[i] = counterResults[i].FmtValue.DoubleValue - } - return rv, nil - } - } - if ret != 0 { - return nil, fmt.Errorf("getCounterArrayData failed - %d", ret) - } - - return nil, nil -} - -// getProcessImageName returns the name of the process image, as expected by -// the performance counter API. -func getProcessImageName() (name string) { - name = filepath.Base(os.Args[0]) - name = strings.TrimRight(name, ".exe") - return -} - -// initialize our counters -func initCounters() (err error) { - - processPid = os.Getpid() - // require an addressible nil pointer - var source uint16 - if err := pdhOpenQuery(&source, 0, &pcHandle); err != nil { - return err - } - - // setup the performance counters, search for all server instances - name := fmt.Sprintf("%s*", getProcessImageName()) - pidQuery := fmt.Sprintf("\\Process(%s)\\ID Process", name) - cpuQuery := fmt.Sprintf("\\Process(%s)\\%% Processor Time", name) - rssQuery := fmt.Sprintf("\\Process(%s)\\Working Set - Private", name) - vssQuery := fmt.Sprintf("\\Process(%s)\\Virtual Bytes", name) - - if err = pdhAddCounter(pcHandle, pidQuery, 0, &pidCounter); err != nil { - return err - } - if err = pdhAddCounter(pcHandle, cpuQuery, 0, &cpuCounter); err != nil { - return err - } - if err = pdhAddCounter(pcHandle, rssQuery, 0, &rssCounter); err != nil { - return err - } - if err = pdhAddCounter(pcHandle, vssQuery, 0, &vssCounter); err != nil { - return err - } - - // prime the counters by collecting once, and sleep to get somewhat - // useful information the first request. Counters for the CPU require - // at least two collect calls. - if err = pdhCollectQueryData(pcHandle); err != nil { - return err - } - time.Sleep(50) - - return nil -} - -// ProcUsage returns process CPU and memory statistics -func ProcUsage(pcpu *float64, rss, vss *int64) error { - var err error - - // For simplicity, protect the entire call. - // Most simultaneous requests will immediately return - // with cached values. - pcQueryLock.Lock() - defer pcQueryLock.Unlock() - - // First time through, initialize counters. - if initialSample { - if err = initCounters(); err != nil { - return err - } - initialSample = false - } else if time.Since(lastSampleTime) < (2 * time.Second) { - // only refresh every two seconds as to minimize impact - // on the server. - *pcpu = prevCPU - *rss = prevRss - *vss = prevVss - return nil - } - - // always save the sample time, even on errors. - defer func() { - lastSampleTime = time.Now() - }() - - // refresh the performance counter data - if err = pdhCollectQueryData(pcHandle); err != nil { - return err - } - - // retrieve the data - var pidAry, cpuAry, rssAry, vssAry []float64 - if pidAry, err = getCounterArrayData(pidCounter); err != nil { - return err - } - if cpuAry, err = getCounterArrayData(cpuCounter); err != nil { - return err - } - if rssAry, err = getCounterArrayData(rssCounter); err != nil { - return err - } - if vssAry, err = getCounterArrayData(vssCounter); err != nil { - return err - } - // find the index of the entry for this process - idx := int(-1) - for i := range pidAry { - if int(pidAry[i]) == processPid { - idx = i - break - } - } - // no pid found... - if idx < 0 { - return fmt.Errorf("could not find pid in performance counter results") - } - // assign values from the performance counters - *pcpu = cpuAry[idx] - *rss = int64(rssAry[idx]) - *vss = int64(vssAry[idx]) - - // save off cache values - prevCPU = *pcpu - prevRss = *rss - prevVss = *vss - - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/pse/pse_windows_test.go b/vendor/github.com/nats-io/gnatsd/server/pse/pse_windows_test.go deleted file mode 100644 index bae1dd58..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/pse/pse_windows_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build windows - -package pse - -import ( - "fmt" - "os/exec" - "runtime" - "strconv" - "strings" - "testing" -) - -func checkValues(t *testing.T, pcpu, tPcpu float64, rss, tRss int64) { - if pcpu != tPcpu { - delta := int64(pcpu - tPcpu) - if delta < 0 { - delta = -delta - } - if delta > 30 { // 30%? - t.Fatalf("CPUs did not match close enough: %f vs %f", pcpu, tPcpu) - } - } - if rss != tRss { - delta := rss - tRss - if delta < 0 { - delta = -delta - } - if delta > 1024*1024 { // 1MB - t.Fatalf("RSSs did not match close enough: %d vs %d", rss, tRss) - } - } -} - -func TestPSEmulationWin(t *testing.T) { - var pcpu, tPcpu float64 - var rss, vss, tRss int64 - - runtime.GC() - - if err := ProcUsage(&pcpu, &rss, &vss); err != nil { - t.Fatalf("Error: %v", err) - } - - runtime.GC() - - imageName := getProcessImageName() - // query the counters using typeperf - out, err := exec.Command("typeperf.exe", - fmt.Sprintf("\\Process(%s)\\%% Processor Time", imageName), - fmt.Sprintf("\\Process(%s)\\Working Set - Private", imageName), - fmt.Sprintf("\\Process(%s)\\Virtual Bytes", imageName), - "-sc", "1").Output() - if err != nil { - t.Fatal("unable to run command", err) - } - - // parse out results - refer to comments in procUsage for detail - results := strings.Split(string(out), "\r\n") - values := strings.Split(results[2], ",") - - // parse pcpu - tPcpu, err = strconv.ParseFloat(strings.Trim(values[1], "\""), 64) - if err != nil { - t.Fatalf("Unable to parse percent cpu: %s", values[1]) - } - - // parse private bytes (rss) - fval, err := strconv.ParseFloat(strings.Trim(values[2], "\""), 64) - if err != nil { - t.Fatalf("Unable to parse private bytes: %s", values[2]) - } - tRss = int64(fval) - - checkValues(t, pcpu, tPcpu, rss, tRss) - - runtime.GC() - - // Again to test caching - if err = ProcUsage(&pcpu, &rss, &vss); err != nil { - t.Fatalf("Error: %v", err) - } - checkValues(t, pcpu, tPcpu, rss, tRss) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/reload.go b/vendor/github.com/nats-io/gnatsd/server/reload.go deleted file mode 100644 index 8839cf99..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/reload.go +++ /dev/null @@ -1,714 +0,0 @@ -// Copyright 2017-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "crypto/tls" - "errors" - "fmt" - "net/url" - "reflect" - "strings" - "sync/atomic" - "time" -) - -// FlagSnapshot captures the server options as specified by CLI flags at -// startup. This should not be modified once the server has started. -var FlagSnapshot *Options - -// option is a hot-swappable configuration setting. -type option interface { - // Apply the server option. - Apply(server *Server) - - // IsLoggingChange indicates if this option requires reloading the logger. - IsLoggingChange() bool - - // IsAuthChange indicates if this option requires reloading authorization. - IsAuthChange() bool -} - -// loggingOption is a base struct that provides default option behaviors for -// logging-related options. -type loggingOption struct{} - -func (l loggingOption) IsLoggingChange() bool { - return true -} - -func (l loggingOption) IsAuthChange() bool { - return false -} - -// traceOption implements the option interface for the `trace` setting. -type traceOption struct { - loggingOption - newValue bool -} - -// Apply is a no-op because logging will be reloaded after options are applied. -func (t *traceOption) Apply(server *Server) { - server.Noticef("Reloaded: trace = %v", t.newValue) -} - -// debugOption implements the option interface for the `debug` setting. -type debugOption struct { - loggingOption - newValue bool -} - -// Apply is a no-op because logging will be reloaded after options are applied. -func (d *debugOption) Apply(server *Server) { - server.Noticef("Reloaded: debug = %v", d.newValue) -} - -// logtimeOption implements the option interface for the `logtime` setting. -type logtimeOption struct { - loggingOption - newValue bool -} - -// Apply is a no-op because logging will be reloaded after options are applied. -func (l *logtimeOption) Apply(server *Server) { - server.Noticef("Reloaded: logtime = %v", l.newValue) -} - -// logfileOption implements the option interface for the `log_file` setting. -type logfileOption struct { - loggingOption - newValue string -} - -// Apply is a no-op because logging will be reloaded after options are applied. -func (l *logfileOption) Apply(server *Server) { - server.Noticef("Reloaded: log_file = %v", l.newValue) -} - -// syslogOption implements the option interface for the `syslog` setting. -type syslogOption struct { - loggingOption - newValue bool -} - -// Apply is a no-op because logging will be reloaded after options are applied. -func (s *syslogOption) Apply(server *Server) { - server.Noticef("Reloaded: syslog = %v", s.newValue) -} - -// remoteSyslogOption implements the option interface for the `remote_syslog` -// setting. -type remoteSyslogOption struct { - loggingOption - newValue string -} - -// Apply is a no-op because logging will be reloaded after options are applied. -func (r *remoteSyslogOption) Apply(server *Server) { - server.Noticef("Reloaded: remote_syslog = %v", r.newValue) -} - -// noopOption is a base struct that provides default no-op behaviors. -type noopOption struct{} - -func (n noopOption) IsLoggingChange() bool { - return false -} - -func (n noopOption) IsAuthChange() bool { - return false -} - -// tlsOption implements the option interface for the `tls` setting. -type tlsOption struct { - noopOption - newValue *tls.Config -} - -// Apply the tls change. -func (t *tlsOption) Apply(server *Server) { - server.mu.Lock() - tlsRequired := t.newValue != nil - server.info.TLSRequired = tlsRequired - message := "disabled" - if tlsRequired { - server.info.TLSVerify = (t.newValue.ClientAuth == tls.RequireAndVerifyClientCert) - message = "enabled" - } - server.mu.Unlock() - server.Noticef("Reloaded: tls = %s", message) -} - -// tlsTimeoutOption implements the option interface for the tls `timeout` -// setting. -type tlsTimeoutOption struct { - noopOption - newValue float64 -} - -// Apply is a no-op because the timeout will be reloaded after options are -// applied. -func (t *tlsTimeoutOption) Apply(server *Server) { - server.Noticef("Reloaded: tls timeout = %v", t.newValue) -} - -// authOption is a base struct that provides default option behaviors. -type authOption struct{} - -func (o authOption) IsLoggingChange() bool { - return false -} - -func (o authOption) IsAuthChange() bool { - return true -} - -// usernameOption implements the option interface for the `username` setting. -type usernameOption struct { - authOption -} - -// Apply is a no-op because authorization will be reloaded after options are -// applied. -func (u *usernameOption) Apply(server *Server) { - server.Noticef("Reloaded: authorization username") -} - -// passwordOption implements the option interface for the `password` setting. -type passwordOption struct { - authOption -} - -// Apply is a no-op because authorization will be reloaded after options are -// applied. -func (p *passwordOption) Apply(server *Server) { - server.Noticef("Reloaded: authorization password") -} - -// authorizationOption implements the option interface for the `token` -// authorization setting. -type authorizationOption struct { - authOption -} - -// Apply is a no-op because authorization will be reloaded after options are -// applied. -func (a *authorizationOption) Apply(server *Server) { - server.Noticef("Reloaded: authorization token") -} - -// authTimeoutOption implements the option interface for the authorization -// `timeout` setting. -type authTimeoutOption struct { - noopOption // Not authOption because this is a no-op; will be reloaded with options. - newValue float64 -} - -// Apply is a no-op because the timeout will be reloaded after options are -// applied. -func (a *authTimeoutOption) Apply(server *Server) { - server.Noticef("Reloaded: authorization timeout = %v", a.newValue) -} - -// usersOption implements the option interface for the authorization `users` -// setting. -type usersOption struct { - authOption - newValue []*User -} - -func (u *usersOption) Apply(server *Server) { - server.Noticef("Reloaded: authorization users") -} - -// clusterOption implements the option interface for the `cluster` setting. -type clusterOption struct { - authOption - newValue ClusterOpts -} - -// Apply the cluster change. -func (c *clusterOption) Apply(server *Server) { - // TODO: support enabling/disabling clustering. - server.mu.Lock() - tlsRequired := c.newValue.TLSConfig != nil - server.routeInfo.TLSRequired = tlsRequired - server.routeInfo.TLSVerify = tlsRequired - server.routeInfo.AuthRequired = c.newValue.Username != "" - if c.newValue.NoAdvertise { - server.routeInfo.ClientConnectURLs = nil - } else { - server.routeInfo.ClientConnectURLs = server.clientConnectURLs - } - server.setRouteInfoHostPortAndIP() - server.mu.Unlock() - server.Noticef("Reloaded: cluster") -} - -// routesOption implements the option interface for the cluster `routes` -// setting. -type routesOption struct { - noopOption - add []*url.URL - remove []*url.URL -} - -// Apply the route changes by adding and removing the necessary routes. -func (r *routesOption) Apply(server *Server) { - server.mu.Lock() - routes := make([]*client, len(server.routes)) - i := 0 - for _, client := range server.routes { - routes[i] = client - i++ - } - server.mu.Unlock() - - // Remove routes. - for _, remove := range r.remove { - for _, client := range routes { - if client.route.url == remove { - // Do not attempt to reconnect when route is removed. - client.setRouteNoReconnectOnClose() - client.closeConnection(RouteRemoved) - server.Noticef("Removed route %v", remove) - } - } - } - - // Add routes. - server.solicitRoutes(r.add) - - server.Noticef("Reloaded: cluster routes") -} - -// maxConnOption implements the option interface for the `max_connections` -// setting. -type maxConnOption struct { - noopOption - newValue int -} - -// Apply the max connections change by closing random connections til we are -// below the limit if necessary. -func (m *maxConnOption) Apply(server *Server) { - server.mu.Lock() - var ( - clients = make([]*client, len(server.clients)) - i = 0 - ) - // Map iteration is random, which allows us to close random connections. - for _, client := range server.clients { - clients[i] = client - i++ - } - server.mu.Unlock() - - if m.newValue > 0 && len(clients) > m.newValue { - // Close connections til we are within the limit. - var ( - numClose = len(clients) - m.newValue - closed = 0 - ) - for _, client := range clients { - client.maxConnExceeded() - closed++ - if closed >= numClose { - break - } - } - server.Noticef("Closed %d connections to fall within max_connections", closed) - } - server.Noticef("Reloaded: max_connections = %v", m.newValue) -} - -// pidFileOption implements the option interface for the `pid_file` setting. -type pidFileOption struct { - noopOption - newValue string -} - -// Apply the setting by logging the pid to the new file. -func (p *pidFileOption) Apply(server *Server) { - if p.newValue == "" { - return - } - if err := server.logPid(); err != nil { - server.Errorf("Failed to write pidfile: %v", err) - } - server.Noticef("Reloaded: pid_file = %v", p.newValue) -} - -// portsFileDirOption implements the option interface for the `portFileDir` setting. -type portsFileDirOption struct { - noopOption - oldValue string - newValue string -} - -func (p *portsFileDirOption) Apply(server *Server) { - server.deletePortsFile(p.oldValue) - server.logPorts() - server.Noticef("Reloaded: ports_file_dir = %v", p.newValue) -} - -// maxControlLineOption implements the option interface for the -// `max_control_line` setting. -type maxControlLineOption struct { - noopOption - newValue int -} - -// Apply is a no-op because the max control line will be reloaded after options -// are applied -func (m *maxControlLineOption) Apply(server *Server) { - server.Noticef("Reloaded: max_control_line = %d", m.newValue) -} - -// maxPayloadOption implements the option interface for the `max_payload` -// setting. -type maxPayloadOption struct { - noopOption - newValue int -} - -// Apply the setting by updating the server info and each client. -func (m *maxPayloadOption) Apply(server *Server) { - server.mu.Lock() - server.info.MaxPayload = m.newValue - for _, client := range server.clients { - atomic.StoreInt64(&client.mpay, int64(m.newValue)) - } - server.mu.Unlock() - server.Noticef("Reloaded: max_payload = %d", m.newValue) -} - -// pingIntervalOption implements the option interface for the `ping_interval` -// setting. -type pingIntervalOption struct { - noopOption - newValue time.Duration -} - -// Apply is a no-op because the ping interval will be reloaded after options -// are applied. -func (p *pingIntervalOption) Apply(server *Server) { - server.Noticef("Reloaded: ping_interval = %s", p.newValue) -} - -// maxPingsOutOption implements the option interface for the `ping_max` -// setting. -type maxPingsOutOption struct { - noopOption - newValue int -} - -// Apply is a no-op because the ping interval will be reloaded after options -// are applied. -func (m *maxPingsOutOption) Apply(server *Server) { - server.Noticef("Reloaded: ping_max = %d", m.newValue) -} - -// writeDeadlineOption implements the option interface for the `write_deadline` -// setting. -type writeDeadlineOption struct { - noopOption - newValue time.Duration -} - -// Apply is a no-op because the write deadline will be reloaded after options -// are applied. -func (w *writeDeadlineOption) Apply(server *Server) { - server.Noticef("Reloaded: write_deadline = %s", w.newValue) -} - -// clientAdvertiseOption implements the option interface for the `client_advertise` setting. -type clientAdvertiseOption struct { - noopOption - newValue string -} - -// Apply the setting by updating the server info and regenerate the infoJSON byte array. -func (c *clientAdvertiseOption) Apply(server *Server) { - server.mu.Lock() - server.setInfoHostPortAndGenerateJSON() - server.mu.Unlock() - server.Noticef("Reload: client_advertise = %s", c.newValue) -} - -// Reload reads the current configuration file and applies any supported -// changes. This returns an error if the server was not started with a config -// file or an option which doesn't support hot-swapping was changed. -func (s *Server) Reload() error { - s.mu.Lock() - if s.configFile == "" { - s.mu.Unlock() - return errors.New("Can only reload config when a file is provided using -c or --config") - } - newOpts, err := ProcessConfigFile(s.configFile) - if err != nil { - s.mu.Unlock() - // TODO: Dump previous good config to a .bak file? - return err - } - clientOrgPort := s.clientActualPort - clusterOrgPort := s.clusterActualPort - s.mu.Unlock() - - // Apply flags over config file settings. - newOpts = MergeOptions(newOpts, FlagSnapshot) - processOptions(newOpts) - - // processOptions sets Port to 0 if set to -1 (RANDOM port) - // If that's the case, set it to the saved value when the accept loop was - // created. - if newOpts.Port == 0 { - newOpts.Port = clientOrgPort - } - // We don't do that for cluster, so check against -1. - if newOpts.Cluster.Port == -1 { - newOpts.Cluster.Port = clusterOrgPort - } - - if err := s.reloadOptions(newOpts); err != nil { - return err - } - s.mu.Lock() - s.configTime = time.Now() - s.mu.Unlock() - return nil -} - -// reloadOptions reloads the server config with the provided options. If an -// option that doesn't support hot-swapping is changed, this returns an error. -func (s *Server) reloadOptions(newOpts *Options) error { - changed, err := s.diffOptions(newOpts) - if err != nil { - return err - } - s.setOpts(newOpts) - s.applyOptions(changed) - return nil -} - -// diffOptions returns a slice containing options which have been changed. If -// an option that doesn't support hot-swapping is changed, this returns an -// error. -func (s *Server) diffOptions(newOpts *Options) ([]option, error) { - var ( - oldConfig = reflect.ValueOf(s.getOpts()).Elem() - newConfig = reflect.ValueOf(newOpts).Elem() - diffOpts = []option{} - ) - - for i := 0; i < oldConfig.NumField(); i++ { - var ( - field = oldConfig.Type().Field(i) - oldValue = oldConfig.Field(i).Interface() - newValue = newConfig.Field(i).Interface() - changed = !reflect.DeepEqual(oldValue, newValue) - ) - if !changed { - continue - } - switch strings.ToLower(field.Name) { - case "trace": - diffOpts = append(diffOpts, &traceOption{newValue: newValue.(bool)}) - case "debug": - diffOpts = append(diffOpts, &debugOption{newValue: newValue.(bool)}) - case "logtime": - diffOpts = append(diffOpts, &logtimeOption{newValue: newValue.(bool)}) - case "logfile": - diffOpts = append(diffOpts, &logfileOption{newValue: newValue.(string)}) - case "syslog": - diffOpts = append(diffOpts, &syslogOption{newValue: newValue.(bool)}) - case "remotesyslog": - diffOpts = append(diffOpts, &remoteSyslogOption{newValue: newValue.(string)}) - case "tlsconfig": - diffOpts = append(diffOpts, &tlsOption{newValue: newValue.(*tls.Config)}) - case "tlstimeout": - diffOpts = append(diffOpts, &tlsTimeoutOption{newValue: newValue.(float64)}) - case "username": - diffOpts = append(diffOpts, &usernameOption{}) - case "password": - diffOpts = append(diffOpts, &passwordOption{}) - case "authorization": - diffOpts = append(diffOpts, &authorizationOption{}) - case "authtimeout": - diffOpts = append(diffOpts, &authTimeoutOption{newValue: newValue.(float64)}) - case "users": - diffOpts = append(diffOpts, &usersOption{newValue: newValue.([]*User)}) - case "cluster": - newClusterOpts := newValue.(ClusterOpts) - if err := validateClusterOpts(oldValue.(ClusterOpts), newClusterOpts); err != nil { - return nil, err - } - diffOpts = append(diffOpts, &clusterOption{newValue: newClusterOpts}) - case "routes": - add, remove := diffRoutes(oldValue.([]*url.URL), newValue.([]*url.URL)) - diffOpts = append(diffOpts, &routesOption{add: add, remove: remove}) - case "maxconn": - diffOpts = append(diffOpts, &maxConnOption{newValue: newValue.(int)}) - case "pidfile": - diffOpts = append(diffOpts, &pidFileOption{newValue: newValue.(string)}) - case "portsfiledir": - diffOpts = append(diffOpts, &portsFileDirOption{newValue: newValue.(string), oldValue: oldValue.(string)}) - case "maxcontrolline": - diffOpts = append(diffOpts, &maxControlLineOption{newValue: newValue.(int)}) - case "maxpayload": - diffOpts = append(diffOpts, &maxPayloadOption{newValue: newValue.(int)}) - case "pinginterval": - diffOpts = append(diffOpts, &pingIntervalOption{newValue: newValue.(time.Duration)}) - case "maxpingsout": - diffOpts = append(diffOpts, &maxPingsOutOption{newValue: newValue.(int)}) - case "writedeadline": - diffOpts = append(diffOpts, &writeDeadlineOption{newValue: newValue.(time.Duration)}) - case "clientadvertise": - cliAdv := newValue.(string) - if cliAdv != "" { - // Validate ClientAdvertise syntax - if _, _, err := parseHostPort(cliAdv, 0); err != nil { - return nil, fmt.Errorf("invalid ClientAdvertise value of %s, err=%v", cliAdv, err) - } - } - diffOpts = append(diffOpts, &clientAdvertiseOption{newValue: cliAdv}) - case "nolog", "nosigs": - // Ignore NoLog and NoSigs options since they are not parsed and only used in - // testing. - continue - case "port": - // check to see if newValue == 0 and continue if so. - if newValue == 0 { - // ignore RANDOM_PORT - continue - } - fallthrough - default: - // Bail out if attempting to reload any unsupported options. - return nil, fmt.Errorf("Config reload not supported for %s: old=%v, new=%v", - field.Name, oldValue, newValue) - } - } - - return diffOpts, nil -} - -func (s *Server) applyOptions(opts []option) { - var ( - reloadLogging = false - reloadAuth = false - ) - for _, opt := range opts { - opt.Apply(s) - if opt.IsLoggingChange() { - reloadLogging = true - } - if opt.IsAuthChange() { - reloadAuth = true - } - } - - if reloadLogging { - s.ConfigureLogger() - } - if reloadAuth { - s.reloadAuthorization() - } - - s.Noticef("Reloaded server configuration") -} - -// reloadAuthorization reconfigures the server authorization settings, -// disconnects any clients who are no longer authorized, and removes any -// unauthorized subscriptions. -func (s *Server) reloadAuthorization() { - s.mu.Lock() - s.configureAuthorization() - clients := make(map[uint64]*client, len(s.clients)) - for i, client := range s.clients { - clients[i] = client - } - routes := make(map[uint64]*client, len(s.routes)) - for i, route := range s.routes { - routes[i] = route - } - s.mu.Unlock() - - for _, client := range clients { - // Disconnect any unauthorized clients. - if !s.isClientAuthorized(client) { - client.authViolation() - continue - } - - // Remove any unauthorized subscriptions. - s.removeUnauthorizedSubs(client) - } - - for _, client := range routes { - // Disconnect any unauthorized routes. - if !s.isRouterAuthorized(client) { - client.setRouteNoReconnectOnClose() - client.authViolation() - } - } -} - -// validateClusterOpts ensures the new ClusterOpts does not change host or -// port, which do not support reload. -func validateClusterOpts(old, new ClusterOpts) error { - if old.Host != new.Host { - return fmt.Errorf("Config reload not supported for cluster host: old=%s, new=%s", - old.Host, new.Host) - } - if old.Port != new.Port { - return fmt.Errorf("Config reload not supported for cluster port: old=%d, new=%d", - old.Port, new.Port) - } - // Validate Cluster.Advertise syntax - if new.Advertise != "" { - if _, _, err := parseHostPort(new.Advertise, 0); err != nil { - return fmt.Errorf("invalid Cluster.Advertise value of %s, err=%v", new.Advertise, err) - } - } - return nil -} - -// diffRoutes diffs the old routes and the new routes and returns the ones that -// should be added and removed from the server. -func diffRoutes(old, new []*url.URL) (add, remove []*url.URL) { - // Find routes to remove. -removeLoop: - for _, oldRoute := range old { - for _, newRoute := range new { - if oldRoute == newRoute { - continue removeLoop - } - } - remove = append(remove, oldRoute) - } - - // Find routes to add. -addLoop: - for _, newRoute := range new { - for _, oldRoute := range old { - if oldRoute == newRoute { - continue addLoop - } - } - add = append(add, newRoute) - } - - return add, remove -} diff --git a/vendor/github.com/nats-io/gnatsd/server/reload_test.go b/vendor/github.com/nats-io/gnatsd/server/reload_test.go deleted file mode 100644 index eda457a0..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/reload_test.go +++ /dev/null @@ -1,1901 +0,0 @@ -// Copyright 2017-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net" - "os" - "path/filepath" - "reflect" - "runtime" - "strings" - "testing" - "time" - - "github.com/nats-io/go-nats" -) - -// Ensure Reload returns an error when attempting to reload a server that did -// not start with a config file. -func TestConfigReloadNoConfigFile(t *testing.T) { - server := New(&Options{NoSigs: true}) - loaded := server.ConfigTime() - if server.Reload() == nil { - t.Fatal("Expected Reload to return an error") - } - if reloaded := server.ConfigTime(); reloaded != loaded { - t.Fatalf("ConfigTime is incorrect.\nexpected: %s\ngot: %s", loaded, reloaded) - } -} - -// Ensure Reload returns an error when attempting to change an option which -// does not support reloading. -func TestConfigReloadUnsupported(t *testing.T) { - server, opts, config := newServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/test.conf") - defer os.Remove(config) - defer server.Shutdown() - - loaded := server.ConfigTime() - - golden := &Options{ - ConfigFile: config, - Host: "0.0.0.0", - Port: 2233, - AuthTimeout: 1.0, - Debug: false, - Trace: false, - Logtime: false, - MaxControlLine: 1024, - MaxPayload: 1048576, - MaxConn: 65536, - PingInterval: 2 * time.Minute, - MaxPingsOut: 2, - WriteDeadline: 2 * time.Second, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: -1, - }, - NoSigs: true, - } - processOptions(golden) - - if !reflect.DeepEqual(golden, server.getOpts()) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } - - // Change config file to bad config by replacing symlink. - createSymlink(t, config, "./configs/reload/reload_unsupported.conf") - - // This should fail because `cluster` host cannot be changed. - if err := server.Reload(); err == nil { - t.Fatal("Expected Reload to return an error") - } - - // Ensure config didn't change. - if !reflect.DeepEqual(golden, server.getOpts()) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } - - if reloaded := server.ConfigTime(); reloaded != loaded { - t.Fatalf("ConfigTime is incorrect.\nexpected: %s\ngot: %s", loaded, reloaded) - } -} - -// This checks that if we change an option that does not support hot-swapping -// we get an error. Using `listen` for now (test may need to be updated if -// server is changed to support change of listen spec). -func TestConfigReloadUnsupportedHotSwapping(t *testing.T) { - orgConfig := "tmp_a.conf" - newConfig := "tmp_b.conf" - defer os.Remove(orgConfig) - defer os.Remove(newConfig) - if err := ioutil.WriteFile(orgConfig, []byte("listen: 127.0.0.1:-1"), 0666); err != nil { - t.Fatalf("Error creating config file: %v", err) - } - if err := ioutil.WriteFile(newConfig, []byte("listen: 127.0.0.1:9999"), 0666); err != nil { - t.Fatalf("Error creating config file: %v", err) - } - - server, _, config := newServerWithSymlinkConfig(t, "tmp.conf", orgConfig) - defer os.Remove(config) - defer server.Shutdown() - - loaded := server.ConfigTime() - - time.Sleep(time.Millisecond) - - // Change config file with unsupported option hot-swap - createSymlink(t, config, newConfig) - - // This should fail because `listen` host cannot be changed. - if err := server.Reload(); err == nil || !strings.Contains(err.Error(), "not supported") { - t.Fatalf("Expected Reload to return a not supported error, got %v", err) - } - - if reloaded := server.ConfigTime(); reloaded != loaded { - t.Fatalf("ConfigTime is incorrect.\nexpected: %s\ngot: %s", loaded, reloaded) - } -} - -// Ensure Reload returns an error when reloading from a bad config file. -func TestConfigReloadInvalidConfig(t *testing.T) { - server, opts, config := newServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/test.conf") - defer os.Remove(config) - defer server.Shutdown() - - loaded := server.ConfigTime() - - golden := &Options{ - ConfigFile: config, - Host: "0.0.0.0", - Port: 2233, - AuthTimeout: 1.0, - Debug: false, - Trace: false, - Logtime: false, - MaxControlLine: 1024, - MaxPayload: 1048576, - MaxConn: 65536, - PingInterval: 2 * time.Minute, - MaxPingsOut: 2, - WriteDeadline: 2 * time.Second, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: -1, - }, - NoSigs: true, - } - processOptions(golden) - - if !reflect.DeepEqual(golden, server.getOpts()) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } - - // Change config file to bad config by replacing symlink. - createSymlink(t, config, "./configs/reload/invalid.conf") - - // This should fail because the new config should not parse. - if err := server.Reload(); err == nil { - t.Fatal("Expected Reload to return an error") - } - - // Ensure config didn't change. - if !reflect.DeepEqual(golden, server.getOpts()) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } - - if reloaded := server.ConfigTime(); reloaded != loaded { - t.Fatalf("ConfigTime is incorrect.\nexpected: %s\ngot: %s", loaded, reloaded) - } -} - -// Ensure Reload returns nil and the config is changed on success. -func TestConfigReload(t *testing.T) { - var content []byte - if runtime.GOOS != "windows" { - content = []byte(` - remote_syslog: "udp://127.0.0.1:514" # change on reload - log_file: "/tmp/gnatsd-2.log" # change on reload - `) - } - platformConf := "platform.conf" - defer os.Remove(platformConf) - if err := ioutil.WriteFile(platformConf, content, 0666); err != nil { - t.Fatalf("Unable to write config file: %v", err) - } - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/test.conf") - defer os.Remove(config) - defer server.Shutdown() - - loaded := server.ConfigTime() - - golden := &Options{ - ConfigFile: config, - Host: "0.0.0.0", - Port: 2233, - AuthTimeout: 1.0, - Debug: false, - Trace: false, - NoLog: true, - Logtime: false, - MaxControlLine: 1024, - MaxPayload: 1048576, - MaxConn: 65536, - PingInterval: 2 * time.Minute, - MaxPingsOut: 2, - WriteDeadline: 2 * time.Second, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: server.ClusterAddr().Port, - }, - NoSigs: true, - } - processOptions(golden) - - if !reflect.DeepEqual(golden, opts) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } - - // Change config file to new config by replacing symlink. - createSymlink(t, config, "./configs/reload/reload.conf") - - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure config changed. - updated := server.getOpts() - if !updated.Trace { - t.Fatal("Expected Trace to be true") - } - if !updated.Debug { - t.Fatal("Expected Debug to be true") - } - if !updated.Logtime { - t.Fatal("Expected Logtime to be true") - } - if !updated.Syslog { - t.Fatal("Expected Syslog to be true") - } - if runtime.GOOS != "windows" { - if updated.RemoteSyslog != "udp://127.0.0.1:514" { - t.Fatalf("RemoteSyslog is incorrect.\nexpected: udp://127.0.0.1:514\ngot: %s", updated.RemoteSyslog) - } - if updated.LogFile != "/tmp/gnatsd-2.log" { - t.Fatalf("LogFile is incorrect.\nexpected: /tmp/gnatsd-2.log\ngot: %s", updated.LogFile) - } - } - if updated.TLSConfig == nil { - t.Fatal("Expected TLSConfig to be non-nil") - } - if !server.info.TLSRequired { - t.Fatal("Expected TLSRequired to be true") - } - if !server.info.TLSVerify { - t.Fatal("Expected TLSVerify to be true") - } - if updated.Username != "tyler" { - t.Fatalf("Username is incorrect.\nexpected: tyler\ngot: %s", updated.Username) - } - if updated.Password != "T0pS3cr3t" { - t.Fatalf("Password is incorrect.\nexpected: T0pS3cr3t\ngot: %s", updated.Password) - } - if updated.AuthTimeout != 2 { - t.Fatalf("AuthTimeout is incorrect.\nexpected: 2\ngot: %f", updated.AuthTimeout) - } - if !server.info.AuthRequired { - t.Fatal("Expected AuthRequired to be true") - } - if !updated.Cluster.NoAdvertise { - t.Fatal("Expected NoAdvertise to be true") - } - if updated.PidFile != "/tmp/gnatsd.pid" { - t.Fatalf("PidFile is incorrect.\nexpected: /tmp/gnatsd.pid\ngot: %s", updated.PidFile) - } - if updated.MaxControlLine != 512 { - t.Fatalf("MaxControlLine is incorrect.\nexpected: 512\ngot: %d", updated.MaxControlLine) - } - if updated.PingInterval != 5*time.Second { - t.Fatalf("PingInterval is incorrect.\nexpected 5s\ngot: %s", updated.PingInterval) - } - if updated.MaxPingsOut != 1 { - t.Fatalf("MaxPingsOut is incorrect.\nexpected 1\ngot: %d", updated.MaxPingsOut) - } - if updated.WriteDeadline != 3*time.Second { - t.Fatalf("WriteDeadline is incorrect.\nexpected 3s\ngot: %s", updated.WriteDeadline) - } - if updated.MaxPayload != 1024 { - t.Fatalf("MaxPayload is incorrect.\nexpected 1024\ngot: %d", updated.MaxPayload) - } - - if reloaded := server.ConfigTime(); !reloaded.After(loaded) { - t.Fatalf("ConfigTime is incorrect.\nexpected greater than: %s\ngot: %s", loaded, reloaded) - } -} - -// Ensure Reload supports TLS config changes. Test this by starting a server -// with TLS enabled, connect to it to verify, reload config using a different -// key pair and client verification enabled, ensure reconnect fails, then -// ensure reconnect succeeds when the client provides a cert. -func TestConfigReloadRotateTLS(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/tls_test.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, server.Addr().(*net.TCPAddr).Port) - - nc, err := nats.Connect(addr, nats.Secure()) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - sub, err := nc.SubscribeSync("foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - defer sub.Unsubscribe() - - // Rotate cert and enable client verification. - createSymlink(t, config, "./configs/reload/tls_verify_test.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr, nats.Secure()); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when client presents cert. - cert := nats.ClientCert("./configs/certs/cert.new.pem", "./configs/certs/key.new.pem") - conn, err := nats.Connect(addr, cert, nats.RootCAs("./configs/certs/cert.new.pem")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() - - // Ensure the original connection can still publish/receive. - if err := nc.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - nc.Flush() - msg, err := sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving msg: %v", err) - } - if string(msg.Data) != "hello" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hello"), msg.Data) - } -} - -// Ensure Reload supports enabling TLS. Test this by starting a server without -// TLS enabled, connect to it to verify, reload config with TLS enabled, ensure -// reconnect fails, then ensure reconnect succeeds when using secure. -func TestConfigReloadEnableTLS(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/basic.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, server.Addr().(*net.TCPAddr).Port) - nc, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - nc.Close() - - // Enable TLS. - createSymlink(t, config, "./configs/reload/tls_test.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when using secure. - nc, err = nats.Connect(addr, nats.Secure()) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - nc.Close() -} - -// Ensure Reload supports disabling TLS. Test this by starting a server with -// TLS enabled, connect to it to verify, reload config with TLS disabled, -// ensure reconnect fails, then ensure reconnect succeeds when connecting -// without secure. -func TestConfigReloadDisableTLS(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/tls_test.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, server.Addr().(*net.TCPAddr).Port) - nc, err := nats.Connect(addr, nats.Secure()) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - nc.Close() - - // Disable TLS. - createSymlink(t, config, "./configs/reload/basic.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr, nats.Secure()); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when not using secure. - nc, err = nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - nc.Close() -} - -// Ensure Reload supports single user authentication config changes. Test this -// by starting a server with authentication enabled, connect to it to verify, -// reload config using a different username/password, ensure reconnect fails, -// then ensure reconnect succeeds when using the correct credentials. -func TestConfigReloadRotateUserAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", - "./configs/reload/single_user_authentication_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr, nats.UserInfo("tyler", "T0pS3cr3t")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - disconnected := make(chan struct{}) - asyncErr := make(chan error) - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - asyncErr <- err - }) - nc.SetDisconnectHandler(func(*nats.Conn) { - disconnected <- struct{}{} - }) - - // Change user credentials. - createSymlink(t, config, "./configs/reload/single_user_authentication_2.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr, nats.UserInfo("tyler", "T0pS3cr3t")); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when using new credentials. - conn, err := nats.Connect(addr, nats.UserInfo("derek", "passw0rd")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() - - // Ensure the previous connection received an authorization error. - select { - case err := <-asyncErr: - if err != nats.ErrAuthorization { - t.Fatalf("Expected ErrAuthorization, got %v", err) - } - case <-time.After(5 * time.Second): - t.Fatal("Expected authorization error") - } - - // Ensure the previous connection was disconnected. - select { - case <-disconnected: - case <-time.After(2 * time.Second): - t.Fatal("Expected connection to be disconnected") - } -} - -// Ensure Reload supports enabling single user authentication. Test this by -// starting a server with authentication disabled, connect to it to verify, -// reload config using with a username/password, ensure reconnect fails, then -// ensure reconnect succeeds when using the correct credentials. -func TestConfigReloadEnableUserAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/basic.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - disconnected := make(chan struct{}) - asyncErr := make(chan error) - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - asyncErr <- err - }) - nc.SetDisconnectHandler(func(*nats.Conn) { - disconnected <- struct{}{} - }) - - // Enable authentication. - createSymlink(t, config, "./configs/reload/single_user_authentication_1.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when using new credentials. - conn, err := nats.Connect(addr, nats.UserInfo("tyler", "T0pS3cr3t")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() - - // Ensure the previous connection received an authorization error. - select { - case err := <-asyncErr: - if err != nats.ErrAuthorization { - t.Fatalf("Expected ErrAuthorization, got %v", err) - } - case <-time.After(2 * time.Second): - t.Fatal("Expected authorization error") - } - - // Ensure the previous connection was disconnected. - select { - case <-disconnected: - case <-time.After(2 * time.Second): - t.Fatal("Expected connection to be disconnected") - } -} - -// Ensure Reload supports disabling single user authentication. Test this by -// starting a server with authentication enabled, connect to it to verify, -// reload config using with authentication disabled, then ensure connecting -// with no credentials succeeds. -func TestConfigReloadDisableUserAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", - "./configs/reload/single_user_authentication_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr, nats.UserInfo("tyler", "T0pS3cr3t")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - t.Fatalf("Client received an unexpected error: %v", err) - }) - - // Disable authentication. - createSymlink(t, config, "./configs/reload/basic.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting succeeds with no credentials. - conn, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() -} - -// Ensure Reload supports token authentication config changes. Test this by -// starting a server with token authentication enabled, connect to it to -// verify, reload config using a different token, ensure reconnect fails, then -// ensure reconnect succeeds when using the correct token. -func TestConfigReloadRotateTokenAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/token_authentication_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - disconnected := make(chan struct{}) - asyncErr := make(chan error) - eh := func(nc *nats.Conn, sub *nats.Subscription, err error) { asyncErr <- err } - dh := func(*nats.Conn) { disconnected <- struct{}{} } - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr, nats.Token("T0pS3cr3t"), nats.ErrorHandler(eh), nats.DisconnectHandler(dh)) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - - // Change authentication token. - createSymlink(t, config, "./configs/reload/token_authentication_2.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr, nats.Token("T0pS3cr3t")); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when using new credentials. - conn, err := nats.Connect(addr, nats.Token("passw0rd")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() - - // Ensure the previous connection received an authorization error. - select { - case err := <-asyncErr: - if err != nats.ErrAuthorization { - t.Fatalf("Expected ErrAuthorization, got %v", err) - } - case <-time.After(2 * time.Second): - t.Fatal("Expected authorization error") - } - - // Ensure the previous connection was disconnected. - select { - case <-disconnected: - case <-time.After(2 * time.Second): - t.Fatal("Expected connection to be disconnected") - } -} - -// Ensure Reload supports enabling token authentication. Test this by starting -// a server with authentication disabled, connect to it to verify, reload -// config using with a token, ensure reconnect fails, then ensure reconnect -// succeeds when using the correct token. -func TestConfigReloadEnableTokenAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/basic.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - disconnected := make(chan struct{}) - asyncErr := make(chan error) - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - asyncErr <- err - }) - nc.SetDisconnectHandler(func(*nats.Conn) { - disconnected <- struct{}{} - }) - - // Enable authentication. - createSymlink(t, config, "./configs/reload/token_authentication_1.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when using new credentials. - conn, err := nats.Connect(addr, nats.Token("T0pS3cr3t")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() - - // Ensure the previous connection received an authorization error. - select { - case err := <-asyncErr: - if err != nats.ErrAuthorization { - t.Fatalf("Expected ErrAuthorization, got %v", err) - } - case <-time.After(2 * time.Second): - t.Fatal("Expected authorization error") - } - - // Ensure the previous connection was disconnected. - select { - case <-disconnected: - case <-time.After(2 * time.Second): - t.Fatal("Expected connection to be disconnected") - } -} - -// Ensure Reload supports disabling single token authentication. Test this by -// starting a server with authentication enabled, connect to it to verify, -// reload config using with authentication disabled, then ensure connecting -// with no token succeeds. -func TestConfigReloadDisableTokenAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/token_authentication_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr, nats.Token("T0pS3cr3t")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - t.Fatalf("Client received an unexpected error: %v", err) - }) - - // Disable authentication. - createSymlink(t, config, "./configs/reload/basic.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting succeeds with no credentials. - conn, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() -} - -// Ensure Reload supports users authentication config changes. Test this by -// starting a server with users authentication enabled, connect to it to -// verify, reload config using a different user, ensure reconnect fails, then -// ensure reconnect succeeds when using the correct credentials. -func TestConfigReloadRotateUsersAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/multiple_users_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr, nats.UserInfo("alice", "foo")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - disconnected := make(chan struct{}) - asyncErr := make(chan error) - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - asyncErr <- err - }) - nc.SetDisconnectHandler(func(*nats.Conn) { - disconnected <- struct{}{} - }) - - // These credentials won't change. - nc2, err := nats.Connect(addr, nats.UserInfo("bob", "bar")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc2.Close() - sub, err := nc2.SubscribeSync("foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - defer sub.Unsubscribe() - - // Change users credentials. - createSymlink(t, config, "./configs/reload/multiple_users_2.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr, nats.UserInfo("alice", "foo")); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when using new credentials. - conn, err := nats.Connect(addr, nats.UserInfo("alice", "baz")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() - - // Ensure the previous connection received an authorization error. - select { - case err := <-asyncErr: - if err != nats.ErrAuthorization { - t.Fatalf("Expected ErrAuthorization, got %v", err) - } - case <-time.After(2 * time.Second): - t.Fatal("Expected authorization error") - } - - // Ensure the previous connection was disconnected. - select { - case <-disconnected: - case <-time.After(2 * time.Second): - t.Fatal("Expected connection to be disconnected") - } - - // Ensure the connection using unchanged credentials can still - // publish/receive. - if err := nc2.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - nc2.Flush() - msg, err := sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving msg: %v", err) - } - if string(msg.Data) != "hello" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hello"), msg.Data) - } -} - -// Ensure Reload supports enabling users authentication. Test this by starting -// a server with authentication disabled, connect to it to verify, reload -// config using with users, ensure reconnect fails, then ensure reconnect -// succeeds when using the correct credentials. -func TestConfigReloadEnableUsersAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/basic.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - disconnected := make(chan struct{}) - asyncErr := make(chan error) - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - asyncErr <- err - }) - nc.SetDisconnectHandler(func(*nats.Conn) { - disconnected <- struct{}{} - }) - - // Enable authentication. - createSymlink(t, config, "./configs/reload/multiple_users_1.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting fails. - if _, err := nats.Connect(addr); err == nil { - t.Fatal("Expected connect to fail") - } - - // Ensure connecting succeeds when using new credentials. - conn, err := nats.Connect(addr, nats.UserInfo("alice", "foo")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() - - // Ensure the previous connection received an authorization error. - select { - case err := <-asyncErr: - if err != nats.ErrAuthorization { - t.Fatalf("Expected ErrAuthorization, got %v", err) - } - case <-time.After(5 * time.Second): - t.Fatal("Expected authorization error") - } - - // Ensure the previous connection was disconnected. - select { - case <-disconnected: - case <-time.After(5 * time.Second): - t.Fatal("Expected connection to be disconnected") - } -} - -// Ensure Reload supports disabling users authentication. Test this by starting -// a server with authentication enabled, connect to it to verify, -// reload config using with authentication disabled, then ensure connecting -// with no credentials succeeds. -func TestConfigReloadDisableUsersAuthentication(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/multiple_users_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Ensure we can connect as a sanity check. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr, nats.UserInfo("alice", "foo")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - t.Fatalf("Client received an unexpected error: %v", err) - }) - - // Disable authentication. - createSymlink(t, config, "./configs/reload/basic.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure connecting succeeds with no credentials. - conn, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - conn.Close() -} - -// Ensure Reload supports changing permissions. Test this by starting a server -// with a user configured with certain permissions, test publish and subscribe, -// reload config with new permissions, ensure the previous subscription was -// closed and publishes fail, then ensure the new permissions succeed. -func TestConfigReloadChangePermissions(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/authorization_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr, nats.UserInfo("bob", "bar")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - asyncErr := make(chan error) - nc.SetErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - asyncErr <- err - }) - // Ensure we can publish and receive messages as a sanity check. - sub, err := nc.SubscribeSync("_INBOX.>") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - nc.Flush() - - conn, err := nats.Connect(addr, nats.UserInfo("alice", "foo")) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer conn.Close() - - sub2, err := conn.SubscribeSync("req.foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - if err := conn.Publish("_INBOX.foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing message: %v", err) - } - conn.Flush() - - msg, err := sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving msg: %v", err) - } - if string(msg.Data) != "hello" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hello"), msg.Data) - } - - if err := nc.Publish("req.foo", []byte("world")); err != nil { - t.Fatalf("Error publishing message: %v", err) - } - nc.Flush() - - msg, err = sub2.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving msg: %v", err) - } - if string(msg.Data) != "world" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("world"), msg.Data) - } - - // Change permissions. - createSymlink(t, config, "./configs/reload/authorization_2.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure we receive an error for the subscription that is no longer - // authorized. - select { - case err := <-asyncErr: - if !strings.Contains(err.Error(), "permissions violation for subscription to \"_inbox.>\"") { - t.Fatalf("Expected permissions violation error, got %v", err) - } - case <-time.After(5 * time.Second): - t.Fatal("Expected permissions violation error") - } - - // Ensure we receive an error when publishing to req.foo and we no longer - // receive messages on _INBOX.>. - if err := nc.Publish("req.foo", []byte("hola")); err != nil { - t.Fatalf("Error publishing message: %v", err) - } - nc.Flush() - if err := conn.Publish("_INBOX.foo", []byte("mundo")); err != nil { - t.Fatalf("Error publishing message: %v", err) - } - conn.Flush() - - select { - case err := <-asyncErr: - if !strings.Contains(err.Error(), "permissions violation for publish to \"req.foo\"") { - t.Fatalf("Expected permissions violation error, got %v", err) - } - case <-time.After(5 * time.Second): - t.Fatal("Expected permissions violation error") - } - - queued, _, err := sub2.Pending() - if err != nil { - t.Fatalf("Failed to get pending messaged: %v", err) - } - if queued != 0 { - t.Fatalf("Pending is incorrect.\nexpected: 0\ngot: %d", queued) - } - - queued, _, err = sub.Pending() - if err != nil { - t.Fatalf("Failed to get pending messaged: %v", err) - } - if queued != 0 { - t.Fatalf("Pending is incorrect.\nexpected: 0\ngot: %d", queued) - } - - // Ensure we can publish to _INBOX.foo.bar and subscribe to _INBOX.foo.>. - sub, err = nc.SubscribeSync("_INBOX.foo.>") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - nc.Flush() - if err := nc.Publish("_INBOX.foo.bar", []byte("testing")); err != nil { - t.Fatalf("Error publishing message: %v", err) - } - nc.Flush() - msg, err = sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving msg: %v", err) - } - if string(msg.Data) != "testing" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("testing"), msg.Data) - } - - select { - case err := <-asyncErr: - t.Fatalf("Received unexpected error: %v", err) - default: - } -} - -// Ensure Reload returns an error when attempting to change cluster address -// host. -func TestConfigReloadClusterHostUnsupported(t *testing.T) { - server, _, config := newServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/srv_a_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Attempt to change cluster listen host. - createSymlink(t, config, "./configs/reload/srv_c_1.conf") - - // This should fail because cluster address cannot be changed. - if err := server.Reload(); err == nil { - t.Fatal("Expected Reload to return an error") - } -} - -// Ensure Reload returns an error when attempting to change cluster address -// port. -func TestConfigReloadClusterPortUnsupported(t *testing.T) { - server, _, config := newServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/srv_a_1.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Attempt to change cluster listen port. - createSymlink(t, config, "./configs/reload/srv_b_1.conf") - - // This should fail because cluster address cannot be changed. - if err := server.Reload(); err == nil { - t.Fatal("Expected Reload to return an error") - } -} - -// Ensure Reload supports enabling route authorization. Test this by starting -// two servers in a cluster without authorization, ensuring messages flow -// between them, then reloading with authorization and ensuring messages no -// longer flow until reloading with the correct credentials. -func TestConfigReloadEnableClusterAuthorization(t *testing.T) { - srvb, srvbOpts, srvbConfig := runServerWithSymlinkConfig(t, "tmp_b.conf", "./configs/reload/srv_b_1.conf") - defer os.Remove(srvbConfig) - defer srvb.Shutdown() - - srva, srvaOpts, srvaConfig := runServerWithSymlinkConfig(t, "tmp_a.conf", "./configs/reload/srv_a_1.conf") - defer os.Remove(srvaConfig) - defer srva.Shutdown() - - checkClusterFormed(t, srva, srvb) - - srvaAddr := fmt.Sprintf("nats://%s:%d", srvaOpts.Host, srvaOpts.Port) - srvaConn, err := nats.Connect(srvaAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvaConn.Close() - sub, err := srvaConn.SubscribeSync("foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - defer sub.Unsubscribe() - if err := srvaConn.Flush(); err != nil { - t.Fatalf("Error flushing: %v", err) - } - - srvbAddr := fmt.Sprintf("nats://%s:%d", srvbOpts.Host, srvbOpts.Port) - srvbConn, err := nats.Connect(srvbAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvbConn.Close() - - if numRoutes := srvb.NumRoutes(); numRoutes != 1 { - t.Fatalf("Expected 1 route, got %d", numRoutes) - } - - // Ensure messages flow through the cluster as a sanity check. - if err := srvbConn.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvbConn.Flush() - msg, err := sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - if string(msg.Data) != "hello" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hello"), msg.Data) - } - - // Enable route authorization. - createSymlink(t, srvbConfig, "./configs/reload/srv_b_2.conf") - if err := srvb.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - if numRoutes := srvb.NumRoutes(); numRoutes != 0 { - t.Fatalf("Expected 0 routes, got %d", numRoutes) - } - - // Ensure messages no longer flow through the cluster. - for i := 0; i < 5; i++ { - if err := srvbConn.Publish("foo", []byte("world")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvbConn.Flush() - } - if _, err := sub.NextMsg(50 * time.Millisecond); err != nats.ErrTimeout { - t.Fatalf("Expected ErrTimeout, got %v", err) - } - - // Reload Server A with correct route credentials. - createSymlink(t, srvaConfig, "./configs/reload/srv_a_2.conf") - defer os.Remove(srvaConfig) - if err := srva.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - checkClusterFormed(t, srva, srvb) - - if numRoutes := srvb.NumRoutes(); numRoutes != 1 { - t.Fatalf("Expected 1 route, got %d", numRoutes) - } - - // Ensure messages flow through the cluster now. - if err := srvbConn.Publish("foo", []byte("hola")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvbConn.Flush() - msg, err = sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - if string(msg.Data) != "hola" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hola"), msg.Data) - } -} - -// Ensure Reload supports disabling route authorization. Test this by starting -// two servers in a cluster with authorization, ensuring messages flow -// between them, then reloading without authorization and ensuring messages -// still flow. -func TestConfigReloadDisableClusterAuthorization(t *testing.T) { - srvb, srvbOpts, srvbConfig := runServerWithSymlinkConfig(t, "tmp_b.conf", "./configs/reload/srv_b_2.conf") - defer os.Remove(srvbConfig) - defer srvb.Shutdown() - - srva, srvaOpts, srvaConfig := runServerWithSymlinkConfig(t, "tmp_a.conf", "./configs/reload/srv_a_2.conf") - defer os.Remove(srvaConfig) - defer srva.Shutdown() - - checkClusterFormed(t, srva, srvb) - - srvaAddr := fmt.Sprintf("nats://%s:%d", srvaOpts.Host, srvaOpts.Port) - srvaConn, err := nats.Connect(srvaAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvaConn.Close() - - sub, err := srvaConn.SubscribeSync("foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - defer sub.Unsubscribe() - if err := srvaConn.Flush(); err != nil { - t.Fatalf("Error flushing: %v", err) - } - - srvbAddr := fmt.Sprintf("nats://%s:%d", srvbOpts.Host, srvbOpts.Port) - srvbConn, err := nats.Connect(srvbAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvbConn.Close() - - if numRoutes := srvb.NumRoutes(); numRoutes != 1 { - t.Fatalf("Expected 1 route, got %d", numRoutes) - } - - // Ensure messages flow through the cluster as a sanity check. - if err := srvbConn.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvbConn.Flush() - msg, err := sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - if string(msg.Data) != "hello" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hello"), msg.Data) - } - - // Disable route authorization. - createSymlink(t, srvbConfig, "./configs/reload/srv_b_1.conf") - if err := srvb.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - checkClusterFormed(t, srva, srvb) - - if numRoutes := srvb.NumRoutes(); numRoutes != 1 { - t.Fatalf("Expected 1 route, got %d", numRoutes) - } - - // Ensure messages still flow through the cluster. - if err := srvbConn.Publish("foo", []byte("hola")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvbConn.Flush() - msg, err = sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - if string(msg.Data) != "hola" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hola"), msg.Data) - } -} - -// Ensure Reload supports changing cluster routes. Test this by starting -// two servers in a cluster, ensuring messages flow between them, then -// reloading with a different route and ensuring messages flow through the new -// cluster. -func TestConfigReloadClusterRoutes(t *testing.T) { - srvb, srvbOpts, srvbConfig := runServerWithSymlinkConfig(t, "tmp_b.conf", "./configs/reload/srv_b_1.conf") - defer os.Remove(srvbConfig) - defer srvb.Shutdown() - - srva, srvaOpts, srvaConfig := runServerWithSymlinkConfig(t, "tmp_a.conf", "./configs/reload/srv_a_1.conf") - defer os.Remove(srvaConfig) - defer srva.Shutdown() - - checkClusterFormed(t, srva, srvb) - - srvcOpts, err := ProcessConfigFile("./configs/reload/srv_c_1.conf") - if err != nil { - t.Fatalf("Error processing config file: %v", err) - } - srvcOpts.NoLog = true - srvcOpts.NoSigs = true - - srvc := RunServer(srvcOpts) - defer srvc.Shutdown() - - srvaAddr := fmt.Sprintf("nats://%s:%d", srvaOpts.Host, srvaOpts.Port) - srvaConn, err := nats.Connect(srvaAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvaConn.Close() - - sub, err := srvaConn.SubscribeSync("foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - defer sub.Unsubscribe() - if err := srvaConn.Flush(); err != nil { - t.Fatalf("Error flushing: %v", err) - } - - srvbAddr := fmt.Sprintf("nats://%s:%d", srvbOpts.Host, srvbOpts.Port) - srvbConn, err := nats.Connect(srvbAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvbConn.Close() - - if numRoutes := srvb.NumRoutes(); numRoutes != 1 { - t.Fatalf("Expected 1 route, got %d", numRoutes) - } - - // Ensure messages flow through the cluster as a sanity check. - if err := srvbConn.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvbConn.Flush() - msg, err := sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - if string(msg.Data) != "hello" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hello"), msg.Data) - } - - // Reload cluster routes. - createSymlink(t, srvaConfig, "./configs/reload/srv_a_3.conf") - if err := srva.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Kill old route server. - srvbConn.Close() - srvb.Shutdown() - - checkClusterFormed(t, srva, srvc) - - srvcAddr := fmt.Sprintf("nats://%s:%d", srvcOpts.Host, srvcOpts.Port) - srvcConn, err := nats.Connect(srvcAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvcConn.Close() - - // Ensure messages flow through the new cluster. - for i := 0; i < 5; i++ { - if err := srvcConn.Publish("foo", []byte("hola")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvcConn.Flush() - } - msg, err = sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - if string(msg.Data) != "hola" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hola"), msg.Data) - } -} - -// Ensure Reload supports removing a solicited route. In this case from A->B -// Test this by starting two servers in a cluster, ensuring messages flow between them. -// Then stop server B, and have server A continue to try to connect. Reload A with a config -// that removes the route and make sure it does not connect to server B when its restarted. -func TestConfigReloadClusterRemoveSolicitedRoutes(t *testing.T) { - srvb, srvbOpts := RunServerWithConfig("./configs/reload/srv_b_1.conf") - defer srvb.Shutdown() - - srva, srvaOpts, srvaConfig := runServerWithSymlinkConfig(t, "tmp_a.conf", "./configs/reload/srv_a_1.conf") - defer os.Remove(srvaConfig) - defer srva.Shutdown() - - checkClusterFormed(t, srva, srvb) - - srvaAddr := fmt.Sprintf("nats://%s:%d", srvaOpts.Host, srvaOpts.Port) - srvaConn, err := nats.Connect(srvaAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvaConn.Close() - sub, err := srvaConn.SubscribeSync("foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - defer sub.Unsubscribe() - if err := srvaConn.Flush(); err != nil { - t.Fatalf("Error flushing: %v", err) - } - - srvbAddr := fmt.Sprintf("nats://%s:%d", srvbOpts.Host, srvbOpts.Port) - srvbConn, err := nats.Connect(srvbAddr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer srvbConn.Close() - - if err := srvbConn.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - srvbConn.Flush() - msg, err := sub.NextMsg(5 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - if string(msg.Data) != "hello" { - t.Fatalf("Msg is incorrect.\nexpected: %+v\ngot: %+v", []byte("hello"), msg.Data) - } - - // Now stop server B. - srvb.Shutdown() - - // Wait til route is dropped. - checkNumRoutes(t, srva, 0) - - // Now change config for server A to not solicit a route to server B. - createSymlink(t, srvaConfig, "./configs/reload/srv_a_4.conf") - defer os.Remove(srvaConfig) - if err := srva.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Restart server B. - srvb, _ = RunServerWithConfig("./configs/reload/srv_b_1.conf") - defer srvb.Shutdown() - - // We should not have a cluster formed here. - numRoutes := 0 - deadline := time.Now().Add(2 * DEFAULT_ROUTE_RECONNECT) - for time.Now().Before(deadline) { - if numRoutes = srva.NumRoutes(); numRoutes != 0 { - break - } else { - time.Sleep(100 * time.Millisecond) - } - } - if numRoutes != 0 { - t.Fatalf("Expected 0 routes for server A, got %d", numRoutes) - } -} - -func reloadUpdateConfig(t *testing.T, s *Server, conf, content string) { - if err := ioutil.WriteFile(conf, []byte(content), 0666); err != nil { - stackFatalf(t, "Error creating config file: %v", err) - } - if err := s.Reload(); err != nil { - stackFatalf(t, "Error on reload: %v", err) - } -} - -func TestConfigReloadClusterAdvertise(t *testing.T) { - conf := "routeadv.conf" - if err := ioutil.WriteFile(conf, []byte(` - listen: "0.0.0.0:-1" - cluster: { - listen: "0.0.0.0:-1" - } - `), 0666); err != nil { - t.Fatalf("Error creating config file: %v", err) - } - defer os.Remove(conf) - opts, err := ProcessConfigFile(conf) - if err != nil { - t.Fatalf("Error processing config file: %v", err) - } - opts.NoLog = true - opts.NoSigs = true - s := RunServer(opts) - defer s.Shutdown() - - orgClusterPort := s.ClusterAddr().Port - - verify := func(expectedHost string, expectedPort int, expectedIP string) { - s.mu.Lock() - routeInfo := s.routeInfo - routeInfoJSON := Info{} - err = json.Unmarshal(s.routeInfoJSON[5:], &routeInfoJSON) // Skip "INFO " - s.mu.Unlock() - if err != nil { - t.Fatalf("Error on Unmarshal: %v", err) - } - if routeInfo.Host != expectedHost || routeInfo.Port != expectedPort || routeInfo.IP != expectedIP { - t.Fatalf("Expected host/port/IP to be %s:%v, %q, got %s:%d, %q", - expectedHost, expectedPort, expectedIP, routeInfo.Host, routeInfo.Port, routeInfo.IP) - } - // Check that server routeInfoJSON was updated too - if !reflect.DeepEqual(routeInfo, routeInfoJSON) { - t.Fatalf("Expected routeInfoJSON to be %+v, got %+v", routeInfo, routeInfoJSON) - } - } - - // Update config with cluster_advertise - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - cluster: { - listen: "0.0.0.0:-1" - cluster_advertise: "me:1" - } - `) - verify("me", 1, "nats-route://me:1/") - - // Update config with cluster_advertise (no port specified) - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - cluster: { - listen: "0.0.0.0:-1" - cluster_advertise: "me" - } - `) - verify("me", orgClusterPort, fmt.Sprintf("nats-route://me:%d/", orgClusterPort)) - - // Update config with cluster_advertise (-1 port specified) - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - cluster: { - listen: "0.0.0.0:-1" - cluster_advertise: "me:-1" - } - `) - verify("me", orgClusterPort, fmt.Sprintf("nats-route://me:%d/", orgClusterPort)) - - // Update to remove cluster_advertise - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - cluster: { - listen: "0.0.0.0:-1" - } - `) - verify("0.0.0.0", orgClusterPort, "") -} - -func TestConfigReloadClusterNoAdvertise(t *testing.T) { - conf := "routeadv.conf" - if err := ioutil.WriteFile(conf, []byte(` - listen: "0.0.0.0:-1" - client_advertise: "me:1" - cluster: { - listen: "0.0.0.0:-1" - } - `), 0666); err != nil { - t.Fatalf("Error creating config file: %v", err) - } - defer os.Remove(conf) - opts, err := ProcessConfigFile(conf) - if err != nil { - t.Fatalf("Error processing config file: %v", err) - } - opts.NoLog = true - opts.NoSigs = true - s := RunServer(opts) - defer s.Shutdown() - - s.mu.Lock() - ccurls := s.routeInfo.ClientConnectURLs - s.mu.Unlock() - if len(ccurls) != 1 && ccurls[0] != "me:1" { - t.Fatalf("Unexpected routeInfo.ClientConnectURLS: %v", ccurls) - } - - // Update config with no_advertise - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - client_advertise: "me:1" - cluster: { - listen: "0.0.0.0:-1" - no_advertise: true - } - `) - - s.mu.Lock() - ccurls = s.routeInfo.ClientConnectURLs - s.mu.Unlock() - if len(ccurls) != 0 { - t.Fatalf("Unexpected routeInfo.ClientConnectURLS: %v", ccurls) - } - - // Update config with cluster_advertise (no port specified) - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - client_advertise: "me:1" - cluster: { - listen: "0.0.0.0:-1" - } - `) - s.mu.Lock() - ccurls = s.routeInfo.ClientConnectURLs - s.mu.Unlock() - if len(ccurls) != 1 && ccurls[0] != "me:1" { - t.Fatalf("Unexpected routeInfo.ClientConnectURLS: %v", ccurls) - } -} - -func TestConfigReloadMaxSubsUnsupported(t *testing.T) { - conf := "maxsubs.conf" - if err := ioutil.WriteFile(conf, []byte(`max_subs: 1`), 0666); err != nil { - t.Fatalf("Error creating config file: %v", err) - } - defer os.Remove(conf) - opts, err := ProcessConfigFile(conf) - if err != nil { - stackFatalf(t, "Error processing config file: %v", err) - } - opts.NoLog = true - opts.NoSigs = true - s := RunServer(opts) - defer s.Shutdown() - - if err := ioutil.WriteFile(conf, []byte(`max_subs: 10`), 0666); err != nil { - t.Fatalf("Error writing config file: %v", err) - } - if err := s.Reload(); err == nil { - t.Fatal("Expected Reload to return an error") - } -} - -func TestConfigReloadClientAdvertise(t *testing.T) { - conf := "clientadv.conf" - if err := ioutil.WriteFile(conf, []byte(`listen: "0.0.0.0:-1"`), 0666); err != nil { - t.Fatalf("Error creating config file: %v", err) - } - defer os.Remove(conf) - opts, err := ProcessConfigFile(conf) - if err != nil { - stackFatalf(t, "Error processing config file: %v", err) - } - opts.NoLog = true - opts.NoSigs = true - s := RunServer(opts) - defer s.Shutdown() - - orgPort := s.Addr().(*net.TCPAddr).Port - - verify := func(expectedHost string, expectedPort int) { - s.mu.Lock() - info := s.info - s.mu.Unlock() - if info.Host != expectedHost || info.Port != expectedPort { - stackFatalf(t, "Expected host/port to be %s:%d, got %s:%d", - expectedHost, expectedPort, info.Host, info.Port) - } - } - - // Update config with ClientAdvertise (port specified) - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - client_advertise: "me:1" - `) - verify("me", 1) - - // Update config with ClientAdvertise (no port specified) - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - client_advertise: "me" - `) - verify("me", orgPort) - - // Update config with ClientAdvertise (-1 port specified) - reloadUpdateConfig(t, s, conf, ` - listen: "0.0.0.0:-1" - client_advertise: "me:-1" - `) - verify("me", orgPort) - - // Now remove ClientAdvertise to check that original values - // are restored. - reloadUpdateConfig(t, s, conf, `listen: "0.0.0.0:-1"`) - verify("0.0.0.0", orgPort) -} - -// Ensure Reload supports changing the max connections. Test this by starting a -// server with no max connections, connecting two clients, reloading with a -// max connections of one, and ensuring one client is disconnected. -func TestConfigReloadMaxConnections(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/basic.conf") - defer os.Remove(config) - defer server.Shutdown() - - // Make two connections. - addr := fmt.Sprintf("nats://%s:%d", opts.Host, server.Addr().(*net.TCPAddr).Port) - nc1, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc1.Close() - closed := make(chan struct{}, 1) - nc1.SetDisconnectHandler(func(*nats.Conn) { - closed <- struct{}{} - }) - nc2, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc2.Close() - nc2.SetDisconnectHandler(func(*nats.Conn) { - closed <- struct{}{} - }) - - if numClients := server.NumClients(); numClients != 2 { - t.Fatalf("Expected 2 clients, got %d", numClients) - } - - // Set max connections to one. - createSymlink(t, config, "./configs/reload/max_connections.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure one connection was closed. - select { - case <-closed: - case <-time.After(5 * time.Second): - t.Fatal("Expected to be disconnected") - } - - if numClients := server.NumClients(); numClients != 1 { - t.Fatalf("Expected 1 client, got %d", numClients) - } - - // Ensure new connections fail. - _, err = nats.Connect(addr) - if err == nil { - t.Fatal("Expected error on connect") - } -} - -// Ensure reload supports changing the max payload size. Test this by starting -// a server with the default size limit, ensuring publishes work, reloading -// with a restrictive limit, and ensuring publishing an oversized message fails -// and disconnects the client. -func TestConfigReloadMaxPayload(t *testing.T) { - server, opts, config := runServerWithSymlinkConfig(t, "tmp.conf", "./configs/reload/basic.conf") - defer os.Remove(config) - defer server.Shutdown() - - addr := fmt.Sprintf("nats://%s:%d", opts.Host, server.Addr().(*net.TCPAddr).Port) - nc, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer nc.Close() - closed := make(chan struct{}) - nc.SetDisconnectHandler(func(*nats.Conn) { - closed <- struct{}{} - }) - - conn, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v", err) - } - defer conn.Close() - sub, err := conn.SubscribeSync("foo") - if err != nil { - t.Fatalf("Error subscribing: %v", err) - } - conn.Flush() - - // Ensure we can publish as a sanity check. - if err := nc.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - nc.Flush() - _, err = sub.NextMsg(2 * time.Second) - if err != nil { - t.Fatalf("Error receiving message: %v", err) - } - - // Set max payload to one. - createSymlink(t, config, "./configs/reload/max_payload.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Ensure oversized messages don't get delivered and the client is - // disconnected. - if err := nc.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error publishing: %v", err) - } - nc.Flush() - _, err = sub.NextMsg(20 * time.Millisecond) - if err != nats.ErrTimeout { - t.Fatalf("Expected ErrTimeout, got: %v", err) - } - - select { - case <-closed: - case <-time.After(5 * time.Second): - t.Fatal("Expected to be disconnected") - } -} - -// Ensure reload supports rotating out files. Test this by starting -// a server with log and pid files, reloading new ones, then check that -// we can rename and delete the old log/pid files. -func TestConfigReloadRotateFiles(t *testing.T) { - opts, config := newOptionsWithSymlinkConfig(t, "tmp.conf", "./configs/reload/file_rotate.conf") - server := RunServer(opts) - defer func() { - os.Remove(config) - os.Remove("log1.txt") - os.Remove("gnatsd1.pid") - }() - defer server.Shutdown() - - // Configure the logger to enable actual logging - server.ConfigureLogger() - - // Load a config that renames the files. - createSymlink(t, config, "./configs/reload/file_rotate1.conf") - if err := server.Reload(); err != nil { - t.Fatalf("Error reloading config: %v", err) - } - - // Make sure the new files exist. - if _, err := os.Stat("log1.txt"); os.IsNotExist(err) { - t.Fatalf("Error reloading config, no new file: %v", err) - } - if _, err := os.Stat("gnatsd1.pid"); os.IsNotExist(err) { - t.Fatalf("Error reloading config, no new file: %v", err) - } - - // Check that old file can be renamed. - if err := os.Rename("log.txt", "log_old.txt"); err != nil { - t.Fatalf("Error reloading config, cannot rename file: %v", err) - } - if err := os.Rename("gnatsd.pid", "gnatsd_old.pid"); err != nil { - t.Fatalf("Error reloading config, cannot rename file: %v", err) - } - - // Check that the old files can be removed after rename. - if err := os.Remove("log_old.txt"); err != nil { - t.Fatalf("Error reloading config, cannot delete file: %v", err) - } - if err := os.Remove("gnatsd_old.pid"); err != nil { - t.Fatalf("Error reloading config, cannot delete file: %v", err) - } -} - -func runServerWithSymlinkConfig(t *testing.T, symlinkName, configName string) (*Server, *Options, string) { - t.Helper() - opts, config := newOptionsWithSymlinkConfig(t, symlinkName, configName) - opts.NoLog = true - opts.NoSigs = true - return RunServer(opts), opts, config -} - -func newServerWithSymlinkConfig(t *testing.T, symlinkName, configName string) (*Server, *Options, string) { - t.Helper() - opts, config := newOptionsWithSymlinkConfig(t, symlinkName, configName) - return New(opts), opts, config -} - -func newOptionsWithSymlinkConfig(t *testing.T, symlinkName, configName string) (*Options, string) { - t.Helper() - dir, err := os.Getwd() - if err != nil { - t.Fatalf("Error getting working directory: %v", err) - } - config := filepath.Join(dir, symlinkName) - createSymlink(t, config, configName) - opts, err := ProcessConfigFile(config) - if err != nil { - t.Fatalf("Error processing config file: %v", err) - } - opts.NoSigs = true - return opts, config -} - -func createSymlink(t *testing.T, symlinkName, fileName string) { - t.Helper() - os.Remove(symlinkName) - if err := os.Symlink(fileName, symlinkName); err != nil { - t.Fatalf("Error creating symlink: %v (ensure you have privileges)", err) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/ring.go b/vendor/github.com/nats-io/gnatsd/server/ring.go deleted file mode 100644 index b9232ca9..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/ring.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -// We wrap to hold onto optional items for /connz. -type closedClient struct { - ConnInfo - subs []string - user string -} - -// Fixed sized ringbuffer for closed connections. -type closedRingBuffer struct { - total uint64 - conns []*closedClient -} - -// Create a new ring buffer with at most max items. -func newClosedRingBuffer(max int) *closedRingBuffer { - rb := &closedRingBuffer{} - rb.conns = make([]*closedClient, max) - return rb -} - -// Adds in a new closed connection. If there is no more room, -// remove the oldest. -func (rb *closedRingBuffer) append(cc *closedClient) { - rb.conns[rb.next()] = cc - rb.total++ -} - -func (rb *closedRingBuffer) next() int { - return int(rb.total % uint64(cap(rb.conns))) -} - -func (rb *closedRingBuffer) len() int { - if rb.total > uint64(cap(rb.conns)) { - return cap(rb.conns) - } - return int(rb.total) -} - -func (rb *closedRingBuffer) totalConns() uint64 { - return rb.total -} - -// This will not be sorted. Will return a copy of the list -// which recipient can modify. If the contents of the client -// itself need to be modified, meaning swapping in any optional items, -// a copy should be made. We could introduce a new lock and hold that -// but since we return this list inside monitor which allows programatic -// access, we do not know when it would be done. -func (rb *closedRingBuffer) closedClients() []*closedClient { - dup := make([]*closedClient, rb.len()) - if rb.total <= uint64(cap(rb.conns)) { - copy(dup, rb.conns[:rb.len()]) - } else { - first := rb.next() - next := cap(rb.conns) - first - copy(dup, rb.conns[first:]) - copy(dup[next:], rb.conns[:next]) - } - return dup -} diff --git a/vendor/github.com/nats-io/gnatsd/server/route.go b/vendor/github.com/nats-io/gnatsd/server/route.go deleted file mode 100644 index c12a713d..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/route.go +++ /dev/null @@ -1,1103 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "crypto/tls" - "encoding/json" - "fmt" - "math/rand" - "net" - "net/url" - "strconv" - "strings" - "sync/atomic" - "time" - - "github.com/nats-io/gnatsd/util" -) - -// RouteType designates the router type -type RouteType int - -// Type of Route -const ( - // This route we learned from speaking to other routes. - Implicit RouteType = iota - // This route was explicitly configured. - Explicit -) - -type route struct { - remoteID string - didSolicit bool - retry bool - routeType RouteType - url *url.URL - authRequired bool - tlsRequired bool - closed bool - connectURLs []string -} - -type connectInfo struct { - Echo bool `json:"echo"` - Verbose bool `json:"verbose"` - Pedantic bool `json:"pedantic"` - User string `json:"user,omitempty"` - Pass string `json:"pass,omitempty"` - TLS bool `json:"tls_required"` - Name string `json:"name"` -} - -// Used to hold onto mappings for unsubscribed -// routed queue subscribers. -type rqsub struct { - group []byte - atime time.Time -} - -// Route protocol constants -const ( - ConProto = "CONNECT %s" + _CRLF_ - InfoProto = "INFO %s" + _CRLF_ -) - -// Clear up the timer and any map held for remote qsubs. -func (s *Server) clearRemoteQSubs() { - s.rqsMu.Lock() - defer s.rqsMu.Unlock() - if s.rqsubsTimer != nil { - s.rqsubsTimer.Stop() - s.rqsubsTimer = nil - } - s.rqsubs = nil -} - -// Check to see if we can remove any of the remote qsubs mappings -func (s *Server) purgeRemoteQSubs() { - ri := s.getOpts().RQSubsSweep - s.rqsMu.Lock() - exp := time.Now().Add(-ri) - for k, rqsub := range s.rqsubs { - if exp.After(rqsub.atime) { - delete(s.rqsubs, k) - } - } - if s.rqsubsTimer != nil { - // Reset timer. - s.rqsubsTimer = time.AfterFunc(ri, s.purgeRemoteQSubs) - } - s.rqsMu.Unlock() -} - -// Lookup a remote queue group sid. -func (s *Server) lookupRemoteQGroup(sid string) []byte { - s.rqsMu.RLock() - rqsub := s.rqsubs[sid] - s.rqsMu.RUnlock() - return rqsub.group -} - -// This will hold onto a remote queue subscriber to allow -// for mapping and handling if we get a message after the -// subscription goes away. -func (s *Server) holdRemoteQSub(sub *subscription) { - // Should not happen, but protect anyway. - if len(sub.queue) == 0 { - return - } - // Add the entry - s.rqsMu.Lock() - // Start timer if needed. - if s.rqsubsTimer == nil { - ri := s.getOpts().RQSubsSweep - s.rqsubsTimer = time.AfterFunc(ri, s.purgeRemoteQSubs) - } - // Create map if needed. - if s.rqsubs == nil { - s.rqsubs = make(map[string]rqsub) - } - group := make([]byte, len(sub.queue)) - copy(group, sub.queue) - rqsub := rqsub{group: group, atime: time.Now()} - s.rqsubs[routeSid(sub)] = rqsub - s.rqsMu.Unlock() -} - -// This is for when we receive a directed message for a queue subscriber -// that has gone away. We reroute like a new message but scope to only -// the queue subscribers that it was originally intended for. We will -// prefer local clients, but will bounce to another route if needed. -func (c *client) reRouteQMsg(r *SublistResult, msgh, msg, group []byte) { - c.Debugf("Attempting redelivery of message for absent queue subscriber on group '%q'", group) - - // We only care about qsubs here. Data structure not setup for optimized - // lookup for our specific group however. - - var qsubs []*subscription - for _, qs := range r.qsubs { - if len(qs) != 0 && bytes.Equal(group, qs[0].queue) { - qsubs = qs - break - } - } - - // If no match return. - if qsubs == nil { - c.Debugf("Redelivery failed, no queue subscribers for message on group '%q'", group) - return - } - - // We have a matched group of queue subscribers. - // We prefer a local subscriber since that was the original target. - - // Spin prand if needed. - if c.in.prand == nil { - c.in.prand = rand.New(rand.NewSource(time.Now().UnixNano())) - } - - // Hold onto a remote if we come across it to utilize in case no locals exist. - var rsub *subscription - - startIndex := c.in.prand.Intn(len(qsubs)) - for i := 0; i < len(qsubs); i++ { - index := (startIndex + i) % len(qsubs) - sub := qsubs[index] - if sub == nil { - continue - } - if rsub == nil && bytes.HasPrefix(sub.sid, []byte(QRSID)) { - rsub = sub - continue - } - mh := c.msgHeader(msgh[:], sub) - if c.deliverMsg(sub, mh, msg) { - c.Debugf("Redelivery succeeded for message on group '%q'", group) - return - } - } - // If we are here we failed to find a local, see if we snapshotted a - // remote sub, and if so deliver to that. - if rsub != nil { - mh := c.msgHeader(msgh[:], rsub) - if c.deliverMsg(rsub, mh, msg) { - c.Debugf("Re-routing message on group '%q' to remote server", group) - return - } - } - c.Debugf("Redelivery failed, no queue subscribers for message on group '%q'", group) -} - -// processRoutedMsg processes messages inbound from a route. -func (c *client) processRoutedMsg(r *SublistResult, msg []byte) { - // Snapshot server. - srv := c.srv - - msgh := c.prepMsgHeader() - si := len(msgh) - - // If we have a queue subscription, deliver direct - // since they are sent direct via L2 semantics over routes. - // If the match is a queue subscription, we will return from - // here regardless if we find a sub. - isq, sub, err := srv.routeSidQueueSubscriber(c.pa.sid) - if isq { - if err != nil { - // We got an invalid QRSID, so stop here - c.Errorf("Unable to deliver routed queue message: %v", err) - return - } - didDeliver := false - if sub != nil { - mh := c.msgHeader(msgh[:si], sub) - didDeliver = c.deliverMsg(sub, mh, msg) - } - if !didDeliver && c.srv != nil { - group := c.srv.lookupRemoteQGroup(string(c.pa.sid)) - c.reRouteQMsg(r, msgh, msg, group) - } - return - } - // Normal pub/sub message here - // Loop over all normal subscriptions that match. - for _, sub := range r.psubs { - // Check if this is a send to a ROUTER, if so we ignore to - // enforce 1-hop semantics. - if sub.client.typ == ROUTER { - continue - } - sub.client.mu.Lock() - if sub.client.nc == nil { - sub.client.mu.Unlock() - continue - } - sub.client.mu.Unlock() - - // Normal delivery - mh := c.msgHeader(msgh[:si], sub) - c.deliverMsg(sub, mh, msg) - } -} - -// Lock should be held entering here. -func (c *client) sendConnect(tlsRequired bool) { - var user, pass string - if userInfo := c.route.url.User; userInfo != nil { - user = userInfo.Username() - pass, _ = userInfo.Password() - } - cinfo := connectInfo{ - Echo: true, - Verbose: false, - Pedantic: false, - User: user, - Pass: pass, - TLS: tlsRequired, - Name: c.srv.info.ID, - } - b, err := json.Marshal(cinfo) - if err != nil { - c.Errorf("Error marshaling CONNECT to route: %v\n", err) - c.closeConnection(ProtocolViolation) - return - } - c.sendProto([]byte(fmt.Sprintf(ConProto, b)), true) -} - -// Process the info message if we are a route. -func (c *client) processRouteInfo(info *Info) { - c.mu.Lock() - // Connection can be closed at any time (by auth timeout, etc). - // Does not make sense to continue here if connection is gone. - if c.route == nil || c.nc == nil { - c.mu.Unlock() - return - } - - s := c.srv - remoteID := c.route.remoteID - - // We receive an INFO from a server that informs us about another server, - // so the info.ID in the INFO protocol does not match the ID of this route. - if remoteID != "" && remoteID != info.ID { - c.mu.Unlock() - - // Process this implicit route. We will check that it is not an explicit - // route and/or that it has not been connected already. - s.processImplicitRoute(info) - return - } - - // Need to set this for the detection of the route to self to work - // in closeConnection(). - c.route.remoteID = info.ID - - // Detect route to self. - if c.route.remoteID == s.info.ID { - c.mu.Unlock() - c.closeConnection(DuplicateRoute) - return - } - - // Copy over important information. - c.route.authRequired = info.AuthRequired - c.route.tlsRequired = info.TLSRequired - - // If we do not know this route's URL, construct one on the fly - // from the information provided. - if c.route.url == nil { - // Add in the URL from host and port - hp := net.JoinHostPort(info.Host, strconv.Itoa(info.Port)) - url, err := url.Parse(fmt.Sprintf("nats-route://%s/", hp)) - if err != nil { - c.Errorf("Error parsing URL from INFO: %v\n", err) - c.mu.Unlock() - c.closeConnection(ParseError) - return - } - c.route.url = url - } - - // Check to see if we have this remote already registered. - // This can happen when both servers have routes to each other. - c.mu.Unlock() - - if added, sendInfo := s.addRoute(c, info); added { - c.Debugf("Registering remote route %q", info.ID) - // Send our local subscriptions to this route. - s.sendLocalSubsToRoute(c) - // sendInfo will be false if the route that we just accepted - // is the only route there is. - if sendInfo { - // The incoming INFO from the route will have IP set - // if it has Cluster.Advertise. In that case, use that - // otherwise contruct it from the remote TCP address. - if info.IP == "" { - // Need to get the remote IP address. - c.mu.Lock() - switch conn := c.nc.(type) { - case *net.TCPConn, *tls.Conn: - addr := conn.RemoteAddr().(*net.TCPAddr) - info.IP = fmt.Sprintf("nats-route://%s/", net.JoinHostPort(addr.IP.String(), - strconv.Itoa(info.Port))) - default: - info.IP = c.route.url.String() - } - c.mu.Unlock() - } - // Now let the known servers know about this new route - s.forwardNewRouteInfoToKnownServers(info) - } - // Unless disabled, possibly update the server's INFO protocol - // and send to clients that know how to handle async INFOs. - if !s.getOpts().Cluster.NoAdvertise { - s.addClientConnectURLsAndSendINFOToClients(info.ClientConnectURLs) - } - } else { - c.Debugf("Detected duplicate remote route %q", info.ID) - c.closeConnection(DuplicateRoute) - } -} - -// sendAsyncInfoToClients sends an INFO protocol to all -// connected clients that accept async INFO updates. -// The server lock is held on entry. -func (s *Server) sendAsyncInfoToClients() { - // If there are no clients supporting async INFO protocols, we are done. - // Also don't send if we are shutting down... - if s.cproto == 0 || s.shutdown { - return - } - - for _, c := range s.clients { - c.mu.Lock() - // Here, we are going to send only to the clients that are fully - // registered (server has received CONNECT and first PING). For - // clients that are not at this stage, this will happen in the - // processing of the first PING (see client.processPing) - if c.opts.Protocol >= ClientProtoInfo && c.flags.isSet(firstPongSent) { - // sendInfo takes care of checking if the connection is still - // valid or not, so don't duplicate tests here. - c.sendInfo(c.generateClientInfoJSON(s.copyInfo())) - } - c.mu.Unlock() - } -} - -// This will process implicit route information received from another server. -// We will check to see if we have configured or are already connected, -// and if so we will ignore. Otherwise we will attempt to connect. -func (s *Server) processImplicitRoute(info *Info) { - remoteID := info.ID - - s.mu.Lock() - defer s.mu.Unlock() - - // Don't connect to ourself - if remoteID == s.info.ID { - return - } - // Check if this route already exists - if _, exists := s.remotes[remoteID]; exists { - return - } - // Check if we have this route as a configured route - if s.hasThisRouteConfigured(info) { - return - } - - // Initiate the connection, using info.IP instead of info.URL here... - r, err := url.Parse(info.IP) - if err != nil { - s.Errorf("Error parsing URL from INFO: %v\n", err) - return - } - - // Snapshot server options. - opts := s.getOpts() - - if info.AuthRequired { - r.User = url.UserPassword(opts.Cluster.Username, opts.Cluster.Password) - } - s.startGoRoutine(func() { s.connectToRoute(r, false) }) -} - -// hasThisRouteConfigured returns true if info.Host:info.Port is present -// in the server's opts.Routes, false otherwise. -// Server lock is assumed to be held by caller. -func (s *Server) hasThisRouteConfigured(info *Info) bool { - urlToCheckExplicit := strings.ToLower(net.JoinHostPort(info.Host, strconv.Itoa(info.Port))) - for _, ri := range s.getOpts().Routes { - if strings.ToLower(ri.Host) == urlToCheckExplicit { - return true - } - } - return false -} - -// forwardNewRouteInfoToKnownServers sends the INFO protocol of the new route -// to all routes known by this server. In turn, each server will contact this -// new route. -func (s *Server) forwardNewRouteInfoToKnownServers(info *Info) { - s.mu.Lock() - defer s.mu.Unlock() - - b, _ := json.Marshal(info) - infoJSON := []byte(fmt.Sprintf(InfoProto, b)) - - for _, r := range s.routes { - r.mu.Lock() - if r.route.remoteID != info.ID { - r.sendInfo(infoJSON) - } - r.mu.Unlock() - } -} - -// canImport is whether or not we will send a SUB for interest to the other side. -// This is for ROUTER connections only. -// Lock is held on entry. -func (c *client) canImport(subject []byte) bool { - // Use pubAllowed() since this checks Publish permissions which - // is what Import maps to. - return c.pubAllowed(subject) -} - -// canExport is whether or not we will accept a SUB from the remote for a given subject. -// This is for ROUTER connections only. -// Lock is held on entry -func (c *client) canExport(subject []byte) bool { - // Use canSubscribe() since this checks Subscribe permissions which - // is what Export maps to. - return c.canSubscribe(subject) -} - -// Initialize or reset cluster's permissions. -// This is for ROUTER connections only. -// Client lock is held on entry -func (c *client) setRoutePermissions(perms *RoutePermissions) { - // Reset if some were set - if perms == nil { - c.perms = nil - return - } - // Convert route permissions to user permissions. - // The Import permission is mapped to Publish - // and Export permission is mapped to Subscribe. - // For meaning of Import/Export, see canImport and canExport. - p := &Permissions{ - Publish: perms.Import, - Subscribe: perms.Export, - } - c.setPermissions(p) -} - -// This will send local subscription state to a new route connection. -// FIXME(dlc) - This could be a DOS or perf issue with many clients -// and large subscription space. Plus buffering in place not a good idea. -func (s *Server) sendLocalSubsToRoute(route *client) { - var raw [4096]*subscription - subs := raw[:0] - - s.sl.localSubs(&subs) - - route.mu.Lock() - for _, sub := range subs { - // Send SUB interest only if subject has a match in import permissions - if !route.canImport(sub.subject) { - continue - } - proto := fmt.Sprintf(subProto, sub.subject, sub.queue, routeSid(sub)) - route.queueOutbound([]byte(proto)) - if route.out.pb > int64(route.out.sz*2) { - route.flushSignal() - } - } - route.flushSignal() - route.mu.Unlock() - - route.Debugf("Sent local subscriptions to route") -} - -func (s *Server) createRoute(conn net.Conn, rURL *url.URL) *client { - // Snapshot server options. - opts := s.getOpts() - - didSolicit := rURL != nil - r := &route{didSolicit: didSolicit} - for _, route := range opts.Routes { - if rURL != nil && (strings.ToLower(rURL.Host) == strings.ToLower(route.Host)) { - r.routeType = Explicit - } - } - - c := &client{srv: s, nc: conn, opts: clientOpts{}, typ: ROUTER, route: r} - - // Grab server variables - s.mu.Lock() - infoJSON := s.routeInfoJSON - authRequired := s.routeInfo.AuthRequired - tlsRequired := s.routeInfo.TLSRequired - s.mu.Unlock() - - // Grab lock - c.mu.Lock() - - // Initialize - c.initClient() - - if didSolicit { - // Do this before the TLS code, otherwise, in case of failure - // and if route is explicit, it would try to reconnect to 'nil'... - r.url = rURL - - // Set permissions associated with the route user (if applicable). - // No lock needed since we are already under client lock. - c.setRoutePermissions(opts.Cluster.Permissions) - } - - // Check for TLS - if tlsRequired { - // Copy off the config to add in ServerName if we - tlsConfig := util.CloneTLSConfig(opts.Cluster.TLSConfig) - - // If we solicited, we will act like the client, otherwise the server. - if didSolicit { - c.Debugf("Starting TLS route client handshake") - // Specify the ServerName we are expecting. - host, _, _ := net.SplitHostPort(rURL.Host) - tlsConfig.ServerName = host - c.nc = tls.Client(c.nc, tlsConfig) - } else { - c.Debugf("Starting TLS route server handshake") - c.nc = tls.Server(c.nc, tlsConfig) - } - - conn := c.nc.(*tls.Conn) - - // Setup the timeout - ttl := secondsToDuration(opts.Cluster.TLSTimeout) - time.AfterFunc(ttl, func() { tlsTimeout(c, conn) }) - conn.SetReadDeadline(time.Now().Add(ttl)) - - c.mu.Unlock() - if err := conn.Handshake(); err != nil { - c.Errorf("TLS route handshake error: %v", err) - c.sendErr("Secure Connection - TLS Required") - c.closeConnection(TLSHandshakeError) - return nil - } - // Reset the read deadline - conn.SetReadDeadline(time.Time{}) - - // Re-Grab lock - c.mu.Lock() - - // Verify that the connection did not go away while we released the lock. - if c.nc == nil { - c.mu.Unlock() - return nil - } - } - - // Do final client initialization - - // Set the Ping timer - c.setPingTimer() - - // For routes, the "client" is added to s.routes only when processing - // the INFO protocol, that is much later. - // In the meantime, if the server shutsdown, there would be no reference - // to the client (connection) to be closed, leaving this readLoop - // uinterrupted, causing the Shutdown() to wait indefinitively. - // We need to store the client in a special map, under a special lock. - s.grMu.Lock() - running := s.grRunning - if running { - s.grTmpClients[c.cid] = c - } - s.grMu.Unlock() - if !running { - c.mu.Unlock() - c.setRouteNoReconnectOnClose() - c.closeConnection(ServerShutdown) - return nil - } - - // Check for Auth required state for incoming connections. - // Make sure to do this before spinning up readLoop. - if authRequired && !didSolicit { - ttl := secondsToDuration(opts.Cluster.AuthTimeout) - c.setAuthTimer(ttl) - } - - // Spin up the read loop. - s.startGoRoutine(func() { c.readLoop() }) - - // Spin up the write loop. - s.startGoRoutine(c.writeLoop) - - if tlsRequired { - c.Debugf("TLS handshake complete") - cs := c.nc.(*tls.Conn).ConnectionState() - c.Debugf("TLS version %s, cipher suite %s", tlsVersion(cs.Version), tlsCipher(cs.CipherSuite)) - } - - // Queue Connect proto if we solicited the connection. - if didSolicit { - c.Debugf("Route connect msg sent") - c.sendConnect(tlsRequired) - } - - // Send our info to the other side. - c.sendInfo(infoJSON) - - c.mu.Unlock() - - c.Noticef("Route connection created") - return c -} - -const ( - _CRLF_ = "\r\n" - _EMPTY_ = "" -) - -const ( - subProto = "SUB %s %s %s" + _CRLF_ - unsubProto = "UNSUB %s" + _CRLF_ -) - -// FIXME(dlc) - Make these reserved and reject if they come in as a sid -// from a client connection. -// Route constants -const ( - RSID = "RSID" - QRSID = "QRSID" - - QRSID_LEN = len(QRSID) -) - -// Parse the given rsid. If the protocol does not start with QRSID, -// returns false and no subscription nor error. -// If it does start with QRSID, returns true and possibly a subscription -// or an error if the QRSID protocol is malformed. -func (s *Server) routeSidQueueSubscriber(rsid []byte) (bool, *subscription, error) { - if !bytes.HasPrefix(rsid, []byte(QRSID)) { - return false, nil, nil - } - cid, sid, err := parseRouteQueueSid(rsid) - if err != nil { - return true, nil, err - } - - s.mu.Lock() - client := s.clients[cid] - s.mu.Unlock() - - if client == nil { - return true, nil, nil - } - - client.mu.Lock() - sub, ok := client.subs[string(sid)] - client.mu.Unlock() - if ok { - return true, sub, nil - } - return true, nil, nil -} - -// Creates a routable sid that can be used -// to reach remote subscriptions. -func routeSid(sub *subscription) string { - var qi string - if len(sub.queue) > 0 { - qi = "Q" - } - return fmt.Sprintf("%s%s:%d:%s", qi, RSID, sub.client.cid, sub.sid) -} - -// Parse the given `rsid` knowing that it starts with `QRSID`. -// Returns the cid and sid or an error not a valid QRSID. -func parseRouteQueueSid(rsid []byte) (uint64, []byte, error) { - var ( - cid uint64 - sid []byte - cidFound bool - sidFound bool - ) - // A valid QRSID needs to be at least QRSID:x:y - // First character here should be `:` - if len(rsid) >= QRSID_LEN+4 { - if rsid[QRSID_LEN] == ':' { - for i, count := QRSID_LEN+1, len(rsid); i < count; i++ { - switch rsid[i] { - case ':': - cid = uint64(parseInt64(rsid[QRSID_LEN+1 : i])) - cidFound = true - sid = rsid[i+1:] - } - } - if cidFound { - // We can't assume the content of sid, so as long - // as it is not len 0, we have to say it is a valid one. - if len(rsid) > 0 { - sidFound = true - } - } - } - } - if cidFound && sidFound { - return cid, sid, nil - } - return 0, nil, fmt.Errorf("invalid QRSID: %s", rsid) -} - -func (s *Server) addRoute(c *client, info *Info) (bool, bool) { - id := c.route.remoteID - sendInfo := false - - s.mu.Lock() - if !s.running { - s.mu.Unlock() - return false, false - } - remote, exists := s.remotes[id] - if !exists { - s.routes[c.cid] = c - s.remotes[id] = c - c.mu.Lock() - c.route.connectURLs = info.ClientConnectURLs - cid := c.cid - c.mu.Unlock() - - // Remove from the temporary map - s.grMu.Lock() - delete(s.grTmpClients, cid) - s.grMu.Unlock() - - // we don't need to send if the only route is the one we just accepted. - sendInfo = len(s.routes) > 1 - } - s.mu.Unlock() - - if exists { - var r *route - - c.mu.Lock() - // upgrade to solicited? - if c.route.didSolicit { - // Make a copy - rs := *c.route - r = &rs - } - c.mu.Unlock() - - remote.mu.Lock() - // r will be not nil if c.route.didSolicit was true - if r != nil { - // If we upgrade to solicited, we still want to keep the remote's - // connectURLs. So transfer those. - r.connectURLs = remote.route.connectURLs - remote.route = r - } - // This is to mitigate the issue where both sides add the route - // on the opposite connection, and therefore end-up with both - // connections being dropped. - remote.route.retry = true - remote.mu.Unlock() - } - - return !exists, sendInfo -} - -func (s *Server) broadcastInterestToRoutes(sub *subscription, proto string) { - var arg []byte - if atomic.LoadInt32(&s.logging.trace) == 1 { - arg = []byte(proto[:len(proto)-LEN_CR_LF]) - } - protoAsBytes := []byte(proto) - s.mu.Lock() - for _, route := range s.routes { - // FIXME(dlc) - Make same logic as deliverMsg - route.mu.Lock() - // The permission of this cluster applies to all routes, and each - // route will have the same `perms`, so check with the first route - // and send SUB interest only if subject has a match in import permissions. - // If there is no match, we stop here. - if !route.canImport(sub.subject) { - route.mu.Unlock() - break - } - route.sendProto(protoAsBytes, true) - route.mu.Unlock() - route.traceOutOp("", arg) - } - s.mu.Unlock() -} - -// broadcastSubscribe will forward a client subscription -// to all active routes. -func (s *Server) broadcastSubscribe(sub *subscription) { - if s.numRoutes() == 0 { - return - } - rsid := routeSid(sub) - proto := fmt.Sprintf(subProto, sub.subject, sub.queue, rsid) - s.broadcastInterestToRoutes(sub, proto) -} - -// broadcastUnSubscribe will forward a client unsubscribe -// action to all active routes. -func (s *Server) broadcastUnSubscribe(sub *subscription) { - if s.numRoutes() == 0 { - return - } - sub.client.mu.Lock() - // Max has no meaning on the other side of a route, so do not send. - hasMax := sub.max > 0 && sub.nm < sub.max - sub.client.mu.Unlock() - if hasMax { - return - } - rsid := routeSid(sub) - proto := fmt.Sprintf(unsubProto, rsid) - s.broadcastInterestToRoutes(sub, proto) -} - -func (s *Server) routeAcceptLoop(ch chan struct{}) { - defer func() { - if ch != nil { - close(ch) - } - }() - - // Snapshot server options. - opts := s.getOpts() - - // Snapshot server options. - port := opts.Cluster.Port - - if port == -1 { - port = 0 - } - - hp := net.JoinHostPort(opts.Cluster.Host, strconv.Itoa(port)) - l, e := net.Listen("tcp", hp) - if e != nil { - s.Fatalf("Error listening on router port: %d - %v", opts.Cluster.Port, e) - return - } - s.Noticef("Listening for route connections on %s", - net.JoinHostPort(opts.Cluster.Host, strconv.Itoa(l.Addr().(*net.TCPAddr).Port))) - - s.mu.Lock() - // Check for TLSConfig - tlsReq := opts.Cluster.TLSConfig != nil - info := Info{ - ID: s.info.ID, - Version: s.info.Version, - AuthRequired: false, - TLSRequired: tlsReq, - TLSVerify: tlsReq, - MaxPayload: s.info.MaxPayload, - } - // Set this if only if advertise is not disabled - if !opts.Cluster.NoAdvertise { - info.ClientConnectURLs = s.clientConnectURLs - } - // If we have selected a random port... - if port == 0 { - // Write resolved port back to options. - opts.Cluster.Port = l.Addr().(*net.TCPAddr).Port - } - // Keep track of actual listen port. This will be needed in case of - // config reload. - s.clusterActualPort = opts.Cluster.Port - // Check for Auth items - if opts.Cluster.Username != "" { - info.AuthRequired = true - } - s.routeInfo = info - // Possibly override Host/Port and set IP based on Cluster.Advertise - if err := s.setRouteInfoHostPortAndIP(); err != nil { - s.Fatalf("Error setting route INFO with Cluster.Advertise value of %s, err=%v", s.opts.Cluster.Advertise, err) - l.Close() - s.mu.Unlock() - return - } - // Setup state that can enable shutdown - s.routeListener = l - s.mu.Unlock() - - // Let them know we are up - close(ch) - ch = nil - - tmpDelay := ACCEPT_MIN_SLEEP - - for s.isRunning() { - conn, err := l.Accept() - if err != nil { - if ne, ok := err.(net.Error); ok && ne.Temporary() { - s.Debugf("Temporary Route Accept Errorf(%v), sleeping %dms", - ne, tmpDelay/time.Millisecond) - time.Sleep(tmpDelay) - tmpDelay *= 2 - if tmpDelay > ACCEPT_MAX_SLEEP { - tmpDelay = ACCEPT_MAX_SLEEP - } - } else if s.isRunning() { - s.Noticef("Accept error: %v", err) - } - continue - } - tmpDelay = ACCEPT_MIN_SLEEP - s.startGoRoutine(func() { - s.createRoute(conn, nil) - s.grWG.Done() - }) - } - s.Debugf("Router accept loop exiting..") - s.done <- true -} - -// Similar to setInfoHostPortAndGenerateJSON, but for routeInfo. -func (s *Server) setRouteInfoHostPortAndIP() error { - if s.opts.Cluster.Advertise != "" { - advHost, advPort, err := parseHostPort(s.opts.Cluster.Advertise, s.opts.Cluster.Port) - if err != nil { - return err - } - s.routeInfo.Host = advHost - s.routeInfo.Port = advPort - s.routeInfo.IP = fmt.Sprintf("nats-route://%s/", net.JoinHostPort(advHost, strconv.Itoa(advPort))) - } else { - s.routeInfo.Host = s.opts.Cluster.Host - s.routeInfo.Port = s.opts.Cluster.Port - s.routeInfo.IP = "" - } - // (re)generate the routeInfoJSON byte array - s.generateRouteInfoJSON() - return nil -} - -// StartRouting will start the accept loop on the cluster host:port -// and will actively try to connect to listed routes. -func (s *Server) StartRouting(clientListenReady chan struct{}) { - defer s.grWG.Done() - - // Wait for the client listen port to be opened, and - // the possible ephemeral port to be selected. - <-clientListenReady - - // Spin up the accept loop - ch := make(chan struct{}) - go s.routeAcceptLoop(ch) - <-ch - - // Solicit Routes if needed. - s.solicitRoutes(s.getOpts().Routes) -} - -func (s *Server) reConnectToRoute(rURL *url.URL, rtype RouteType) { - tryForEver := rtype == Explicit - // If A connects to B, and B to A (regardless if explicit or - // implicit - due to auto-discovery), and if each server first - // registers the route on the opposite TCP connection, the - // two connections will end-up being closed. - // Add some random delay to reduce risk of repeated failures. - delay := time.Duration(rand.Intn(100)) * time.Millisecond - if tryForEver { - delay += DEFAULT_ROUTE_RECONNECT - } - time.Sleep(delay) - s.connectToRoute(rURL, tryForEver) -} - -// Checks to make sure the route is still valid. -func (s *Server) routeStillValid(rURL *url.URL) bool { - for _, ri := range s.getOpts().Routes { - if ri == rURL { - return true - } - } - return false -} - -func (s *Server) connectToRoute(rURL *url.URL, tryForEver bool) { - // Snapshot server options. - opts := s.getOpts() - - defer s.grWG.Done() - - attempts := 0 - for s.isRunning() && rURL != nil { - if tryForEver && !s.routeStillValid(rURL) { - return - } - s.Debugf("Trying to connect to route on %s", rURL.Host) - conn, err := net.DialTimeout("tcp", rURL.Host, DEFAULT_ROUTE_DIAL) - if err != nil { - s.Errorf("Error trying to connect to route: %v", err) - if !tryForEver { - if opts.Cluster.ConnectRetries <= 0 { - return - } - attempts++ - if attempts > opts.Cluster.ConnectRetries { - return - } - } - select { - case <-s.quitCh: - return - case <-time.After(DEFAULT_ROUTE_CONNECT): - continue - } - } - - if tryForEver && !s.routeStillValid(rURL) { - conn.Close() - return - } - - // We have a route connection here. - // Go ahead and create it and exit this func. - s.createRoute(conn, rURL) - return - } -} - -func (c *client) isSolicitedRoute() bool { - c.mu.Lock() - defer c.mu.Unlock() - return c.typ == ROUTER && c.route != nil && c.route.didSolicit -} - -func (s *Server) solicitRoutes(routes []*url.URL) { - for _, r := range routes { - route := r - s.startGoRoutine(func() { s.connectToRoute(route, true) }) - } -} - -func (s *Server) numRoutes() int { - s.mu.Lock() - defer s.mu.Unlock() - return len(s.routes) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/routes_test.go b/vendor/github.com/nats-io/gnatsd/server/routes_test.go deleted file mode 100644 index de1226eb..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/routes_test.go +++ /dev/null @@ -1,1000 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "fmt" - "net" - "net/url" - "reflect" - "strconv" - "strings" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/nats-io/go-nats" -) - -func checkNumRoutes(t *testing.T, s *Server, expected int) { - t.Helper() - checkFor(t, 5*time.Second, 15*time.Millisecond, func() error { - if nr := s.NumRoutes(); nr != expected { - return fmt.Errorf("Expected %v routes, got %v", expected, nr) - } - return nil - }) -} - -func TestRouteConfig(t *testing.T) { - opts, err := ProcessConfigFile("./configs/cluster.conf") - if err != nil { - t.Fatalf("Received an error reading route config file: %v\n", err) - } - - golden := &Options{ - ConfigFile: "./configs/cluster.conf", - Host: "127.0.0.1", - Port: 4242, - Username: "derek", - Password: "porkchop", - AuthTimeout: 1.0, - Cluster: ClusterOpts{ - Host: "127.0.0.1", - Port: 4244, - Username: "route_user", - Password: "top_secret", - AuthTimeout: 1.0, - NoAdvertise: true, - ConnectRetries: 2, - }, - PidFile: "/tmp/nats_cluster_test.pid", - } - - // Setup URLs - r1, _ := url.Parse("nats-route://foo:bar@127.0.0.1:4245") - r2, _ := url.Parse("nats-route://foo:bar@127.0.0.1:4246") - - golden.Routes = []*url.URL{r1, r2} - - if !reflect.DeepEqual(golden, opts) { - t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", - golden, opts) - } -} - -func TestClusterAdvertise(t *testing.T) { - lst, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("Error starting listener: %v", err) - } - ch := make(chan error) - go func() { - c, err := lst.Accept() - if err != nil { - ch <- err - return - } - c.Close() - ch <- nil - }() - - optsA, _ := ProcessConfigFile("./configs/seed.conf") - optsA.NoSigs, optsA.NoLog = true, true - srvA := RunServer(optsA) - defer srvA.Shutdown() - - srvARouteURL := fmt.Sprintf("nats://%s:%d", optsA.Cluster.Host, srvA.ClusterAddr().Port) - optsB := nextServerOpts(optsA) - optsB.Routes = RoutesFromStr(srvARouteURL) - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - // Wait for these 2 to connect to each other - checkClusterFormed(t, srvA, srvB) - - // Now start server C that connects to A. A should ask B to connect to C, - // based on C's URL. But since C configures a Cluster.Advertise, it will connect - // to our listener. - optsC := nextServerOpts(optsB) - optsC.Cluster.Advertise = lst.Addr().String() - optsC.ClientAdvertise = "me:1" - optsC.Routes = RoutesFromStr(srvARouteURL) - - srvC := RunServer(optsC) - defer srvC.Shutdown() - - select { - case e := <-ch: - if e != nil { - t.Fatalf("Error: %v", e) - } - case <-time.After(2 * time.Second): - t.Fatalf("Test timed out") - } -} - -func TestClusterAdvertiseErrorOnStartup(t *testing.T) { - opts := DefaultOptions() - // Set invalid address - opts.Cluster.Advertise = "addr:::123" - s := New(opts) - defer s.Shutdown() - dl := &DummyLogger{} - s.SetLogger(dl, false, false) - - // Start will keep running, so start in a go-routine. - wg := &sync.WaitGroup{} - wg.Add(1) - go func() { - s.Start() - wg.Done() - }() - checkFor(t, 2*time.Second, 15*time.Millisecond, func() error { - dl.Lock() - msg := dl.msg - dl.Unlock() - if strings.Contains(msg, "Cluster.Advertise") { - return nil - } - return fmt.Errorf("Did not get expected error, got %v", msg) - }) - s.Shutdown() - wg.Wait() -} - -func TestClientAdvertise(t *testing.T) { - optsA, _ := ProcessConfigFile("./configs/seed.conf") - optsA.NoSigs, optsA.NoLog = true, true - - srvA := RunServer(optsA) - defer srvA.Shutdown() - - optsB := nextServerOpts(optsA) - optsB.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", optsA.Cluster.Host, optsA.Cluster.Port)) - optsB.ClientAdvertise = "me:1" - srvB := RunServer(optsB) - defer srvB.Shutdown() - - checkClusterFormed(t, srvA, srvB) - - nc, err := nats.Connect(fmt.Sprintf("nats://%s:%d", optsA.Host, optsA.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nc.Close() - checkFor(t, time.Second, 15*time.Millisecond, func() error { - ds := nc.DiscoveredServers() - if len(ds) == 1 { - if ds[0] == "nats://me:1" { - return nil - } - } - return fmt.Errorf("Did not get expected discovered servers: %v", nc.DiscoveredServers()) - }) -} - -func TestServerRoutesWithClients(t *testing.T) { - optsA, _ := ProcessConfigFile("./configs/srv_a.conf") - optsB, _ := ProcessConfigFile("./configs/srv_b.conf") - - optsA.NoSigs, optsA.NoLog = true, true - optsB.NoSigs, optsB.NoLog = true, true - - srvA := RunServer(optsA) - defer srvA.Shutdown() - - urlA := fmt.Sprintf("nats://%s:%d/", optsA.Host, optsA.Port) - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, optsB.Port) - - nc1, err := nats.Connect(urlA) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc1.Close() - - ch := make(chan bool) - sub, _ := nc1.Subscribe("foo", func(m *nats.Msg) { ch <- true }) - nc1.QueueSubscribe("foo", "bar", func(m *nats.Msg) {}) - nc1.Publish("foo", []byte("Hello")) - // Wait for message - <-ch - sub.Unsubscribe() - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - // Wait for route to form. - checkClusterFormed(t, srvA, srvB) - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc2.Close() - nc2.Publish("foo", []byte("Hello")) - nc2.Flush() -} - -func TestServerRoutesWithAuthAndBCrypt(t *testing.T) { - optsA, _ := ProcessConfigFile("./configs/srv_a_bcrypt.conf") - optsB, _ := ProcessConfigFile("./configs/srv_b_bcrypt.conf") - - optsA.NoSigs, optsA.NoLog = true, true - optsB.NoSigs, optsB.NoLog = true, true - - srvA := RunServer(optsA) - defer srvA.Shutdown() - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - // Wait for route to form. - checkClusterFormed(t, srvA, srvB) - - urlA := fmt.Sprintf("nats://%s:%s@%s:%d/", optsA.Username, optsA.Password, optsA.Host, optsA.Port) - urlB := fmt.Sprintf("nats://%s:%s@%s:%d/", optsB.Username, optsB.Password, optsB.Host, optsB.Port) - - nc1, err := nats.Connect(urlA) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc1.Close() - - // Test that we are connected. - ch := make(chan bool) - sub, err := nc1.Subscribe("foo", func(m *nats.Msg) { ch <- true }) - if err != nil { - t.Fatalf("Error creating subscription: %v\n", err) - } - nc1.Flush() - defer sub.Unsubscribe() - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc2.Close() - nc2.Publish("foo", []byte("Hello")) - nc2.Flush() - - // Wait for message - select { - case <-ch: - case <-time.After(2 * time.Second): - t.Fatal("Timeout waiting for message across route") - } -} - -// Helper function to check that a cluster is formed -func checkClusterFormed(t *testing.T, servers ...*Server) { - t.Helper() - expectedNumRoutes := len(servers) - 1 - checkFor(t, 10*time.Second, 100*time.Millisecond, func() error { - for _, s := range servers { - if numRoutes := s.NumRoutes(); numRoutes != expectedNumRoutes { - return fmt.Errorf("Expected %d routes for server %q, got %d", expectedNumRoutes, s.ID(), numRoutes) - } - } - return nil - }) -} - -// Helper function to generate next opts to make sure no port conflicts etc. -func nextServerOpts(opts *Options) *Options { - nopts := *opts - nopts.Port = -1 - nopts.Cluster.Port = -1 - nopts.HTTPPort = -1 - return &nopts -} - -func TestSeedSolicitWorks(t *testing.T) { - optsSeed, _ := ProcessConfigFile("./configs/seed.conf") - - optsSeed.NoSigs, optsSeed.NoLog = true, true - - srvSeed := RunServer(optsSeed) - defer srvSeed.Shutdown() - - optsA := nextServerOpts(optsSeed) - optsA.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", optsSeed.Cluster.Host, - srvSeed.ClusterAddr().Port)) - - srvA := RunServer(optsA) - defer srvA.Shutdown() - - urlA := fmt.Sprintf("nats://%s:%d/", optsA.Host, srvA.ClusterAddr().Port) - - nc1, err := nats.Connect(urlA) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc1.Close() - - // Test that we are connected. - ch := make(chan bool) - nc1.Subscribe("foo", func(m *nats.Msg) { ch <- true }) - nc1.Flush() - - optsB := nextServerOpts(optsA) - optsB.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", optsSeed.Cluster.Host, - srvSeed.ClusterAddr().Port)) - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, srvB.ClusterAddr().Port) - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc2.Close() - - checkClusterFormed(t, srvSeed, srvA, srvB) - - nc2.Publish("foo", []byte("Hello")) - - // Wait for message - select { - case <-ch: - case <-time.After(2 * time.Second): - t.Fatal("Timeout waiting for message across route") - } -} - -func TestTLSSeedSolicitWorks(t *testing.T) { - optsSeed, _ := ProcessConfigFile("./configs/seed_tls.conf") - - optsSeed.NoSigs, optsSeed.NoLog = true, true - - srvSeed := RunServer(optsSeed) - defer srvSeed.Shutdown() - - seedRouteUrl := fmt.Sprintf("nats://%s:%d", optsSeed.Cluster.Host, - srvSeed.ClusterAddr().Port) - optsA := nextServerOpts(optsSeed) - optsA.Routes = RoutesFromStr(seedRouteUrl) - - srvA := RunServer(optsA) - defer srvA.Shutdown() - - urlA := fmt.Sprintf("nats://%s:%d/", optsA.Host, srvA.Addr().(*net.TCPAddr).Port) - - nc1, err := nats.Connect(urlA) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc1.Close() - - // Test that we are connected. - ch := make(chan bool) - nc1.Subscribe("foo", func(m *nats.Msg) { ch <- true }) - nc1.Flush() - - optsB := nextServerOpts(optsA) - optsB.Routes = RoutesFromStr(seedRouteUrl) - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, srvB.Addr().(*net.TCPAddr).Port) - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc2.Close() - - checkClusterFormed(t, srvSeed, srvA, srvB) - - nc2.Publish("foo", []byte("Hello")) - - // Wait for message - select { - case <-ch: - case <-time.After(2 * time.Second): - t.Fatal("Timeout waiting for message across route") - } -} - -func TestChainedSolicitWorks(t *testing.T) { - optsSeed, _ := ProcessConfigFile("./configs/seed.conf") - - optsSeed.NoSigs, optsSeed.NoLog = true, true - - srvSeed := RunServer(optsSeed) - defer srvSeed.Shutdown() - - seedRouteUrl := fmt.Sprintf("nats://%s:%d", optsSeed.Cluster.Host, - srvSeed.ClusterAddr().Port) - optsA := nextServerOpts(optsSeed) - optsA.Routes = RoutesFromStr(seedRouteUrl) - - srvA := RunServer(optsA) - defer srvA.Shutdown() - - urlSeed := fmt.Sprintf("nats://%s:%d/", optsSeed.Host, srvA.Addr().(*net.TCPAddr).Port) - - nc1, err := nats.Connect(urlSeed) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc1.Close() - - // Test that we are connected. - ch := make(chan bool) - nc1.Subscribe("foo", func(m *nats.Msg) { ch <- true }) - nc1.Flush() - - optsB := nextServerOpts(optsA) - // Server B connects to A - optsB.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", optsA.Cluster.Host, - srvA.ClusterAddr().Port)) - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, srvB.Addr().(*net.TCPAddr).Port) - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc2.Close() - - checkClusterFormed(t, srvSeed, srvA, srvB) - - nc2.Publish("foo", []byte("Hello")) - - // Wait for message - select { - case <-ch: - case <-time.After(2 * time.Second): - t.Fatal("Timeout waiting for message across route") - } -} - -// Helper function to check that a server (or list of servers) have the -// expected number of subscriptions. -func checkExpectedSubs(t *testing.T, expected int, servers ...*Server) { - t.Helper() - checkFor(t, 10*time.Second, 10*time.Millisecond, func() error { - for _, s := range servers { - if numSubs := int(s.NumSubscriptions()); numSubs != expected { - return fmt.Errorf("Expected %d subscriptions for server %q, got %d", expected, s.ID(), numSubs) - } - } - return nil - }) -} - -func TestTLSChainedSolicitWorks(t *testing.T) { - optsSeed, _ := ProcessConfigFile("./configs/seed_tls.conf") - - optsSeed.NoSigs, optsSeed.NoLog = true, true - - srvSeed := RunServer(optsSeed) - defer srvSeed.Shutdown() - - urlSeedRoute := fmt.Sprintf("nats://%s:%d", optsSeed.Cluster.Host, - srvSeed.ClusterAddr().Port) - optsA := nextServerOpts(optsSeed) - optsA.Routes = RoutesFromStr(urlSeedRoute) - - srvA := RunServer(optsA) - defer srvA.Shutdown() - - urlSeed := fmt.Sprintf("nats://%s:%d/", optsSeed.Host, srvSeed.Addr().(*net.TCPAddr).Port) - - nc1, err := nats.Connect(urlSeed) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc1.Close() - - // Test that we are connected. - ch := make(chan bool) - nc1.Subscribe("foo", func(m *nats.Msg) { ch <- true }) - nc1.Flush() - - optsB := nextServerOpts(optsA) - // Server B connects to A - optsB.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", optsA.Cluster.Host, - srvA.ClusterAddr().Port)) - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - checkClusterFormed(t, srvSeed, srvA, srvB) - checkExpectedSubs(t, 1, srvA, srvB) - - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, srvB.Addr().(*net.TCPAddr).Port) - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc2.Close() - - nc2.Publish("foo", []byte("Hello")) - - // Wait for message - select { - case <-ch: - case <-time.After(2 * time.Second): - t.Fatal("Timeout waiting for message across route") - } -} - -func TestRouteTLSHandshakeError(t *testing.T) { - optsSeed, _ := ProcessConfigFile("./configs/seed_tls.conf") - optsSeed.NoLog = true - optsSeed.NoSigs = true - srvSeed := RunServer(optsSeed) - defer srvSeed.Shutdown() - - opts := DefaultOptions() - opts.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", optsSeed.Cluster.Host, optsSeed.Cluster.Port)) - - srv := RunServer(opts) - defer srv.Shutdown() - - time.Sleep(500 * time.Millisecond) - - checkNumRoutes(t, srv, 0) -} - -func TestBlockedShutdownOnRouteAcceptLoopFailure(t *testing.T) { - opts := DefaultOptions() - opts.Cluster.Host = "x.x.x.x" - opts.Cluster.Port = 7222 - - s := New(opts) - go s.Start() - // Wait a second - time.Sleep(time.Second) - ch := make(chan bool) - go func() { - s.Shutdown() - ch <- true - }() - - timeout := time.NewTimer(5 * time.Second) - select { - case <-ch: - return - case <-timeout.C: - t.Fatal("Shutdown did not complete") - } -} - -func TestRouteUseIPv6(t *testing.T) { - opts := DefaultOptions() - opts.Cluster.Host = "::" - opts.Cluster.Port = 6222 - - // I believe that there is no IPv6 support on Travis... - // Regardless, cannot have this test fail simply because IPv6 is disabled - // on the host. - hp := net.JoinHostPort(opts.Cluster.Host, strconv.Itoa(opts.Cluster.Port)) - _, err := net.ResolveTCPAddr("tcp", hp) - if err != nil { - t.Skipf("Skipping this test since there is no IPv6 support on this host: %v", err) - } - - s := RunServer(opts) - defer s.Shutdown() - - routeUp := false - timeout := time.Now().Add(5 * time.Second) - for time.Now().Before(timeout) && !routeUp { - // We know that the server is local and listening to - // all IPv6 interfaces. Try connect using IPv6 loopback. - if conn, err := net.Dial("tcp", "[::1]:6222"); err != nil { - // Travis seem to have the server actually listening to 0.0.0.0, - // so try with 127.0.0.1 - if conn, err := net.Dial("tcp", "127.0.0.1:6222"); err != nil { - time.Sleep(time.Second) - continue - } else { - conn.Close() - } - } else { - conn.Close() - } - routeUp = true - } - if !routeUp { - t.Fatal("Server failed to start route accept loop") - } -} - -func TestClientConnectToRoutePort(t *testing.T) { - opts := DefaultOptions() - - // Since client will first connect to the route listen port, set the - // cluster's Host to 127.0.0.1 so it works on Windows too, since on - // Windows, a client can't use 0.0.0.0 in a connect. - opts.Cluster.Host = "127.0.0.1" - s := RunServer(opts) - defer s.Shutdown() - - url := fmt.Sprintf("nats://%s:%d", opts.Cluster.Host, s.ClusterAddr().Port) - clientURL := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - // When connecting to the ROUTE port, the client library will receive the - // CLIENT port in the INFO protocol. This URL is added to the client's pool - // and will be tried after the initial connect failure. So all those - // nats.Connect() should succeed. - // The only reason for a failure would be if there are too many FDs in time-wait - // which would delay the creation of TCP connection. So keep the total of - // attempts rather small. - total := 10 - for i := 0; i < total; i++ { - nc, err := nats.Connect(url) - if err != nil { - t.Fatalf("Unexepected error on connect: %v", err) - } - defer nc.Close() - if nc.ConnectedUrl() != clientURL { - t.Fatalf("Expected client to be connected to %v, got %v", clientURL, nc.ConnectedUrl()) - } - } - - s.Shutdown() - // Try again with NoAdvertise and this time, the client should fail to connect. - opts.Cluster.NoAdvertise = true - s = RunServer(opts) - defer s.Shutdown() - - for i := 0; i < total; i++ { - nc, err := nats.Connect(url) - if err == nil { - nc.Close() - t.Fatal("Expected error on connect, got none") - } - } -} - -type checkDuplicateRouteLogger struct { - sync.Mutex - gotDuplicate bool -} - -func (l *checkDuplicateRouteLogger) Noticef(format string, v ...interface{}) {} -func (l *checkDuplicateRouteLogger) Errorf(format string, v ...interface{}) {} -func (l *checkDuplicateRouteLogger) Fatalf(format string, v ...interface{}) {} -func (l *checkDuplicateRouteLogger) Tracef(format string, v ...interface{}) {} -func (l *checkDuplicateRouteLogger) Debugf(format string, v ...interface{}) { - l.Lock() - defer l.Unlock() - msg := fmt.Sprintf(format, v...) - if strings.Contains(msg, "duplicate remote route") { - l.gotDuplicate = true - } -} - -func TestRoutesToEachOther(t *testing.T) { - optsA := DefaultOptions() - optsA.Cluster.Port = 7246 - optsA.Routes = RoutesFromStr("nats://127.0.0.1:7247") - - optsB := DefaultOptions() - optsB.Cluster.Port = 7247 - optsB.Routes = RoutesFromStr("nats://127.0.0.1:7246") - - srvALogger := &checkDuplicateRouteLogger{} - srvA := New(optsA) - srvA.SetLogger(srvALogger, true, false) - defer srvA.Shutdown() - - srvBLogger := &checkDuplicateRouteLogger{} - srvB := New(optsB) - srvB.SetLogger(srvBLogger, true, false) - defer srvB.Shutdown() - - go srvA.Start() - go srvB.Start() - - start := time.Now() - checkClusterFormed(t, srvA, srvB) - end := time.Now() - - srvALogger.Lock() - gotIt := srvALogger.gotDuplicate - srvALogger.Unlock() - if !gotIt { - srvBLogger.Lock() - gotIt = srvBLogger.gotDuplicate - srvBLogger.Unlock() - } - if gotIt { - dur := end.Sub(start) - // It should not take too long to have a successful connection - // between the 2 servers. - if dur > 5*time.Second { - t.Logf("Cluster formed, but took a long time: %v", dur) - } - } else { - t.Log("Was not able to get duplicate route this time!") - } -} - -func wait(ch chan bool) error { - select { - case <-ch: - return nil - case <-time.After(5 * time.Second): - } - return fmt.Errorf("timeout") -} - -func TestServerPoolUpdatedWhenRouteGoesAway(t *testing.T) { - s1Opts := DefaultOptions() - s1Opts.Host = "127.0.0.1" - s1Opts.Port = 4222 - s1Opts.Cluster.Host = "127.0.0.1" - s1Opts.Cluster.Port = 6222 - s1Opts.Routes = RoutesFromStr("nats://127.0.0.1:6223,nats://127.0.0.1:6224") - s1 := RunServer(s1Opts) - defer s1.Shutdown() - - s1Url := "nats://127.0.0.1:4222" - s2Url := "nats://127.0.0.1:4223" - s3Url := "nats://127.0.0.1:4224" - - ch := make(chan bool, 1) - chch := make(chan bool, 1) - connHandler := func(_ *nats.Conn) { - chch <- true - } - nc, err := nats.Connect(s1Url, - nats.ReconnectHandler(connHandler), - nats.DiscoveredServersHandler(func(_ *nats.Conn) { - ch <- true - })) - if err != nil { - t.Fatalf("Error on connect") - } - - s2Opts := DefaultOptions() - s2Opts.Host = "127.0.0.1" - s2Opts.Port = s1Opts.Port + 1 - s2Opts.Cluster.Host = "127.0.0.1" - s2Opts.Cluster.Port = 6223 - s2Opts.Routes = RoutesFromStr("nats://127.0.0.1:6222,nats://127.0.0.1:6224") - s2 := RunServer(s2Opts) - defer s2.Shutdown() - - // Wait to be notified - if err := wait(ch); err != nil { - t.Fatal("New server callback was not invoked") - } - - checkPool := func(expected []string) { - // Don't use discovered here, but Servers to have the full list. - // Also, there may be cases where the mesh is not formed yet, - // so try again on failure. - checkFor(t, 5*time.Second, 50*time.Millisecond, func() error { - ds := nc.Servers() - if len(ds) == len(expected) { - m := make(map[string]struct{}, len(ds)) - for _, url := range ds { - m[url] = struct{}{} - } - ok := true - for _, url := range expected { - if _, present := m[url]; !present { - ok = false - break - } - } - if ok { - return nil - } - } - return fmt.Errorf("Expected %v, got %v", expected, ds) - }) - } - // Verify that we now know about s2 - checkPool([]string{s1Url, s2Url}) - - s3Opts := DefaultOptions() - s3Opts.Host = "127.0.0.1" - s3Opts.Port = s2Opts.Port + 1 - s3Opts.Cluster.Host = "127.0.0.1" - s3Opts.Cluster.Port = 6224 - s3Opts.Routes = RoutesFromStr("nats://127.0.0.1:6222,nats://127.0.0.1:6223") - s3 := RunServer(s3Opts) - defer s3.Shutdown() - - // Wait to be notified - if err := wait(ch); err != nil { - t.Fatal("New server callback was not invoked") - } - // Verify that we now know about s3 - checkPool([]string{s1Url, s2Url, s3Url}) - - // Stop s1. Since this was passed to the Connect() call, this one should - // still be present. - s1.Shutdown() - // Wait for reconnect - if err := wait(chch); err != nil { - t.Fatal("Reconnect handler not invoked") - } - checkPool([]string{s1Url, s2Url, s3Url}) - - // Check the server we reconnected to. - reConnectedTo := nc.ConnectedUrl() - expected := []string{s1Url} - if reConnectedTo == s2Url { - s2.Shutdown() - expected = append(expected, s3Url) - } else if reConnectedTo == s3Url { - s3.Shutdown() - expected = append(expected, s2Url) - } else { - t.Fatalf("Unexpected server client has reconnected to: %v", reConnectedTo) - } - // Wait for reconnect - if err := wait(chch); err != nil { - t.Fatal("Reconnect handler not invoked") - } - // The implicit server that we just shutdown should have been removed from the pool - checkPool(expected) - nc.Close() -} - -func TestRoutedQueueAutoUnsubscribe(t *testing.T) { - optsA, _ := ProcessConfigFile("./configs/seed.conf") - optsA.NoSigs, optsA.NoLog = true, true - optsA.RQSubsSweep = 500 * time.Millisecond - srvA := RunServer(optsA) - defer srvA.Shutdown() - - srvARouteURL := fmt.Sprintf("nats://%s:%d", optsA.Cluster.Host, srvA.ClusterAddr().Port) - optsB := nextServerOpts(optsA) - optsB.Routes = RoutesFromStr(srvARouteURL) - - srvB := RunServer(optsB) - defer srvB.Shutdown() - - // Wait for these 2 to connect to each other - checkClusterFormed(t, srvA, srvB) - - // Have a client connection to each server - ncA, err := nats.Connect(fmt.Sprintf("nats://%s:%d", optsA.Host, optsA.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer ncA.Close() - - ncB, err := nats.Connect(fmt.Sprintf("nats://%s:%d", optsB.Host, optsB.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer ncB.Close() - - rbar := int32(0) - barCb := func(m *nats.Msg) { - atomic.AddInt32(&rbar, 1) - } - rbaz := int32(0) - bazCb := func(m *nats.Msg) { - atomic.AddInt32(&rbaz, 1) - } - - // Create 125 queue subs with auto-unsubscribe to each server for - // group bar and group baz. So 250 total per queue group. - cons := []*nats.Conn{ncA, ncB} - for _, c := range cons { - for i := 0; i < 125; i++ { - qsub, err := c.QueueSubscribe("foo", "bar", barCb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - if err := qsub.AutoUnsubscribe(1); err != nil { - t.Fatalf("Error on auto-unsubscribe: %v", err) - } - qsub, err = c.QueueSubscribe("foo", "baz", bazCb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - if err := qsub.AutoUnsubscribe(1); err != nil { - t.Fatalf("Error on auto-unsubscribe: %v", err) - } - } - c.Flush() - } - - expected := int32(250) - // Now send messages from each server - for i := int32(0); i < expected; i++ { - c := cons[i%2] - c.Publish("foo", []byte("Don't Drop Me!")) - } - for _, c := range cons { - c.Flush() - } - - checkFor(t, 10*time.Second, 100*time.Millisecond, func() error { - nbar := atomic.LoadInt32(&rbar) - nbaz := atomic.LoadInt32(&rbaz) - if nbar == expected && nbaz == expected { - time.Sleep(500 * time.Millisecond) - // Now check all mappings are gone. - srvA.rqsMu.RLock() - nrqsa := len(srvA.rqsubs) - srvA.rqsMu.RUnlock() - srvB.rqsMu.RLock() - nrqsb := len(srvB.rqsubs) - srvB.rqsMu.RUnlock() - if nrqsa != 0 || nrqsb != 0 { - return fmt.Errorf("Expected rqs mappings to have cleared, but got A:%d, B:%d", - nrqsa, nrqsb) - } - return nil - } - return fmt.Errorf("Did not receive all %d queue messages, received %d for 'bar' and %d for 'baz'", - expected, atomic.LoadInt32(&rbar), atomic.LoadInt32(&rbaz)) - }) -} - -func TestRouteFailedConnRemovedFromTmpMap(t *testing.T) { - optsA, _ := ProcessConfigFile("./configs/srv_a.conf") - optsA.NoSigs, optsA.NoLog = true, true - - optsB, _ := ProcessConfigFile("./configs/srv_b.conf") - optsB.NoSigs, optsB.NoLog = true, true - - srvA := New(optsA) - defer srvA.Shutdown() - srvB := New(optsB) - defer srvB.Shutdown() - - // Start this way to increase chance of having the two connect - // to each other at the same time. This will cause one of the - // route to be dropped. - wg := &sync.WaitGroup{} - wg.Add(2) - go func() { - srvA.Start() - wg.Done() - }() - go func() { - srvB.Start() - wg.Done() - }() - - checkClusterFormed(t, srvA, srvB) - - // Ensure that maps are empty - checkMap := func(s *Server) { - s.grMu.Lock() - l := len(s.grTmpClients) - s.grMu.Unlock() - if l != 0 { - stackFatalf(t, "grTmpClients map should be empty, got %v", l) - } - } - checkMap(srvA) - checkMap(srvB) - - srvB.Shutdown() - srvA.Shutdown() - wg.Wait() -} diff --git a/vendor/github.com/nats-io/gnatsd/server/server.go b/vendor/github.com/nats-io/gnatsd/server/server.go deleted file mode 100644 index bddb9de3..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/server.go +++ /dev/null @@ -1,1420 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "crypto/tls" - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "net" - "net/http" - "os" - "path" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - // Allow dynamic profiling. - _ "net/http/pprof" - - "github.com/nats-io/gnatsd/util" -) - -// Info is the information sent to clients to help them understand information -// about this server. -type Info struct { - ID string `json:"server_id"` - Version string `json:"version"` - Proto int `json:"proto"` - GitCommit string `json:"git_commit,omitempty"` - GoVersion string `json:"go"` - Host string `json:"host"` - Port int `json:"port"` - AuthRequired bool `json:"auth_required,omitempty"` - TLSRequired bool `json:"tls_required,omitempty"` - TLSVerify bool `json:"tls_verify,omitempty"` - MaxPayload int `json:"max_payload"` - IP string `json:"ip,omitempty"` - CID uint64 `json:"client_id,omitempty"` - ClientConnectURLs []string `json:"connect_urls,omitempty"` // Contains URLs a client can connect to. -} - -// Server is our main struct. -type Server struct { - gcid uint64 - stats - mu sync.Mutex - info Info - sl *Sublist - configFile string - optsMu sync.RWMutex - opts *Options - running bool - shutdown bool - listener net.Listener - clients map[uint64]*client - routes map[uint64]*client - remotes map[string]*client - users map[string]*User - totalClients uint64 - closed *closedRingBuffer - done chan bool - start time.Time - http net.Listener - httpHandler http.Handler - profiler net.Listener - httpReqStats map[string]uint64 - routeListener net.Listener - routeInfo Info - routeInfoJSON []byte - quitCh chan struct{} - - // Tracking for remote QRSID tags. - rqsMu sync.RWMutex - rqsubs map[string]rqsub - rqsubsTimer *time.Timer - - // Tracking Go routines - grMu sync.Mutex - grTmpClients map[uint64]*client - grRunning bool - grWG sync.WaitGroup // to wait on various go routines - - cproto int64 // number of clients supporting async INFO - configTime time.Time // last time config was loaded - - logging struct { - sync.RWMutex - logger Logger - trace int32 - debug int32 - } - - clientConnectURLs []string - - // Used internally for quick look-ups. - clientConnectURLsMap map[string]struct{} - - lastCURLsUpdate int64 - - // These store the real client/cluster listen ports. They are - // required during config reload to reset the Options (after - // reload) to the actual listen port values. - clientActualPort int - clusterActualPort int - - // Used by tests to check that http.Servers do - // not set any timeout. - monitoringServer *http.Server - profilingServer *http.Server -} - -// Make sure all are 64bits for atomic use -type stats struct { - inMsgs int64 - outMsgs int64 - inBytes int64 - outBytes int64 - slowConsumers int64 -} - -// New will setup a new server struct after parsing the options. -func New(opts *Options) *Server { - processOptions(opts) - - // Process TLS options, including whether we require client certificates. - tlsReq := opts.TLSConfig != nil - verify := (tlsReq && opts.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert) - - info := Info{ - ID: genID(), - Version: VERSION, - Proto: PROTO, - GitCommit: gitCommit, - GoVersion: runtime.Version(), - Host: opts.Host, - Port: opts.Port, - AuthRequired: false, - TLSRequired: tlsReq, - TLSVerify: verify, - MaxPayload: opts.MaxPayload, - } - - now := time.Now() - s := &Server{ - configFile: opts.ConfigFile, - info: info, - sl: NewSublist(), - opts: opts, - done: make(chan bool, 1), - start: now, - configTime: now, - } - - s.mu.Lock() - defer s.mu.Unlock() - - // This is normally done in the AcceptLoop, once the - // listener has been created (possibly with random port), - // but since some tests may expect the INFO to be properly - // set after New(), let's do it now. - s.setInfoHostPortAndGenerateJSON() - - // Used internally for quick look-ups. - s.clientConnectURLsMap = make(map[string]struct{}) - - // For tracking clients - s.clients = make(map[uint64]*client) - - // For tracking closed clients. - s.closed = newClosedRingBuffer(opts.MaxClosedClients) - - // For tracking connections that are not yet registered - // in s.routes, but for which readLoop has started. - s.grTmpClients = make(map[uint64]*client) - - // For tracking routes and their remote ids - s.routes = make(map[uint64]*client) - s.remotes = make(map[string]*client) - - // Used to kick out all go routines possibly waiting on server - // to shutdown. - s.quitCh = make(chan struct{}) - - // Used to setup Authorization. - s.configureAuthorization() - - // Start signal handler - s.handleSignals() - - return s -} - -func (s *Server) getOpts() *Options { - s.optsMu.RLock() - opts := s.opts - s.optsMu.RUnlock() - return opts -} - -func (s *Server) setOpts(opts *Options) { - s.optsMu.Lock() - s.opts = opts - s.optsMu.Unlock() -} - -func (s *Server) generateRouteInfoJSON() { - b, _ := json.Marshal(s.routeInfo) - pcs := [][]byte{[]byte("INFO"), b, []byte(CR_LF)} - s.routeInfoJSON = bytes.Join(pcs, []byte(" ")) -} - -// PrintAndDie is exported for access in other packages. -func PrintAndDie(msg string) { - fmt.Fprintf(os.Stderr, "%s\n", msg) - os.Exit(1) -} - -// PrintServerAndExit will print our version and exit. -func PrintServerAndExit() { - fmt.Printf("nats-server version %s\n", VERSION) - os.Exit(0) -} - -// ProcessCommandLineArgs takes the command line arguments -// validating and setting flags for handling in case any -// sub command was present. -func ProcessCommandLineArgs(cmd *flag.FlagSet) (showVersion bool, showHelp bool, err error) { - if len(cmd.Args()) > 0 { - arg := cmd.Args()[0] - switch strings.ToLower(arg) { - case "version": - return true, false, nil - case "help": - return false, true, nil - default: - return false, false, fmt.Errorf("unrecognized command: %q", arg) - } - } - - return false, false, nil -} - -// Protected check on running state -func (s *Server) isRunning() bool { - s.mu.Lock() - defer s.mu.Unlock() - return s.running -} - -func (s *Server) logPid() error { - pidStr := strconv.Itoa(os.Getpid()) - return ioutil.WriteFile(s.getOpts().PidFile, []byte(pidStr), 0660) -} - -// Start up the server, this will block. -// Start via a Go routine if needed. -func (s *Server) Start() { - s.Noticef("Starting nats-server version %s", VERSION) - s.Debugf("Go build version %s", s.info.GoVersion) - gc := gitCommit - if gc == "" { - gc = "not set" - } - s.Noticef("Git commit [%s]", gc) - - // Avoid RACE between Start() and Shutdown() - s.mu.Lock() - s.running = true - s.mu.Unlock() - - s.grMu.Lock() - s.grRunning = true - s.grMu.Unlock() - - // Snapshot server options. - opts := s.getOpts() - - // Log the pid to a file - if opts.PidFile != _EMPTY_ { - if err := s.logPid(); err != nil { - PrintAndDie(fmt.Sprintf("Could not write pidfile: %v\n", err)) - } - } - - // Start monitoring if needed - if err := s.StartMonitoring(); err != nil { - s.Fatalf("Can't start monitoring: %v", err) - return - } - - // The Routing routine needs to wait for the client listen - // port to be opened and potential ephemeral port selected. - clientListenReady := make(chan struct{}) - - // Start up routing as well if needed. - if opts.Cluster.Port != 0 { - s.startGoRoutine(func() { - s.StartRouting(clientListenReady) - }) - } - - // Pprof http endpoint for the profiler. - if opts.ProfPort != 0 { - s.StartProfiler() - } - - if opts.PortsFileDir != _EMPTY_ { - s.logPorts() - } - - // Wait for clients. - s.AcceptLoop(clientListenReady) -} - -// Shutdown will shutdown the server instance by kicking out the AcceptLoop -// and closing all associated clients. -func (s *Server) Shutdown() { - s.mu.Lock() - // Prevent issues with multiple calls. - if s.shutdown { - s.mu.Unlock() - return - } - - opts := s.getOpts() - - s.shutdown = true - s.running = false - s.grMu.Lock() - s.grRunning = false - s.grMu.Unlock() - - conns := make(map[uint64]*client) - - // Copy off the clients - for i, c := range s.clients { - conns[i] = c - } - // Copy off the connections that are not yet registered - // in s.routes, but for which the readLoop has started - s.grMu.Lock() - for i, c := range s.grTmpClients { - conns[i] = c - } - s.grMu.Unlock() - // Copy off the routes - for i, r := range s.routes { - r.setRouteNoReconnectOnClose() - conns[i] = r - } - - // Number of done channel responses we expect. - doneExpected := 0 - - // Kick client AcceptLoop() - if s.listener != nil { - doneExpected++ - s.listener.Close() - s.listener = nil - } - - // Kick route AcceptLoop() - if s.routeListener != nil { - doneExpected++ - s.routeListener.Close() - s.routeListener = nil - } - - // Kick HTTP monitoring if its running - if s.http != nil { - doneExpected++ - s.http.Close() - s.http = nil - } - - // Kick Profiling if its running - if s.profiler != nil { - doneExpected++ - s.profiler.Close() - } - - // Clear any remote qsub mappings - s.clearRemoteQSubs() - s.mu.Unlock() - - // Release go routines that wait on that channel - close(s.quitCh) - - // Close client and route connections - for _, c := range conns { - c.closeConnection(ServerShutdown) - } - - // Block until the accept loops exit - for doneExpected > 0 { - <-s.done - doneExpected-- - } - - // Wait for go routines to be done. - s.grWG.Wait() - - if opts.PortsFileDir != _EMPTY_ { - s.deletePortsFile(opts.PortsFileDir) - } -} - -// AcceptLoop is exported for easier testing. -func (s *Server) AcceptLoop(clr chan struct{}) { - // If we were to exit before the listener is setup properly, - // make sure we close the channel. - defer func() { - if clr != nil { - close(clr) - } - }() - - // Snapshot server options. - opts := s.getOpts() - - hp := net.JoinHostPort(opts.Host, strconv.Itoa(opts.Port)) - l, e := net.Listen("tcp", hp) - if e != nil { - s.Fatalf("Error listening on port: %s, %q", hp, e) - return - } - s.Noticef("Listening for client connections on %s", - net.JoinHostPort(opts.Host, strconv.Itoa(l.Addr().(*net.TCPAddr).Port))) - - // Alert of TLS enabled. - if opts.TLSConfig != nil { - s.Noticef("TLS required for client connections") - } - - s.Debugf("Server id is %s", s.info.ID) - s.Noticef("Server is ready") - - // Setup state that can enable shutdown - s.mu.Lock() - s.listener = l - - // If server was started with RANDOM_PORT (-1), opts.Port would be equal - // to 0 at the beginning this function. So we need to get the actual port - if opts.Port == 0 { - // Write resolved port back to options. - opts.Port = l.Addr().(*net.TCPAddr).Port - } - // Keep track of actual listen port. This will be needed in case of - // config reload. - s.clientActualPort = opts.Port - - // Now that port has been set (if it was set to RANDOM), set the - // server's info Host/Port with either values from Options or - // ClientAdvertise. Also generate the JSON byte array. - if err := s.setInfoHostPortAndGenerateJSON(); err != nil { - s.Fatalf("Error setting server INFO with ClientAdvertise value of %s, err=%v", s.opts.ClientAdvertise, err) - s.mu.Unlock() - return - } - // Keep track of client connect URLs. We may need them later. - s.clientConnectURLs = s.getClientConnectURLs() - s.mu.Unlock() - - // Let the caller know that we are ready - close(clr) - clr = nil - - tmpDelay := ACCEPT_MIN_SLEEP - - for s.isRunning() { - conn, err := l.Accept() - if err != nil { - if ne, ok := err.(net.Error); ok && ne.Temporary() { - s.Errorf("Temporary Client Accept Error (%v), sleeping %dms", - ne, tmpDelay/time.Millisecond) - time.Sleep(tmpDelay) - tmpDelay *= 2 - if tmpDelay > ACCEPT_MAX_SLEEP { - tmpDelay = ACCEPT_MAX_SLEEP - } - } else if s.isRunning() { - s.Errorf("Client Accept Error: %v", err) - } - continue - } - tmpDelay = ACCEPT_MIN_SLEEP - s.startGoRoutine(func() { - s.createClient(conn) - s.grWG.Done() - }) - } - s.Noticef("Server Exiting..") - s.done <- true -} - -// This function sets the server's info Host/Port based on server Options. -// Note that this function may be called during config reload, this is why -// Host/Port may be reset to original Options if the ClientAdvertise option -// is not set (since it may have previously been). -// The function then generates the server infoJSON. -func (s *Server) setInfoHostPortAndGenerateJSON() error { - // When this function is called, opts.Port is set to the actual listen - // port (if option was originally set to RANDOM), even during a config - // reload. So use of s.opts.Port is safe. - if s.opts.ClientAdvertise != "" { - h, p, err := parseHostPort(s.opts.ClientAdvertise, s.opts.Port) - if err != nil { - return err - } - s.info.Host = h - s.info.Port = p - } else { - s.info.Host = s.opts.Host - s.info.Port = s.opts.Port - } - return nil -} - -// StartProfiler is called to enable dynamic profiling. -func (s *Server) StartProfiler() { - // Snapshot server options. - opts := s.getOpts() - - port := opts.ProfPort - - // Check for Random Port - if port == -1 { - port = 0 - } - - hp := net.JoinHostPort(opts.Host, strconv.Itoa(port)) - - l, err := net.Listen("tcp", hp) - s.Noticef("profiling port: %d", l.Addr().(*net.TCPAddr).Port) - - if err != nil { - s.Fatalf("error starting profiler: %s", err) - } - - srv := &http.Server{ - Addr: hp, - Handler: http.DefaultServeMux, - MaxHeaderBytes: 1 << 20, - } - - s.mu.Lock() - s.profiler = l - s.profilingServer = srv - s.mu.Unlock() - - go func() { - // if this errors out, it's probably because the server is being shutdown - err := srv.Serve(l) - if err != nil { - s.mu.Lock() - shutdown := s.shutdown - s.mu.Unlock() - if !shutdown { - s.Fatalf("error starting profiler: %s", err) - } - } - s.done <- true - }() -} - -// StartHTTPMonitoring will enable the HTTP monitoring port. -// DEPRECATED: Should use StartMonitoring. -func (s *Server) StartHTTPMonitoring() { - s.startMonitoring(false) -} - -// StartHTTPSMonitoring will enable the HTTPS monitoring port. -// DEPRECATED: Should use StartMonitoring. -func (s *Server) StartHTTPSMonitoring() { - s.startMonitoring(true) -} - -// StartMonitoring starts the HTTP or HTTPs server if needed. -func (s *Server) StartMonitoring() error { - // Snapshot server options. - opts := s.getOpts() - - // Specifying both HTTP and HTTPS ports is a misconfiguration - if opts.HTTPPort != 0 && opts.HTTPSPort != 0 { - return fmt.Errorf("can't specify both HTTP (%v) and HTTPs (%v) ports", opts.HTTPPort, opts.HTTPSPort) - } - var err error - if opts.HTTPPort != 0 { - err = s.startMonitoring(false) - } else if opts.HTTPSPort != 0 { - if opts.TLSConfig == nil { - return fmt.Errorf("TLS cert and key required for HTTPS") - } - err = s.startMonitoring(true) - } - return err -} - -// HTTP endpoints -const ( - RootPath = "/" - VarzPath = "/varz" - ConnzPath = "/connz" - RoutezPath = "/routez" - SubszPath = "/subsz" - StackszPath = "/stacksz" -) - -// Start the monitoring server -func (s *Server) startMonitoring(secure bool) error { - // Snapshot server options. - opts := s.getOpts() - - // Used to track HTTP requests - s.httpReqStats = map[string]uint64{ - RootPath: 0, - VarzPath: 0, - ConnzPath: 0, - RoutezPath: 0, - SubszPath: 0, - } - - var ( - hp string - err error - httpListener net.Listener - port int - ) - - monitorProtocol := "http" - - if secure { - monitorProtocol += "s" - port = opts.HTTPSPort - if port == -1 { - port = 0 - } - hp = net.JoinHostPort(opts.HTTPHost, strconv.Itoa(port)) - config := util.CloneTLSConfig(opts.TLSConfig) - config.ClientAuth = tls.NoClientCert - httpListener, err = tls.Listen("tcp", hp, config) - - } else { - port = opts.HTTPPort - if port == -1 { - port = 0 - } - hp = net.JoinHostPort(opts.HTTPHost, strconv.Itoa(port)) - httpListener, err = net.Listen("tcp", hp) - } - - if err != nil { - return fmt.Errorf("can't listen to the monitor port: %v", err) - } - - s.Noticef("Starting %s monitor on %s", monitorProtocol, - net.JoinHostPort(opts.HTTPHost, strconv.Itoa(httpListener.Addr().(*net.TCPAddr).Port))) - - mux := http.NewServeMux() - - // Root - mux.HandleFunc(RootPath, s.HandleRoot) - // Varz - mux.HandleFunc(VarzPath, s.HandleVarz) - // Connz - mux.HandleFunc(ConnzPath, s.HandleConnz) - // Routez - mux.HandleFunc(RoutezPath, s.HandleRoutez) - // Subz - mux.HandleFunc(SubszPath, s.HandleSubsz) - // Subz alias for backwards compatibility - mux.HandleFunc("/subscriptionsz", s.HandleSubsz) - // Stacksz - mux.HandleFunc(StackszPath, s.HandleStacksz) - - // Do not set a WriteTimeout because it could cause cURL/browser - // to return empty response or unable to display page if the - // server needs more time to build the response. - srv := &http.Server{ - Addr: hp, - Handler: mux, - MaxHeaderBytes: 1 << 20, - } - s.mu.Lock() - s.http = httpListener - s.httpHandler = mux - s.monitoringServer = srv - s.mu.Unlock() - - go func() { - srv.Serve(httpListener) - srv.Handler = nil - s.mu.Lock() - s.httpHandler = nil - s.mu.Unlock() - s.done <- true - }() - - return nil -} - -// HTTPHandler returns the http.Handler object used to handle monitoring -// endpoints. It will return nil if the server is not configured for -// monitoring, or if the server has not been started yet (Server.Start()). -func (s *Server) HTTPHandler() http.Handler { - s.mu.Lock() - defer s.mu.Unlock() - return s.httpHandler -} - -// Perform a conditional deep copy due to reference nature of ClientConnectURLs. -// If updates are made to Info, this function should be consulted and updated. -// Assume lock is held. -func (s *Server) copyInfo() Info { - info := s.info - if info.ClientConnectURLs != nil { - info.ClientConnectURLs = make([]string, len(s.info.ClientConnectURLs)) - copy(info.ClientConnectURLs, s.info.ClientConnectURLs) - } - return info -} - -func (s *Server) createClient(conn net.Conn) *client { - // Snapshot server options. - opts := s.getOpts() - - max_pay := int64(opts.MaxPayload) - max_subs := opts.MaxSubs - now := time.Now() - - c := &client{srv: s, nc: conn, opts: defaultOpts, mpay: max_pay, msubs: max_subs, start: now, last: now} - - // Grab JSON info string - s.mu.Lock() - info := s.copyInfo() - s.totalClients++ - s.mu.Unlock() - - // Grab lock - c.mu.Lock() - - // Initialize - c.initClient() - - c.Debugf("Client connection created") - - // Send our information. - c.sendInfo(c.generateClientInfoJSON(info)) - - // Unlock to register - c.mu.Unlock() - - // Register with the server. - s.mu.Lock() - // If server is not running, Shutdown() may have already gathered the - // list of connections to close. It won't contain this one, so we need - // to bail out now otherwise the readLoop started down there would not - // be interrupted. - if !s.running { - s.mu.Unlock() - return c - } - - // If there is a max connections specified, check that adding - // this new client would not push us over the max - if opts.MaxConn > 0 && len(s.clients) >= opts.MaxConn { - s.mu.Unlock() - c.maxConnExceeded() - return nil - } - s.clients[c.cid] = c - s.mu.Unlock() - - // Re-Grab lock - c.mu.Lock() - - // Check for TLS - if info.TLSRequired { - c.Debugf("Starting TLS client connection handshake") - c.nc = tls.Server(c.nc, opts.TLSConfig) - conn := c.nc.(*tls.Conn) - - // Setup the timeout - ttl := secondsToDuration(opts.TLSTimeout) - time.AfterFunc(ttl, func() { tlsTimeout(c, conn) }) - conn.SetReadDeadline(time.Now().Add(ttl)) - - // Force handshake - c.mu.Unlock() - if err := conn.Handshake(); err != nil { - c.Errorf("TLS handshake error: %v", err) - c.closeConnection(TLSHandshakeError) - return nil - } - // Reset the read deadline - conn.SetReadDeadline(time.Time{}) - - // Re-Grab lock - c.mu.Lock() - - // Indicate that handshake is complete (used in monitoring) - c.flags.set(handshakeComplete) - } - - // The connection may have been closed - if c.nc == nil { - c.mu.Unlock() - return c - } - - // Check for Auth. We schedule this timer after the TLS handshake to avoid - // the race where the timer fires during the handshake and causes the - // server to write bad data to the socket. See issue #432. - if info.AuthRequired { - c.setAuthTimer(secondsToDuration(opts.AuthTimeout)) - } - - // Do final client initialization - - // Set the Ping timer - c.setPingTimer() - - // Spin up the read loop. - s.startGoRoutine(c.readLoop) - - // Spin up the write loop. - s.startGoRoutine(c.writeLoop) - - if info.TLSRequired { - c.Debugf("TLS handshake complete") - cs := c.nc.(*tls.Conn).ConnectionState() - c.Debugf("TLS version %s, cipher suite %s", tlsVersion(cs.Version), tlsCipher(cs.CipherSuite)) - } - - c.mu.Unlock() - - return c -} - -// This will save off a closed client in a ring buffer such that -// /connz can inspect. Useful for debugging, etc. -func (s *Server) saveClosedClient(c *client, nc net.Conn, reason ClosedState) { - now := time.Now() - - c.mu.Lock() - - cc := &closedClient{} - cc.fill(c, nc, now) - cc.Stop = &now - cc.Reason = reason.String() - - // Do subs, do not place by default in main ConnInfo - if len(c.subs) > 0 { - cc.subs = make([]string, 0, len(c.subs)) - for _, sub := range c.subs { - cc.subs = append(cc.subs, string(sub.subject)) - } - } - // Hold user as well. - cc.user = c.opts.Username - c.mu.Unlock() - - // Place in the ring buffer - s.mu.Lock() - s.closed.append(cc) - s.mu.Unlock() -} - -// Adds the given array of urls to the server's INFO.ClientConnectURLs -// array. The server INFO JSON is regenerated. -// Note that a check is made to ensure that given URLs are not -// already present. So the INFO JSON is regenerated only if new ULRs -// were added. -// If there was a change, an INFO protocol is sent to registered clients -// that support async INFO protocols. -func (s *Server) addClientConnectURLsAndSendINFOToClients(urls []string) { - s.updateServerINFOAndSendINFOToClients(urls, true) -} - -// Removes the given array of urls from the server's INFO.ClientConnectURLs -// array. The server INFO JSON is regenerated if needed. -// If there was a change, an INFO protocol is sent to registered clients -// that support async INFO protocols. -func (s *Server) removeClientConnectURLsAndSendINFOToClients(urls []string) { - s.updateServerINFOAndSendINFOToClients(urls, false) -} - -// Updates the server's Info object with the given array of URLs and re-generate -// the infoJSON byte array, then send an (async) INFO protocol to clients that -// support it. -func (s *Server) updateServerINFOAndSendINFOToClients(urls []string, add bool) { - s.mu.Lock() - defer s.mu.Unlock() - - // Will be set to true if we alter the server's Info object. - wasUpdated := false - remove := !add - for _, url := range urls { - _, present := s.clientConnectURLsMap[url] - if add && !present { - s.clientConnectURLsMap[url] = struct{}{} - wasUpdated = true - } else if remove && present { - delete(s.clientConnectURLsMap, url) - wasUpdated = true - } - } - if wasUpdated { - // Recreate the info.ClientConnectURL array from the map - s.info.ClientConnectURLs = s.info.ClientConnectURLs[:0] - // Add this server client connect ULRs first... - s.info.ClientConnectURLs = append(s.info.ClientConnectURLs, s.clientConnectURLs...) - for url := range s.clientConnectURLsMap { - s.info.ClientConnectURLs = append(s.info.ClientConnectURLs, url) - } - // Update the time of this update - s.lastCURLsUpdate = time.Now().UnixNano() - // Send to all registered clients that support async INFO protocols. - s.sendAsyncInfoToClients() - } -} - -// Handle closing down a connection when the handshake has timedout. -func tlsTimeout(c *client, conn *tls.Conn) { - c.mu.Lock() - nc := c.nc - c.mu.Unlock() - // Check if already closed - if nc == nil { - return - } - cs := conn.ConnectionState() - if !cs.HandshakeComplete { - c.Errorf("TLS handshake timeout") - c.sendErr("Secure Connection - TLS Required") - c.closeConnection(TLSHandshakeError) - } -} - -// Seems silly we have to write these -func tlsVersion(ver uint16) string { - switch ver { - case tls.VersionTLS10: - return "1.0" - case tls.VersionTLS11: - return "1.1" - case tls.VersionTLS12: - return "1.2" - } - return fmt.Sprintf("Unknown [%x]", ver) -} - -// We use hex here so we don't need multiple versions -func tlsCipher(cs uint16) string { - name, present := cipherMapByID[cs] - if present { - return name - } - return fmt.Sprintf("Unknown [%x]", cs) -} - -// Remove a client or route from our internal accounting. -func (s *Server) removeClient(c *client) { - var rID string - c.mu.Lock() - cid := c.cid - typ := c.typ - r := c.route - if r != nil { - rID = r.remoteID - } - updateProtoInfoCount := false - if typ == CLIENT && c.opts.Protocol >= ClientProtoInfo { - updateProtoInfoCount = true - } - c.mu.Unlock() - - s.mu.Lock() - switch typ { - case CLIENT: - delete(s.clients, cid) - if updateProtoInfoCount { - s.cproto-- - } - case ROUTER: - delete(s.routes, cid) - if r != nil { - rc, ok := s.remotes[rID] - // Only delete it if it is us.. - if ok && c == rc { - delete(s.remotes, rID) - } - } - // Remove from temporary map in case it is there. - s.grMu.Lock() - delete(s.grTmpClients, cid) - s.grMu.Unlock() - } - s.mu.Unlock() -} - -///////////////////////////////////////////////////////////////// -// These are some helpers for accounting in functional tests. -///////////////////////////////////////////////////////////////// - -// NumRoutes will report the number of registered routes. -func (s *Server) NumRoutes() int { - s.mu.Lock() - nr := len(s.routes) - s.mu.Unlock() - return nr -} - -// NumRemotes will report number of registered remotes. -func (s *Server) NumRemotes() int { - s.mu.Lock() - defer s.mu.Unlock() - return len(s.remotes) -} - -// NumClients will report the number of registered clients. -func (s *Server) NumClients() int { - s.mu.Lock() - defer s.mu.Unlock() - return len(s.clients) -} - -// getClient will return the client associated with cid. -func (s *Server) getClient(cid uint64) *client { - s.mu.Lock() - defer s.mu.Unlock() - return s.clients[cid] -} - -// NumSubscriptions will report how many subscriptions are active. -func (s *Server) NumSubscriptions() uint32 { - s.mu.Lock() - subs := s.sl.Count() - s.mu.Unlock() - return subs -} - -// NumSlowConsumers will report the number of slow consumers. -func (s *Server) NumSlowConsumers() int64 { - return atomic.LoadInt64(&s.slowConsumers) -} - -// ConfigTime will report the last time the server configuration was loaded. -func (s *Server) ConfigTime() time.Time { - s.mu.Lock() - defer s.mu.Unlock() - return s.configTime -} - -// Addr will return the net.Addr object for the current listener. -func (s *Server) Addr() net.Addr { - s.mu.Lock() - defer s.mu.Unlock() - if s.listener == nil { - return nil - } - return s.listener.Addr() -} - -// MonitorAddr will return the net.Addr object for the monitoring listener. -func (s *Server) MonitorAddr() *net.TCPAddr { - s.mu.Lock() - defer s.mu.Unlock() - if s.http == nil { - return nil - } - return s.http.Addr().(*net.TCPAddr) -} - -// ClusterAddr returns the net.Addr object for the route listener. -func (s *Server) ClusterAddr() *net.TCPAddr { - s.mu.Lock() - defer s.mu.Unlock() - if s.routeListener == nil { - return nil - } - return s.routeListener.Addr().(*net.TCPAddr) -} - -// ProfilerAddr returns the net.Addr object for the route listener. -func (s *Server) ProfilerAddr() *net.TCPAddr { - s.mu.Lock() - defer s.mu.Unlock() - if s.profiler == nil { - return nil - } - return s.profiler.Addr().(*net.TCPAddr) -} - -// ReadyForConnections returns `true` if the server is ready to accept client -// and, if routing is enabled, route connections. If after the duration -// `dur` the server is still not ready, returns `false`. -func (s *Server) ReadyForConnections(dur time.Duration) bool { - // Snapshot server options. - opts := s.getOpts() - - end := time.Now().Add(dur) - for time.Now().Before(end) { - s.mu.Lock() - ok := s.listener != nil && (opts.Cluster.Port == 0 || s.routeListener != nil) - s.mu.Unlock() - if ok { - return true - } - time.Sleep(25 * time.Millisecond) - } - return false -} - -// ID returns the server's ID -func (s *Server) ID() string { - s.mu.Lock() - defer s.mu.Unlock() - return s.info.ID -} - -func (s *Server) startGoRoutine(f func()) { - s.grMu.Lock() - if s.grRunning { - s.grWG.Add(1) - go f() - } - s.grMu.Unlock() -} - -func (s *Server) numClosedConns() int { - s.mu.Lock() - defer s.mu.Unlock() - return s.closed.len() -} - -func (s *Server) totalClosedConns() uint64 { - s.mu.Lock() - defer s.mu.Unlock() - return s.closed.totalConns() -} - -func (s *Server) closedClients() []*closedClient { - s.mu.Lock() - defer s.mu.Unlock() - return s.closed.closedClients() -} - -// getClientConnectURLs returns suitable URLs for clients to connect to the listen -// port based on the server options' Host and Port. If the Host corresponds to -// "any" interfaces, this call returns the list of resolved IP addresses. -// If ClientAdvertise is set, returns the client advertise host and port. -// The server lock is assumed held on entry. -func (s *Server) getClientConnectURLs() []string { - // Snapshot server options. - opts := s.getOpts() - - urls := make([]string, 0, 1) - - // short circuit if client advertise is set - if opts.ClientAdvertise != "" { - // just use the info host/port. This is updated in s.New() - urls = append(urls, net.JoinHostPort(s.info.Host, strconv.Itoa(s.info.Port))) - } else { - sPort := strconv.Itoa(opts.Port) - ipAddr, err := net.ResolveIPAddr("ip", opts.Host) - // If the host is "any" (0.0.0.0 or ::), get specific IPs from available - // interfaces. - if err == nil && ipAddr.IP.IsUnspecified() { - var ip net.IP - ifaces, _ := net.Interfaces() - for _, i := range ifaces { - addrs, _ := i.Addrs() - for _, addr := range addrs { - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - // Skip non global unicast addresses - if !ip.IsGlobalUnicast() || ip.IsUnspecified() { - ip = nil - continue - } - urls = append(urls, net.JoinHostPort(ip.String(), sPort)) - } - } - } - if err != nil || len(urls) == 0 { - // We are here if s.opts.Host is not "0.0.0.0" nor "::", or if for some - // reason we could not add any URL in the loop above. - // We had a case where a Windows VM was hosed and would have err == nil - // and not add any address in the array in the loop above, and we - // ended-up returning 0.0.0.0, which is problematic for Windows clients. - // Check for 0.0.0.0 or :: specifically, and ignore if that's the case. - if opts.Host == "0.0.0.0" || opts.Host == "::" { - s.Errorf("Address %q can not be resolved properly", opts.Host) - } else { - urls = append(urls, net.JoinHostPort(opts.Host, sPort)) - } - } - } - - return urls -} - -// if the ip is not specified, attempt to resolve it -func resolveHostPorts(addr net.Listener) []string { - hostPorts := make([]string, 0) - hp := addr.Addr().(*net.TCPAddr) - port := strconv.Itoa(hp.Port) - if hp.IP.IsUnspecified() { - var ip net.IP - ifaces, _ := net.Interfaces() - for _, i := range ifaces { - addrs, _ := i.Addrs() - for _, addr := range addrs { - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - hostPorts = append(hostPorts, net.JoinHostPort(ip.String(), port)) - case *net.IPAddr: - ip = v.IP - hostPorts = append(hostPorts, net.JoinHostPort(ip.String(), port)) - default: - continue - } - } - } - } else { - hostPorts = append(hostPorts, net.JoinHostPort(hp.IP.String(), port)) - } - return hostPorts -} - -// format the address of a net.Listener with a protocol -func formatURL(protocol string, addr net.Listener) []string { - hostports := resolveHostPorts(addr) - for i, hp := range hostports { - hostports[i] = fmt.Sprintf("%s://%s", protocol, hp) - } - return hostports -} - -// Ports describes URLs that the server can be contacted in -type Ports struct { - Nats []string `json:"nats,omitempty"` - Monitoring []string `json:"monitoring,omitempty"` - Cluster []string `json:"cluster,omitempty"` - Profile []string `json:"profile,omitempty"` -} - -// Attempts to resolve all the ports. If after maxWait the ports are not -// resolved, it returns nil. Otherwise it returns a Ports struct -// describing ports where the server can be contacted -func (s *Server) PortsInfo(maxWait time.Duration) *Ports { - if s.readyForListeners(maxWait) { - opts := s.getOpts() - - s.mu.Lock() - info := s.copyInfo() - listener := s.listener - httpListener := s.http - clusterListener := s.routeListener - profileListener := s.profiler - s.mu.Unlock() - - ports := Ports{} - - if listener != nil { - natsProto := "nats" - if info.TLSRequired { - natsProto = "tls" - } - ports.Nats = formatURL(natsProto, listener) - } - - if httpListener != nil { - monProto := "http" - if opts.HTTPSPort != 0 { - monProto = "https" - } - ports.Monitoring = formatURL(monProto, httpListener) - } - - if clusterListener != nil { - clusterProto := "nats" - if opts.Cluster.TLSConfig != nil { - clusterProto = "tls" - } - ports.Cluster = formatURL(clusterProto, clusterListener) - } - - if profileListener != nil { - ports.Profile = formatURL("http", profileListener) - } - - return &ports - } - - return nil -} - -// Returns the portsFile. If a non-empty dirHint is provided, the dirHint -// path is used instead of the server option value -func (s *Server) portFile(dirHint string) string { - dirname := s.getOpts().PortsFileDir - if dirHint != "" { - dirname = dirHint - } - if dirname == _EMPTY_ { - return _EMPTY_ - } - return path.Join(dirname, fmt.Sprintf("%s_%d.ports", path.Base(os.Args[0]), os.Getpid())) -} - -// Delete the ports file. If a non-empty dirHint is provided, the dirHint -// path is used instead of the server option value -func (s *Server) deletePortsFile(hintDir string) { - portsFile := s.portFile(hintDir) - if portsFile != "" { - if err := os.Remove(portsFile); err != nil { - s.Errorf("Error cleaning up ports file %s: %v", portsFile, err) - } - } -} - -// Writes a file with a serialized Ports to the specified ports_file_dir. -// The name of the file is `exename_pid.ports`, typically gnatsd_pid.ports. -// if ports file is not set, this function has no effect -func (s *Server) logPorts() { - opts := s.getOpts() - portsFile := s.portFile(opts.PortsFileDir) - if portsFile != _EMPTY_ { - go func() { - info := s.PortsInfo(5 * time.Second) - if info == nil { - s.Errorf("Unable to resolve the ports in the specified time") - return - } - data, err := json.Marshal(info) - if err != nil { - s.Errorf("Error marshaling ports file: %v", err) - return - } - if err := ioutil.WriteFile(portsFile, data, 0666); err != nil { - s.Errorf("Error writing ports file (%s): %v", portsFile, err) - return - } - - }() - } -} - -// waits until a calculated list of listeners is resolved or a timeout -func (s *Server) readyForListeners(dur time.Duration) bool { - end := time.Now().Add(dur) - for time.Now().Before(end) { - s.mu.Lock() - listeners := s.serviceListeners() - s.mu.Unlock() - if len(listeners) == 0 { - return false - } - - ok := true - for _, l := range listeners { - if l == nil { - ok = false - break - } - } - if ok { - return true - } - select { - case <-s.quitCh: - return false - case <-time.After(25 * time.Millisecond): - // continue - unable to select from quit - we are still running - } - } - return false -} - -// returns a list of listeners that are intended for the process -// if the entry is nil, the interface is yet to be resolved -func (s *Server) serviceListeners() []net.Listener { - listeners := make([]net.Listener, 0) - opts := s.getOpts() - listeners = append(listeners, s.listener) - if opts.Cluster.Port != 0 { - listeners = append(listeners, s.routeListener) - } - if opts.HTTPPort != 0 || opts.HTTPSPort != 0 { - listeners = append(listeners, s.http) - } - if opts.ProfPort != 0 { - listeners = append(listeners, s.profiler) - } - return listeners -} diff --git a/vendor/github.com/nats-io/gnatsd/server/server_test.go b/vendor/github.com/nats-io/gnatsd/server/server_test.go deleted file mode 100644 index 20b8dbe2..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/server_test.go +++ /dev/null @@ -1,700 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "flag" - "fmt" - "net" - "os" - "strings" - "testing" - "time" - - "github.com/nats-io/go-nats" -) - -func checkFor(t *testing.T, totalWait, sleepDur time.Duration, f func() error) { - t.Helper() - timeout := time.Now().Add(totalWait) - var err error - for time.Now().Before(timeout) { - err = f() - if err == nil { - return - } - time.Sleep(sleepDur) - } - if err != nil { - t.Fatal(err.Error()) - } -} - -func DefaultOptions() *Options { - return &Options{ - Host: "127.0.0.1", - Port: -1, - HTTPPort: -1, - Cluster: ClusterOpts{Port: -1}, - NoLog: true, - NoSigs: true, - Debug: true, - Trace: true, - } -} - -// New Go Routine based server -func RunServer(opts *Options) *Server { - if opts == nil { - opts = DefaultOptions() - } - s := New(opts) - - if s == nil { - panic("No NATS Server object returned.") - } - - if !opts.NoLog { - s.ConfigureLogger() - } - - // Run server in Go routine. - go s.Start() - - // Wait for accept loop(s) to be started - if !s.ReadyForConnections(10 * time.Second) { - panic("Unable to start NATS Server in Go Routine") - } - return s -} - -// LoadConfig loads a configuration from a filename -func LoadConfig(configFile string) (opts *Options) { - opts, err := ProcessConfigFile(configFile) - if err != nil { - panic(fmt.Sprintf("Error processing configuration file: %v", err)) - } - opts.NoSigs, opts.NoLog = true, true - return -} - -// RunServerWithConfig starts a new Go routine based server with a configuration file. -func RunServerWithConfig(configFile string) (srv *Server, opts *Options) { - opts = LoadConfig(configFile) - srv = RunServer(opts) - return -} - -func TestVersionMatchesTag(t *testing.T) { - tag := os.Getenv("TRAVIS_TAG") - if tag == "" { - t.SkipNow() - } - // We expect a tag of the form vX.Y.Z. If that's not the case, - // we need someone to have a look. So fail if first letter is not - // a `v` - if tag[0] != 'v' { - t.Fatalf("Expect tag to start with `v`, tag is: %s", tag) - } - // Strip the `v` from the tag for the version comparison. - if VERSION != tag[1:] { - t.Fatalf("Version (%s) does not match tag (%s)", VERSION, tag[1:]) - } -} - -func TestStartProfiler(t *testing.T) { - s := New(DefaultOptions()) - s.StartProfiler() - s.mu.Lock() - s.profiler.Close() - s.mu.Unlock() -} - -func TestStartupAndShutdown(t *testing.T) { - - opts := DefaultOptions() - - s := RunServer(opts) - defer s.Shutdown() - - if !s.isRunning() { - t.Fatal("Could not run server") - } - - // Debug stuff. - numRoutes := s.NumRoutes() - if numRoutes != 0 { - t.Fatalf("Expected numRoutes to be 0 vs %d\n", numRoutes) - } - - numRemotes := s.NumRemotes() - if numRemotes != 0 { - t.Fatalf("Expected numRemotes to be 0 vs %d\n", numRemotes) - } - - numClients := s.NumClients() - if numClients != 0 && numClients != 1 { - t.Fatalf("Expected numClients to be 1 or 0 vs %d\n", numClients) - } - - numSubscriptions := s.NumSubscriptions() - if numSubscriptions != 0 { - t.Fatalf("Expected numSubscriptions to be 0 vs %d\n", numSubscriptions) - } -} - -func TestTlsCipher(t *testing.T) { - if strings.Compare(tlsCipher(0x0005), "TLS_RSA_WITH_RC4_128_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0x000a), "TLS_RSA_WITH_3DES_EDE_CBC_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0x002f), "TLS_RSA_WITH_AES_128_CBC_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0x0035), "TLS_RSA_WITH_AES_256_CBC_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc007), "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc009), "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc00a), "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc011), "TLS_ECDHE_RSA_WITH_RC4_128_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc012), "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc013), "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc014), "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA") != 0 { - t.Fatalf("IUnknownnvalid tls cipher") - } - if strings.Compare(tlsCipher(0xc02f), "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc02b), "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc030), "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384") != 0 { - t.Fatalf("Invalid tls cipher") - } - if strings.Compare(tlsCipher(0xc02c), "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384") != 0 { - t.Fatalf("Invalid tls cipher") - } - if !strings.Contains(tlsCipher(0x9999), "Unknown") { - t.Fatalf("Expected an unknown cipher.") - } -} - -func TestGetConnectURLs(t *testing.T) { - opts := DefaultOptions() - opts.Port = 4222 - - var globalIP net.IP - - checkGlobalConnectURLs := func() { - s := New(opts) - defer s.Shutdown() - - s.mu.Lock() - urls := s.getClientConnectURLs() - s.mu.Unlock() - if len(urls) == 0 { - t.Fatalf("Expected to get a list of urls, got none for listen addr: %v", opts.Host) - } - for _, u := range urls { - tcpaddr, err := net.ResolveTCPAddr("tcp", u) - if err != nil { - t.Fatalf("Error resolving: %v", err) - } - ip := tcpaddr.IP - if !ip.IsGlobalUnicast() { - t.Fatalf("IP %v is not global", ip.String()) - } - if ip.IsUnspecified() { - t.Fatalf("IP %v is unspecified", ip.String()) - } - addr := strings.TrimSuffix(u, ":4222") - if addr == opts.Host { - t.Fatalf("Returned url is not right: %v", u) - } - if globalIP == nil { - globalIP = ip - } - } - } - - listenAddrs := []string{"0.0.0.0", "::"} - for _, listenAddr := range listenAddrs { - opts.Host = listenAddr - checkGlobalConnectURLs() - } - - checkConnectURLsHasOnlyOne := func() { - s := New(opts) - defer s.Shutdown() - - s.mu.Lock() - urls := s.getClientConnectURLs() - s.mu.Unlock() - if len(urls) != 1 { - t.Fatalf("Expected one URL, got %v", urls) - } - tcpaddr, err := net.ResolveTCPAddr("tcp", urls[0]) - if err != nil { - t.Fatalf("Error resolving: %v", err) - } - ip := tcpaddr.IP - if ip.String() != opts.Host { - t.Fatalf("Expected connect URL to be %v, got %v", opts.Host, ip.String()) - } - } - - singleConnectReturned := []string{"127.0.0.1", "::1"} - if globalIP != nil { - singleConnectReturned = append(singleConnectReturned, globalIP.String()) - } - for _, listenAddr := range singleConnectReturned { - opts.Host = listenAddr - checkConnectURLsHasOnlyOne() - } -} - -func TestClientAdvertiseConnectURL(t *testing.T) { - opts := DefaultOptions() - opts.Port = 4222 - opts.ClientAdvertise = "nats.example.com" - s := New(opts) - defer s.Shutdown() - - s.mu.Lock() - urls := s.getClientConnectURLs() - s.mu.Unlock() - if len(urls) != 1 { - t.Fatalf("Expected to get one url, got none: %v with ClientAdvertise %v", - opts.Host, opts.ClientAdvertise) - } - if urls[0] != "nats.example.com:4222" { - t.Fatalf("Expected to get '%s', got: '%v'", "nats.example.com:4222", urls[0]) - } - s.Shutdown() - - opts.ClientAdvertise = "nats.example.com:7777" - s = New(opts) - s.mu.Lock() - urls = s.getClientConnectURLs() - s.mu.Unlock() - if len(urls) != 1 { - t.Fatalf("Expected to get one url, got none: %v with ClientAdvertise %v", - opts.Host, opts.ClientAdvertise) - } - if urls[0] != "nats.example.com:7777" { - t.Fatalf("Expected 'nats.example.com:7777', got: '%v'", urls[0]) - } - if s.info.Host != "nats.example.com" { - t.Fatalf("Expected host to be set to nats.example.com") - } - if s.info.Port != 7777 { - t.Fatalf("Expected port to be set to 7777") - } - s.Shutdown() - - opts = DefaultOptions() - opts.Port = 0 - opts.ClientAdvertise = "nats.example.com:7777" - s = New(opts) - if s.info.Host != "nats.example.com" && s.info.Port != 7777 { - t.Fatalf("Expected Client Advertise Host:Port to be nats.example.com:7777, got: %s:%d", - s.info.Host, s.info.Port) - } - s.Shutdown() -} - -func TestClientAdvertiseErrorOnStartup(t *testing.T) { - opts := DefaultOptions() - // Set invalid address - opts.ClientAdvertise = "addr:::123" - s := New(opts) - defer s.Shutdown() - dl := &DummyLogger{} - s.SetLogger(dl, false, false) - - // Expect this to return due to failure - s.Start() - dl.Lock() - msg := dl.msg - dl.Unlock() - if !strings.Contains(msg, "ClientAdvertise") { - t.Fatalf("Unexpected error: %v", msg) - } -} - -func TestNoDeadlockOnStartFailure(t *testing.T) { - opts := DefaultOptions() - opts.Host = "x.x.x.x" // bad host - opts.Port = 4222 - opts.HTTPHost = opts.Host - opts.Cluster.Host = "127.0.0.1" - opts.Cluster.Port = -1 - opts.ProfPort = -1 - s := New(opts) - - // This should return since it should fail to start a listener - // on x.x.x.x:4222 - s.Start() - - // We should be able to shutdown - s.Shutdown() -} - -func TestMaxConnections(t *testing.T) { - opts := DefaultOptions() - opts.MaxConn = 1 - s := RunServer(opts) - defer s.Shutdown() - - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc.Close() - - nc2, err := nats.Connect(addr) - if err == nil { - nc2.Close() - t.Fatal("Expected connection to fail") - } -} - -func TestMaxSubscriptions(t *testing.T) { - opts := DefaultOptions() - opts.MaxSubs = 10 - s := RunServer(opts) - defer s.Shutdown() - - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(addr) - if err != nil { - t.Fatalf("Error creating client: %v\n", err) - } - defer nc.Close() - - for i := 0; i < 10; i++ { - _, err := nc.Subscribe(fmt.Sprintf("foo.%d", i), func(*nats.Msg) {}) - if err != nil { - t.Fatalf("Error subscribing: %v\n", err) - } - } - // This should cause the error. - nc.Subscribe("foo.22", func(*nats.Msg) {}) - nc.Flush() - if err := nc.LastError(); err == nil { - t.Fatal("Expected an error but got none\n") - } -} - -func TestProcessCommandLineArgs(t *testing.T) { - var host string - var port int - cmd := flag.NewFlagSet("gnatsd", flag.ExitOnError) - cmd.StringVar(&host, "a", "0.0.0.0", "Host.") - cmd.IntVar(&port, "p", 4222, "Port.") - - cmd.Parse([]string{"-a", "127.0.0.1", "-p", "9090"}) - showVersion, showHelp, err := ProcessCommandLineArgs(cmd) - if err != nil { - t.Errorf("Expected no errors, got: %s", err) - } - if showVersion || showHelp { - t.Errorf("Expected not having to handle subcommands") - } - - cmd.Parse([]string{"version"}) - showVersion, showHelp, err = ProcessCommandLineArgs(cmd) - if err != nil { - t.Errorf("Expected no errors, got: %s", err) - } - if !showVersion { - t.Errorf("Expected having to handle version command") - } - if showHelp { - t.Errorf("Expected not having to handle help command") - } - - cmd.Parse([]string{"help"}) - showVersion, showHelp, err = ProcessCommandLineArgs(cmd) - if err != nil { - t.Errorf("Expected no errors, got: %s", err) - } - if showVersion { - t.Errorf("Expected not having to handle version command") - } - if !showHelp { - t.Errorf("Expected having to handle help command") - } - - cmd.Parse([]string{"foo", "-p", "9090"}) - _, _, err = ProcessCommandLineArgs(cmd) - if err == nil { - t.Errorf("Expected an error handling the command arguments") - } -} - -func TestWriteDeadline(t *testing.T) { - opts := DefaultOptions() - opts.WriteDeadline = 30 * time.Millisecond - s := RunServer(opts) - defer s.Shutdown() - - c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", opts.Host, opts.Port), 3*time.Second) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer c.Close() - if _, err := c.Write([]byte("CONNECT {}\r\nPING\r\nSUB foo 1\r\n")); err != nil { - t.Fatalf("Error sending protocols to server: %v", err) - } - // Reduce socket buffer to increase reliability of getting - // write deadline errors. - c.(*net.TCPConn).SetReadBuffer(4) - - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - sender, err := nats.Connect(url) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer sender.Close() - - payload := make([]byte, 1000000) - for i := 0; i < 10; i++ { - if err := sender.Publish("foo", payload); err != nil { - t.Fatalf("Error on publish: %v", err) - } - } - // Flush sender connection to ensure that all data has been sent. - if err := sender.Flush(); err != nil { - t.Fatalf("Error on flush: %v", err) - } - - // At this point server should have closed connection c. - - // On certain platforms, it may take more than one call before - // getting the error. - for i := 0; i < 100; i++ { - if _, err := c.Write([]byte("PUB bar 5\r\nhello\r\n")); err != nil { - // ok - return - } - } - t.Fatal("Connection should have been closed") -} - -func TestSlowConsumerPendingBytes(t *testing.T) { - opts := DefaultOptions() - opts.WriteDeadline = 30 * time.Second // Wait for long time so write deadline does not trigger slow consumer. - opts.MaxPending = 1 * 1024 * 1024 // Set to low value (1MB) to allow SC to trip. - s := RunServer(opts) - defer s.Shutdown() - - c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", opts.Host, opts.Port), 3*time.Second) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer c.Close() - if _, err := c.Write([]byte("CONNECT {}\r\nPING\r\nSUB foo 1\r\n")); err != nil { - t.Fatalf("Error sending protocols to server: %v", err) - } - // Reduce socket buffer to increase reliability of data backing up in the server destined - // for our subscribed client. - c.(*net.TCPConn).SetReadBuffer(128) - - url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - sender, err := nats.Connect(url) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer sender.Close() - - payload := make([]byte, 1024*1024) - for i := 0; i < 100; i++ { - if err := sender.Publish("foo", payload); err != nil { - t.Fatalf("Error on publish: %v", err) - } - } - - // Flush sender connection to ensure that all data has been sent. - if err := sender.Flush(); err != nil { - t.Fatalf("Error on flush: %v", err) - } - - // At this point server should have closed connection c. - - // On certain platforms, it may take more than one call before - // getting the error. - for i := 0; i < 100; i++ { - if _, err := c.Write([]byte("PUB bar 5\r\nhello\r\n")); err != nil { - // ok - return - } - } - t.Fatal("Connection should have been closed") -} - -func TestRandomPorts(t *testing.T) { - opts := DefaultOptions() - opts.HTTPPort = -1 - opts.Port = -1 - s := RunServer(opts) - - defer s.Shutdown() - - if s.Addr() == nil || s.Addr().(*net.TCPAddr).Port <= 0 { - t.Fatal("Should have dynamically assigned server port.") - } - - if s.Addr() == nil || s.Addr().(*net.TCPAddr).Port == 4222 { - t.Fatal("Should not have dynamically assigned default port: 4222.") - } - - if s.MonitorAddr() == nil || s.MonitorAddr().Port <= 0 { - t.Fatal("Should have dynamically assigned monitoring port.") - } - -} - -func TestNilMonitoringPort(t *testing.T) { - opts := DefaultOptions() - opts.HTTPPort = 0 - opts.HTTPSPort = 0 - s := RunServer(opts) - - defer s.Shutdown() - - if s.MonitorAddr() != nil { - t.Fatal("HttpAddr should be nil.") - } -} - -type DummyAuth struct{} - -func (d *DummyAuth) Check(c ClientAuthentication) bool { - return c.GetOpts().Username == "valid" -} - -func TestCustomClientAuthentication(t *testing.T) { - var clientAuth DummyAuth - - opts := DefaultOptions() - opts.CustomClientAuthentication = &clientAuth - - s := RunServer(opts) - - defer s.Shutdown() - - addr := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port) - - nc, err := nats.Connect(addr, nats.UserInfo("valid", "")) - if err != nil { - t.Fatalf("Expected client to connect, got: %s", err) - } - nc.Close() - if _, err := nats.Connect(addr, nats.UserInfo("invalid", "")); err == nil { - t.Fatal("Expected client to fail to connect") - } -} - -func TestCustomRouterAuthentication(t *testing.T) { - opts := DefaultOptions() - opts.CustomRouterAuthentication = &DummyAuth{} - opts.Cluster.Host = "127.0.0.1" - s := RunServer(opts) - defer s.Shutdown() - clusterPort := s.ClusterAddr().Port - - opts2 := DefaultOptions() - opts2.Cluster.Host = "127.0.0.1" - opts2.Routes = RoutesFromStr(fmt.Sprintf("nats://invalid@127.0.0.1:%d", clusterPort)) - s2 := RunServer(opts2) - defer s2.Shutdown() - - // s2 will attempt to connect to s, which should reject. - // Keep in mind that s2 will try again... - time.Sleep(50 * time.Millisecond) - checkNumRoutes(t, s2, 0) - - opts3 := DefaultOptions() - opts3.Cluster.Host = "127.0.0.1" - opts3.Routes = RoutesFromStr(fmt.Sprintf("nats://valid@127.0.0.1:%d", clusterPort)) - s3 := RunServer(opts3) - defer s3.Shutdown() - checkClusterFormed(t, s, s3) - checkNumRoutes(t, s3, 1) -} - -func TestMonitoringNoTimeout(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - s.mu.Lock() - srv := s.monitoringServer - s.mu.Unlock() - - if srv == nil { - t.Fatalf("Monitoring server not set") - } - if srv.ReadTimeout != 0 { - t.Fatalf("ReadTimeout should not be set, was set to %v", srv.ReadTimeout) - } - if srv.WriteTimeout != 0 { - t.Fatalf("WriteTimeout should not be set, was set to %v", srv.WriteTimeout) - } -} - -func TestProfilingNoTimeout(t *testing.T) { - opts := DefaultOptions() - opts.ProfPort = -1 - s := RunServer(opts) - defer s.Shutdown() - - paddr := s.ProfilerAddr() - if paddr == nil { - t.Fatalf("Profiler not started") - } - pport := paddr.Port - if pport <= 0 { - t.Fatalf("Expected profiler port to be set, got %v", pport) - } - s.mu.Lock() - srv := s.profilingServer - s.mu.Unlock() - - if srv == nil { - t.Fatalf("Profiling server not set") - } - if srv.ReadTimeout != 0 { - t.Fatalf("ReadTimeout should not be set, was set to %v", srv.ReadTimeout) - } - if srv.WriteTimeout != 0 { - t.Fatalf("WriteTimeout should not be set, was set to %v", srv.WriteTimeout) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/service.go b/vendor/github.com/nats-io/gnatsd/server/service.go deleted file mode 100644 index a44cbac3..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/service.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -package server - -// Run starts the NATS server. This wrapper function allows Windows to add a -// hook for running NATS as a service. -func Run(server *Server) error { - server.Start() - return nil -} - -// isWindowsService indicates if NATS is running as a Windows service. -func isWindowsService() bool { - return false -} diff --git a/vendor/github.com/nats-io/gnatsd/server/service_test.go b/vendor/github.com/nats-io/gnatsd/server/service_test.go deleted file mode 100644 index 8811c185..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/service_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -package server - -import ( - "errors" - "testing" - "time" -) - -func TestRun(t *testing.T) { - var ( - s = New(DefaultOptions()) - started = make(chan error, 1) - errC = make(chan error, 1) - ) - go func() { - errC <- Run(s) - }() - go func() { - if !s.ReadyForConnections(time.Second) { - started <- errors.New("failed to start in time") - return - } - s.Shutdown() - close(started) - }() - - select { - case err := <-errC: - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - case <-time.After(2 * time.Second): - t.Fatal("Timed out") - } - if err := <-started; err != nil { - t.Fatalf("Unexpected error: %v", err) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/service_windows.go b/vendor/github.com/nats-io/gnatsd/server/service_windows.go deleted file mode 100644 index 43cc2b5c..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/service_windows.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "os" - "time" - - "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/debug" -) - -const ( - serviceName = "gnatsd" - reopenLogCode = 128 - reopenLogCmd = svc.Cmd(reopenLogCode) - acceptReopenLog = svc.Accepted(reopenLogCode) -) - -// winServiceWrapper implements the svc.Handler interface for implementing -// gnatsd as a Windows service. -type winServiceWrapper struct { - server *Server -} - -var dockerized = false - -func init() { - if v, exists := os.LookupEnv("NATS_DOCKERIZED"); exists && v == "1" { - dockerized = true - } -} - -// Execute will be called by the package code at the start of -// the service, and the service will exit once Execute completes. -// Inside Execute you must read service change requests from r and -// act accordingly. You must keep service control manager up to date -// about state of your service by writing into s as required. -// args contains service name followed by argument strings passed -// to the service. -// You can provide service exit code in exitCode return parameter, -// with 0 being "no error". You can also indicate if exit code, -// if any, is service specific or not by using svcSpecificEC -// parameter. -func (w *winServiceWrapper) Execute(args []string, changes <-chan svc.ChangeRequest, - status chan<- svc.Status) (bool, uint32) { - - status <- svc.Status{State: svc.StartPending} - go w.server.Start() - - // Wait for accept loop(s) to be started - if !w.server.ReadyForConnections(10 * time.Second) { - // Failed to start. - return false, 1 - } - - status <- svc.Status{ - State: svc.Running, - Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.AcceptParamChange | acceptReopenLog, - } - -loop: - for change := range changes { - switch change.Cmd { - case svc.Interrogate: - status <- change.CurrentStatus - case svc.Stop, svc.Shutdown: - w.server.Shutdown() - break loop - case reopenLogCmd: - // File log re-open for rotating file logs. - w.server.ReOpenLogFile() - case svc.ParamChange: - if err := w.server.Reload(); err != nil { - w.server.Errorf("Failed to reload server configuration: %s", err) - } - default: - w.server.Debugf("Unexpected control request: %v", change.Cmd) - } - } - - status <- svc.Status{State: svc.StopPending} - return false, 0 -} - -// Run starts the NATS server as a Windows service. -func Run(server *Server) error { - if dockerized { - server.Start() - return nil - } - run := svc.Run - isInteractive, err := svc.IsAnInteractiveSession() - if err != nil { - return err - } - if isInteractive { - run = debug.Run - } - return run(serviceName, &winServiceWrapper{server}) -} - -// isWindowsService indicates if NATS is running as a Windows service. -func isWindowsService() bool { - if dockerized { - return false - } - isInteractive, _ := svc.IsAnInteractiveSession() - return !isInteractive -} diff --git a/vendor/github.com/nats-io/gnatsd/server/signal.go b/vendor/github.com/nats-io/gnatsd/server/signal.go deleted file mode 100644 index 6a432f7c..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/signal.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -package server - -import ( - "errors" - "fmt" - "os" - "os/exec" - "os/signal" - "strconv" - "strings" - "syscall" -) - -const processName = "gnatsd" - -// Signal Handling -func (s *Server) handleSignals() { - if s.getOpts().NoSigs { - return - } - c := make(chan os.Signal, 1) - - signal.Notify(c, syscall.SIGINT, syscall.SIGUSR1, syscall.SIGHUP) - - s.grWG.Add(1) - go func() { - defer s.grWG.Done() - for { - select { - case sig := <-c: - s.Debugf("Trapped %q signal", sig) - switch sig { - case syscall.SIGINT: - s.Noticef("Server Exiting..") - os.Exit(0) - case syscall.SIGUSR1: - // File log re-open for rotating file logs. - s.ReOpenLogFile() - case syscall.SIGHUP: - // Config reload. - if err := s.Reload(); err != nil { - s.Errorf("Failed to reload server configuration: %s", err) - } - } - case <-s.quitCh: - return - } - } - }() -} - -// ProcessSignal sends the given signal command to the given process. If pidStr -// is empty, this will send the signal to the single running instance of -// gnatsd. If multiple instances are running, it returns an error. This returns -// an error if the given process is not running or the command is invalid. -func ProcessSignal(command Command, pidStr string) error { - var pid int - if pidStr == "" { - pids, err := resolvePids() - if err != nil { - return err - } - if len(pids) == 0 { - return errors.New("no gnatsd processes running") - } - if len(pids) > 1 { - errStr := "multiple gnatsd processes running:\n" - prefix := "" - for _, p := range pids { - errStr += fmt.Sprintf("%s%d", prefix, p) - prefix = "\n" - } - return errors.New(errStr) - } - pid = pids[0] - } else { - p, err := strconv.Atoi(pidStr) - if err != nil { - return fmt.Errorf("invalid pid: %s", pidStr) - } - pid = p - } - - var err error - switch command { - case CommandStop: - err = kill(pid, syscall.SIGKILL) - case CommandQuit: - err = kill(pid, syscall.SIGINT) - case CommandReopen: - err = kill(pid, syscall.SIGUSR1) - case CommandReload: - err = kill(pid, syscall.SIGHUP) - default: - err = fmt.Errorf("unknown signal %q", command) - } - return err -} - -// resolvePids returns the pids for all running gnatsd processes. -func resolvePids() ([]int, error) { - // If pgrep isn't available, this will just bail out and the user will be - // required to specify a pid. - output, err := pgrep() - if err != nil { - switch err.(type) { - case *exec.ExitError: - // ExitError indicates non-zero exit code, meaning no processes - // found. - break - default: - return nil, errors.New("unable to resolve pid, try providing one") - } - } - var ( - myPid = os.Getpid() - pidStrs = strings.Split(string(output), "\n") - pids = make([]int, 0, len(pidStrs)) - ) - for _, pidStr := range pidStrs { - if pidStr == "" { - continue - } - pid, err := strconv.Atoi(pidStr) - if err != nil { - return nil, errors.New("unable to resolve pid, try providing one") - } - // Ignore the current process. - if pid == myPid { - continue - } - pids = append(pids, pid) - } - return pids, nil -} - -var kill = func(pid int, signal syscall.Signal) error { - return syscall.Kill(pid, signal) -} - -var pgrep = func() ([]byte, error) { - return exec.Command("pgrep", processName).Output() -} diff --git a/vendor/github.com/nats-io/gnatsd/server/signal_test.go b/vendor/github.com/nats-io/gnatsd/server/signal_test.go deleted file mode 100644 index 3fa27559..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/signal_test.go +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -package server - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "os/exec" - "strings" - "syscall" - "testing" - "time" - - "github.com/nats-io/gnatsd/logger" -) - -func TestSignalToReOpenLogFile(t *testing.T) { - logFile := "test.log" - defer os.Remove(logFile) - defer os.Remove(logFile + ".bak") - opts := &Options{ - Host: "127.0.0.1", - Port: -1, - NoSigs: false, - LogFile: logFile, - } - s := RunServer(opts) - defer s.SetLogger(nil, false, false) - defer s.Shutdown() - - // Set the file log - fileLog := logger.NewFileLogger(s.opts.LogFile, s.opts.Logtime, s.opts.Debug, s.opts.Trace, true) - s.SetLogger(fileLog, false, false) - - // Add a trace - expectedStr := "This is a Notice" - s.Noticef(expectedStr) - buf, err := ioutil.ReadFile(logFile) - if err != nil { - t.Fatalf("Error reading file: %v", err) - } - if !strings.Contains(string(buf), expectedStr) { - t.Fatalf("Expected log to contain %q, got %q", expectedStr, string(buf)) - } - // Rename the file - if err := os.Rename(logFile, logFile+".bak"); err != nil { - t.Fatalf("Unable to rename file: %v", err) - } - // This should cause file to be reopened. - syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) - // Wait a bit for action to be performed - time.Sleep(500 * time.Millisecond) - buf, err = ioutil.ReadFile(logFile) - if err != nil { - t.Fatalf("Error reading file: %v", err) - } - expectedStr = "File log re-opened" - if !strings.Contains(string(buf), expectedStr) { - t.Fatalf("Expected log to contain %q, got %q", expectedStr, string(buf)) - } -} - -func TestSignalToReloadConfig(t *testing.T) { - opts, err := ProcessConfigFile("./configs/reload/basic.conf") - if err != nil { - t.Fatalf("Error processing config file: %v", err) - } - opts.NoLog = true - s := RunServer(opts) - defer s.Shutdown() - - // Repeat test to make sure that server services signals more than once... - for i := 0; i < 2; i++ { - loaded := s.ConfigTime() - - // Wait a bit to ensure ConfigTime changes. - time.Sleep(5 * time.Millisecond) - - // This should cause config to be reloaded. - syscall.Kill(syscall.Getpid(), syscall.SIGHUP) - // Wait a bit for action to be performed - time.Sleep(500 * time.Millisecond) - - if reloaded := s.ConfigTime(); !reloaded.After(loaded) { - t.Fatalf("ConfigTime is incorrect.\nexpected greater than: %s\ngot: %s", loaded, reloaded) - } - } -} - -func TestProcessSignalNoProcesses(t *testing.T) { - pgrepBefore := pgrep - pgrep = func() ([]byte, error) { - return nil, &exec.ExitError{} - } - defer func() { - pgrep = pgrepBefore - }() - - err := ProcessSignal(CommandStop, "") - if err == nil { - t.Fatal("Expected error") - } - expectedStr := "no gnatsd processes running" - if err.Error() != expectedStr { - t.Fatalf("Error is incorrect.\nexpected: %s\ngot: %s", expectedStr, err.Error()) - } -} - -func TestProcessSignalMultipleProcesses(t *testing.T) { - pid := os.Getpid() - pgrepBefore := pgrep - pgrep = func() ([]byte, error) { - return []byte(fmt.Sprintf("123\n456\n%d\n", pid)), nil - } - defer func() { - pgrep = pgrepBefore - }() - - err := ProcessSignal(CommandStop, "") - if err == nil { - t.Fatal("Expected error") - } - expectedStr := "multiple gnatsd processes running:\n123\n456" - if err.Error() != expectedStr { - t.Fatalf("Error is incorrect.\nexpected: %s\ngot: %s", expectedStr, err.Error()) - } -} - -func TestProcessSignalPgrepError(t *testing.T) { - pgrepBefore := pgrep - pgrep = func() ([]byte, error) { - return nil, errors.New("error") - } - defer func() { - pgrep = pgrepBefore - }() - - err := ProcessSignal(CommandStop, "") - if err == nil { - t.Fatal("Expected error") - } - expectedStr := "unable to resolve pid, try providing one" - if err.Error() != expectedStr { - t.Fatalf("Error is incorrect.\nexpected: %s\ngot: %s", expectedStr, err.Error()) - } -} - -func TestProcessSignalPgrepMangled(t *testing.T) { - pgrepBefore := pgrep - pgrep = func() ([]byte, error) { - return []byte("12x"), nil - } - defer func() { - pgrep = pgrepBefore - }() - - err := ProcessSignal(CommandStop, "") - if err == nil { - t.Fatal("Expected error") - } - expectedStr := "unable to resolve pid, try providing one" - if err.Error() != expectedStr { - t.Fatalf("Error is incorrect.\nexpected: %s\ngot: %s", expectedStr, err.Error()) - } -} - -func TestProcessSignalResolveSingleProcess(t *testing.T) { - pid := os.Getpid() - pgrepBefore := pgrep - pgrep = func() ([]byte, error) { - return []byte(fmt.Sprintf("123\n%d\n", pid)), nil - } - defer func() { - pgrep = pgrepBefore - }() - killBefore := kill - called := false - kill = func(pid int, signal syscall.Signal) error { - called = true - if pid != 123 { - t.Fatalf("pid is incorrect.\nexpected: 123\ngot: %d", pid) - } - if signal != syscall.SIGKILL { - t.Fatalf("signal is incorrect.\nexpected: killed\ngot: %v", signal) - } - return nil - } - defer func() { - kill = killBefore - }() - - if err := ProcessSignal(CommandStop, ""); err != nil { - t.Fatalf("ProcessSignal failed: %v", err) - } - - if !called { - t.Fatal("Expected kill to be called") - } -} - -func TestProcessSignalInvalidCommand(t *testing.T) { - err := ProcessSignal(Command("invalid"), "123") - if err == nil { - t.Fatal("Expected error") - } - expectedStr := "unknown signal \"invalid\"" - if err.Error() != expectedStr { - t.Fatalf("Error is incorrect.\nexpected: %s\ngot: %s", expectedStr, err.Error()) - } -} - -func TestProcessSignalInvalidPid(t *testing.T) { - err := ProcessSignal(CommandStop, "abc") - if err == nil { - t.Fatal("Expected error") - } - expectedStr := "invalid pid: abc" - if err.Error() != expectedStr { - t.Fatalf("Error is incorrect.\nexpected: %s\ngot: %s", expectedStr, err.Error()) - } -} - -func TestProcessSignalQuitProcess(t *testing.T) { - killBefore := kill - called := false - kill = func(pid int, signal syscall.Signal) error { - called = true - if pid != 123 { - t.Fatalf("pid is incorrect.\nexpected: 123\ngot: %d", pid) - } - if signal != syscall.SIGINT { - t.Fatalf("signal is incorrect.\nexpected: interrupt\ngot: %v", signal) - } - return nil - } - defer func() { - kill = killBefore - }() - - if err := ProcessSignal(CommandQuit, "123"); err != nil { - t.Fatalf("ProcessSignal failed: %v", err) - } - - if !called { - t.Fatal("Expected kill to be called") - } -} - -func TestProcessSignalReopenProcess(t *testing.T) { - killBefore := kill - called := false - kill = func(pid int, signal syscall.Signal) error { - called = true - if pid != 123 { - t.Fatalf("pid is incorrect.\nexpected: 123\ngot: %d", pid) - } - if signal != syscall.SIGUSR1 { - t.Fatalf("signal is incorrect.\nexpected: user defined signal 1\ngot: %v", signal) - } - return nil - } - defer func() { - kill = killBefore - }() - - if err := ProcessSignal(CommandReopen, "123"); err != nil { - t.Fatalf("ProcessSignal failed: %v", err) - } - - if !called { - t.Fatal("Expected kill to be called") - } -} - -func TestProcessSignalReloadProcess(t *testing.T) { - killBefore := kill - called := false - kill = func(pid int, signal syscall.Signal) error { - called = true - if pid != 123 { - t.Fatalf("pid is incorrect.\nexpected: 123\ngot: %d", pid) - } - if signal != syscall.SIGHUP { - t.Fatalf("signal is incorrect.\nexpected: hangup\ngot: %v", signal) - } - return nil - } - defer func() { - kill = killBefore - }() - - if err := ProcessSignal(CommandReload, "123"); err != nil { - t.Fatalf("ProcessSignal failed: %v", err) - } - - if !called { - t.Fatal("Expected kill to be called") - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/signal_windows.go b/vendor/github.com/nats-io/gnatsd/server/signal_windows.go deleted file mode 100644 index 368077dd..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/signal_windows.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "fmt" - "os" - "os/signal" - "time" - - "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/mgr" -) - -// Signal Handling -func (s *Server) handleSignals() { - if s.getOpts().NoSigs { - return - } - c := make(chan os.Signal, 1) - - signal.Notify(c, os.Interrupt) - - go func() { - for sig := range c { - s.Debugf("Trapped %q signal", sig) - s.Noticef("Server Exiting..") - os.Exit(0) - } - }() -} - -// ProcessSignal sends the given signal command to the running gnatsd service. -// If service is empty, this signals the "gnatsd" service. This returns an -// error is the given service is not running or the command is invalid. -func ProcessSignal(command Command, service string) error { - if service == "" { - service = serviceName - } - - m, err := mgr.Connect() - if err != nil { - return err - } - defer m.Disconnect() - - s, err := m.OpenService(service) - if err != nil { - return fmt.Errorf("could not access service: %v", err) - } - defer s.Close() - - var ( - cmd svc.Cmd - to svc.State - ) - - switch command { - case CommandStop, CommandQuit: - cmd = svc.Stop - to = svc.Stopped - case CommandReopen: - cmd = reopenLogCmd - to = svc.Running - case CommandReload: - cmd = svc.ParamChange - to = svc.Running - default: - return fmt.Errorf("unknown signal %q", command) - } - - status, err := s.Control(cmd) - if err != nil { - return fmt.Errorf("could not send control=%d: %v", cmd, err) - } - - timeout := time.Now().Add(10 * time.Second) - for status.State != to { - if timeout.Before(time.Now()) { - return fmt.Errorf("timeout waiting for service to go to state=%d", to) - } - time.Sleep(300 * time.Millisecond) - status, err = s.Query() - if err != nil { - return fmt.Errorf("could not retrieve service status: %v", err) - } - } - - return nil -} diff --git a/vendor/github.com/nats-io/gnatsd/server/split_test.go b/vendor/github.com/nats-io/gnatsd/server/split_test.go deleted file mode 100644 index 77dd2efb..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/split_test.go +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "bytes" - "net" - "testing" -) - -func TestSplitBufferSubOp(t *testing.T) { - cli, trash := net.Pipe() - defer cli.Close() - defer trash.Close() - - s := &Server{sl: NewSublist()} - c := &client{srv: s, subs: make(map[string]*subscription), nc: cli} - - subop := []byte("SUB foo 1\r\n") - subop1 := subop[:6] - subop2 := subop[6:] - - if err := c.parse(subop1); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != SUB_ARG { - t.Fatalf("Expected SUB_ARG state vs %d\n", c.state) - } - if err := c.parse(subop2); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected OP_START state vs %d\n", c.state) - } - r := s.sl.Match("foo") - if r == nil || len(r.psubs) != 1 { - t.Fatalf("Did not match subscription properly: %+v\n", r) - } - sub := r.psubs[0] - if !bytes.Equal(sub.subject, []byte("foo")) { - t.Fatalf("Subject did not match expected 'foo' : '%s'\n", sub.subject) - } - if !bytes.Equal(sub.sid, []byte("1")) { - t.Fatalf("Sid did not match expected '1' : '%s'\n", sub.sid) - } - if sub.queue != nil { - t.Fatalf("Received a non-nil queue: '%s'\n", sub.queue) - } -} - -func TestSplitBufferUnsubOp(t *testing.T) { - s := &Server{sl: NewSublist()} - c := &client{srv: s, subs: make(map[string]*subscription)} - - subop := []byte("SUB foo 1024\r\n") - if err := c.parse(subop); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected OP_START state vs %d\n", c.state) - } - - unsubop := []byte("UNSUB 1024\r\n") - unsubop1 := unsubop[:8] - unsubop2 := unsubop[8:] - - if err := c.parse(unsubop1); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != UNSUB_ARG { - t.Fatalf("Expected UNSUB_ARG state vs %d\n", c.state) - } - if err := c.parse(unsubop2); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected OP_START state vs %d\n", c.state) - } - r := s.sl.Match("foo") - if r != nil && len(r.psubs) != 0 { - t.Fatalf("Should be no subscriptions in results: %+v\n", r) - } -} - -func TestSplitBufferPubOp(t *testing.T) { - c := &client{subs: make(map[string]*subscription)} - pub := []byte("PUB foo.bar INBOX.22 11\r\nhello world\r") - pub1 := pub[:2] - pub2 := pub[2:9] - pub3 := pub[9:15] - pub4 := pub[15:22] - pub5 := pub[22:25] - pub6 := pub[25:33] - pub7 := pub[33:] - - if err := c.parse(pub1); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != OP_PU { - t.Fatalf("Expected OP_PU state vs %d\n", c.state) - } - if err := c.parse(pub2); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != PUB_ARG { - t.Fatalf("Expected OP_PU state vs %d\n", c.state) - } - if err := c.parse(pub3); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != PUB_ARG { - t.Fatalf("Expected OP_PU state vs %d\n", c.state) - } - if err := c.parse(pub4); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != PUB_ARG { - t.Fatalf("Expected PUB_ARG state vs %d\n", c.state) - } - if err := c.parse(pub5); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_PAYLOAD { - t.Fatalf("Expected MSG_PAYLOAD state vs %d\n", c.state) - } - - // Check c.pa - if !bytes.Equal(c.pa.subject, []byte("foo.bar")) { - t.Fatalf("PUB arg subject incorrect: '%s'\n", c.pa.subject) - } - if !bytes.Equal(c.pa.reply, []byte("INBOX.22")) { - t.Fatalf("PUB arg reply subject incorrect: '%s'\n", c.pa.reply) - } - if c.pa.size != 11 { - t.Fatalf("PUB arg msg size incorrect: %d\n", c.pa.size) - } - if err := c.parse(pub6); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_PAYLOAD { - t.Fatalf("Expected MSG_PAYLOAD state vs %d\n", c.state) - } - if err := c.parse(pub7); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_END { - t.Fatalf("Expected MSG_END state vs %d\n", c.state) - } -} - -func TestSplitBufferPubOp2(t *testing.T) { - c := &client{subs: make(map[string]*subscription)} - pub := []byte("PUB foo.bar INBOX.22 11\r\nhello world\r\n") - pub1 := pub[:30] - pub2 := pub[30:] - - if err := c.parse(pub1); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_PAYLOAD { - t.Fatalf("Expected MSG_PAYLOAD state vs %d\n", c.state) - } - if err := c.parse(pub2); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != OP_START { - t.Fatalf("Expected OP_START state vs %d\n", c.state) - } -} - -func TestSplitBufferPubOp3(t *testing.T) { - c := &client{subs: make(map[string]*subscription)} - pubAll := []byte("PUB foo bar 11\r\nhello world\r\n") - pub := pubAll[:16] - - if err := c.parse(pub); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if !bytes.Equal(c.pa.subject, []byte("foo")) { - t.Fatalf("Unexpected subject: '%s' vs '%s'\n", c.pa.subject, "foo") - } - - // Simulate next read of network, make sure pub state is saved - // until msg payload has cleared. - copy(pubAll, "XXXXXXXXXXXXXXXX") - if !bytes.Equal(c.pa.subject, []byte("foo")) { - t.Fatalf("Unexpected subject: '%s' vs '%s'\n", c.pa.subject, "foo") - } - if !bytes.Equal(c.pa.reply, []byte("bar")) { - t.Fatalf("Unexpected reply: '%s' vs '%s'\n", c.pa.reply, "bar") - } - if !bytes.Equal(c.pa.szb, []byte("11")) { - t.Fatalf("Unexpected size bytes: '%s' vs '%s'\n", c.pa.szb, "11") - } -} - -func TestSplitBufferPubOp4(t *testing.T) { - c := &client{subs: make(map[string]*subscription)} - pubAll := []byte("PUB foo 11\r\nhello world\r\n") - pub := pubAll[:12] - - if err := c.parse(pub); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if !bytes.Equal(c.pa.subject, []byte("foo")) { - t.Fatalf("Unexpected subject: '%s' vs '%s'\n", c.pa.subject, "foo") - } - - // Simulate next read of network, make sure pub state is saved - // until msg payload has cleared. - copy(pubAll, "XXXXXXXXXXXX") - if !bytes.Equal(c.pa.subject, []byte("foo")) { - t.Fatalf("Unexpected subject: '%s' vs '%s'\n", c.pa.subject, "foo") - } - if !bytes.Equal(c.pa.reply, []byte("")) { - t.Fatalf("Unexpected reply: '%s' vs '%s'\n", c.pa.reply, "") - } - if !bytes.Equal(c.pa.szb, []byte("11")) { - t.Fatalf("Unexpected size bytes: '%s' vs '%s'\n", c.pa.szb, "11") - } -} - -func TestSplitBufferPubOp5(t *testing.T) { - c := &client{subs: make(map[string]*subscription)} - pubAll := []byte("PUB foo 11\r\nhello world\r\n") - - // Splits need to be on MSG_END now too, so make sure we check that. - // Split between \r and \n - pub := pubAll[:len(pubAll)-1] - - if err := c.parse(pub); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.msgBuf == nil { - t.Fatalf("msgBuf should not be nil!\n") - } - if !bytes.Equal(c.msgBuf, []byte("hello world\r")) { - t.Fatalf("c.msgBuf did not snaphot the msg") - } -} - -func TestSplitConnectArg(t *testing.T) { - c := &client{subs: make(map[string]*subscription)} - connectAll := []byte("CONNECT {\"verbose\":false,\"tls_required\":false," + - "\"user\":\"test\",\"pedantic\":true,\"pass\":\"pass\"}\r\n") - - argJSON := connectAll[8:] - - c1 := connectAll[:5] - c2 := connectAll[5:22] - c3 := connectAll[22 : len(connectAll)-2] - c4 := connectAll[len(connectAll)-2:] - - if err := c.parse(c1); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.argBuf != nil { - t.Fatalf("Unexpected argBug placeholder.\n") - } - - if err := c.parse(c2); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.argBuf == nil { - t.Fatalf("Expected argBug to not be nil.\n") - } - if !bytes.Equal(c.argBuf, argJSON[:14]) { - t.Fatalf("argBuf not correct, received %q, wanted %q\n", argJSON[:14], c.argBuf) - } - - if err := c.parse(c3); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.argBuf == nil { - t.Fatalf("Expected argBug to not be nil.\n") - } - if !bytes.Equal(c.argBuf, argJSON[:len(argJSON)-2]) { - t.Fatalf("argBuf not correct, received %q, wanted %q\n", - argJSON[:len(argJSON)-2], c.argBuf) - } - - if err := c.parse(c4); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.argBuf != nil { - t.Fatalf("Unexpected argBuf placeholder.\n") - } -} - -func TestSplitDanglingArgBuf(t *testing.T) { - s := New(&defaultServerOptions) - c := &client{srv: s, subs: make(map[string]*subscription)} - - // We test to make sure we do not dangle any argBufs after processing - // since that could lead to performance issues. - - // SUB - subop := []byte("SUB foo 1\r\n") - c.parse(subop[:6]) - c.parse(subop[6:]) - if c.argBuf != nil { - t.Fatalf("Expected c.argBuf to be nil: %q\n", c.argBuf) - } - - // UNSUB - unsubop := []byte("UNSUB 1024\r\n") - c.parse(unsubop[:8]) - c.parse(unsubop[8:]) - if c.argBuf != nil { - t.Fatalf("Expected c.argBuf to be nil: %q\n", c.argBuf) - } - - // PUB - pubop := []byte("PUB foo.bar INBOX.22 11\r\nhello world\r\n") - c.parse(pubop[:22]) - c.parse(pubop[22:25]) - if c.argBuf == nil { - t.Fatal("Expected a non-nil argBuf!") - } - c.parse(pubop[25:]) - if c.argBuf != nil { - t.Fatalf("Expected c.argBuf to be nil: %q\n", c.argBuf) - } - - // MINUS_ERR - errop := []byte("-ERR Too Long\r\n") - c.parse(errop[:8]) - c.parse(errop[8:]) - if c.argBuf != nil { - t.Fatalf("Expected c.argBuf to be nil: %q\n", c.argBuf) - } - - // CONNECT_ARG - connop := []byte("CONNECT {\"verbose\":false,\"tls_required\":false," + - "\"user\":\"test\",\"pedantic\":true,\"pass\":\"pass\"}\r\n") - c.parse(connop[:22]) - c.parse(connop[22:]) - if c.argBuf != nil { - t.Fatalf("Expected c.argBuf to be nil: %q\n", c.argBuf) - } - - // INFO_ARG - infoop := []byte("INFO {\"server_id\":\"id\"}\r\n") - c.parse(infoop[:8]) - c.parse(infoop[8:]) - if c.argBuf != nil { - t.Fatalf("Expected c.argBuf to be nil: %q\n", c.argBuf) - } - - // MSG (the client has to be a ROUTE) - c = &client{subs: make(map[string]*subscription), typ: ROUTER} - msgop := []byte("MSG foo RSID:2:1 5\r\nhello\r\n") - c.parse(msgop[:5]) - c.parse(msgop[5:10]) - if c.argBuf == nil { - t.Fatal("Expected a non-nil argBuf") - } - if string(c.argBuf) != "foo RS" { - t.Fatalf("Expected argBuf to be \"foo 1 \", got %q", string(c.argBuf)) - } - c.parse(msgop[10:]) - if c.argBuf != nil { - t.Fatalf("Expected argBuf to be nil: %q", c.argBuf) - } - if c.msgBuf != nil { - t.Fatalf("Expected msgBuf to be nil: %q", c.msgBuf) - } - - c.state = OP_START - // Parse up-to somewhere in the middle of the payload. - // Verify that we have saved the MSG_ARG info - c.parse(msgop[:23]) - if c.argBuf == nil { - t.Fatal("Expected a non-nil argBuf") - } - if string(c.pa.subject) != "foo" { - t.Fatalf("Expected subject to be \"foo\", got %q", c.pa.subject) - } - if string(c.pa.reply) != "" { - t.Fatalf("Expected reply to be \"\", got %q", c.pa.reply) - } - if string(c.pa.sid) != "RSID:2:1" { - t.Fatalf("Expected sid to \"RSID:2:1\", got %q", c.pa.sid) - } - if c.pa.size != 5 { - t.Fatalf("Expected sid to 5, got %v", c.pa.size) - } - // msg buffer should be - if c.msgBuf == nil || string(c.msgBuf) != "hel" { - t.Fatalf("Expected msgBuf to be \"hel\", got %q", c.msgBuf) - } - c.parse(msgop[23:]) - // At the end, we should have cleaned-up both arg and msg buffers. - if c.argBuf != nil { - t.Fatalf("Expected argBuf to be nil: %q", c.argBuf) - } - if c.msgBuf != nil { - t.Fatalf("Expected msgBuf to be nil: %q", c.msgBuf) - } -} - -func TestSplitMsgArg(t *testing.T) { - _, c, _ := setupClient() - // Allow parser to process MSG - c.typ = ROUTER - - b := make([]byte, 1024) - - copy(b, []byte("MSG hello.world RSID:14:8 6040\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")) - c.parse(b) - - copy(b, []byte("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\r\n")) - c.parse(b) - - wantSubject := "hello.world" - wantSid := "RSID:14:8" - wantSzb := "6040" - - if string(c.pa.subject) != wantSubject { - t.Fatalf("Incorrect subject: want %q, got %q", wantSubject, c.pa.subject) - } - - if string(c.pa.sid) != wantSid { - t.Fatalf("Incorrect sid: want %q, got %q", wantSid, c.pa.sid) - } - - if string(c.pa.szb) != wantSzb { - t.Fatalf("Incorrect szb: want %q, got %q", wantSzb, c.pa.szb) - } -} - -func TestSplitBufferMsgOp(t *testing.T) { - c := &client{subs: make(map[string]*subscription), typ: ROUTER} - msg := []byte("MSG foo.bar QRSID:15:3 _INBOX.22 11\r\nhello world\r") - msg1 := msg[:2] - msg2 := msg[2:9] - msg3 := msg[9:15] - msg4 := msg[15:22] - msg5 := msg[22:25] - msg6 := msg[25:37] - msg7 := msg[37:42] - msg8 := msg[42:] - - if err := c.parse(msg1); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != OP_MS { - t.Fatalf("Expected OP_MS state vs %d\n", c.state) - } - if err := c.parse(msg2); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_ARG { - t.Fatalf("Expected MSG_ARG state vs %d\n", c.state) - } - if err := c.parse(msg3); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_ARG { - t.Fatalf("Expected MSG_ARG state vs %d\n", c.state) - } - if err := c.parse(msg4); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_ARG { - t.Fatalf("Expected MSG_ARG state vs %d\n", c.state) - } - if err := c.parse(msg5); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_ARG { - t.Fatalf("Expected MSG_ARG state vs %d\n", c.state) - } - if err := c.parse(msg6); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_PAYLOAD { - t.Fatalf("Expected MSG_PAYLOAD state vs %d\n", c.state) - } - - // Check c.pa - if !bytes.Equal(c.pa.subject, []byte("foo.bar")) { - t.Fatalf("MSG arg subject incorrect: '%s'\n", c.pa.subject) - } - if !bytes.Equal(c.pa.sid, []byte("QRSID:15:3")) { - t.Fatalf("MSG arg sid incorrect: '%s'\n", c.pa.sid) - } - if !bytes.Equal(c.pa.reply, []byte("_INBOX.22")) { - t.Fatalf("MSG arg reply subject incorrect: '%s'\n", c.pa.reply) - } - if c.pa.size != 11 { - t.Fatalf("MSG arg msg size incorrect: %d\n", c.pa.size) - } - if err := c.parse(msg7); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_PAYLOAD { - t.Fatalf("Expected MSG_PAYLOAD state vs %d\n", c.state) - } - if err := c.parse(msg8); err != nil { - t.Fatalf("Unexpected parse error: %v\n", err) - } - if c.state != MSG_END { - t.Fatalf("Expected MSG_END state vs %d\n", c.state) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/server/sublist.go b/vendor/github.com/nats-io/gnatsd/server/sublist.go deleted file mode 100644 index e699f2af..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/sublist.go +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sublist is a routing mechanism to handle subject distribution -// and provides a facility to match subjects from published messages to -// interested subscribers. Subscribers can have wildcard subjects to match -// multiple published subjects. -package server - -import ( - "bytes" - "errors" - "strings" - "sync" - "sync/atomic" -) - -// Common byte variables for wildcards and token separator. -const ( - pwc = '*' - fwc = '>' - tsep = "." - btsep = '.' -) - -// Sublist related errors -var ( - ErrInvalidSubject = errors.New("sublist: Invalid Subject") - ErrNotFound = errors.New("sublist: No Matches Found") -) - -const ( - // cacheMax is used to bound limit the frontend cache - slCacheMax = 1024 - // plistMin is our lower bounds to create a fast plist for Match. - plistMin = 256 -) - -// A result structure better optimized for queue subs. -type SublistResult struct { - psubs []*subscription - qsubs [][]*subscription // don't make this a map, too expensive to iterate -} - -// A Sublist stores and efficiently retrieves subscriptions. -type Sublist struct { - sync.RWMutex - genid uint64 - matches uint64 - cacheHits uint64 - inserts uint64 - removes uint64 - cache map[string]*SublistResult - root *level - count uint32 -} - -// A node contains subscriptions and a pointer to the next level. -type node struct { - next *level - psubs map[*subscription]*subscription - qsubs map[string](map[*subscription]*subscription) - plist []*subscription -} - -// A level represents a group of nodes and special pointers to -// wildcard nodes. -type level struct { - nodes map[string]*node - pwc, fwc *node -} - -// Create a new default node. -func newNode() *node { - return &node{psubs: make(map[*subscription]*subscription)} -} - -// Create a new default level. -func newLevel() *level { - return &level{nodes: make(map[string]*node)} -} - -// New will create a default sublist -func NewSublist() *Sublist { - return &Sublist{root: newLevel(), cache: make(map[string]*SublistResult)} -} - -// Insert adds a subscription into the sublist -func (s *Sublist) Insert(sub *subscription) error { - // copy the subject since we hold this and this might be part of a large byte slice. - subject := string(sub.subject) - tsa := [32]string{} - tokens := tsa[:0] - start := 0 - for i := 0; i < len(subject); i++ { - if subject[i] == btsep { - tokens = append(tokens, subject[start:i]) - start = i + 1 - } - } - tokens = append(tokens, subject[start:]) - - s.Lock() - - sfwc := false - l := s.root - var n *node - - for _, t := range tokens { - lt := len(t) - if lt == 0 || sfwc { - s.Unlock() - return ErrInvalidSubject - } - - if lt > 1 { - n = l.nodes[t] - } else { - switch t[0] { - case pwc: - n = l.pwc - case fwc: - n = l.fwc - sfwc = true - default: - n = l.nodes[t] - } - } - if n == nil { - n = newNode() - if lt > 1 { - l.nodes[t] = n - } else { - switch t[0] { - case pwc: - l.pwc = n - case fwc: - l.fwc = n - default: - l.nodes[t] = n - } - } - } - if n.next == nil { - n.next = newLevel() - } - l = n.next - } - if sub.queue == nil { - n.psubs[sub] = sub - if n.plist != nil { - n.plist = append(n.plist, sub) - } else if len(n.psubs) > plistMin { - n.plist = make([]*subscription, 0, len(n.psubs)) - // Populate - for _, psub := range n.psubs { - n.plist = append(n.plist, psub) - } - } - } else { - if n.qsubs == nil { - n.qsubs = make(map[string]map[*subscription]*subscription) - } - qname := string(sub.queue) - // This is a queue subscription - subs, ok := n.qsubs[qname] - if !ok { - subs = make(map[*subscription]*subscription) - n.qsubs[qname] = subs - } - subs[sub] = sub - } - - s.count++ - s.inserts++ - - s.addToCache(subject, sub) - atomic.AddUint64(&s.genid, 1) - - s.Unlock() - return nil -} - -// Deep copy -func copyResult(r *SublistResult) *SublistResult { - nr := &SublistResult{} - nr.psubs = append([]*subscription(nil), r.psubs...) - for _, qr := range r.qsubs { - nqr := append([]*subscription(nil), qr...) - nr.qsubs = append(nr.qsubs, nqr) - } - return nr -} - -// addToCache will add the new entry to existing cache -// entries if needed. Assumes write lock is held. -func (s *Sublist) addToCache(subject string, sub *subscription) { - for k, r := range s.cache { - if matchLiteral(k, subject) { - // Copy since others may have a reference. - nr := copyResult(r) - if sub.queue == nil { - nr.psubs = append(nr.psubs, sub) - } else { - if i := findQSliceForSub(sub, nr.qsubs); i >= 0 { - nr.qsubs[i] = append(nr.qsubs[i], sub) - } else { - nr.qsubs = append(nr.qsubs, []*subscription{sub}) - } - } - s.cache[k] = nr - } - } -} - -// removeFromCache will remove the sub from any active cache entries. -// Assumes write lock is held. -func (s *Sublist) removeFromCache(subject string, sub *subscription) { - for k := range s.cache { - if !matchLiteral(k, subject) { - continue - } - // Since someone else may be referecing, can't modify the list - // safely, just let it re-populate. - delete(s.cache, k) - } -} - -// Match will match all entries to the literal subject. -// It will return a set of results for both normal and queue subscribers. -func (s *Sublist) Match(subject string) *SublistResult { - s.RLock() - atomic.AddUint64(&s.matches, 1) - rc, ok := s.cache[subject] - s.RUnlock() - if ok { - atomic.AddUint64(&s.cacheHits, 1) - return rc - } - - tsa := [32]string{} - tokens := tsa[:0] - start := 0 - for i := 0; i < len(subject); i++ { - if subject[i] == btsep { - tokens = append(tokens, subject[start:i]) - start = i + 1 - } - } - tokens = append(tokens, subject[start:]) - - // FIXME(dlc) - Make shared pool between sublist and client readLoop? - result := &SublistResult{} - - s.Lock() - matchLevel(s.root, tokens, result) - - // Add to our cache - s.cache[subject] = result - // Bound the number of entries to sublistMaxCache - if len(s.cache) > slCacheMax { - for k := range s.cache { - delete(s.cache, k) - break - } - } - s.Unlock() - - return result -} - -// This will add in a node's results to the total results. -func addNodeToResults(n *node, results *SublistResult) { - // Normal subscriptions - if n.plist != nil { - results.psubs = append(results.psubs, n.plist...) - } else { - for _, psub := range n.psubs { - results.psubs = append(results.psubs, psub) - } - } - // Queue subscriptions - for qname, qr := range n.qsubs { - if len(qr) == 0 { - continue - } - tsub := &subscription{subject: nil, queue: []byte(qname)} - // Need to find matching list in results - if i := findQSliceForSub(tsub, results.qsubs); i >= 0 { - for _, sub := range qr { - results.qsubs[i] = append(results.qsubs[i], sub) - } - } else { - var nqsub []*subscription - for _, sub := range qr { - nqsub = append(nqsub, sub) - } - results.qsubs = append(results.qsubs, nqsub) - } - } -} - -// We do not use a map here since we want iteration to be past when -// processing publishes in L1 on client. So we need to walk sequentially -// for now. Keep an eye on this in case we start getting large number of -// different queue subscribers for the same subject. -func findQSliceForSub(sub *subscription, qsl [][]*subscription) int { - if sub.queue == nil { - return -1 - } - for i, qr := range qsl { - if len(qr) > 0 && bytes.Equal(sub.queue, qr[0].queue) { - return i - } - } - return -1 -} - -// matchLevel is used to recursively descend into the trie. -func matchLevel(l *level, toks []string, results *SublistResult) { - var pwc, n *node - for i, t := range toks { - if l == nil { - return - } - if l.fwc != nil { - addNodeToResults(l.fwc, results) - } - if pwc = l.pwc; pwc != nil { - matchLevel(pwc.next, toks[i+1:], results) - } - n = l.nodes[t] - if n != nil { - l = n.next - } else { - l = nil - } - } - if n != nil { - addNodeToResults(n, results) - } - if pwc != nil { - addNodeToResults(pwc, results) - } -} - -// lnt is used to track descent into levels for a removal for pruning. -type lnt struct { - l *level - n *node - t string -} - -// Raw low level remove, can do batches with lock held outside. -func (s *Sublist) remove(sub *subscription, shouldLock bool) error { - subject := string(sub.subject) - tsa := [32]string{} - tokens := tsa[:0] - start := 0 - for i := 0; i < len(subject); i++ { - if subject[i] == btsep { - tokens = append(tokens, subject[start:i]) - start = i + 1 - } - } - tokens = append(tokens, subject[start:]) - - if shouldLock { - s.Lock() - defer s.Unlock() - } - - sfwc := false - l := s.root - var n *node - - // Track levels for pruning - var lnts [32]lnt - levels := lnts[:0] - - for _, t := range tokens { - lt := len(t) - if lt == 0 || sfwc { - return ErrInvalidSubject - } - if l == nil { - return ErrNotFound - } - if lt > 1 { - n = l.nodes[t] - } else { - switch t[0] { - case pwc: - n = l.pwc - case fwc: - n = l.fwc - sfwc = true - default: - n = l.nodes[t] - } - } - if n != nil { - levels = append(levels, lnt{l, n, t}) - l = n.next - } else { - l = nil - } - } - if !s.removeFromNode(n, sub) { - return ErrNotFound - } - - s.count-- - s.removes++ - - for i := len(levels) - 1; i >= 0; i-- { - l, n, t := levels[i].l, levels[i].n, levels[i].t - if n.isEmpty() { - l.pruneNode(n, t) - } - } - s.removeFromCache(subject, sub) - atomic.AddUint64(&s.genid, 1) - - return nil -} - -// Remove will remove a subscription. -func (s *Sublist) Remove(sub *subscription) error { - return s.remove(sub, true) -} - -// RemoveBatch will remove a list of subscriptions. -func (s *Sublist) RemoveBatch(subs []*subscription) error { - s.Lock() - defer s.Unlock() - - for _, sub := range subs { - if err := s.remove(sub, false); err != nil { - return err - } - } - return nil -} - -// pruneNode is used to prune an empty node from the tree. -func (l *level) pruneNode(n *node, t string) { - if n == nil { - return - } - if n == l.fwc { - l.fwc = nil - } else if n == l.pwc { - l.pwc = nil - } else { - delete(l.nodes, t) - } -} - -// isEmpty will test if the node has any entries. Used -// in pruning. -func (n *node) isEmpty() bool { - if len(n.psubs) == 0 && len(n.qsubs) == 0 { - if n.next == nil || n.next.numNodes() == 0 { - return true - } - } - return false -} - -// Return the number of nodes for the given level. -func (l *level) numNodes() int { - num := len(l.nodes) - if l.pwc != nil { - num++ - } - if l.fwc != nil { - num++ - } - return num -} - -// Remove the sub for the given node. -func (s *Sublist) removeFromNode(n *node, sub *subscription) (found bool) { - if n == nil { - return false - } - if sub.queue == nil { - _, found = n.psubs[sub] - delete(n.psubs, sub) - if found && n.plist != nil { - // This will brute force remove the plist to perform - // correct behavior. Will get repopulated on a call - //to Match as needed. - n.plist = nil - } - return found - } - - // We have a queue group subscription here - qname := string(sub.queue) - qsub := n.qsubs[qname] - _, found = qsub[sub] - delete(qsub, sub) - if len(qsub) == 0 { - delete(n.qsubs, qname) - } - return found -} - -// Count returns the number of subscriptions. -func (s *Sublist) Count() uint32 { - s.RLock() - defer s.RUnlock() - return s.count -} - -// CacheCount returns the number of result sets in the cache. -func (s *Sublist) CacheCount() int { - s.RLock() - defer s.RUnlock() - return len(s.cache) -} - -// Public stats for the sublist -type SublistStats struct { - NumSubs uint32 `json:"num_subscriptions"` - NumCache uint32 `json:"num_cache"` - NumInserts uint64 `json:"num_inserts"` - NumRemoves uint64 `json:"num_removes"` - NumMatches uint64 `json:"num_matches"` - CacheHitRate float64 `json:"cache_hit_rate"` - MaxFanout uint32 `json:"max_fanout"` - AvgFanout float64 `json:"avg_fanout"` -} - -// Stats will return a stats structure for the current state. -func (s *Sublist) Stats() *SublistStats { - s.Lock() - defer s.Unlock() - - st := &SublistStats{} - st.NumSubs = s.count - st.NumCache = uint32(len(s.cache)) - st.NumInserts = s.inserts - st.NumRemoves = s.removes - st.NumMatches = atomic.LoadUint64(&s.matches) - if st.NumMatches > 0 { - st.CacheHitRate = float64(atomic.LoadUint64(&s.cacheHits)) / float64(st.NumMatches) - } - // whip through cache for fanout stats - tot, max := 0, 0 - for _, r := range s.cache { - l := len(r.psubs) + len(r.qsubs) - tot += l - if l > max { - max = l - } - } - st.MaxFanout = uint32(max) - if tot > 0 { - st.AvgFanout = float64(tot) / float64(len(s.cache)) - } - return st -} - -// numLevels will return the maximum number of levels -// contained in the Sublist tree. -func (s *Sublist) numLevels() int { - return visitLevel(s.root, 0) -} - -// visitLevel is used to descend the Sublist tree structure -// recursively. -func visitLevel(l *level, depth int) int { - if l == nil || l.numNodes() == 0 { - return depth - } - - depth++ - maxDepth := depth - - for _, n := range l.nodes { - if n == nil { - continue - } - newDepth := visitLevel(n.next, depth) - if newDepth > maxDepth { - maxDepth = newDepth - } - } - if l.pwc != nil { - pwcDepth := visitLevel(l.pwc.next, depth) - if pwcDepth > maxDepth { - maxDepth = pwcDepth - } - } - if l.fwc != nil { - fwcDepth := visitLevel(l.fwc.next, depth) - if fwcDepth > maxDepth { - maxDepth = fwcDepth - } - } - return maxDepth -} - -// IsValidSubject returns true if a subject is valid, false otherwise -func IsValidSubject(subject string) bool { - if subject == "" { - return false - } - sfwc := false - tokens := strings.Split(subject, tsep) - for _, t := range tokens { - if len(t) == 0 || sfwc { - return false - } - if len(t) > 1 { - continue - } - switch t[0] { - case fwc: - sfwc = true - } - } - return true -} - -// IsValidLiteralSubject returns true if a subject is valid and literal (no wildcards), false otherwise -func IsValidLiteralSubject(subject string) bool { - tokens := strings.Split(subject, tsep) - for _, t := range tokens { - if len(t) == 0 { - return false - } - if len(t) > 1 { - continue - } - switch t[0] { - case pwc, fwc: - return false - } - } - return true -} - -// matchLiteral is used to test literal subjects, those that do not have any -// wildcards, with a target subject. This is used in the cache layer. -func matchLiteral(literal, subject string) bool { - li := 0 - ll := len(literal) - ls := len(subject) - for i := 0; i < ls; i++ { - if li >= ll { - return false - } - // This function has been optimized for speed. - // For instance, do not set b:=subject[i] here since - // we may bump `i` in this loop to avoid `continue` or - // skiping common test in a particular test. - // Run Benchmark_SublistMatchLiteral before making any change. - switch subject[i] { - case pwc: - // NOTE: This is not testing validity of a subject, instead ensures - // that wildcards are treated as such if they follow some basic rules, - // namely that they are a token on their own. - if i == 0 || subject[i-1] == btsep { - if i == ls-1 { - // There is no more token in the subject after this wildcard. - // Skip token in literal and expect to not find a separator. - for { - // End of literal, this is a match. - if li >= ll { - return true - } - // Presence of separator, this can't be a match. - if literal[li] == btsep { - return false - } - li++ - } - } else if subject[i+1] == btsep { - // There is another token in the subject after this wildcard. - // Skip token in literal and expect to get a separator. - for { - // We found the end of the literal before finding a separator, - // this can't be a match. - if li >= ll { - return false - } - if literal[li] == btsep { - break - } - li++ - } - // Bump `i` since we know there is a `.` following, we are - // safe. The common test below is going to check `.` with `.` - // which is good. A `continue` here is too costly. - i++ - } - } - case fwc: - // For `>` to be a wildcard, it means being the only or last character - // in the string preceded by a `.` - if (i == 0 || subject[i-1] == btsep) && i == ls-1 { - return true - } - } - if subject[i] != literal[li] { - return false - } - li++ - } - // Make sure we have processed all of the literal's chars.. - return li >= ll -} - -func addLocalSub(sub *subscription, subs *[]*subscription) { - if sub != nil && sub.client != nil && sub.client.typ == CLIENT { - *subs = append(*subs, sub) - } -} - -func (s *Sublist) addNodeToSubs(n *node, subs *[]*subscription) { - // Normal subscriptions - if n.plist != nil { - for _, sub := range n.plist { - addLocalSub(sub, subs) - } - } else { - for _, sub := range n.psubs { - addLocalSub(sub, subs) - } - } - // Queue subscriptions - for _, qr := range n.qsubs { - for _, sub := range qr { - addLocalSub(sub, subs) - } - } -} - -func (s *Sublist) collectLocalSubs(l *level, subs *[]*subscription) { - if len(l.nodes) > 0 { - for _, n := range l.nodes { - s.addNodeToSubs(n, subs) - s.collectLocalSubs(n.next, subs) - } - } - if l.pwc != nil { - s.addNodeToSubs(l.pwc, subs) - s.collectLocalSubs(l.pwc.next, subs) - } - if l.fwc != nil { - s.addNodeToSubs(l.fwc, subs) - s.collectLocalSubs(l.fwc.next, subs) - } -} - -// Return all local client subscriptions. Use the supplied slice. -func (s *Sublist) localSubs(subs *[]*subscription) { - s.RLock() - s.collectLocalSubs(s.root, subs) - s.RUnlock() -} diff --git a/vendor/github.com/nats-io/gnatsd/server/sublist_test.go b/vendor/github.com/nats-io/gnatsd/server/sublist_test.go deleted file mode 100644 index a534dd4a..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/sublist_test.go +++ /dev/null @@ -1,1019 +0,0 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "fmt" - "runtime" - "strings" - "sync" - "testing" - - dbg "runtime/debug" -) - -func stackFatalf(t *testing.T, f string, args ...interface{}) { - lines := make([]string, 0, 32) - msg := fmt.Sprintf(f, args...) - lines = append(lines, msg) - - // Generate the Stack of callers: Skip us and verify* frames. - for i := 2; true; i++ { - _, file, line, ok := runtime.Caller(i) - if !ok { - break - } - msg := fmt.Sprintf("%d - %s:%d", i, file, line) - lines = append(lines, msg) - } - t.Fatalf("%s", strings.Join(lines, "\n")) -} - -func verifyCount(s *Sublist, count uint32, t *testing.T) { - if s.Count() != count { - stackFatalf(t, "Count is %d, should be %d", s.Count(), count) - } -} - -func verifyLen(r []*subscription, l int, t *testing.T) { - if len(r) != l { - stackFatalf(t, "Results len is %d, should be %d", len(r), l) - } -} - -func verifyQLen(r [][]*subscription, l int, t *testing.T) { - if len(r) != l { - stackFatalf(t, "Queue Results len is %d, should be %d", len(r), l) - } -} - -func verifyNumLevels(s *Sublist, expected int, t *testing.T) { - dl := s.numLevels() - if dl != expected { - stackFatalf(t, "NumLevels is %d, should be %d", dl, expected) - } -} - -func verifyQMember(qsubs [][]*subscription, val *subscription, t *testing.T) { - verifyMember(qsubs[findQSliceForSub(val, qsubs)], val, t) -} - -func verifyMember(r []*subscription, val *subscription, t *testing.T) { - for _, v := range r { - if v == nil { - continue - } - if v == val { - return - } - } - stackFatalf(t, "Subscription (%p) for [%s : %s] not found in results", val, val.subject, val.queue) -} - -// Helpers to generate test subscriptions. -func newSub(subject string) *subscription { - return &subscription{subject: []byte(subject)} -} - -func newQSub(subject, queue string) *subscription { - if queue != "" { - return &subscription{subject: []byte(subject), queue: []byte(queue)} - } - return newSub(subject) -} - -func TestSublistInit(t *testing.T) { - s := NewSublist() - verifyCount(s, 0, t) -} - -func TestSublistInsertCount(t *testing.T) { - s := NewSublist() - s.Insert(newSub("foo")) - s.Insert(newSub("bar")) - s.Insert(newSub("foo.bar")) - verifyCount(s, 3, t) -} - -func TestSublistSimple(t *testing.T) { - s := NewSublist() - subject := "foo" - sub := newSub(subject) - s.Insert(sub) - r := s.Match(subject) - verifyLen(r.psubs, 1, t) - verifyMember(r.psubs, sub, t) -} - -func TestSublistSimpleMultiTokens(t *testing.T) { - s := NewSublist() - subject := "foo.bar.baz" - sub := newSub(subject) - s.Insert(sub) - r := s.Match(subject) - verifyLen(r.psubs, 1, t) - verifyMember(r.psubs, sub, t) -} - -func TestSublistPartialWildcard(t *testing.T) { - s := NewSublist() - lsub := newSub("a.b.c") - psub := newSub("a.*.c") - s.Insert(lsub) - s.Insert(psub) - r := s.Match("a.b.c") - verifyLen(r.psubs, 2, t) - verifyMember(r.psubs, lsub, t) - verifyMember(r.psubs, psub, t) -} - -func TestSublistPartialWildcardAtEnd(t *testing.T) { - s := NewSublist() - lsub := newSub("a.b.c") - psub := newSub("a.b.*") - s.Insert(lsub) - s.Insert(psub) - r := s.Match("a.b.c") - verifyLen(r.psubs, 2, t) - verifyMember(r.psubs, lsub, t) - verifyMember(r.psubs, psub, t) -} - -func TestSublistFullWildcard(t *testing.T) { - s := NewSublist() - lsub := newSub("a.b.c") - fsub := newSub("a.>") - s.Insert(lsub) - s.Insert(fsub) - r := s.Match("a.b.c") - verifyLen(r.psubs, 2, t) - verifyMember(r.psubs, lsub, t) - verifyMember(r.psubs, fsub, t) -} - -func TestSublistRemove(t *testing.T) { - s := NewSublist() - subject := "a.b.c.d" - sub := newSub(subject) - s.Insert(sub) - verifyCount(s, 1, t) - r := s.Match(subject) - verifyLen(r.psubs, 1, t) - s.Remove(newSub("a.b.c")) - verifyCount(s, 1, t) - s.Remove(sub) - verifyCount(s, 0, t) - r = s.Match(subject) - verifyLen(r.psubs, 0, t) -} - -func TestSublistRemoveWildcard(t *testing.T) { - s := NewSublist() - subject := "a.b.c.d" - sub := newSub(subject) - psub := newSub("a.b.*.d") - fsub := newSub("a.b.>") - s.Insert(sub) - s.Insert(psub) - s.Insert(fsub) - verifyCount(s, 3, t) - r := s.Match(subject) - verifyLen(r.psubs, 3, t) - s.Remove(sub) - verifyCount(s, 2, t) - s.Remove(fsub) - verifyCount(s, 1, t) - s.Remove(psub) - verifyCount(s, 0, t) - r = s.Match(subject) - verifyLen(r.psubs, 0, t) -} - -func TestSublistRemoveCleanup(t *testing.T) { - s := NewSublist() - literal := "a.b.c.d.e.f" - depth := len(strings.Split(literal, tsep)) - sub := newSub(literal) - verifyNumLevels(s, 0, t) - s.Insert(sub) - verifyNumLevels(s, depth, t) - s.Remove(sub) - verifyNumLevels(s, 0, t) -} - -func TestSublistRemoveCleanupWildcards(t *testing.T) { - s := NewSublist() - subject := "a.b.*.d.e.>" - depth := len(strings.Split(subject, tsep)) - sub := newSub(subject) - verifyNumLevels(s, 0, t) - s.Insert(sub) - verifyNumLevels(s, depth, t) - s.Remove(sub) - verifyNumLevels(s, 0, t) -} - -func TestSublistRemoveWithLargeSubs(t *testing.T) { - subject := "foo" - s := NewSublist() - for i := 0; i < plistMin*2; i++ { - sub := newSub(subject) - s.Insert(sub) - } - r := s.Match(subject) - verifyLen(r.psubs, plistMin*2, t) - // Remove one that is in the middle - s.Remove(r.psubs[plistMin]) - // Remove first one - s.Remove(r.psubs[0]) - // Remove last one - s.Remove(r.psubs[len(r.psubs)-1]) - // Check len again - r = s.Match(subject) - verifyLen(r.psubs, plistMin*2-3, t) -} - -func TestSublistInvalidSubjectsInsert(t *testing.T) { - s := NewSublist() - - // Insert, or subscriptions, can have wildcards, but not empty tokens, - // and can not have a FWC that is not the terminal token. - - // beginning empty token - if err := s.Insert(newSub(".foo")); err != ErrInvalidSubject { - t.Fatal("Expected invalid subject error") - } - - // trailing empty token - if err := s.Insert(newSub("foo.")); err != ErrInvalidSubject { - t.Fatal("Expected invalid subject error") - } - // empty middle token - if err := s.Insert(newSub("foo..bar")); err != ErrInvalidSubject { - t.Fatal("Expected invalid subject error") - } - // empty middle token #2 - if err := s.Insert(newSub("foo.bar..baz")); err != ErrInvalidSubject { - t.Fatal("Expected invalid subject error") - } - // fwc not terminal - if err := s.Insert(newSub("foo.>.bar")); err != ErrInvalidSubject { - t.Fatal("Expected invalid subject error") - } -} - -func TestSublistCache(t *testing.T) { - s := NewSublist() - - // Test add a remove logistics - subject := "a.b.c.d" - sub := newSub(subject) - psub := newSub("a.b.*.d") - fsub := newSub("a.b.>") - s.Insert(sub) - r := s.Match(subject) - verifyLen(r.psubs, 1, t) - s.Insert(psub) - s.Insert(fsub) - verifyCount(s, 3, t) - r = s.Match(subject) - verifyLen(r.psubs, 3, t) - s.Remove(sub) - verifyCount(s, 2, t) - s.Remove(fsub) - verifyCount(s, 1, t) - s.Remove(psub) - verifyCount(s, 0, t) - - // Check that cache is now empty - if cc := s.CacheCount(); cc != 0 { - t.Fatalf("Cache should be zero, got %d\n", cc) - } - - r = s.Match(subject) - verifyLen(r.psubs, 0, t) - - for i := 0; i < 2*slCacheMax; i++ { - s.Match(fmt.Sprintf("foo-%d\n", i)) - } - - if cc := s.CacheCount(); cc > slCacheMax { - t.Fatalf("Cache should be constrained by cacheMax, got %d for current count\n", cc) - } -} - -func TestSublistBasicQueueResults(t *testing.T) { - s := NewSublist() - - // Test some basics - subject := "foo" - sub := newSub(subject) - sub1 := newQSub(subject, "bar") - sub2 := newQSub(subject, "baz") - - s.Insert(sub1) - r := s.Match(subject) - verifyLen(r.psubs, 0, t) - verifyQLen(r.qsubs, 1, t) - verifyLen(r.qsubs[0], 1, t) - verifyQMember(r.qsubs, sub1, t) - - s.Insert(sub2) - r = s.Match(subject) - verifyLen(r.psubs, 0, t) - verifyQLen(r.qsubs, 2, t) - verifyLen(r.qsubs[0], 1, t) - verifyLen(r.qsubs[1], 1, t) - verifyQMember(r.qsubs, sub1, t) - verifyQMember(r.qsubs, sub2, t) - - s.Insert(sub) - r = s.Match(subject) - verifyLen(r.psubs, 1, t) - verifyQLen(r.qsubs, 2, t) - verifyLen(r.qsubs[0], 1, t) - verifyLen(r.qsubs[1], 1, t) - verifyQMember(r.qsubs, sub1, t) - verifyQMember(r.qsubs, sub2, t) - verifyMember(r.psubs, sub, t) - - sub3 := newQSub(subject, "bar") - sub4 := newQSub(subject, "baz") - - s.Insert(sub3) - s.Insert(sub4) - - r = s.Match(subject) - verifyLen(r.psubs, 1, t) - verifyQLen(r.qsubs, 2, t) - verifyLen(r.qsubs[0], 2, t) - verifyLen(r.qsubs[1], 2, t) - verifyQMember(r.qsubs, sub1, t) - verifyQMember(r.qsubs, sub2, t) - verifyQMember(r.qsubs, sub3, t) - verifyQMember(r.qsubs, sub4, t) - verifyMember(r.psubs, sub, t) - - // Now removal - s.Remove(sub) - - r = s.Match(subject) - verifyLen(r.psubs, 0, t) - verifyQLen(r.qsubs, 2, t) - verifyLen(r.qsubs[0], 2, t) - verifyLen(r.qsubs[1], 2, t) - verifyQMember(r.qsubs, sub1, t) - verifyQMember(r.qsubs, sub2, t) - - s.Remove(sub1) - r = s.Match(subject) - verifyLen(r.psubs, 0, t) - verifyQLen(r.qsubs, 2, t) - verifyLen(r.qsubs[findQSliceForSub(sub1, r.qsubs)], 1, t) - verifyLen(r.qsubs[findQSliceForSub(sub2, r.qsubs)], 2, t) - verifyQMember(r.qsubs, sub2, t) - verifyQMember(r.qsubs, sub3, t) - verifyQMember(r.qsubs, sub4, t) - - s.Remove(sub3) // Last one - r = s.Match(subject) - verifyLen(r.psubs, 0, t) - verifyQLen(r.qsubs, 1, t) - verifyLen(r.qsubs[0], 2, t) // this is sub2/baz now - verifyQMember(r.qsubs, sub2, t) - - s.Remove(sub2) - s.Remove(sub4) - r = s.Match(subject) - verifyLen(r.psubs, 0, t) - verifyQLen(r.qsubs, 0, t) -} - -func checkBool(b, expected bool, t *testing.T) { - if b != expected { - dbg.PrintStack() - t.Fatalf("Expected %v, but got %v\n", expected, b) - } -} - -func TestSublistValidLiteralSubjects(t *testing.T) { - checkBool(IsValidLiteralSubject("foo"), true, t) - checkBool(IsValidLiteralSubject(".foo"), false, t) - checkBool(IsValidLiteralSubject("foo."), false, t) - checkBool(IsValidLiteralSubject("foo..bar"), false, t) - checkBool(IsValidLiteralSubject("foo.bar.*"), false, t) - checkBool(IsValidLiteralSubject("foo.bar.>"), false, t) - checkBool(IsValidLiteralSubject("*"), false, t) - checkBool(IsValidLiteralSubject(">"), false, t) - // The followings have widlcards characters but are not - // considered as such because they are not individual tokens. - checkBool(IsValidLiteralSubject("foo*"), true, t) - checkBool(IsValidLiteralSubject("foo**"), true, t) - checkBool(IsValidLiteralSubject("foo.**"), true, t) - checkBool(IsValidLiteralSubject("foo*bar"), true, t) - checkBool(IsValidLiteralSubject("foo.*bar"), true, t) - checkBool(IsValidLiteralSubject("foo*.bar"), true, t) - checkBool(IsValidLiteralSubject("*bar"), true, t) - checkBool(IsValidLiteralSubject("foo>"), true, t) - checkBool(IsValidLiteralSubject("foo>>"), true, t) - checkBool(IsValidLiteralSubject("foo.>>"), true, t) - checkBool(IsValidLiteralSubject("foo>bar"), true, t) - checkBool(IsValidLiteralSubject("foo.>bar"), true, t) - checkBool(IsValidLiteralSubject("foo>.bar"), true, t) - checkBool(IsValidLiteralSubject(">bar"), true, t) -} - -func TestSublistValidlSubjects(t *testing.T) { - checkBool(IsValidSubject("."), false, t) - checkBool(IsValidSubject(".foo"), false, t) - checkBool(IsValidSubject("foo."), false, t) - checkBool(IsValidSubject("foo..bar"), false, t) - checkBool(IsValidSubject(">.bar"), false, t) - checkBool(IsValidSubject("foo.>.bar"), false, t) - checkBool(IsValidSubject("foo"), true, t) - checkBool(IsValidSubject("foo.bar.*"), true, t) - checkBool(IsValidSubject("foo.bar.>"), true, t) - checkBool(IsValidSubject("*"), true, t) - checkBool(IsValidSubject(">"), true, t) - checkBool(IsValidSubject("foo*"), true, t) - checkBool(IsValidSubject("foo**"), true, t) - checkBool(IsValidSubject("foo.**"), true, t) - checkBool(IsValidSubject("foo*bar"), true, t) - checkBool(IsValidSubject("foo.*bar"), true, t) - checkBool(IsValidSubject("foo*.bar"), true, t) - checkBool(IsValidSubject("*bar"), true, t) - checkBool(IsValidSubject("foo>"), true, t) - checkBool(IsValidSubject("foo.>>"), true, t) - checkBool(IsValidSubject("foo>bar"), true, t) - checkBool(IsValidSubject("foo.>bar"), true, t) - checkBool(IsValidSubject("foo>.bar"), true, t) - checkBool(IsValidSubject(">bar"), true, t) -} - -func TestSublistMatchLiterals(t *testing.T) { - checkBool(matchLiteral("foo", "foo"), true, t) - checkBool(matchLiteral("foo", "bar"), false, t) - checkBool(matchLiteral("foo", "*"), true, t) - checkBool(matchLiteral("foo", ">"), true, t) - checkBool(matchLiteral("foo.bar", ">"), true, t) - checkBool(matchLiteral("foo.bar", "foo.>"), true, t) - checkBool(matchLiteral("foo.bar", "bar.>"), false, t) - checkBool(matchLiteral("stats.test.22", "stats.>"), true, t) - checkBool(matchLiteral("stats.test.22", "stats.*.*"), true, t) - checkBool(matchLiteral("foo.bar", "foo"), false, t) - checkBool(matchLiteral("stats.test.foos", "stats.test.foos"), true, t) - checkBool(matchLiteral("stats.test.foos", "stats.test.foo"), false, t) - checkBool(matchLiteral("stats.test", "stats.test.*"), false, t) - checkBool(matchLiteral("stats.test.foos", "stats.*"), false, t) - checkBool(matchLiteral("stats.test.foos", "stats.*.*.foos"), false, t) - - // These are cases where wildcards characters should not be considered - // wildcards since they do not follow the rules of wildcards. - checkBool(matchLiteral("*bar", "*bar"), true, t) - checkBool(matchLiteral("foo*", "foo*"), true, t) - checkBool(matchLiteral("foo*bar", "foo*bar"), true, t) - checkBool(matchLiteral("foo.***.bar", "foo.***.bar"), true, t) - checkBool(matchLiteral(">bar", ">bar"), true, t) - checkBool(matchLiteral("foo>", "foo>"), true, t) - checkBool(matchLiteral("foo>bar", "foo>bar"), true, t) - checkBool(matchLiteral("foo.>>>.bar", "foo.>>>.bar"), true, t) -} - -func TestSublistBadSubjectOnRemove(t *testing.T) { - bad := "a.b..d" - sub := newSub(bad) - - s := NewSublist() - if err := s.Insert(sub); err != ErrInvalidSubject { - t.Fatalf("Expected ErrInvalidSubject, got %v\n", err) - } - - if err := s.Remove(sub); err != ErrInvalidSubject { - t.Fatalf("Expected ErrInvalidSubject, got %v\n", err) - } - - badfwc := "a.>.b" - if err := s.Remove(newSub(badfwc)); err != ErrInvalidSubject { - t.Fatalf("Expected ErrInvalidSubject, got %v\n", err) - } -} - -// This is from bug report #18 -func TestSublistTwoTokenPubMatchSingleTokenSub(t *testing.T) { - s := NewSublist() - sub := newSub("foo") - s.Insert(sub) - r := s.Match("foo") - verifyLen(r.psubs, 1, t) - verifyMember(r.psubs, sub, t) - r = s.Match("foo.bar") - verifyLen(r.psubs, 0, t) -} - -func TestSublistInsertWithWildcardsAsLiterals(t *testing.T) { - s := NewSublist() - subjects := []string{"foo.*-", "foo.>-"} - for _, subject := range subjects { - sub := newSub(subject) - s.Insert(sub) - // Should find no match - r := s.Match("foo.bar") - verifyLen(r.psubs, 0, t) - // Should find a match - r = s.Match(subject) - verifyLen(r.psubs, 1, t) - } -} - -func TestSublistRemoveWithWildcardsAsLiterals(t *testing.T) { - s := NewSublist() - subjects := []string{"foo.*-", "foo.>-"} - for _, subject := range subjects { - sub := newSub(subject) - s.Insert(sub) - // Should find no match - rsub := newSub("foo.bar") - s.Remove(rsub) - if c := s.Count(); c != 1 { - t.Fatalf("Expected sublist to still contain sub, got %v", c) - } - s.Remove(sub) - if c := s.Count(); c != 0 { - t.Fatalf("Expected sublist to be empty, got %v", c) - } - } -} - -func TestSublistRaceOnRemove(t *testing.T) { - s := NewSublist() - - var ( - total = 100 - subs = make(map[int]*subscription, total) // use map for randomness - ) - for i := 0; i < total; i++ { - sub := newQSub("foo", "bar") - subs[i] = sub - } - - for i := 0; i < 2; i++ { - for _, sub := range subs { - s.Insert(sub) - } - // Call Match() once or twice, to make sure we get from cache - if i == 1 { - s.Match("foo") - } - // This will be from cache when i==1 - r := s.Match("foo") - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - for _, sub := range subs { - s.Remove(sub) - } - wg.Done() - }() - for _, qsub := range r.qsubs { - for i := 0; i < len(qsub); i++ { - sub := qsub[i] - if string(sub.queue) != "bar" { - t.Fatalf("Queue name should be bar, got %s", qsub[i].queue) - } - } - } - wg.Wait() - } - - // Repeat tests with regular subs - for i := 0; i < total; i++ { - sub := newSub("foo") - subs[i] = sub - } - - for i := 0; i < 2; i++ { - for _, sub := range subs { - s.Insert(sub) - } - // Call Match() once or twice, to make sure we get from cache - if i == 1 { - s.Match("foo") - } - // This will be from cache when i==1 - r := s.Match("foo") - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - for _, sub := range subs { - s.Remove(sub) - } - wg.Done() - }() - for i := 0; i < len(r.psubs); i++ { - sub := r.psubs[i] - if string(sub.subject) != "foo" { - t.Fatalf("Subject should be foo, got %s", sub.subject) - } - } - wg.Wait() - } -} - -func TestSublistRaceOnInsert(t *testing.T) { - s := NewSublist() - - var ( - total = 100 - subs = make(map[int]*subscription, total) // use map for randomness - wg sync.WaitGroup - ) - for i := 0; i < total; i++ { - sub := newQSub("foo", "bar") - subs[i] = sub - } - wg.Add(1) - go func() { - for _, sub := range subs { - s.Insert(sub) - } - wg.Done() - }() - for i := 0; i < 1000; i++ { - r := s.Match("foo") - for _, qsubs := range r.qsubs { - for _, qsub := range qsubs { - if string(qsub.queue) != "bar" { - t.Fatalf("Expected queue name to be bar, got %v", string(qsub.queue)) - } - } - } - } - wg.Wait() - - // Repeat the test with plain subs - for i := 0; i < total; i++ { - sub := newSub("foo") - subs[i] = sub - } - wg.Add(1) - go func() { - for _, sub := range subs { - s.Insert(sub) - } - wg.Done() - }() - for i := 0; i < 1000; i++ { - r := s.Match("foo") - for _, sub := range r.psubs { - if string(sub.subject) != "foo" { - t.Fatalf("Expected subject to be foo, got %v", string(sub.subject)) - } - } - } - wg.Wait() -} - -func TestSublistRaceOnMatch(t *testing.T) { - s := NewSublist() - s.Insert(newQSub("foo.*", "workers")) - s.Insert(newQSub("foo.bar", "workers")) - s.Insert(newSub("foo.*")) - s.Insert(newSub("foo.bar")) - - wg := sync.WaitGroup{} - wg.Add(2) - errCh := make(chan error, 2) - f := func() { - defer wg.Done() - for i := 0; i < 10; i++ { - r := s.Match("foo.bar") - for _, sub := range r.psubs { - if !strings.HasPrefix(string(sub.subject), "foo.") { - errCh <- fmt.Errorf("Wrong subject: %s", sub.subject) - return - } - } - for _, qsub := range r.qsubs { - for _, sub := range qsub { - if string(sub.queue) != "workers" { - errCh <- fmt.Errorf("Wrong queue name: %s", sub.queue) - return - } - } - } - // Empty cache to maximize chance for race - s.Lock() - delete(s.cache, "foo.bar") - s.Unlock() - } - } - go f() - go f() - wg.Wait() - select { - case e := <-errCh: - t.Fatalf(e.Error()) - default: - } -} - -// -- Benchmarks Setup -- - -var subs []*subscription -var toks = []string{"apcera", "continuum", "component", "router", "api", "imgr", "jmgr", "auth"} -var sl = NewSublist() - -func init() { - subs = make([]*subscription, 0, 256*1024) - subsInit("") - for i := 0; i < len(subs); i++ { - sl.Insert(subs[i]) - } - addWildcards() -} - -func subsInit(pre string) { - var sub string - for _, t := range toks { - if len(pre) > 0 { - sub = pre + tsep + t - } else { - sub = t - } - subs = append(subs, newSub(sub)) - if len(strings.Split(sub, tsep)) < 5 { - subsInit(sub) - } - } -} - -func addWildcards() { - sl.Insert(newSub("cloud.>")) - sl.Insert(newSub("cloud.continuum.component.>")) - sl.Insert(newSub("cloud.*.*.router.*")) -} - -// -- Benchmarks Setup End -- - -func Benchmark______________________SublistInsert(b *testing.B) { - s := NewSublist() - for i, l := 0, len(subs); i < b.N; i++ { - index := i % l - s.Insert(subs[index]) - } -} - -func Benchmark____________SublistMatchSingleToken(b *testing.B) { - for i := 0; i < b.N; i++ { - sl.Match("apcera") - } -} - -func Benchmark______________SublistMatchTwoTokens(b *testing.B) { - for i := 0; i < b.N; i++ { - sl.Match("apcera.continuum") - } -} - -func Benchmark____________SublistMatchThreeTokens(b *testing.B) { - for i := 0; i < b.N; i++ { - sl.Match("apcera.continuum.component") - } -} - -func Benchmark_____________SublistMatchFourTokens(b *testing.B) { - for i := 0; i < b.N; i++ { - sl.Match("apcera.continuum.component.router") - } -} - -func Benchmark_SublistMatchFourTokensSingleResult(b *testing.B) { - for i := 0; i < b.N; i++ { - sl.Match("apcera.continuum.component.router") - } -} - -func Benchmark_SublistMatchFourTokensMultiResults(b *testing.B) { - for i := 0; i < b.N; i++ { - sl.Match("cloud.continuum.component.router") - } -} - -func Benchmark_______SublistMissOnLastTokenOfFive(b *testing.B) { - for i := 0; i < b.N; i++ { - sl.Match("apcera.continuum.component.router.ZZZZ") - } -} - -func multiRead(b *testing.B, num int) { - b.StopTimer() - var swg, fwg sync.WaitGroup - swg.Add(num) - fwg.Add(num) - s := "apcera.continuum.component.router" - for i := 0; i < num; i++ { - go func() { - swg.Done() - swg.Wait() - for i := 0; i < b.N; i++ { - sl.Match(s) - } - fwg.Done() - }() - } - swg.Wait() - b.StartTimer() - fwg.Wait() -} - -func Benchmark____________Sublist10XMultipleReads(b *testing.B) { - multiRead(b, 10) -} - -func Benchmark___________Sublist100XMultipleReads(b *testing.B) { - multiRead(b, 100) -} - -func Benchmark__________Sublist1000XMultipleReads(b *testing.B) { - multiRead(b, 1000) -} - -func Benchmark________________SublistMatchLiteral(b *testing.B) { - b.StopTimer() - cachedSubj := "foo.foo.foo.foo.foo.foo.foo.foo.foo.foo" - subjects := []string{ - "foo.foo.foo.foo.foo.foo.foo.foo.foo.foo", - "foo.foo.foo.foo.foo.foo.foo.foo.foo.>", - "foo.foo.foo.foo.foo.foo.foo.foo.>", - "foo.foo.foo.foo.foo.foo.foo.>", - "foo.foo.foo.foo.foo.foo.>", - "foo.foo.foo.foo.foo.>", - "foo.foo.foo.foo.>", - "foo.foo.foo.>", - "foo.foo.>", - "foo.>", - ">", - "foo.foo.foo.foo.foo.foo.foo.foo.foo.*", - "foo.foo.foo.foo.foo.foo.foo.foo.*.*", - "foo.foo.foo.foo.foo.foo.foo.*.*.*", - "foo.foo.foo.foo.foo.foo.*.*.*.*", - "foo.foo.foo.foo.foo.*.*.*.*.*", - "foo.foo.foo.foo.*.*.*.*.*.*", - "foo.foo.foo.*.*.*.*.*.*.*", - "foo.foo.*.*.*.*.*.*.*.*", - "foo.*.*.*.*.*.*.*.*.*", - "*.*.*.*.*.*.*.*.*.*", - } - b.StartTimer() - for i := 0; i < b.N; i++ { - for _, subject := range subjects { - if !matchLiteral(cachedSubj, subject) { - b.Fatalf("Subject %q no match with %q", cachedSubj, subject) - } - } - } -} - -func Benchmark_____SublistMatch10kSubsWithNoCache(b *testing.B) { - var nsubs = 512 - b.StopTimer() - s := NewSublist() - subject := "foo" - for i := 0; i < nsubs; i++ { - s.Insert(newSub(subject)) - } - b.StartTimer() - for i := 0; i < b.N; i++ { - r := s.Match(subject) - if len(r.psubs) != nsubs { - b.Fatalf("Results len is %d, should be %d", len(r.psubs), nsubs) - } - delete(s.cache, subject) - } -} - -func removeTest(b *testing.B, singleSubject, doBatch bool, qgroup string) { - b.StopTimer() - s := NewSublist() - subject := "foo" - - subs := make([]*subscription, 0, b.N) - for i := 0; i < b.N; i++ { - var sub *subscription - if singleSubject { - sub = newQSub(subject, qgroup) - } else { - sub = newQSub(fmt.Sprintf("%s.%d\n", subject, i), qgroup) - } - s.Insert(sub) - subs = append(subs, sub) - } - - // Actual test on Remove - b.StartTimer() - if doBatch { - s.RemoveBatch(subs) - } else { - for _, sub := range subs { - s.Remove(sub) - } - } -} - -func Benchmark__________SublistRemove1TokenSingle(b *testing.B) { - removeTest(b, true, false, "") -} - -func Benchmark___________SublistRemove1TokenBatch(b *testing.B) { - removeTest(b, true, true, "") -} - -func Benchmark_________SublistRemove2TokensSingle(b *testing.B) { - removeTest(b, false, false, "") -} - -func Benchmark__________SublistRemove2TokensBatch(b *testing.B) { - removeTest(b, false, true, "") -} - -func Benchmark________SublistRemove1TokenQGSingle(b *testing.B) { - removeTest(b, true, false, "bar") -} - -func Benchmark_________SublistRemove1TokenQGBatch(b *testing.B) { - removeTest(b, true, true, "bar") -} - -func removeMultiTest(b *testing.B, singleSubject, doBatch bool) { - b.StopTimer() - s := NewSublist() - subject := "foo" - var swg, fwg sync.WaitGroup - swg.Add(b.N) - fwg.Add(b.N) - - // We will have b.N go routines each with 1k subscriptions. - sc := 1000 - - for i := 0; i < b.N; i++ { - go func() { - subs := make([]*subscription, 0, sc) - for n := 0; n < sc; n++ { - var sub *subscription - if singleSubject { - sub = newSub(subject) - } else { - sub = newSub(fmt.Sprintf("%s.%d\n", subject, n)) - } - s.Insert(sub) - subs = append(subs, sub) - } - // Wait to start test - swg.Done() - swg.Wait() - // Actual test on Remove - if doBatch { - s.RemoveBatch(subs) - } else { - for _, sub := range subs { - s.Remove(sub) - } - } - fwg.Done() - }() - } - swg.Wait() - b.StartTimer() - fwg.Wait() -} - -// Check contention rates for remove from multiple Go routines. -// Reason for BatchRemove. -func Benchmark_________SublistRemove1kSingleMulti(b *testing.B) { - removeMultiTest(b, true, false) -} - -// Batch version -func Benchmark__________SublistRemove1kBatchMulti(b *testing.B) { - removeMultiTest(b, true, true) -} - -func Benchmark__SublistRemove1kSingle2TokensMulti(b *testing.B) { - removeMultiTest(b, false, false) -} - -// Batch version -func Benchmark___SublistRemove1kBatch2TokensMulti(b *testing.B) { - removeMultiTest(b, false, true) -} diff --git a/vendor/github.com/nats-io/gnatsd/server/util.go b/vendor/github.com/nats-io/gnatsd/server/util.go deleted file mode 100644 index 3a2ffe66..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/util.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "errors" - "fmt" - "net" - "strconv" - "strings" - "time" - - "github.com/nats-io/nuid" -) - -// Use nuid. -func genID() string { - return nuid.Next() -} - -// Ascii numbers 0-9 -const ( - asciiZero = 48 - asciiNine = 57 -) - -// parseSize expects decimal positive numbers. We -// return -1 to signal error. -func parseSize(d []byte) (n int) { - l := len(d) - if l == 0 { - return -1 - } - var ( - i int - dec byte - ) - - // Note: Use `goto` here to avoid for loop in order - // to have the function be inlined. - // See: https://github.com/golang/go/issues/14768 -loop: - dec = d[i] - if dec < asciiZero || dec > asciiNine { - return -1 - } - n = n*10 + (int(dec) - asciiZero) - - i++ - if i < l { - goto loop - } - return n -} - -// parseInt64 expects decimal positive numbers. We -// return -1 to signal error -func parseInt64(d []byte) (n int64) { - if len(d) == 0 { - return -1 - } - for _, dec := range d { - if dec < asciiZero || dec > asciiNine { - return -1 - } - n = n*10 + (int64(dec) - asciiZero) - } - return n -} - -// Helper to move from float seconds to time.Duration -func secondsToDuration(seconds float64) time.Duration { - ttl := seconds * float64(time.Second) - return time.Duration(ttl) -} - -// Parse a host/port string with a default port to use -// if none (or 0 or -1) is specified in `hostPort` string. -func parseHostPort(hostPort string, defaultPort int) (host string, port int, err error) { - if hostPort != "" { - host, sPort, err := net.SplitHostPort(hostPort) - switch err.(type) { - case *net.AddrError: - // try appending the current port - host, sPort, err = net.SplitHostPort(fmt.Sprintf("%s:%d", hostPort, defaultPort)) - } - if err != nil { - return "", -1, err - } - port, err = strconv.Atoi(strings.TrimSpace(sPort)) - if err != nil { - return "", -1, err - } - if port == 0 || port == -1 { - port = defaultPort - } - return strings.TrimSpace(host), port, nil - } - return "", -1, errors.New("No hostport specified") -} diff --git a/vendor/github.com/nats-io/gnatsd/server/util_test.go b/vendor/github.com/nats-io/gnatsd/server/util_test.go deleted file mode 100644 index 29748a42..00000000 --- a/vendor/github.com/nats-io/gnatsd/server/util_test.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "math/rand" - "strconv" - "sync" - "testing" - "time" -) - -func TestParseSize(t *testing.T) { - if parseSize(nil) != -1 { - t.Fatal("Should error on nil byte slice") - } - n := []byte("12345678") - if pn := parseSize(n); pn != 12345678 { - t.Fatalf("Did not parse %q correctly, res=%d\n", n, pn) - } -} - -func TestParseSInt64(t *testing.T) { - if parseInt64(nil) != -1 { - t.Fatal("Should error on nil byte slice") - } - n := []byte("12345678") - if pn := parseInt64(n); pn != 12345678 { - t.Fatalf("Did not parse %q correctly, res=%d\n", n, pn) - } -} - -func TestParseHostPort(t *testing.T) { - check := func(hostPort string, defaultPort int, expectedHost string, expectedPort int, expectedErr bool) { - h, p, err := parseHostPort(hostPort, defaultPort) - if expectedErr { - if err == nil { - stackFatalf(t, "Expected an error, did not get one") - } - // expected error, so we are done - return - } - if !expectedErr && err != nil { - stackFatalf(t, "Unexpected error: %v", err) - } - if expectedHost != h { - stackFatalf(t, "Expected host %q, got %q", expectedHost, h) - } - if expectedPort != p { - stackFatalf(t, "Expected port %d, got %d", expectedPort, p) - } - } - check("addr:1234", 5678, "addr", 1234, false) - check(" addr:1234 ", 5678, "addr", 1234, false) - check(" addr : 1234 ", 5678, "addr", 1234, false) - check("addr", 5678, "addr", 5678, false) - check(" addr ", 5678, "addr", 5678, false) - check("addr:-1", 5678, "addr", 5678, false) - check(" addr:-1 ", 5678, "addr", 5678, false) - check(" addr : -1 ", 5678, "addr", 5678, false) - check("addr:0", 5678, "addr", 5678, false) - check(" addr:0 ", 5678, "addr", 5678, false) - check(" addr : 0 ", 5678, "addr", 5678, false) - check("addr:addr", 0, "", 0, true) - check("addr:::1234", 0, "", 0, true) - check("", 0, "", 0, true) -} - -func BenchmarkParseInt(b *testing.B) { - b.SetBytes(1) - n := "12345678" - for i := 0; i < b.N; i++ { - strconv.ParseInt(n, 10, 0) - } -} - -func BenchmarkParseSize(b *testing.B) { - b.SetBytes(1) - n := []byte("12345678") - for i := 0; i < b.N; i++ { - parseSize(n) - } -} - -func deferUnlock(mu *sync.Mutex) { - mu.Lock() - defer mu.Unlock() - // see noDeferUnlock - if false { - return - } -} - -func BenchmarkDeferMutex(b *testing.B) { - var mu sync.Mutex - b.SetBytes(1) - for i := 0; i < b.N; i++ { - deferUnlock(&mu) - } -} - -func noDeferUnlock(mu *sync.Mutex) { - mu.Lock() - // prevent staticcheck warning about empty critical section - if false { - return - } - mu.Unlock() -} - -func BenchmarkNoDeferMutex(b *testing.B) { - var mu sync.Mutex - b.SetBytes(1) - for i := 0; i < b.N; i++ { - noDeferUnlock(&mu) - } -} - -func createTestSub() *subscription { - return &subscription{ - subject: []byte("foo"), - queue: []byte("bar"), - sid: []byte("22"), - } -} - -func BenchmarkArrayRand(b *testing.B) { - b.StopTimer() - r := rand.New(rand.NewSource(time.Now().UnixNano())) - // Create an array of 10 items - subs := []*subscription{} - for i := 0; i < 10; i++ { - subs = append(subs, createTestSub()) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - index := r.Intn(len(subs)) - _ = subs[index] - } -} - -func BenchmarkMapRange(b *testing.B) { - b.StopTimer() - // Create an map of 10 items - subs := map[int]*subscription{} - for i := 0; i < 10; i++ { - subs[i] = createTestSub() - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - for range subs { - break - } - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/auth_test.go b/vendor/github.com/nats-io/gnatsd/test/auth_test.go deleted file mode 100644 index ed391b25..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/auth_test.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "encoding/json" - "fmt" - "net" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" -) - -func doAuthConnect(t tLogger, c net.Conn, token, user, pass string) { - cs := fmt.Sprintf("CONNECT {\"verbose\":true,\"auth_token\":\"%s\",\"user\":\"%s\",\"pass\":\"%s\"}\r\n", token, user, pass) - sendProto(t, c, cs) -} - -func testInfoForAuth(t tLogger, infojs []byte) bool { - var sinfo server.Info - err := json.Unmarshal(infojs, &sinfo) - if err != nil { - t.Fatalf("Could not unmarshal INFO json: %v\n", err) - } - return sinfo.AuthRequired -} - -func expectAuthRequired(t tLogger, c net.Conn) { - buf := expectResult(t, c, infoRe) - infojs := infoRe.FindAllSubmatch(buf, 1)[0][1] - if !testInfoForAuth(t, infojs) { - t.Fatalf("Expected server to require authorization: '%s'", infojs) - } -} - -//////////////////////////////////////////////////////////// -// The authorization token version -//////////////////////////////////////////////////////////// - -const AUTH_PORT = 10422 -const AUTH_TOKEN = "_YZZ22_" - -func runAuthServerWithToken() *server.Server { - opts := DefaultTestOptions - opts.Port = AUTH_PORT - opts.Authorization = AUTH_TOKEN - return RunServer(&opts) -} - -func TestNoAuthClient(t *testing.T) { - s := runAuthServerWithToken() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", "", "") - expectResult(t, c, errRe) -} - -func TestAuthClientBadToken(t *testing.T) { - s := runAuthServerWithToken() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "ZZZ", "", "") - expectResult(t, c, errRe) -} - -func TestAuthClientNoConnect(t *testing.T) { - s := runAuthServerWithToken() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - // This is timing dependent.. - time.Sleep(server.AUTH_TIMEOUT) - expectResult(t, c, errRe) -} - -func TestAuthClientGoodConnect(t *testing.T) { - s := runAuthServerWithToken() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, AUTH_TOKEN, "", "") - expectResult(t, c, okRe) -} - -func TestAuthClientFailOnEverythingElse(t *testing.T) { - s := runAuthServerWithToken() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - sendProto(t, c, "PUB foo 2\r\nok\r\n") - expectResult(t, c, errRe) -} - -//////////////////////////////////////////////////////////// -// The username/password version -//////////////////////////////////////////////////////////// - -const AUTH_USER = "derek" -const AUTH_PASS = "foobar" - -func runAuthServerWithUserPass() *server.Server { - opts := DefaultTestOptions - opts.Port = AUTH_PORT - opts.Username = AUTH_USER - opts.Password = AUTH_PASS - return RunServer(&opts) -} - -func TestNoUserOrPasswordClient(t *testing.T) { - s := runAuthServerWithUserPass() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", "", "") - expectResult(t, c, errRe) -} - -func TestBadUserClient(t *testing.T) { - s := runAuthServerWithUserPass() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", "derekzz", AUTH_PASS) - expectResult(t, c, errRe) -} - -func TestBadPasswordClient(t *testing.T) { - s := runAuthServerWithUserPass() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", AUTH_USER, "ZZ") - expectResult(t, c, errRe) -} - -func TestPasswordClientGoodConnect(t *testing.T) { - s := runAuthServerWithUserPass() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", AUTH_USER, AUTH_PASS) - expectResult(t, c, okRe) -} - -//////////////////////////////////////////////////////////// -// The bcrypt username/password version -//////////////////////////////////////////////////////////// - -// Generated with util/mkpasswd (Cost 4 because of cost of --race, default is 11) -const BCRYPT_AUTH_PASS = "IW@$6v(y1(t@fhPDvf!5^%" -const BCRYPT_AUTH_HASH = "$2a$04$Q.CgCP2Sl9pkcTXEZHazaeMwPaAkSHk7AI51HkyMt5iJQQyUA4qxq" - -func runAuthServerWithBcryptUserPass() *server.Server { - opts := DefaultTestOptions - opts.Port = AUTH_PORT - opts.Username = AUTH_USER - opts.Password = BCRYPT_AUTH_HASH - return RunServer(&opts) -} - -func TestBadBcryptPassword(t *testing.T) { - s := runAuthServerWithBcryptUserPass() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", AUTH_USER, BCRYPT_AUTH_HASH) - expectResult(t, c, errRe) -} - -func TestGoodBcryptPassword(t *testing.T) { - s := runAuthServerWithBcryptUserPass() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", AUTH_USER, BCRYPT_AUTH_PASS) - expectResult(t, c, okRe) -} - -//////////////////////////////////////////////////////////// -// The bcrypt authorization token version -//////////////////////////////////////////////////////////// - -const BCRYPT_AUTH_TOKEN = "0uhJOSr3GW7xvHvtd^K6pa" -const BCRYPT_AUTH_TOKEN_HASH = "$2a$04$u5ZClXpcjHgpfc61Ee0VKuwI1K3vTC4zq7SjphjnlHMeb1Llkb5Y6" - -func runAuthServerWithBcryptToken() *server.Server { - opts := DefaultTestOptions - opts.Port = AUTH_PORT - opts.Authorization = BCRYPT_AUTH_TOKEN_HASH - return RunServer(&opts) -} - -func TestBadBcryptToken(t *testing.T) { - s := runAuthServerWithBcryptToken() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, BCRYPT_AUTH_TOKEN_HASH, "", "") - expectResult(t, c, errRe) -} - -func TestGoodBcryptToken(t *testing.T) { - s := runAuthServerWithBcryptToken() - defer s.Shutdown() - c := createClientConn(t, "127.0.0.1", AUTH_PORT) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, BCRYPT_AUTH_TOKEN, "", "") - expectResult(t, c, okRe) -} diff --git a/vendor/github.com/nats-io/gnatsd/test/bench_results.txt b/vendor/github.com/nats-io/gnatsd/test/bench_results.txt deleted file mode 100644 index 89850ca5..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/bench_results.txt +++ /dev/null @@ -1,79 +0,0 @@ -2017 iMac Pro 3Ghz (Turbo 4Ghz) 10-Core Skylake -OSX High Sierra 10.13.2 - -=================== -Go version go1.9.2 -=================== - -Benchmark_____Pub0b_Payload-20 30000000 55.1 ns/op 199.78 MB/s -Benchmark_____Pub8b_Payload-20 30000000 55.8 ns/op 340.21 MB/s -Benchmark____Pub32b_Payload-20 20000000 63.4 ns/op 694.34 MB/s -Benchmark___Pub128B_Payload-20 20000000 79.8 ns/op 1766.47 MB/s -Benchmark___Pub256B_Payload-20 20000000 98.1 ns/op 2741.51 MB/s -Benchmark_____Pub1K_Payload-20 5000000 283 ns/op 3660.72 MB/s -Benchmark_____Pub4K_Payload-20 1000000 1395 ns/op 2945.30 MB/s -Benchmark_____Pub8K_Payload-20 500000 2846 ns/op 2882.35 MB/s -Benchmark_AuthPub0b_Payload-20 10000000 126 ns/op 86.82 MB/s -Benchmark____________PubSub-20 10000000 135 ns/op -Benchmark____PubSubTwoConns-20 10000000 136 ns/op -Benchmark____PubTwoQueueSub-20 10000000 152 ns/op -Benchmark___PubFourQueueSub-20 10000000 152 ns/op -Benchmark__PubEightQueueSub-20 10000000 152 ns/op -Benchmark___RoutedPubSub_0b-20 5000000 385 ns/op -Benchmark___RoutedPubSub_1K-20 1000000 1076 ns/op -Benchmark_RoutedPubSub_100K-20 20000 78501 ns/op - - -2015 iMac5k 4Ghz i7 Haswell -OSX El Capitan 10.11.3 - -=================== -Go version go1.6 -=================== - -Benchmark____PubNo_Payload-8 20000000 88.6 ns/op 124.11 MB/s -Benchmark____Pub8b_Payload-8 20000000 89.8 ns/op 211.63 MB/s -Benchmark___Pub32b_Payload-8 20000000 97.3 ns/op 452.20 MB/s -Benchmark__Pub256B_Payload-8 10000000 129 ns/op 2078.43 MB/s -Benchmark____Pub1K_Payload-8 5000000 216 ns/op 4791.00 MB/s -Benchmark____Pub4K_Payload-8 1000000 1123 ns/op 3657.53 MB/s -Benchmark____Pub8K_Payload-8 500000 2309 ns/op 3553.09 MB/s -Benchmark___________PubSub-8 10000000 210 ns/op -Benchmark___PubSubTwoConns-8 10000000 205 ns/op -Benchmark___PubTwoQueueSub-8 10000000 231 ns/op -Benchmark__PubFourQueueSub-8 10000000 233 ns/op -Benchmark_PubEightQueueSub-8 5000000 231 ns/op - -OSX Yosemite 10.10.5 - -=================== -Go version go1.4.2 -=================== - -Benchmark___PubNo_Payload 10000000 133 ns/op 82.44 MB/s -Benchmark___Pub8b_Payload 10000000 135 ns/op 140.27 MB/s -Benchmark__Pub32b_Payload 10000000 147 ns/op 297.56 MB/s -Benchmark_Pub256B_Payload 10000000 211 ns/op 1273.82 MB/s -Benchmark___Pub1K_Payload 3000000 447 ns/op 2321.55 MB/s -Benchmark___Pub4K_Payload 1000000 1677 ns/op 2450.43 MB/s -Benchmark___Pub8K_Payload 300000 3670 ns/op 2235.80 MB/s -Benchmark__________PubSub 5000000 263 ns/op -Benchmark__PubSubTwoConns 5000000 268 ns/op -Benchmark__PubTwoQueueSub 2000000 936 ns/op -Benchmark_PubFourQueueSub 1000000 1103 ns/op - -=================== -Go version go1.5.0 -=================== - -Benchmark___PubNo_Payload-8 10000000 122 ns/op 89.94 MB/s -Benchmark___Pub8b_Payload-8 10000000 124 ns/op 152.72 MB/s -Benchmark__Pub32b_Payload-8 10000000 135 ns/op 325.73 MB/s -Benchmark_Pub256B_Payload-8 10000000 159 ns/op 1685.78 MB/s -Benchmark___Pub1K_Payload-8 5000000 256 ns/op 4047.90 MB/s -Benchmark___Pub4K_Payload-8 1000000 1164 ns/op 3530.77 MB/s -Benchmark___Pub8K_Payload-8 500000 2444 ns/op 3357.34 MB/s -Benchmark__________PubSub-8 5000000 254 ns/op -Benchmark__PubSubTwoConns-8 5000000 245 ns/op -Benchmark__PubTwoQueueSub-8 2000000 845 ns/op -Benchmark_PubFourQueueSub-8 1000000 1004 ns/op diff --git a/vendor/github.com/nats-io/gnatsd/test/bench_test.go b/vendor/github.com/nats-io/gnatsd/test/bench_test.go deleted file mode 100644 index 4f90b411..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/bench_test.go +++ /dev/null @@ -1,674 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "bufio" - "fmt" - "math/rand" - "net" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" -) - -const PERF_PORT = 8422 - -// For Go routine based server. -func runBenchServer() *server.Server { - opts := DefaultTestOptions - opts.Port = PERF_PORT - return RunServer(&opts) -} - -const defaultRecBufSize = 32768 -const defaultSendBufSize = 32768 - -func flushConnection(b *testing.B, c net.Conn) { - buf := make([]byte, 32) - c.Write([]byte("PING\r\n")) - c.SetReadDeadline(time.Now().Add(5 * time.Second)) - n, err := c.Read(buf) - c.SetReadDeadline(time.Time{}) - if err != nil { - b.Fatalf("Failed read: %v\n", err) - } - if n != 6 && buf[0] != 'P' && buf[1] != 'O' { - b.Fatalf("Failed read of PONG: %s\n", buf) - } -} - -func benchPub(b *testing.B, subject, payload string) { - b.StopTimer() - s := runBenchServer() - c := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c) - bw := bufio.NewWriterSize(c, defaultSendBufSize) - sendOp := []byte(fmt.Sprintf("PUB %s %d\r\n%s\r\n", subject, len(payload), payload)) - b.SetBytes(int64(len(sendOp))) - b.StartTimer() - for i := 0; i < b.N; i++ { - bw.Write(sendOp) - } - bw.Flush() - flushConnection(b, c) - b.StopTimer() - c.Close() - s.Shutdown() -} - -var ch = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@$#%^&*()") - -func sizedBytes(sz int) []byte { - b := make([]byte, sz) - for i := range b { - b[i] = ch[rand.Intn(len(ch))] - } - return b -} - -func sizedString(sz int) string { - return string(sizedBytes(sz)) -} - -// Publish subject for pub benchmarks. -var psub = "a" - -func Benchmark______Pub0b_Payload(b *testing.B) { - benchPub(b, psub, "") -} - -func Benchmark______Pub8b_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(8) - benchPub(b, psub, s) -} - -func Benchmark_____Pub32b_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(32) - benchPub(b, psub, s) -} - -func Benchmark____Pub128B_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(128) - benchPub(b, psub, s) -} - -func Benchmark____Pub256B_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(256) - benchPub(b, psub, s) -} - -func Benchmark______Pub1K_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(1024) - benchPub(b, psub, s) -} - -func Benchmark______Pub4K_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(4 * 1024) - benchPub(b, psub, s) -} - -func Benchmark______Pub8K_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(8 * 1024) - benchPub(b, psub, s) -} - -func Benchmark______Pub32K_Payload(b *testing.B) { - b.StopTimer() - s := sizedString(32 * 1024) - benchPub(b, psub, s) -} - -func drainConnection(b *testing.B, c net.Conn, ch chan bool, expected int) { - buf := make([]byte, defaultRecBufSize) - bytes := 0 - - for { - c.SetReadDeadline(time.Now().Add(30 * time.Second)) - n, err := c.Read(buf) - if err != nil { - b.Errorf("Error on read: %v\n", err) - break - } - bytes += n - if bytes >= expected { - break - } - } - if bytes != expected { - b.Errorf("Did not receive all bytes: %d vs %d\n", bytes, expected) - } - ch <- true -} - -// Benchmark the authorization code path. -func Benchmark__AuthPub0b_Payload(b *testing.B) { - b.StopTimer() - - srv, opts := RunServerWithConfig("./configs/authorization.conf") - defer srv.Shutdown() - - c := createClientConn(b, opts.Host, opts.Port) - defer c.Close() - expectAuthRequired(b, c) - - cs := fmt.Sprintf("CONNECT {\"verbose\":false,\"user\":\"%s\",\"pass\":\"%s\"}\r\n", "bench", DefaultPass) - sendProto(b, c, cs) - - bw := bufio.NewWriterSize(c, defaultSendBufSize) - sendOp := []byte("PUB a 0\r\n\r\n") - b.SetBytes(int64(len(sendOp))) - b.StartTimer() - for i := 0; i < b.N; i++ { - bw.Write(sendOp) - } - bw.Flush() - flushConnection(b, c) - b.StopTimer() -} - -func Benchmark_____________PubSub(b *testing.B) { - b.StopTimer() - s := runBenchServer() - c := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c) - sendProto(b, c, "SUB foo 1\r\n") - bw := bufio.NewWriterSize(c, defaultSendBufSize) - sendOp := []byte(fmt.Sprintf("PUB foo 2\r\nok\r\n")) - ch := make(chan bool) - expected := len("MSG foo 1 2\r\nok\r\n") * b.N - go drainConnection(b, c, ch, expected) - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, err := bw.Write(sendOp) - if err != nil { - b.Errorf("Received error on PUB write: %v\n", err) - } - } - err := bw.Flush() - if err != nil { - b.Errorf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - c.Close() - s.Shutdown() -} - -func Benchmark_____PubSubTwoConns(b *testing.B) { - b.StopTimer() - s := runBenchServer() - c := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c) - bw := bufio.NewWriterSize(c, defaultSendBufSize) - - c2 := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c2) - sendProto(b, c2, "SUB foo 1\r\n") - flushConnection(b, c2) - - sendOp := []byte(fmt.Sprintf("PUB foo 2\r\nok\r\n")) - ch := make(chan bool) - - expected := len("MSG foo 1 2\r\nok\r\n") * b.N - go drainConnection(b, c2, ch, expected) - - b.StartTimer() - for i := 0; i < b.N; i++ { - bw.Write(sendOp) - } - err := bw.Flush() - if err != nil { - b.Errorf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - c.Close() - c2.Close() - s.Shutdown() -} - -func Benchmark_PubSub512kTwoConns(b *testing.B) { - b.StopTimer() - s := runBenchServer() - c := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c) - bw := bufio.NewWriterSize(c, defaultSendBufSize) - - c2 := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c2) - sendProto(b, c2, "SUB foo 1\r\n") - flushConnection(b, c2) - - sz := 1024 * 512 - payload := sizedString(sz) - - sendOp := []byte(fmt.Sprintf("PUB foo %d\r\n%s\r\n", sz, payload)) - ch := make(chan bool) - - expected := len(fmt.Sprintf("MSG foo 1 %d\r\n%s\r\n", sz, payload)) * b.N - go drainConnection(b, c2, ch, expected) - - b.StartTimer() - for i := 0; i < b.N; i++ { - bw.Write(sendOp) - } - err := bw.Flush() - if err != nil { - b.Errorf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - c.Close() - c2.Close() - s.Shutdown() -} - -func Benchmark_____PubTwoQueueSub(b *testing.B) { - b.StopTimer() - s := runBenchServer() - c := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c) - sendProto(b, c, "SUB foo group1 1\r\n") - sendProto(b, c, "SUB foo group1 2\r\n") - bw := bufio.NewWriterSize(c, defaultSendBufSize) - sendOp := []byte(fmt.Sprintf("PUB foo 2\r\nok\r\n")) - ch := make(chan bool) - expected := len("MSG foo 1 2\r\nok\r\n") * b.N - go drainConnection(b, c, ch, expected) - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, err := bw.Write(sendOp) - if err != nil { - b.Fatalf("Received error on PUB write: %v\n", err) - } - } - err := bw.Flush() - if err != nil { - b.Fatalf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - c.Close() - s.Shutdown() -} - -func Benchmark____PubFourQueueSub(b *testing.B) { - b.StopTimer() - s := runBenchServer() - c := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c) - sendProto(b, c, "SUB foo group1 1\r\n") - sendProto(b, c, "SUB foo group1 2\r\n") - sendProto(b, c, "SUB foo group1 3\r\n") - sendProto(b, c, "SUB foo group1 4\r\n") - bw := bufio.NewWriterSize(c, defaultSendBufSize) - sendOp := []byte(fmt.Sprintf("PUB foo 2\r\nok\r\n")) - ch := make(chan bool) - expected := len("MSG foo 1 2\r\nok\r\n") * b.N - go drainConnection(b, c, ch, expected) - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, err := bw.Write(sendOp) - if err != nil { - b.Fatalf("Received error on PUB write: %v\n", err) - } - } - err := bw.Flush() - if err != nil { - b.Fatalf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - c.Close() - s.Shutdown() -} - -func Benchmark___PubEightQueueSub(b *testing.B) { - b.StopTimer() - s := runBenchServer() - c := createClientConn(b, "127.0.0.1", PERF_PORT) - doDefaultConnect(b, c) - sendProto(b, c, "SUB foo group1 1\r\n") - sendProto(b, c, "SUB foo group1 2\r\n") - sendProto(b, c, "SUB foo group1 3\r\n") - sendProto(b, c, "SUB foo group1 4\r\n") - sendProto(b, c, "SUB foo group1 5\r\n") - sendProto(b, c, "SUB foo group1 6\r\n") - sendProto(b, c, "SUB foo group1 7\r\n") - sendProto(b, c, "SUB foo group1 8\r\n") - bw := bufio.NewWriterSize(c, defaultSendBufSize) - sendOp := []byte(fmt.Sprintf("PUB foo 2\r\nok\r\n")) - ch := make(chan bool) - expected := len("MSG foo 1 2\r\nok\r\n") * b.N - go drainConnection(b, c, ch, expected) - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, err := bw.Write(sendOp) - if err != nil { - b.Fatalf("Received error on PUB write: %v\n", err) - } - } - err := bw.Flush() - if err != nil { - b.Fatalf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - c.Close() - s.Shutdown() -} - -func routePubSub(b *testing.B, size int) { - b.StopTimer() - - s1, o1 := RunServerWithConfig("./configs/srv_a.conf") - defer s1.Shutdown() - s2, o2 := RunServerWithConfig("./configs/srv_b.conf") - defer s2.Shutdown() - - sub := createClientConn(b, o1.Host, o1.Port) - doDefaultConnect(b, sub) - sendProto(b, sub, "SUB foo 1\r\n") - flushConnection(b, sub) - - payload := sizedString(size) - - pub := createClientConn(b, o2.Host, o2.Port) - doDefaultConnect(b, pub) - bw := bufio.NewWriterSize(pub, defaultSendBufSize) - - ch := make(chan bool) - sendOp := []byte(fmt.Sprintf("PUB foo %d\r\n%s\r\n", len(payload), payload)) - expected := len(fmt.Sprintf("MSG foo 1 %d\r\n%s\r\n", len(payload), payload)) * b.N - go drainConnection(b, sub, ch, expected) - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, err := bw.Write(sendOp) - if err != nil { - b.Fatalf("Received error on PUB write: %v\n", err) - } - - } - err := bw.Flush() - if err != nil { - b.Errorf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - pub.Close() - sub.Close() -} - -func Benchmark____RoutedPubSub_0b(b *testing.B) { - routePubSub(b, 2) -} - -func Benchmark____RoutedPubSub_1K(b *testing.B) { - routePubSub(b, 1024) -} - -func Benchmark__RoutedPubSub_100K(b *testing.B) { - routePubSub(b, 100*1024) -} - -func routeQueue(b *testing.B, numQueueSubs, size int) { - b.StopTimer() - - s1, o1 := RunServerWithConfig("./configs/srv_a.conf") - defer s1.Shutdown() - s2, o2 := RunServerWithConfig("./configs/srv_b.conf") - defer s2.Shutdown() - - sub := createClientConn(b, o1.Host, o1.Port) - doDefaultConnect(b, sub) - for i := 0; i < numQueueSubs; i++ { - sendProto(b, sub, fmt.Sprintf("SUB foo bar %d\r\n", 100+i)) - } - flushConnection(b, sub) - - payload := sizedString(size) - - pub := createClientConn(b, o2.Host, o2.Port) - doDefaultConnect(b, pub) - bw := bufio.NewWriterSize(pub, defaultSendBufSize) - - ch := make(chan bool) - sendOp := []byte(fmt.Sprintf("PUB foo %d\r\n%s\r\n", len(payload), payload)) - expected := len(fmt.Sprintf("MSG foo 100 %d\r\n%s\r\n", len(payload), payload)) * b.N - go drainConnection(b, sub, ch, expected) - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, err := bw.Write(sendOp) - if err != nil { - b.Fatalf("Received error on PUB write: %v\n", err) - } - - } - err := bw.Flush() - if err != nil { - b.Errorf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connection to be drained - <-ch - - b.StopTimer() - pub.Close() - sub.Close() -} - -func Benchmark____Routed2QueueSub(b *testing.B) { - routeQueue(b, 2, 2) -} - -func Benchmark____Routed4QueueSub(b *testing.B) { - routeQueue(b, 4, 2) -} - -func Benchmark____Routed8QueueSub(b *testing.B) { - routeQueue(b, 8, 2) -} - -func Benchmark___Routed16QueueSub(b *testing.B) { - routeQueue(b, 16, 2) -} - -func doFanout(b *testing.B, numServers, numConnections, subsPerConnection int, subject, payload string) { - var s1, s2 *server.Server - var o1, o2 *server.Options - - switch numServers { - case 1: - s1, o1 = RunServerWithConfig("./configs/srv_a.conf") - defer s1.Shutdown() - s2, o2 = s1, o1 - case 2: - s1, o1 = RunServerWithConfig("./configs/srv_a.conf") - defer s1.Shutdown() - s2, o2 = RunServerWithConfig("./configs/srv_b.conf") - defer s2.Shutdown() - default: - b.Fatalf("%d servers not supported for this test\n", numServers) - } - - // To get a consistent length sid in MSG sent to us for drainConnection. - var sidFloor int - switch { - case subsPerConnection <= 100: - sidFloor = 100 - case subsPerConnection <= 1000: - sidFloor = 1000 - case subsPerConnection <= 10000: - sidFloor = 10000 - default: - b.Fatalf("Unsupported SubsPerConnection argument of %d\n", subsPerConnection) - } - - msgOp := fmt.Sprintf("MSG %s %d %d\r\n%s\r\n", subject, sidFloor, len(payload), payload) - expected := len(msgOp) * subsPerConnection * b.N - - // Client connections and subscriptions. - clients := make([]chan bool, 0, numConnections) - for i := 0; i < numConnections; i++ { - c := createClientConn(b, o2.Host, o2.Port) - doDefaultConnect(b, c) - defer c.Close() - - ch := make(chan bool) - clients = append(clients, ch) - - for s := 0; s < subsPerConnection; s++ { - subOp := fmt.Sprintf("SUB %s %d\r\n", subject, sidFloor+s) - sendProto(b, c, subOp) - } - flushConnection(b, c) - go drainConnection(b, c, ch, expected) - } - // Publish Connection - c := createClientConn(b, o1.Host, o1.Port) - doDefaultConnect(b, c) - bw := bufio.NewWriterSize(c, defaultSendBufSize) - sendOp := []byte(fmt.Sprintf("PUB %s %d\r\n%s\r\n", subject, len(payload), payload)) - flushConnection(b, c) - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - _, err := bw.Write(sendOp) - if err != nil { - b.Errorf("Received error on PUB write: %v\n", err) - } - } - err := bw.Flush() - if err != nil { - b.Errorf("Received error on FLUSH write: %v\n", err) - } - - // Wait for connections to be drained - for i := 0; i < numConnections; i++ { - <-clients[i] - } - b.StopTimer() -} - -var sub = "x" -var payload = "12345678" - -func Benchmark___FanOut_512x1kx1k(b *testing.B) { - doFanout(b, 1, 1000, 1000, sub, sizedString(512)) -} - -func Benchmark__FanOut_8x1000x100(b *testing.B) { - doFanout(b, 1, 1000, 100, sub, payload) -} - -func Benchmark______FanOut_8x1x10(b *testing.B) { - doFanout(b, 1, 1, 10, sub, payload) -} - -func Benchmark_____FanOut_8x1x100(b *testing.B) { - doFanout(b, 1, 1, 100, sub, payload) -} - -func Benchmark____FanOut_8x10x100(b *testing.B) { - doFanout(b, 1, 10, 100, sub, payload) -} - -func Benchmark___FanOut_8x10x1000(b *testing.B) { - doFanout(b, 1, 10, 1000, sub, payload) -} - -func Benchmark___FanOut_8x100x100(b *testing.B) { - doFanout(b, 1, 100, 100, sub, payload) -} - -func Benchmark__FanOut_8x100x1000(b *testing.B) { - doFanout(b, 1, 100, 1000, sub, payload) -} - -func Benchmark__FanOut_8x10x10000(b *testing.B) { - doFanout(b, 1, 10, 10000, sub, payload) -} - -func Benchmark__FanOut_1kx10x1000(b *testing.B) { - doFanout(b, 1, 10, 1000, sub, sizedString(1024)) -} - -func Benchmark_____RFanOut_8x1x10(b *testing.B) { - doFanout(b, 2, 1, 10, sub, payload) -} - -func Benchmark____RFanOut_8x1x100(b *testing.B) { - doFanout(b, 2, 1, 100, sub, payload) -} - -func Benchmark___RFanOut_8x10x100(b *testing.B) { - doFanout(b, 2, 10, 100, sub, payload) -} - -func Benchmark__RFanOut_8x10x1000(b *testing.B) { - doFanout(b, 2, 10, 1000, sub, payload) -} - -func Benchmark__RFanOut_8x100x100(b *testing.B) { - doFanout(b, 2, 100, 100, sub, payload) -} - -func Benchmark_RFanOut_8x100x1000(b *testing.B) { - doFanout(b, 2, 100, 1000, sub, payload) -} - -func Benchmark_RFanOut_8x10x10000(b *testing.B) { - doFanout(b, 2, 10, 10000, sub, payload) -} - -func Benchmark_RFanOut_1kx10x1000(b *testing.B) { - doFanout(b, 2, 10, 1000, sub, sizedString(1024)) -} diff --git a/vendor/github.com/nats-io/gnatsd/test/client_auth_test.go b/vendor/github.com/nats-io/gnatsd/test/client_auth_test.go deleted file mode 100644 index 9255c4bd..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/client_auth_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "fmt" - "io/ioutil" - "os" - "testing" - - "github.com/nats-io/go-nats" -) - -func TestMultipleUserAuth(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/multi_user.conf") - defer srv.Shutdown() - - if opts.Users == nil { - t.Fatal("Expected a user array that is not nil") - } - if len(opts.Users) != 2 { - t.Fatal("Expected a user array that had 2 users") - } - - // Test first user - url := fmt.Sprintf("nats://%s:%s@%s:%d/", - opts.Users[0].Username, - opts.Users[0].Password, - opts.Host, opts.Port) - - nc, err := nats.Connect(url) - if err != nil { - t.Fatalf("Expected a successful connect, got %v\n", err) - } - defer nc.Close() - - if !nc.AuthRequired() { - t.Fatal("Expected auth to be required for the server") - } - - // Test second user - url = fmt.Sprintf("nats://%s:%s@%s:%d/", - opts.Users[1].Username, - opts.Users[1].Password, - opts.Host, opts.Port) - - nc, err = nats.Connect(url) - if err != nil { - t.Fatalf("Expected a successful connect, got %v\n", err) - } - defer nc.Close() -} - -// Resolves to "test" -const testToken = "$2a$05$3sSWEVA1eMCbV0hWavDjXOx.ClBjI6u1CuUdLqf22cbJjXsnzz8/." - -func TestTokenInConfig(t *testing.T) { - confFileName := "test.conf" - defer os.Remove(confFileName) - content := ` - listen: 127.0.0.1:4567 - authorization={ - token: ` + testToken + ` - timeout: 5 - }` - if err := ioutil.WriteFile(confFileName, []byte(content), 0666); err != nil { - t.Fatalf("Error writing config file: %v", err) - } - s, opts := RunServerWithConfig(confFileName) - defer s.Shutdown() - - url := fmt.Sprintf("nats://test@%s:%d/", opts.Host, opts.Port) - nc, err := nats.Connect(url) - if err != nil { - t.Fatalf("Expected a successful connect, got %v\n", err) - } - defer nc.Close() - if !nc.AuthRequired() { - t.Fatal("Expected auth to be required for the server") - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/client_cluster_test.go b/vendor/github.com/nats-io/gnatsd/test/client_cluster_test.go deleted file mode 100644 index 503a3e5e..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/client_cluster_test.go +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "fmt" - "math/rand" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/nats-io/go-nats" -) - -func TestServerRestartReSliceIssue(t *testing.T) { - srvA, srvB, optsA, optsB := runServers(t) - defer srvA.Shutdown() - - urlA := fmt.Sprintf("nats://%s:%d/", optsA.Host, optsA.Port) - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, optsB.Port) - - // msg to send.. - msg := []byte("Hello World") - - servers := []string{urlA, urlB} - - opts := nats.GetDefaultOptions() - opts.Timeout = (5 * time.Second) - opts.ReconnectWait = (50 * time.Millisecond) - opts.MaxReconnect = 1000 - - numClients := 20 - - reconnects := int32(0) - reconnectsDone := make(chan bool, numClients) - opts.ReconnectedCB = func(nc *nats.Conn) { - atomic.AddInt32(&reconnects, 1) - reconnectsDone <- true - } - - clients := make([]*nats.Conn, numClients) - - // Create 20 random clients. - // Half connected to A and half to B.. - for i := 0; i < numClients; i++ { - opts.Url = servers[i%2] - nc, err := opts.Connect() - if err != nil { - t.Fatalf("Failed to create connection: %v\n", err) - } - clients[i] = nc - defer nc.Close() - - // Create 10 subscriptions each.. - for x := 0; x < 10; x++ { - subject := fmt.Sprintf("foo.%d", (rand.Int()%50)+1) - nc.Subscribe(subject, func(m *nats.Msg) { - // Just eat it.. - }) - } - // Pick one subject to send to.. - subject := fmt.Sprintf("foo.%d", (rand.Int()%50)+1) - go func() { - time.Sleep(10 * time.Millisecond) - for i := 1; i <= 100; i++ { - if err := nc.Publish(subject, msg); err != nil { - return - } - if i%10 == 0 { - time.Sleep(time.Millisecond) - } - } - }() - } - - // Wait for a short bit.. - time.Sleep(20 * time.Millisecond) - - // Restart SrvB - srvB.Shutdown() - srvB = RunServer(optsB) - defer srvB.Shutdown() - - // Check that all expected clients have reconnected - done := false - for i := 0; i < numClients/2 && !done; i++ { - select { - case <-reconnectsDone: - done = true - case <-time.After(3 * time.Second): - t.Fatalf("Expected %d reconnects, got %d\n", numClients/2, reconnects) - } - } - - // Since srvB was restarted, its defer Shutdown() was last, so will - // exectue first, which would cause clients that have reconnected to - // it to try to reconnect (causing delays on Windows). So let's - // explicitly close them here. - // NOTE: With fix of NATS GO client (reconnect loop yields to Close()), - // this change would not be required, however, it still speeeds up - // the test, from more than 7s to less than one. - for i := 0; i < numClients; i++ { - nc := clients[i] - nc.Close() - } -} - -// This will test queue subscriber semantics across a cluster in the presence -// of server restarts. -func TestServerRestartAndQueueSubs(t *testing.T) { - srvA, srvB, optsA, optsB := runServers(t) - - urlA := fmt.Sprintf("nats://%s:%d/", optsA.Host, optsA.Port) - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, optsB.Port) - - // Client options - opts := nats.GetDefaultOptions() - opts.Timeout = (5 * time.Second) - opts.ReconnectWait = (50 * time.Millisecond) - opts.MaxReconnect = 1000 - opts.NoRandomize = true - - // Allow us to block on a reconnect completion. - reconnectsDone := make(chan bool) - opts.ReconnectedCB = func(nc *nats.Conn) { - reconnectsDone <- true - } - - // Helper to wait on a reconnect. - waitOnReconnect := func() { - var rcs int64 - for { - select { - case <-reconnectsDone: - atomic.AddInt64(&rcs, 1) - if rcs >= 2 { - return - } - case <-time.After(2 * time.Second): - t.Fatalf("Expected a reconnect, timedout!\n") - } - } - } - - // Create two clients.. - opts.Servers = []string{urlA} - nc1, err := opts.Connect() - if err != nil { - t.Fatalf("Failed to create connection for nc1: %v\n", err) - } - - opts.Servers = []string{urlB} - nc2, err := opts.Connect() - if err != nil { - t.Fatalf("Failed to create connection for nc2: %v\n", err) - } - - c1, _ := nats.NewEncodedConn(nc1, "json") - defer c1.Close() - c2, _ := nats.NewEncodedConn(nc2, "json") - defer c2.Close() - - // Flusher helper function. - flush := func() { - // Wait for processing. - c1.Flush() - c2.Flush() - // Wait for a short bit for cluster propagation. - time.Sleep(50 * time.Millisecond) - } - - // To hold queue results. - results := make(map[int]int) - var mu sync.Mutex - - // This corresponds to the subsriptions below. - const ExpectedMsgCount = 3 - - // Make sure we got what we needed, 1 msg only and all seqnos accounted for.. - checkResults := func(numSent int) { - mu.Lock() - defer mu.Unlock() - - for i := 0; i < numSent; i++ { - if results[i] != ExpectedMsgCount { - t.Fatalf("Received incorrect number of messages, [%d] vs [%d] for seq: %d\n", results[i], ExpectedMsgCount, i) - } - } - - // Auto reset results map - results = make(map[int]int) - } - - subj := "foo.bar" - qgroup := "workers" - - cb := func(seqno int) { - mu.Lock() - defer mu.Unlock() - results[seqno] = results[seqno] + 1 - } - - // Create queue subscribers - c1.QueueSubscribe(subj, qgroup, cb) - c2.QueueSubscribe(subj, qgroup, cb) - - // Do a wildcard subscription. - c1.Subscribe("foo.*", cb) - c2.Subscribe("foo.*", cb) - - // Wait for processing. - flush() - - sendAndCheckMsgs := func(numToSend int) { - for i := 0; i < numToSend; i++ { - if i%2 == 0 { - c1.Publish(subj, i) - } else { - c2.Publish(subj, i) - } - } - // Wait for processing. - flush() - // Check Results - checkResults(numToSend) - } - - //////////////////////////////////////////////////////////////////////////// - // Base Test - //////////////////////////////////////////////////////////////////////////// - - // Make sure subscriptions are propagated in the cluster - if err := checkExpectedSubs(4, srvA, srvB); err != nil { - t.Fatalf("%v", err) - } - - // Now send 10 messages, from each client.. - sendAndCheckMsgs(10) - - //////////////////////////////////////////////////////////////////////////// - // Now restart SrvA and srvB, re-run test - //////////////////////////////////////////////////////////////////////////// - - srvA.Shutdown() - srvA = RunServer(optsA) - defer srvA.Shutdown() - - srvB.Shutdown() - srvB = RunServer(optsB) - defer srvB.Shutdown() - - waitOnReconnect() - - // Make sure the cluster is reformed - checkClusterFormed(t, srvA, srvB) - - // Make sure subscriptions are propagated in the cluster - if err := checkExpectedSubs(4, srvA, srvB); err != nil { - t.Fatalf("%v", err) - } - - // Now send another 10 messages, from each client.. - sendAndCheckMsgs(10) - - // Since servers are restarted after all client's close defer calls, - // their defer Shutdown() are last, and so will be executed first, - // which would cause clients to try to reconnect on exit, causing - // delays on Windows. So let's explicitly close them here. - c1.Close() - c2.Close() -} - -// This will test request semantics across a route -func TestRequestsAcrossRoutes(t *testing.T) { - srvA, srvB, optsA, optsB := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - urlA := fmt.Sprintf("nats://%s:%d/", optsA.Host, optsA.Port) - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, optsB.Port) - - nc1, err := nats.Connect(urlA) - if err != nil { - t.Fatalf("Failed to create connection for nc1: %v\n", err) - } - defer nc1.Close() - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Failed to create connection for nc2: %v\n", err) - } - defer nc2.Close() - - ec2, _ := nats.NewEncodedConn(nc2, nats.JSON_ENCODER) - - response := []byte("I will help you") - - // Connect responder to srvA - nc1.Subscribe("foo-req", func(m *nats.Msg) { - nc1.Publish(m.Reply, response) - }) - // Make sure the route and the subscription are propagated. - nc1.Flush() - - var resp string - - for i := 0; i < 100; i++ { - if err := ec2.Request("foo-req", i, &resp, 100*time.Millisecond); err != nil { - t.Fatalf("Received an error on Request test [%d]: %s", i, err) - } - } -} - -// This will test request semantics across a route to queues -func TestRequestsAcrossRoutesToQueues(t *testing.T) { - srvA, srvB, optsA, optsB := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - urlA := fmt.Sprintf("nats://%s:%d/", optsA.Host, optsA.Port) - urlB := fmt.Sprintf("nats://%s:%d/", optsB.Host, optsB.Port) - - nc1, err := nats.Connect(urlA) - if err != nil { - t.Fatalf("Failed to create connection for nc1: %v\n", err) - } - defer nc1.Close() - - nc2, err := nats.Connect(urlB) - if err != nil { - t.Fatalf("Failed to create connection for nc2: %v\n", err) - } - defer nc2.Close() - - ec1, _ := nats.NewEncodedConn(nc1, nats.JSON_ENCODER) - ec2, _ := nats.NewEncodedConn(nc2, nats.JSON_ENCODER) - - response := []byte("I will help you") - - // Connect one responder to srvA - nc1.QueueSubscribe("foo-req", "booboo", func(m *nats.Msg) { - nc1.Publish(m.Reply, response) - }) - // Make sure the route and the subscription are propagated. - nc1.Flush() - - // Connect the other responder to srvB - nc2.QueueSubscribe("foo-req", "booboo", func(m *nats.Msg) { - nc2.Publish(m.Reply, response) - }) - - var resp string - - for i := 0; i < 100; i++ { - if err := ec2.Request("foo-req", i, &resp, 500*time.Millisecond); err != nil { - t.Fatalf("Received an error on Request test [%d]: %s", i, err) - } - } - - for i := 0; i < 100; i++ { - if err := ec1.Request("foo-req", i, &resp, 500*time.Millisecond); err != nil { - t.Fatalf("Received an error on Request test [%d]: %s", i, err) - } - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/cluster_test.go b/vendor/github.com/nats-io/gnatsd/test/cluster_test.go deleted file mode 100644 index 4bca2fc3..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/cluster_test.go +++ /dev/null @@ -1,491 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "errors" - "fmt" - "runtime" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" -) - -// Helper function to check that a cluster is formed -func checkClusterFormed(t *testing.T, servers ...*server.Server) { - t.Helper() - expectedNumRoutes := len(servers) - 1 - checkFor(t, 10*time.Second, 100*time.Millisecond, func() error { - for _, s := range servers { - if numRoutes := s.NumRoutes(); numRoutes != expectedNumRoutes { - return fmt.Errorf("Expected %d routes for server %q, got %d", expectedNumRoutes, s.ID(), numRoutes) - } - } - return nil - }) -} - -func checkNumRoutes(t *testing.T, s *server.Server, expected int) { - t.Helper() - checkFor(t, 5*time.Second, 15*time.Millisecond, func() error { - if nr := s.NumRoutes(); nr != expected { - return fmt.Errorf("Expected %v routes, got %v", expected, nr) - } - return nil - }) -} - -// Helper function to check that a server (or list of servers) have the -// expected number of subscriptions. -func checkExpectedSubs(expected int, servers ...*server.Server) error { - var err string - maxTime := time.Now().Add(10 * time.Second) - for time.Now().Before(maxTime) { - err = "" - for _, s := range servers { - if numSubs := int(s.NumSubscriptions()); numSubs != expected { - err = fmt.Sprintf("Expected %d subscriptions for server %q, got %d", expected, s.ID(), numSubs) - break - } - } - if err != "" { - time.Sleep(10 * time.Millisecond) - } else { - break - } - } - if err != "" { - return errors.New(err) - } - return nil -} - -func runServers(t *testing.T) (srvA, srvB *server.Server, optsA, optsB *server.Options) { - srvA, optsA = RunServerWithConfig("./configs/srv_a.conf") - srvB, optsB = RunServerWithConfig("./configs/srv_b.conf") - - checkClusterFormed(t, srvA, srvB) - return -} - -func TestProperServerWithRoutesShutdown(t *testing.T) { - before := runtime.NumGoroutine() - srvA, srvB, _, _ := runServers(t) - srvA.Shutdown() - srvB.Shutdown() - time.Sleep(100 * time.Millisecond) - - after := runtime.NumGoroutine() - delta := after - before - // There may be some finalizers or IO, but in general more than - // 2 as a delta represents a problem. - if delta > 2 { - t.Fatalf("Expected same number of goroutines, %d vs %d\n", before, after) - } -} - -func TestDoubleRouteConfig(t *testing.T) { - srvA, srvB, _, _ := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() -} - -func TestBasicClusterPubSub(t *testing.T) { - srvA, srvB, optsA, optsB := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - clientA := createClientConn(t, optsA.Host, optsA.Port) - defer clientA.Close() - - clientB := createClientConn(t, optsB.Host, optsB.Port) - defer clientB.Close() - - sendA, expectA := setupConn(t, clientA) - sendA("SUB foo 22\r\n") - sendA("PING\r\n") - expectA(pongRe) - - if err := checkExpectedSubs(1, srvA, srvB); err != nil { - t.Fatalf("%v", err) - } - - sendB, expectB := setupConn(t, clientB) - sendB("PUB foo 2\r\nok\r\n") - sendB("PING\r\n") - expectB(pongRe) - - expectMsgs := expectMsgsCommand(t, expectA) - - matches := expectMsgs(1) - checkMsg(t, matches[0], "foo", "22", "", "2", "ok") -} - -func TestClusterQueueSubs(t *testing.T) { - srvA, srvB, optsA, optsB := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - clientA := createClientConn(t, optsA.Host, optsA.Port) - defer clientA.Close() - - clientB := createClientConn(t, optsB.Host, optsB.Port) - defer clientB.Close() - - sendA, expectA := setupConn(t, clientA) - sendB, expectB := setupConn(t, clientB) - - expectMsgsA := expectMsgsCommand(t, expectA) - expectMsgsB := expectMsgsCommand(t, expectB) - - // Capture sids for checking later. - qg1SidsA := []string{"1", "2", "3"} - - // Three queue subscribers - for _, sid := range qg1SidsA { - sendA(fmt.Sprintf("SUB foo qg1 %s\r\n", sid)) - } - sendA("PING\r\n") - expectA(pongRe) - - // Make sure the subs have propagated to srvB before continuing - if err := checkExpectedSubs(len(qg1SidsA), srvB); err != nil { - t.Fatalf("%v", err) - } - - sendB("PUB foo 2\r\nok\r\n") - sendB("PING\r\n") - expectB(pongRe) - - // Make sure we get only 1. - matches := expectMsgsA(1) - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - - // Capture sids for checking later. - pSids := []string{"4", "5", "6"} - - // Create 3 normal subscribers - for _, sid := range pSids { - sendA(fmt.Sprintf("SUB foo %s\r\n", sid)) - } - - // Create a FWC Subscriber - pSids = append(pSids, "7") - sendA("SUB > 7\r\n") - sendA("PING\r\n") - expectA(pongRe) - - // Make sure the subs have propagated to srvB before continuing - if err := checkExpectedSubs(len(qg1SidsA)+len(pSids), srvB); err != nil { - t.Fatalf("%v", err) - } - - // Send to B - sendB("PUB foo 2\r\nok\r\n") - sendB("PING\r\n") - expectB(pongRe) - - // Should receive 5. - matches = expectMsgsA(5) - checkForQueueSid(t, matches, qg1SidsA) - checkForPubSids(t, matches, pSids) - - // Send to A - sendA("PUB foo 2\r\nok\r\n") - - // Should receive 5. - matches = expectMsgsA(5) - checkForQueueSid(t, matches, qg1SidsA) - checkForPubSids(t, matches, pSids) - - // Now add queue subscribers to B - qg2SidsB := []string{"1", "2", "3"} - for _, sid := range qg2SidsB { - sendB(fmt.Sprintf("SUB foo qg2 %s\r\n", sid)) - } - sendB("PING\r\n") - expectB(pongRe) - - // Make sure the subs have propagated to srvA before continuing - if err := checkExpectedSubs(len(qg1SidsA)+len(pSids)+len(qg2SidsB), srvA); err != nil { - t.Fatalf("%v", err) - } - - // Send to B - sendB("PUB foo 2\r\nok\r\n") - - // Should receive 1 from B. - matches = expectMsgsB(1) - checkForQueueSid(t, matches, qg2SidsB) - - // Should receive 5 still from A. - matches = expectMsgsA(5) - checkForQueueSid(t, matches, qg1SidsA) - checkForPubSids(t, matches, pSids) - - // Now drop queue subscribers from A - for _, sid := range qg1SidsA { - sendA(fmt.Sprintf("UNSUB %s\r\n", sid)) - } - sendA("PING\r\n") - expectA(pongRe) - - // Make sure the subs have propagated to srvB before continuing - if err := checkExpectedSubs(len(pSids)+len(qg2SidsB), srvB); err != nil { - t.Fatalf("%v", err) - } - - // Send to B - sendB("PUB foo 2\r\nok\r\n") - - // Should receive 1 from B. - matches = expectMsgsB(1) - checkForQueueSid(t, matches, qg2SidsB) - - sendB("PING\r\n") - expectB(pongRe) - - // Should receive 4 now. - matches = expectMsgsA(4) - checkForPubSids(t, matches, pSids) - - // Send to A - sendA("PUB foo 2\r\nok\r\n") - - // Should receive 4 now. - matches = expectMsgsA(4) - checkForPubSids(t, matches, pSids) -} - -// Issue #22 -func TestClusterDoubleMsgs(t *testing.T) { - srvA, srvB, optsA, optsB := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - clientA1 := createClientConn(t, optsA.Host, optsA.Port) - defer clientA1.Close() - - clientA2 := createClientConn(t, optsA.Host, optsA.Port) - defer clientA2.Close() - - clientB := createClientConn(t, optsB.Host, optsB.Port) - defer clientB.Close() - - sendA1, expectA1 := setupConn(t, clientA1) - sendA2, expectA2 := setupConn(t, clientA2) - sendB, expectB := setupConn(t, clientB) - - expectMsgsA1 := expectMsgsCommand(t, expectA1) - expectMsgsA2 := expectMsgsCommand(t, expectA2) - - // Capture sids for checking later. - qg1SidsA := []string{"1", "2", "3"} - - // Three queue subscribers - for _, sid := range qg1SidsA { - sendA1(fmt.Sprintf("SUB foo qg1 %s\r\n", sid)) - } - sendA1("PING\r\n") - expectA1(pongRe) - - // Make sure the subs have propagated to srvB before continuing - if err := checkExpectedSubs(len(qg1SidsA), srvB); err != nil { - t.Fatalf("%v", err) - } - - sendB("PUB foo 2\r\nok\r\n") - sendB("PING\r\n") - expectB(pongRe) - - // Make sure we get only 1. - matches := expectMsgsA1(1) - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - checkForQueueSid(t, matches, qg1SidsA) - - // Add a FWC subscriber on A2 - sendA2("SUB > 1\r\n") - sendA2("SUB foo 2\r\n") - sendA2("PING\r\n") - expectA2(pongRe) - pSids := []string{"1", "2"} - - // Make sure the subs have propagated to srvB before continuing - if err := checkExpectedSubs(len(qg1SidsA)+2, srvB); err != nil { - t.Fatalf("%v", err) - } - - sendB("PUB foo 2\r\nok\r\n") - sendB("PING\r\n") - expectB(pongRe) - - matches = expectMsgsA1(1) - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - checkForQueueSid(t, matches, qg1SidsA) - - matches = expectMsgsA2(2) - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - checkForPubSids(t, matches, pSids) - - // Close ClientA1 - clientA1.Close() - - sendB("PUB foo 2\r\nok\r\n") - sendB("PING\r\n") - expectB(pongRe) - - matches = expectMsgsA2(2) - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - checkForPubSids(t, matches, pSids) -} - -// This will test that we drop remote sids correctly. -func TestClusterDropsRemoteSids(t *testing.T) { - srvA, srvB, optsA, _ := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - clientA := createClientConn(t, optsA.Host, optsA.Port) - defer clientA.Close() - - sendA, expectA := setupConn(t, clientA) - - // Add a subscription - sendA("SUB foo 1\r\n") - sendA("PING\r\n") - expectA(pongRe) - - // Wait for propagation. - time.Sleep(100 * time.Millisecond) - - if sc := srvA.NumSubscriptions(); sc != 1 { - t.Fatalf("Expected one subscription for srvA, got %d\n", sc) - } - if sc := srvB.NumSubscriptions(); sc != 1 { - t.Fatalf("Expected one subscription for srvB, got %d\n", sc) - } - - // Add another subscription - sendA("SUB bar 2\r\n") - sendA("PING\r\n") - expectA(pongRe) - - // Wait for propagation. - time.Sleep(100 * time.Millisecond) - - if sc := srvA.NumSubscriptions(); sc != 2 { - t.Fatalf("Expected two subscriptions for srvA, got %d\n", sc) - } - if sc := srvB.NumSubscriptions(); sc != 2 { - t.Fatalf("Expected two subscriptions for srvB, got %d\n", sc) - } - - // unsubscription - sendA("UNSUB 1\r\n") - sendA("PING\r\n") - expectA(pongRe) - - // Wait for propagation. - time.Sleep(100 * time.Millisecond) - - if sc := srvA.NumSubscriptions(); sc != 1 { - t.Fatalf("Expected one subscription for srvA, got %d\n", sc) - } - if sc := srvB.NumSubscriptions(); sc != 1 { - t.Fatalf("Expected one subscription for srvB, got %d\n", sc) - } - - // Close the client and make sure we remove subscription state. - clientA.Close() - - // Wait for propagation. - time.Sleep(100 * time.Millisecond) - if sc := srvA.NumSubscriptions(); sc != 0 { - t.Fatalf("Expected no subscriptions for srvA, got %d\n", sc) - } - if sc := srvB.NumSubscriptions(); sc != 0 { - t.Fatalf("Expected no subscriptions for srvB, got %d\n", sc) - } -} - -// This will test that we drop remote sids correctly. -func TestAutoUnsubscribePropagation(t *testing.T) { - srvA, srvB, optsA, _ := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - clientA := createClientConn(t, optsA.Host, optsA.Port) - defer clientA.Close() - - sendA, expectA := setupConn(t, clientA) - expectMsgs := expectMsgsCommand(t, expectA) - - // We will create subscriptions that will auto-unsubscribe and make sure - // we are not accumulating orphan subscriptions on the other side. - for i := 1; i <= 100; i++ { - sub := fmt.Sprintf("SUB foo %d\r\n", i) - auto := fmt.Sprintf("UNSUB %d 1\r\n", i) - sendA(sub) - sendA(auto) - // This will trip the auto-unsubscribe - sendA("PUB foo 2\r\nok\r\n") - expectMsgs(1) - } - - sendA("PING\r\n") - expectA(pongRe) - - time.Sleep(50 * time.Millisecond) - - // Make sure number of subscriptions on B is correct - if subs := srvB.NumSubscriptions(); subs != 0 { - t.Fatalf("Expected no subscriptions on remote server, got %d\n", subs) - } -} - -func TestAutoUnsubscribePropagationOnClientDisconnect(t *testing.T) { - srvA, srvB, optsA, _ := runServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - cluster := []*server.Server{srvA, srvB} - - clientA := createClientConn(t, optsA.Host, optsA.Port) - defer clientA.Close() - - sendA, expectA := setupConn(t, clientA) - - // No subscriptions. Ready to test. - if err := checkExpectedSubs(0, cluster...); err != nil { - t.Fatalf("%v", err) - } - - sendA("SUB foo 1\r\n") - sendA("UNSUB 1 1\r\n") - sendA("PING\r\n") - expectA(pongRe) - - // Waiting cluster subs propagation - if err := checkExpectedSubs(1, cluster...); err != nil { - t.Fatalf("%v", err) - } - - clientA.Close() - - // No subs should be on the cluster when all clients is disconnected - if err := checkExpectedSubs(0, cluster...); err != nil { - t.Fatalf("%v", err) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/cluster_tls_test.go b/vendor/github.com/nats-io/gnatsd/test/cluster_tls_test.go deleted file mode 100644 index e215667f..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/cluster_tls_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "testing" - - "github.com/nats-io/gnatsd/server" -) - -func runTLSServers(t *testing.T) (srvA, srvB *server.Server, optsA, optsB *server.Options) { - srvA, optsA = RunServerWithConfig("./configs/srv_a_tls.conf") - srvB, optsB = RunServerWithConfig("./configs/srv_b_tls.conf") - checkClusterFormed(t, srvA, srvB) - return -} - -func TestTLSClusterConfig(t *testing.T) { - srvA, srvB, _, _ := runTLSServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() -} - -func TestBasicTLSClusterPubSub(t *testing.T) { - srvA, srvB, optsA, optsB := runTLSServers(t) - defer srvA.Shutdown() - defer srvB.Shutdown() - - clientA := createClientConn(t, optsA.Host, optsA.Port) - defer clientA.Close() - - clientB := createClientConn(t, optsB.Host, optsB.Port) - defer clientB.Close() - - sendA, expectA := setupConn(t, clientA) - sendA("SUB foo 22\r\n") - sendA("PING\r\n") - expectA(pongRe) - - sendB, expectB := setupConn(t, clientB) - sendB("PUB foo 2\r\nok\r\n") - sendB("PING\r\n") - expectB(pongRe) - - if err := checkExpectedSubs(1, srvA, srvB); err != nil { - t.Fatalf("%v", err) - } - - expectMsgs := expectMsgsCommand(t, expectA) - - matches := expectMsgs(1) - checkMsg(t, matches[0], "foo", "22", "", "2", "ok") -} diff --git a/vendor/github.com/nats-io/gnatsd/test/fanout_test.go b/vendor/github.com/nats-io/gnatsd/test/fanout_test.go deleted file mode 100644 index c7350100..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/fanout_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !race - -package test - -import ( - "fmt" - "sync" - "testing" - - "github.com/nats-io/gnatsd/server" - "github.com/nats-io/go-nats" -) - -// IMPORTANT: Tests in this file are not executed when running with the -race flag. - -// As we look to improve high fanout situations make sure we -// have a test that checks ordering for all subscriptions from a single subscriber. -func TestHighFanoutOrdering(t *testing.T) { - opts := &server.Options{Host: "127.0.0.1", Port: server.RANDOM_PORT} - - s := RunServer(opts) - defer s.Shutdown() - - url := fmt.Sprintf("nats://%s", s.Addr()) - - const ( - nconns = 100 - nsubs = 100 - npubs = 500 - ) - - // make unique - subj := nats.NewInbox() - - var wg sync.WaitGroup - wg.Add(nconns * nsubs) - - for i := 0; i < nconns; i++ { - nc, err := nats.Connect(url) - if err != nil { - t.Fatalf("Expected a successful connect on %d, got %v\n", i, err) - } - - nc.SetErrorHandler(func(c *nats.Conn, s *nats.Subscription, e error) { - t.Fatalf("Got an error %v for %+v\n", s, err) - }) - - ec, _ := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER) - - for y := 0; y < nsubs; y++ { - expected := 0 - ec.Subscribe(subj, func(n int) { - if n != expected { - t.Fatalf("Expected %d but received %d\n", expected, n) - } - expected++ - if expected >= npubs { - wg.Done() - } - }) - } - ec.Flush() - defer ec.Close() - } - - nc, _ := nats.Connect(url) - ec, _ := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER) - - for i := 0; i < npubs; i++ { - ec.Publish(subj, i) - } - defer ec.Close() - - wg.Wait() -} - -func TestRouteFormTimeWithHighSubscriptions(t *testing.T) { - srvA, optsA := RunServerWithConfig("./configs/srv_a.conf") - defer srvA.Shutdown() - - clientA := createClientConn(t, optsA.Host, optsA.Port) - defer clientA.Close() - - sendA, expectA := setupConn(t, clientA) - - // Now add lots of subscriptions. These will need to be forwarded - // to new routes when they are added. - subsTotal := 100000 - for i := 0; i < subsTotal; i++ { - subject := fmt.Sprintf("FOO.BAR.BAZ.%d", i) - sendA(fmt.Sprintf("SUB %s %d\r\n", subject, i)) - } - sendA("PING\r\n") - expectA(pongRe) - - srvB, _ := RunServerWithConfig("./configs/srv_b.conf") - defer srvB.Shutdown() - - checkClusterFormed(t, srvA, srvB) - - // Now wait for all subscriptions to be processed. - if err := checkExpectedSubs(subsTotal, srvB); err != nil { - // Make sure we are not a slow consumer - // Check for slow consumer status - if srvA.NumSlowConsumers() > 0 { - t.Fatal("Did not receive all subscriptions due to slow consumer") - } else { - t.Fatalf("%v", err) - } - } - // Just double check the slow consumer status. - if srvA.NumSlowConsumers() > 0 { - t.Fatalf("Received a slow consumer notification: %d", srvA.NumSlowConsumers()) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/gosrv_test.go b/vendor/github.com/nats-io/gnatsd/test/gosrv_test.go deleted file mode 100644 index c7940cad..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/gosrv_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "net" - "runtime" - "testing" - "time" -) - -func TestSimpleGoServerShutdown(t *testing.T) { - base := runtime.NumGoroutine() - opts := DefaultTestOptions - opts.Port = -1 - s := RunServer(&opts) - s.Shutdown() - time.Sleep(100 * time.Millisecond) - delta := (runtime.NumGoroutine() - base) - if delta > 1 { - t.Fatalf("%d Go routines still exist post Shutdown()", delta) - } -} - -func TestGoServerShutdownWithClients(t *testing.T) { - base := runtime.NumGoroutine() - opts := DefaultTestOptions - opts.Port = -1 - s := RunServer(&opts) - addr := s.Addr().(*net.TCPAddr) - for i := 0; i < 50; i++ { - createClientConn(t, "127.0.0.1", addr.Port) - } - s.Shutdown() - // Wait longer for client connections - time.Sleep(1 * time.Second) - delta := (runtime.NumGoroutine() - base) - // There may be some finalizers or IO, but in general more than - // 2 as a delta represents a problem. - if delta > 2 { - t.Fatalf("%d Go routines still exist post Shutdown()", delta) - } -} - -func TestGoServerMultiShutdown(t *testing.T) { - opts := DefaultTestOptions - opts.Port = -1 - s := RunServer(&opts) - s.Shutdown() - s.Shutdown() -} diff --git a/vendor/github.com/nats-io/gnatsd/test/maxpayload_test.go b/vendor/github.com/nats-io/gnatsd/test/maxpayload_test.go deleted file mode 100644 index 9fe68782..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/maxpayload_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "fmt" - "net" - "runtime" - "strings" - "testing" - "time" - - "github.com/nats-io/go-nats" -) - -func TestMaxPayload(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/override.conf") - defer srv.Shutdown() - - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - nc, err := nats.Connect(fmt.Sprintf("nats://%s/", endpoint)) - if err != nil { - t.Fatalf("Could not connect to server: %v", err) - } - defer nc.Close() - - size := 4 * 1024 * 1024 - big := sizedBytes(size) - err = nc.Publish("foo", big) - - if err != nats.ErrMaxPayload { - t.Fatalf("Expected a Max Payload error") - } - - conn, err := net.DialTimeout("tcp", endpoint, nc.Opts.Timeout) - if err != nil { - t.Fatalf("Could not make a raw connection to the server: %v", err) - } - defer conn.Close() - info := make([]byte, 512) - _, err = conn.Read(info) - if err != nil { - t.Fatalf("Expected an info message to be sent by the server: %s", err) - } - pub := fmt.Sprintf("PUB bar %d\r\n", size) - conn.Write([]byte(pub)) - if err != nil { - t.Fatalf("Could not publish event to the server: %s", err) - } - - errMsg := make([]byte, 35) - _, err = conn.Read(errMsg) - if err != nil { - t.Fatalf("Expected an error message to be sent by the server: %s", err) - } - - if !strings.Contains(string(errMsg), "Maximum Payload Violation") { - t.Errorf("Received wrong error message (%v)\n", string(errMsg)) - } - - // Client proactively omits sending the message so server - // does not close the connection. - if nc.IsClosed() { - t.Errorf("Expected connection to not be closed.") - } - - // On the other hand client which did not proactively omitted - // publishing the bytes following what is suggested by server - // in the info message has its connection closed. - _, err = conn.Write(big) - if err == nil && runtime.GOOS != "windows" { - t.Errorf("Expected error due to maximum payload transgression.") - } - - // On windows, the previous write will not fail because the connection - // is not fully closed at this stage. - if runtime.GOOS == "windows" { - // Issuing a PING and not expecting the PONG. - _, err = conn.Write([]byte("PING\r\n")) - if err == nil { - conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) - _, err = conn.Read(big) - if err == nil { - t.Errorf("Expected closed connection due to maximum payload transgression.") - } - } - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/monitor_test.go b/vendor/github.com/nats-io/gnatsd/test/monitor_test.go deleted file mode 100644 index a0851126..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/monitor_test.go +++ /dev/null @@ -1,739 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "crypto/tls" - "crypto/x509" - "encoding/json" - "fmt" - "io/ioutil" - "net" - "net/http" - "strings" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" - "github.com/nats-io/go-nats" -) - -const CLIENT_PORT = 11422 -const MONITOR_PORT = 11522 - -func runMonitorServer() *server.Server { - resetPreviousHTTPConnections() - opts := DefaultTestOptions - opts.Port = CLIENT_PORT - opts.HTTPPort = MONITOR_PORT - opts.HTTPHost = "127.0.0.1" - - return RunServer(&opts) -} - -// Runs a clustered pair of monitor servers for testing the /routez endpoint -func runMonitorServerClusteredPair(t *testing.T) (*server.Server, *server.Server) { - resetPreviousHTTPConnections() - opts := DefaultTestOptions - opts.Port = CLIENT_PORT - opts.HTTPPort = MONITOR_PORT - opts.HTTPHost = "127.0.0.1" - opts.Cluster = server.ClusterOpts{Host: "127.0.0.1", Port: 10223} - opts.Routes = server.RoutesFromStr("nats-route://127.0.0.1:10222") - - s1 := RunServer(&opts) - - opts2 := DefaultTestOptions - opts2.Port = CLIENT_PORT + 1 - opts2.HTTPPort = MONITOR_PORT + 1 - opts2.HTTPHost = "127.0.0.1" - opts2.Cluster = server.ClusterOpts{Host: "127.0.0.1", Port: 10222} - opts2.Routes = server.RoutesFromStr("nats-route://127.0.0.1:10223") - - s2 := RunServer(&opts2) - - checkClusterFormed(t, s1, s2) - - return s1, s2 -} - -func runMonitorServerNoHTTPPort() *server.Server { - resetPreviousHTTPConnections() - opts := DefaultTestOptions - opts.Port = CLIENT_PORT - opts.HTTPPort = 0 - - return RunServer(&opts) -} - -func resetPreviousHTTPConnections() { - http.DefaultTransport.(*http.Transport).CloseIdleConnections() -} - -// Make sure that we do not run the http server for monitoring unless asked. -func TestNoMonitorPort(t *testing.T) { - s := runMonitorServerNoHTTPPort() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT) - if resp, err := http.Get(url + "varz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } - if resp, err := http.Get(url + "healthz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } - if resp, err := http.Get(url + "connz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } -} - -// testEndpointDataRace tests a monitoring endpoint for data races by polling -// while client code acts to ensure statistics are updated. It is designed to -// run under the -race flag to catch violations. The caller must start the -// NATS server. -func testEndpointDataRace(endpoint string, t *testing.T) { - var doneWg sync.WaitGroup - - url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT) - - // Poll as fast as we can, while creating connections, publishing, - // and subscribing. - clientDone := int64(0) - doneWg.Add(1) - go func() { - for atomic.LoadInt64(&clientDone) == 0 { - resp, err := http.Get(url + endpoint) - if err != nil { - t.Errorf("Expected no error: Got %v\n", err) - } else { - resp.Body.Close() - } - } - doneWg.Done() - }() - - // create connections, subscriptions, and publish messages to - // update the monitor variables. - var conns []net.Conn - for i := 0; i < 50; i++ { - cl := createClientConnSubscribeAndPublish(t) - // keep a few connections around to test monitor variables. - if i%10 == 0 { - conns = append(conns, cl) - } else { - cl.Close() - } - } - atomic.AddInt64(&clientDone, 1) - - // wait for the endpoint polling goroutine to exit - doneWg.Wait() - - // cleanup the conns - for _, cl := range conns { - cl.Close() - } -} - -func TestEndpointDataRaces(t *testing.T) { - // setup a small cluster to test /routez - s1, s2 := runMonitorServerClusteredPair(t) - defer s1.Shutdown() - defer s2.Shutdown() - - // test all of our endpoints - testEndpointDataRace("varz", t) - testEndpointDataRace("connz", t) - testEndpointDataRace("routez", t) - testEndpointDataRace("subsz", t) - testEndpointDataRace("stacksz", t) -} - -func TestVarz(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT) - resp, err := http.Get(url + "varz") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - - v := server.Varz{} - if err := json.Unmarshal(body, &v); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - // Do some sanity checks on values - if time.Since(v.Start) > 10*time.Second { - t.Fatal("Expected start time to be within 10 seconds.") - } - - cl := createClientConnSubscribeAndPublish(t) - defer cl.Close() - - resp, err = http.Get(url + "varz") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err = ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - - if strings.Contains(string(body), "cluster_port") { - t.Fatal("Varz body contains cluster information when no cluster is defined.") - } - - v = server.Varz{} - if err := json.Unmarshal(body, &v); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - if v.Connections != 1 { - t.Fatalf("Expected Connections of 1, got %v\n", v.Connections) - } - if v.InMsgs != 1 { - t.Fatalf("Expected InMsgs of 1, got %v\n", v.InMsgs) - } - if v.OutMsgs != 1 { - t.Fatalf("Expected OutMsgs of 1, got %v\n", v.OutMsgs) - } - if v.InBytes != 5 { - t.Fatalf("Expected InBytes of 5, got %v\n", v.InBytes) - } - if v.OutBytes != 5 { - t.Fatalf("Expected OutBytes of 5, got %v\n", v.OutBytes) - } - if v.MaxPending != server.MAX_PENDING_SIZE { - t.Fatalf("Expected MaxPending of %d, got %v\n", - server.MAX_PENDING_SIZE, v.MaxPending) - } - if v.WriteDeadline != server.DEFAULT_FLUSH_DEADLINE { - t.Fatalf("Expected WriteDeadline of %d, got %v\n", - server.DEFAULT_FLUSH_DEADLINE, v.WriteDeadline) - } -} - -func TestConnz(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT) - resp, err := http.Get(url + "connz") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - - c := server.Connz{} - if err := json.Unmarshal(body, &c); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - // Test contents.. - if c.NumConns != 0 { - t.Fatalf("Expected 0 connections, got %d\n", c.NumConns) - } - if c.Total != 0 { - t.Fatalf("Expected 0 live connections, got %d\n", c.Total) - } - if c.Conns == nil || len(c.Conns) != 0 { - t.Fatalf("Expected 0 connections in array, got %p\n", c.Conns) - } - - cl := createClientConnSubscribeAndPublish(t) - defer cl.Close() - - resp, err = http.Get(url + "connz") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err = ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - if err := json.Unmarshal(body, &c); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - if c.NumConns != 1 { - t.Fatalf("Expected 1 connection, got %d\n", c.NumConns) - } - if c.Total != 1 { - t.Fatalf("Expected 1 live connection, got %d\n", c.Total) - } - if c.Conns == nil || len(c.Conns) != 1 { - t.Fatalf("Expected 1 connection in array, got %p\n", c.Conns) - } - - if c.Limit != server.DefaultConnListSize { - t.Fatalf("Expected limit of %d, got %v\n", server.DefaultConnListSize, c.Limit) - } - - if c.Offset != 0 { - t.Fatalf("Expected offset of 0, got %v\n", c.Offset) - } - - // Test inside details of each connection - ci := c.Conns[0] - - if ci.Cid == 0 { - t.Fatalf("Expected non-zero cid, got %v\n", ci.Cid) - } - if ci.IP != "127.0.0.1" { - t.Fatalf("Expected \"127.0.0.1\" for IP, got %v\n", ci.IP) - } - if ci.Port == 0 { - t.Fatalf("Expected non-zero port, got %v\n", ci.Port) - } - if ci.NumSubs != 1 { - t.Fatalf("Expected num_subs of 1, got %v\n", ci.NumSubs) - } - if len(ci.Subs) != 0 { - t.Fatalf("Expected subs of 0, got %v\n", ci.Subs) - } - if ci.InMsgs != 1 { - t.Fatalf("Expected InMsgs of 1, got %v\n", ci.InMsgs) - } - if ci.OutMsgs != 1 { - t.Fatalf("Expected OutMsgs of 1, got %v\n", ci.OutMsgs) - } - if ci.InBytes != 5 { - t.Fatalf("Expected InBytes of 1, got %v\n", ci.InBytes) - } - if ci.OutBytes != 5 { - t.Fatalf("Expected OutBytes of 1, got %v\n", ci.OutBytes) - } -} - -func TestTLSConnz(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/tls.conf") - defer srv.Shutdown() - rootCAFile := "./configs/certs/ca.pem" - clientCertFile := "./configs/certs/client-cert.pem" - clientKeyFile := "./configs/certs/client-key.pem" - - // Test with secure connection - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint) - nc, err := nats.Connect(nurl, nats.RootCAs(rootCAFile)) - if err != nil { - t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err) - } - defer nc.Close() - ch := make(chan struct{}) - nc.Subscribe("foo", func(m *nats.Msg) { ch <- struct{}{} }) - nc.Publish("foo", []byte("Hello")) - - // Wait for message - <-ch - - url := fmt.Sprintf("https://127.0.0.1:%d/", opts.HTTPSPort) - tlsConfig := &tls.Config{} - caCert, err := ioutil.ReadFile(rootCAFile) - if err != nil { - t.Fatalf("Got error reading RootCA file: %s", err) - } - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - tlsConfig.RootCAs = caCertPool - - cert, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile) - if err != nil { - t.Fatalf("Got error reading client certificates: %s", err) - } - tlsConfig.Certificates = []tls.Certificate{cert} - transport := &http.Transport{TLSClientConfig: tlsConfig} - httpClient := &http.Client{Transport: transport} - - resp, err := httpClient.Get(url + "connz") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - c := server.Connz{} - if err := json.Unmarshal(body, &c); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - if c.NumConns != 1 { - t.Fatalf("Expected 1 connection, got %d\n", c.NumConns) - } - if c.Total != 1 { - t.Fatalf("Expected 1 live connection, got %d\n", c.Total) - } - if c.Conns == nil || len(c.Conns) != 1 { - t.Fatalf("Expected 1 connection in array, got %d\n", len(c.Conns)) - } - - // Test inside details of each connection - ci := c.Conns[0] - - if ci.Cid == 0 { - t.Fatalf("Expected non-zero cid, got %v\n", ci.Cid) - } - if ci.IP != "127.0.0.1" { - t.Fatalf("Expected \"127.0.0.1\" for IP, got %v\n", ci.IP) - } - if ci.Port == 0 { - t.Fatalf("Expected non-zero port, got %v\n", ci.Port) - } - if ci.NumSubs != 1 { - t.Fatalf("Expected num_subs of 1, got %v\n", ci.NumSubs) - } - if len(ci.Subs) != 0 { - t.Fatalf("Expected subs of 0, got %v\n", ci.Subs) - } - if ci.InMsgs != 1 { - t.Fatalf("Expected InMsgs of 1, got %v\n", ci.InMsgs) - } - if ci.OutMsgs != 1 { - t.Fatalf("Expected OutMsgs of 1, got %v\n", ci.OutMsgs) - } - if ci.InBytes != 5 { - t.Fatalf("Expected InBytes of 1, got %v\n", ci.InBytes) - } - if ci.OutBytes != 5 { - t.Fatalf("Expected OutBytes of 1, got %v\n", ci.OutBytes) - } - if ci.Start.IsZero() { - t.Fatalf("Expected Start to be valid\n") - } - if ci.Uptime == "" { - t.Fatalf("Expected Uptime to be valid\n") - } - if ci.LastActivity.IsZero() { - t.Fatalf("Expected LastActivity to be valid\n") - } - if ci.LastActivity.UnixNano() < ci.Start.UnixNano() { - t.Fatalf("Expected LastActivity [%v] to be > Start [%v]\n", ci.LastActivity, ci.Start) - } - if ci.Idle == "" { - t.Fatalf("Expected Idle to be valid\n") - } -} - -func TestConnzWithSubs(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - cl := createClientConnSubscribeAndPublish(t) - defer cl.Close() - - url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT) - resp, err := http.Get(url + "connz?subs=1") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - - c := server.Connz{} - if err := json.Unmarshal(body, &c); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - // Test inside details of each connection - ci := c.Conns[0] - if len(ci.Subs) != 1 || ci.Subs[0] != "foo" { - t.Fatalf("Expected subs of 1, got %v\n", ci.Subs) - } -} - -func TestConnzWithAuth(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/multi_user.conf") - defer srv.Shutdown() - - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - curl := fmt.Sprintf("nats://%s:%s@%s/", opts.Users[0].Username, opts.Users[0].Password, endpoint) - nc, err := nats.Connect(curl) - if err != nil { - t.Fatalf("Got an error on Connect: %+v\n", err) - } - defer nc.Close() - - ch := make(chan struct{}) - nc.Subscribe("foo", func(m *nats.Msg) { ch <- struct{}{} }) - nc.Publish("foo", []byte("Hello")) - - // Wait for message - <-ch - - url := fmt.Sprintf("http://127.0.0.1:%d/", opts.HTTPPort) - - resp, err := http.Get(url + "connz?auth=1") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - - c := server.Connz{} - if err := json.Unmarshal(body, &c); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - // Test that we have authorized_user and its Alice. - ci := c.Conns[0] - if ci.AuthorizedUser != opts.Users[0].Username { - t.Fatalf("Expected authorized_user to be %q, got %q\n", - opts.Users[0].Username, ci.AuthorizedUser) - } - -} - -func TestConnzWithOffsetAndLimit(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - cl1 := createClientConnSubscribeAndPublish(t) - defer cl1.Close() - - cl2 := createClientConnSubscribeAndPublish(t) - defer cl2.Close() - - url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT) - resp, err := http.Get(url + "connz?offset=1&limit=1") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - - c := server.Connz{} - if err := json.Unmarshal(body, &c); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - if c.Limit != 1 { - t.Fatalf("Expected limit of 1, got %v\n", c.Limit) - } - - if c.Offset != 1 { - t.Fatalf("Expected offset of 1, got %v\n", c.Offset) - } - - if len(c.Conns) != 1 { - t.Fatalf("Expected conns of 1, got %v\n", len(c.Conns)) - } -} - -func TestSubsz(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - cl := createClientConnSubscribeAndPublish(t) - defer cl.Close() - - url := fmt.Sprintf("http://127.0.0.1:%d/", MONITOR_PORT) - resp, err := http.Get(url + "subscriptionsz") - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - if resp.StatusCode != 200 { - t.Fatalf("Expected a 200 response, got %d\n", resp.StatusCode) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Got an error reading the body: %v\n", err) - } - - su := server.Subsz{} - if err := json.Unmarshal(body, &su); err != nil { - t.Fatalf("Got an error unmarshalling the body: %v\n", err) - } - - // Do some sanity checks on values - if su.NumSubs != 1 { - t.Fatalf("Expected num_subs of 1, got %v\n", su.NumSubs) - } -} - -func TestHTTPHost(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - // Grab non-127.0.0.1 address and try to use that to connect. - // Should fail. - var ip net.IP - ifaces, _ := net.Interfaces() - for _, i := range ifaces { - addrs, _ := i.Addrs() - for _, addr := range addrs { - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - // Skip loopback/127.0.0.1 or any ipv6 for now. - if ip.IsLoopback() || ip.To4() == nil { - ip = nil - continue - } - break - } - if ip != nil { - break - } - } - if ip == nil { - t.Fatalf("Could not find non-loopback IPV4 address") - } - url := fmt.Sprintf("http://%v:%d/", ip, MONITOR_PORT) - if resp, err := http.Get(url + "varz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } -} - -// Create a connection to test ConnInfo -func createClientConnSubscribeAndPublish(t *testing.T) net.Conn { - cl := createClientConn(t, "127.0.0.1", CLIENT_PORT) - send, expect := setupConn(t, cl) - expectMsgs := expectMsgsCommand(t, expect) - - send("SUB foo 1\r\nPUB foo 5\r\nhello\r\n") - expectMsgs(1) - - return cl -} - -func TestMonitorNoTLSConfig(t *testing.T) { - opts := DefaultTestOptions - opts.Port = CLIENT_PORT - opts.HTTPHost = "127.0.0.1" - opts.HTTPSPort = MONITOR_PORT - s := server.New(&opts) - defer s.Shutdown() - // Check with manually starting the monitoring, which should return an error - if err := s.StartMonitoring(); err == nil || !strings.Contains(err.Error(), "TLS") { - t.Fatalf("Expected error about missing TLS config, got %v", err) - } - // Also check by calling Start(), which should produce a fatal error - dl := &dummyLogger{} - s.SetLogger(dl, false, false) - defer s.SetLogger(nil, false, false) - s.Start() - if !strings.Contains(dl.msg, "TLS") { - t.Fatalf("Expected error about missing TLS config, got %v", dl.msg) - } -} - -func TestMonitorErrorOnListen(t *testing.T) { - s := runMonitorServer() - defer s.Shutdown() - - opts := DefaultTestOptions - opts.Port = CLIENT_PORT + 1 - opts.HTTPHost = "127.0.0.1" - opts.HTTPPort = MONITOR_PORT - s2 := server.New(&opts) - defer s2.Shutdown() - if err := s2.StartMonitoring(); err == nil || !strings.Contains(err.Error(), "listen") { - t.Fatalf("Expected error about not able to start listener, got %v", err) - } -} - -func TestMonitorBothPortsConfigured(t *testing.T) { - opts := DefaultTestOptions - opts.Port = CLIENT_PORT - opts.HTTPHost = "127.0.0.1" - opts.HTTPPort = MONITOR_PORT - opts.HTTPSPort = MONITOR_PORT + 1 - s := server.New(&opts) - defer s.Shutdown() - if err := s.StartMonitoring(); err == nil || !strings.Contains(err.Error(), "specify both") { - t.Fatalf("Expected error about ports configured, got %v", err) - } -} - -func TestMonitorStop(t *testing.T) { - resetPreviousHTTPConnections() - opts := DefaultTestOptions - opts.Port = CLIENT_PORT - opts.HTTPHost = "127.0.0.1" - opts.HTTPPort = MONITOR_PORT - url := fmt.Sprintf("http://%v:%d/", opts.HTTPHost, MONITOR_PORT) - // Create a server instance and start only the monitoring http server. - s := server.New(&opts) - if err := s.StartMonitoring(); err != nil { - t.Fatalf("Error starting monitoring: %v", err) - } - // Make sure http server is started - resp, err := http.Get(url + "varz") - if err != nil { - t.Fatalf("Error on http request: %v", err) - } - resp.Body.Close() - // Although the server itself was not started (we did not call s.Start()), - // Shutdown() should stop the http server. - s.Shutdown() - // HTTP request should now fail - if resp, err := http.Get(url + "varz"); err == nil { - t.Fatalf("Expected error: Got %+v\n", resp) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/opts_test.go b/vendor/github.com/nats-io/gnatsd/test/opts_test.go deleted file mode 100644 index 0275989e..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/opts_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import "testing" - -func TestServerConfig(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/override.conf") - defer srv.Shutdown() - - c := createClientConn(t, opts.Host, opts.Port) - defer c.Close() - - sinfo := checkInfoMsg(t, c) - if sinfo.MaxPayload != opts.MaxPayload { - t.Fatalf("Expected max_payload from server, got %d vs %d", - opts.MaxPayload, sinfo.MaxPayload) - } -} - -func TestTLSConfig(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/tls.conf") - defer srv.Shutdown() - - c := createClientConn(t, opts.Host, opts.Port) - defer c.Close() - - sinfo := checkInfoMsg(t, c) - if !sinfo.TLSRequired { - t.Fatal("Expected TLSRequired to be true when configured") - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/pedantic_test.go b/vendor/github.com/nats-io/gnatsd/test/pedantic_test.go deleted file mode 100644 index 62585336..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/pedantic_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "testing" - - "github.com/nats-io/gnatsd/server" -) - -func runPedanticServer() *server.Server { - opts := DefaultTestOptions - - opts.NoLog = false - opts.Trace = true - - opts.Port = PROTO_TEST_PORT - return RunServer(&opts) -} - -func TestPedanticSub(t *testing.T) { - s := runPedanticServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send := sendCommand(t, c) - expect := expectCommand(t, c) - doConnect(t, c, false, true, false) - - // Ping should still be same - send("PING\r\n") - expect(pongRe) - - // Test malformed subjects for SUB - // Sub can contain wildcards, but - // subject must still be legit. - - // Empty terminal token - send("SUB foo. 1\r\n") - expect(errRe) - - // Empty beginning token - send("SUB .foo. 1\r\n") - expect(errRe) - - // Empty middle token - send("SUB foo..bar 1\r\n") - expect(errRe) - - // Bad non-terminal FWC - send("SUB foo.>.bar 1\r\n") - buf := expect(errRe) - - // Check that itr is 'Invalid Subject' - matches := errRe.FindAllSubmatch(buf, -1) - if len(matches) != 1 { - t.Fatal("Wanted one overall match") - } - if string(matches[0][1]) != "'Invalid Subject'" { - t.Fatalf("Expected 'Invalid Subject', got %s", string(matches[0][1])) - } -} - -func TestPedanticPub(t *testing.T) { - s := runPedanticServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send := sendCommand(t, c) - expect := expectCommand(t, c) - doConnect(t, c, false, true, false) - - // Ping should still be same - send("PING\r\n") - expect(pongRe) - - // Test malformed subjects for PUB - // PUB subjects can not have wildcards - // This will error in pedantic mode - send("PUB foo.* 2\r\nok\r\n") - expect(errRe) - - send("PUB foo.> 2\r\nok\r\n") - expect(errRe) - - send("PUB foo. 2\r\nok\r\n") - expect(errRe) - - send("PUB .foo 2\r\nok\r\n") - expect(errRe) - - send("PUB foo..* 2\r\nok\r\n") - expect(errRe) -} diff --git a/vendor/github.com/nats-io/gnatsd/test/pid_test.go b/vendor/github.com/nats-io/gnatsd/test/pid_test.go deleted file mode 100644 index a4bd9f31..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/pid_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "fmt" - "io/ioutil" - "os" - "testing" -) - -func TestPidFile(t *testing.T) { - opts := DefaultTestOptions - - tmpDir, err := ioutil.TempDir("", "_gnatsd") - if err != nil { - t.Fatal("Could not create tmp dir") - } - defer os.RemoveAll(tmpDir) - - file, err := ioutil.TempFile(tmpDir, "gnatsd:pid_") - if err != nil { - t.Fatalf("Unable to create temp file: %v", err) - } - file.Close() - opts.PidFile = file.Name() - - s := RunServer(&opts) - s.Shutdown() - - buf, err := ioutil.ReadFile(opts.PidFile) - if err != nil { - t.Fatalf("Could not read pid_file: %v", err) - } - if len(buf) <= 0 { - t.Fatal("Expected a non-zero length pid_file") - } - - pid := 0 - fmt.Sscanf(string(buf), "%d", &pid) - if pid != os.Getpid() { - t.Fatalf("Expected pid to be %d, got %d\n", os.Getpid(), pid) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/ping_test.go b/vendor/github.com/nats-io/gnatsd/test/ping_test.go deleted file mode 100644 index ceb14814..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/ping_test.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "crypto/tls" - "fmt" - "net" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" -) - -const ( - PING_TEST_PORT = 9972 - PING_INTERVAL = 50 * time.Millisecond - PING_MAX = 2 -) - -func runPingServer() *server.Server { - opts := DefaultTestOptions - opts.Port = PING_TEST_PORT - opts.PingInterval = PING_INTERVAL - opts.MaxPingsOut = PING_MAX - return RunServer(&opts) -} - -func TestPingSentToTLSConnection(t *testing.T) { - opts := DefaultTestOptions - opts.Port = PING_TEST_PORT - opts.PingInterval = PING_INTERVAL - opts.MaxPingsOut = PING_MAX - opts.TLSCert = "configs/certs/server-cert.pem" - opts.TLSKey = "configs/certs/server-key.pem" - opts.TLSCaCert = "configs/certs/ca.pem" - - tc := server.TLSConfigOpts{} - tc.CertFile = opts.TLSCert - tc.KeyFile = opts.TLSKey - tc.CaFile = opts.TLSCaCert - - opts.TLSConfig, _ = server.GenTLSConfig(&tc) - opts.TLSTimeout = 5 - s := RunServer(&opts) - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PING_TEST_PORT) - defer c.Close() - - checkInfoMsg(t, c) - c = tls.Client(c, &tls.Config{InsecureSkipVerify: true}) - tlsConn := c.(*tls.Conn) - tlsConn.Handshake() - - cs := fmt.Sprintf("CONNECT {\"verbose\":%v,\"pedantic\":%v,\"tls_required\":%v}\r\n", false, false, true) - sendProto(t, c, cs) - - expect := expectCommand(t, c) - - // Expect the max to be delivered correctly.. - for i := 0; i < PING_MAX; i++ { - time.Sleep(PING_INTERVAL / 2) - expect(pingRe) - } - - // We should get an error from the server - time.Sleep(PING_INTERVAL) - expect(errRe) - - // Server should close the connection at this point.. - time.Sleep(PING_INTERVAL) - c.SetWriteDeadline(time.Now().Add(PING_INTERVAL)) - - var err error - for { - _, err = c.Write([]byte("PING\r\n")) - if err != nil { - break - } - } - c.SetWriteDeadline(time.Time{}) - - if err == nil { - t.Fatal("No error: Expected to have connection closed") - } - if ne, ok := err.(net.Error); ok && ne.Timeout() { - t.Fatal("timeout: Expected to have connection closed") - } -} - -func TestPingInterval(t *testing.T) { - s := runPingServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PING_TEST_PORT) - defer c.Close() - - doConnect(t, c, false, false, false) - - expect := expectCommand(t, c) - - // Expect the max to be delivered correctly.. - for i := 0; i < PING_MAX; i++ { - time.Sleep(PING_INTERVAL / 2) - expect(pingRe) - } - - // We should get an error from the server - time.Sleep(PING_INTERVAL) - expect(errRe) - - // Server should close the connection at this point.. - time.Sleep(PING_INTERVAL) - c.SetWriteDeadline(time.Now().Add(PING_INTERVAL)) - - var err error - for { - _, err = c.Write([]byte("PING\r\n")) - if err != nil { - break - } - } - c.SetWriteDeadline(time.Time{}) - - if err == nil { - t.Fatal("No error: Expected to have connection closed") - } - if ne, ok := err.(net.Error); ok && ne.Timeout() { - t.Fatal("timeout: Expected to have connection closed") - } -} - -func TestUnpromptedPong(t *testing.T) { - s := runPingServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PING_TEST_PORT) - defer c.Close() - - doConnect(t, c, false, false, false) - - expect := expectCommand(t, c) - - // Send lots of PONGs in a row... - for i := 0; i < 100; i++ { - c.Write([]byte("PONG\r\n")) - } - - // The server should still send the max number of PINGs and then - // close the connection. - for i := 0; i < PING_MAX; i++ { - time.Sleep(PING_INTERVAL / 2) - expect(pingRe) - } - - // We should get an error from the server - time.Sleep(PING_INTERVAL) - expect(errRe) - - // Server should close the connection at this point.. - c.SetWriteDeadline(time.Now().Add(PING_INTERVAL)) - var err error - for { - _, err = c.Write([]byte("PING\r\n")) - if err != nil { - break - } - } - c.SetWriteDeadline(time.Time{}) - - if err == nil { - t.Fatal("No error: Expected to have connection closed") - } - if ne, ok := err.(net.Error); ok && ne.Timeout() { - t.Fatal("timeout: Expected to have connection closed") - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/port_test.go b/vendor/github.com/nats-io/gnatsd/test/port_test.go deleted file mode 100644 index 1c750384..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/port_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2014-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "net" - "strconv" - "testing" - - "github.com/nats-io/gnatsd/server" -) - -func TestResolveRandomPort(t *testing.T) { - opts := &server.Options{Host: "127.0.0.1", Port: server.RANDOM_PORT, NoSigs: true} - s := RunServer(opts) - defer s.Shutdown() - - addr := s.Addr() - _, port, err := net.SplitHostPort(addr.String()) - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - - portNum, err := strconv.Atoi(port) - if err != nil { - t.Fatalf("Expected no error: Got %v\n", err) - } - - if portNum == server.DEFAULT_PORT { - t.Fatalf("Expected server to choose a random port\nGot: %d", server.DEFAULT_PORT) - } - - if portNum == server.RANDOM_PORT { - t.Fatalf("Expected server to choose a random port\nGot: %d", server.RANDOM_PORT) - } - - if opts.Port != portNum { - t.Fatalf("Options port (%d) should have been overridden by chosen random port (%d)", - opts.Port, portNum) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/ports_test.go b/vendor/github.com/nats-io/gnatsd/test/ports_test.go deleted file mode 100644 index ec7237b4..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/ports_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "path" - "strings" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" -) - -// waits until a calculated list of listeners is resolved or a timeout -func waitForFile(path string, dur time.Duration) ([]byte, error) { - end := time.Now().Add(dur) - for time.Now().Before(end) { - if _, err := os.Stat(path); os.IsNotExist(err) { - time.Sleep(25 * time.Millisecond) - continue - } else { - return ioutil.ReadFile(path) - } - } - return nil, errors.New("Timeout") -} - -func portFile(dirname string) string { - return path.Join(dirname, fmt.Sprintf("%s_%d.ports", path.Base(os.Args[0]), os.Getpid())) -} - -func TestPortsFile(t *testing.T) { - portFileDir := os.TempDir() - - opts := DefaultTestOptions - opts.PortsFileDir = portFileDir - opts.Port = -1 - opts.HTTPPort = -1 - opts.ProfPort = -1 - opts.Cluster.Port = -1 - - s := RunServer(&opts) - // this for test cleanup in case we fail - will be ignored if server already shutdown - defer s.Shutdown() - - ports := s.PortsInfo(5 * time.Second) - - if ports == nil { - t.Fatal("services failed to start in 5 seconds") - } - - // the pid file should be - portsFile := portFile(portFileDir) - - if portsFile == "" { - t.Fatal("Expected a ports file") - } - - // try to read a file here - the file should be a json - buf, err := waitForFile(portsFile, 5*time.Second) - if err != nil { - t.Fatalf("Could not read ports file: %v", err) - } - if len(buf) <= 0 { - t.Fatal("Expected a non-zero length ports file") - } - - readPorts := server.Ports{} - json.Unmarshal(buf, &readPorts) - - if len(readPorts.Nats) == 0 || !strings.HasPrefix(readPorts.Nats[0], "nats://") { - t.Fatal("Expected at least one nats url") - } - - if len(readPorts.Monitoring) == 0 || !strings.HasPrefix(readPorts.Monitoring[0], "http://") { - t.Fatal("Expected at least one monitoring url") - } - - if len(readPorts.Cluster) == 0 || !strings.HasPrefix(readPorts.Cluster[0], "nats://") { - t.Fatal("Expected at least one cluster listen url") - } - - if len(readPorts.Profile) == 0 || !strings.HasPrefix(readPorts.Profile[0], "http://") { - t.Fatal("Expected at least one profile listen url") - } - - // testing cleanup - s.Shutdown() - // if we called shutdown, the cleanup code should have kicked - if _, err := os.Stat(portsFile); os.IsNotExist(err) { - // good - } else { - t.Fatalf("the port file %s was not deleted", portsFile) - } -} - -// makes a temp directory with two directories 'A' and 'B' -// the location of the ports file is changed from dir A to dir B. -func TestPortsFileReload(t *testing.T) { - // make a temp dir - tempDir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Error creating temp director (%s): %v", tempDir, err) - } - defer os.RemoveAll(tempDir) - - // make child temp dir A - dirA := path.Join(tempDir, "A") - os.MkdirAll(dirA, 0777) - - // write the config file with a reference to A - config := fmt.Sprintf("ports_file_dir %s\nport -1", dirA) - confPath := path.Join(tempDir, fmt.Sprintf("%d.conf", os.Getpid())) - if err := ioutil.WriteFile(confPath, []byte(config), 0666); err != nil { - t.Fatalf("Error writing ports file (%s): %v", confPath, err) - } - - opts, err := server.ProcessConfigFile(confPath) - if err != nil { - t.Fatalf("Error processing the configuration: %v", err) - } - - s := RunServer(opts) - defer s.Shutdown() - - ports := s.PortsInfo(5 * time.Second) - if ports == nil { - t.Fatal("services failed to start in 5 seconds") - } - - // get the ports file path name - portsFileInA := portFile(dirA) - // the file should be in dirA - if !strings.HasPrefix(portsFileInA, dirA) { - t.Fatalf("expected ports file to be in [%s] but was in [%s]", dirA, portsFileInA) - } - // wait for it - buf, err := waitForFile(portsFileInA, 5*time.Second) - if err != nil { - t.Fatalf("Could not read ports file: %v", err) - } - if len(buf) <= 0 { - t.Fatal("Expected a non-zero length ports file") - } - - // change the configuration for the ports file to dirB - dirB := path.Join(tempDir, "B") - os.MkdirAll(dirB, 0777) - - config = fmt.Sprintf("ports_file_dir %s\nport -1", dirB) - if err := ioutil.WriteFile(confPath, []byte(config), 0666); err != nil { - t.Fatalf("Error writing ports file (%s): %v", confPath, err) - } - - // reload the server - if err := s.Reload(); err != nil { - t.Fatalf("error reloading server: %v", err) - } - - // wait for the new file to show up - portsFileInB := portFile(dirB) - buf, err = waitForFile(portsFileInB, 5*time.Second) - if !strings.HasPrefix(portsFileInB, dirB) { - t.Fatalf("expected ports file to be in [%s] but was in [%s]", dirB, portsFileInB) - } - if err != nil { - t.Fatalf("Could not read ports file: %v", err) - } - if len(buf) <= 0 { - t.Fatal("Expected a non-zero length ports file") - } - - // the file in dirA should have deleted - if _, err := os.Stat(portsFileInA); os.IsNotExist(err) { - // good - } else { - t.Fatalf("the port file %s was not deleted", portsFileInA) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/proto_test.go b/vendor/github.com/nats-io/gnatsd/test/proto_test.go deleted file mode 100644 index 200c49bf..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/proto_test.go +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "encoding/json" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" -) - -const PROTO_TEST_PORT = 9922 - -func runProtoServer() *server.Server { - opts := DefaultTestOptions - opts.Port = PROTO_TEST_PORT - return RunServer(&opts) -} - -func TestProtoBasics(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - expectMsgs := expectMsgsCommand(t, expect) - - // Ping - send("PING\r\n") - expect(pongRe) - - // Single Msg - send("SUB foo 1\r\nPUB foo 5\r\nhello\r\n") - matches := expectMsgs(1) - checkMsg(t, matches[0], "foo", "1", "", "5", "hello") - - // 2 Messages - send("SUB * 2\r\nPUB foo 2\r\nok\r\n") - matches = expectMsgs(2) - // Could arrive in any order - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - checkMsg(t, matches[1], "foo", "", "", "2", "ok") -} - -func TestProtoErr(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - - // Make sure we get an error on bad proto - send("ZZZ") - expect(errRe) -} - -func TestUnsubMax(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - expectMsgs := expectMsgsCommand(t, expect) - - send("SUB foo 22\r\n") - send("UNSUB 22 2\r\n") - for i := 0; i < 100; i++ { - send("PUB foo 2\r\nok\r\n") - } - - time.Sleep(50 * time.Millisecond) - - matches := expectMsgs(2) - checkMsg(t, matches[0], "foo", "22", "", "2", "ok") - checkMsg(t, matches[1], "foo", "22", "", "2", "ok") -} - -func TestQueueSub(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - expectMsgs := expectMsgsCommand(t, expect) - - sent := 100 - send("SUB foo qgroup1 22\r\n") - send("SUB foo qgroup1 32\r\n") - for i := 0; i < sent; i++ { - send("PUB foo 2\r\nok\r\n") - } - // Wait for responses - time.Sleep(250 * time.Millisecond) - - matches := expectMsgs(sent) - sids := make(map[string]int) - for _, m := range matches { - sids[string(m[sidIndex])]++ - } - if len(sids) != 2 { - t.Fatalf("Expected only 2 sids, got %d\n", len(sids)) - } - for k, c := range sids { - if c < 35 { - t.Fatalf("Expected ~50 (+-15) msgs for sid:'%s', got %d\n", k, c) - } - } -} - -func TestMultipleQueueSub(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - expectMsgs := expectMsgsCommand(t, expect) - - sent := 100 - send("SUB foo g1 1\r\n") - send("SUB foo g1 2\r\n") - send("SUB foo g2 3\r\n") - send("SUB foo g2 4\r\n") - - for i := 0; i < sent; i++ { - send("PUB foo 2\r\nok\r\n") - } - // Wait for responses - time.Sleep(250 * time.Millisecond) - - matches := expectMsgs(sent * 2) - sids := make(map[string]int) - for _, m := range matches { - sids[string(m[sidIndex])]++ - } - if len(sids) != 4 { - t.Fatalf("Expected 4 sids, got %d\n", len(sids)) - } - for k, c := range sids { - if c < 35 { - t.Fatalf("Expected ~50 (+-15) msgs for '%s', got %d\n", k, c) - } - } -} - -func TestPubToArgState(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - - send("PUBS foo 2\r\nok\r\n") - expect(errRe) -} - -func TestSubToArgState(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - - send("SUBZZZ foo 1\r\n") - expect(errRe) -} - -// Issue #63 -func TestProtoCrash(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := sendCommand(t, c), expectCommand(t, c) - - checkInfoMsg(t, c) - - send("CONNECT {\"verbose\":true,\"tls_required\":false,\"user\":\"test\",\"pedantic\":true,\"pass\":\"password\"}") - - time.Sleep(100 * time.Millisecond) - - send("\r\n") - expect(okRe) -} - -// Issue #136 -func TestDuplicateProtoSub(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - - send("PING\r\n") - expect(pongRe) - - send("SUB foo 1\r\n") - - send("SUB foo 1\r\n") - - ns := 0 - - for i := 0; i < 5; i++ { - ns = int(s.NumSubscriptions()) - if ns == 0 { - time.Sleep(50 * time.Millisecond) - } else { - break - } - } - - if ns != 1 { - t.Fatalf("Expected 1 subscription, got %d\n", ns) - } -} - -func TestIncompletePubArg(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - send, expect := setupConn(t, c) - - size := 10000 - goodBuf := "" - for i := 0; i < size; i++ { - goodBuf += "A" - } - goodBuf += "\r\n" - - badSize := 3371 - badBuf := "" - for i := 0; i < badSize; i++ { - badBuf += "B" - } - // Message is corrupted and since we are still reading from client, - // next PUB accidentally becomes part of the payload of the - // incomplete message thus breaking the protocol. - badBuf2 := "" - for i := 0; i < size; i++ { - badBuf2 += "C" - } - badBuf2 += "\r\n" - - pub := "PUB example 10000\r\n" - send(pub + goodBuf + pub + goodBuf + pub + badBuf + pub + badBuf2) - expect(errRe) -} - -func TestControlLineMaximums(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - send, expect := setupConn(t, c) - - pubTooLong := "PUB foo " - for i := 0; i < 32; i++ { - pubTooLong += "2222222222" - } - send(pubTooLong) - expect(errRe) -} - -func TestServerInfoWithClientAdvertise(t *testing.T) { - opts := DefaultTestOptions - opts.Port = PROTO_TEST_PORT - opts.ClientAdvertise = "me:1" - s := RunServer(&opts) - defer s.Shutdown() - - c := createClientConn(t, opts.Host, PROTO_TEST_PORT) - defer c.Close() - - buf := expectResult(t, c, infoRe) - js := infoRe.FindAllSubmatch(buf, 1)[0][1] - var sinfo server.Info - err := json.Unmarshal(js, &sinfo) - if err != nil { - t.Fatalf("Could not unmarshal INFO json: %v\n", err) - } - if sinfo.Host != "me" || sinfo.Port != 1 { - t.Fatalf("Expected INFO Host:Port to be me:1, got %s:%d", sinfo.Host, sinfo.Port) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/route_discovery_test.go b/vendor/github.com/nats-io/gnatsd/test/route_discovery_test.go deleted file mode 100644 index 417993d3..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/route_discovery_test.go +++ /dev/null @@ -1,665 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "bufio" - "encoding/json" - "fmt" - "io/ioutil" - "net" - "net/http" - "runtime" - "strconv" - "strings" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" -) - -func runSeedServer(t *testing.T) (*server.Server, *server.Options) { - return RunServerWithConfig("./configs/seed.conf") -} - -func runAuthSeedServer(t *testing.T) (*server.Server, *server.Options) { - return RunServerWithConfig("./configs/auth_seed.conf") -} - -func TestSeedFirstRouteInfo(t *testing.T) { - s, opts := runSeedServer(t) - defer s.Shutdown() - - rc := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc.Close() - - _, routeExpect := setupRoute(t, rc, opts) - buf := routeExpect(infoRe) - - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - - if info.ID != s.ID() { - t.Fatalf("Expected seed's ID %q, got %q", s.ID(), info.ID) - } -} - -func TestSeedMultipleRouteInfo(t *testing.T) { - s, opts := runSeedServer(t) - defer s.Shutdown() - - rc1 := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc1.Close() - - rc1ID := "2222" - rc1Port := 22 - rc1Host := "127.0.0.1" - - routeSend1, route1Expect := setupRouteEx(t, rc1, opts, rc1ID) - route1Expect(infoRe) - - // register ourselves via INFO - r1Info := server.Info{ID: rc1ID, Host: rc1Host, Port: rc1Port} - b, _ := json.Marshal(r1Info) - infoJSON := fmt.Sprintf(server.InfoProto, b) - routeSend1(infoJSON) - routeSend1("PING\r\n") - route1Expect(pongRe) - - rc2 := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc2.Close() - - rc2ID := "2224" - rc2Port := 24 - rc2Host := "127.0.0.1" - - routeSend2, route2Expect := setupRouteEx(t, rc2, opts, rc2ID) - - hp2 := fmt.Sprintf("nats-route://%s/", net.JoinHostPort(rc2Host, strconv.Itoa(rc2Port))) - - // register ourselves via INFO - r2Info := server.Info{ID: rc2ID, Host: rc2Host, Port: rc2Port} - b, _ = json.Marshal(r2Info) - infoJSON = fmt.Sprintf(server.InfoProto, b) - routeSend2(infoJSON) - - // Now read back the second INFO route1 should receive letting - // it know about route2 - buf := route1Expect(infoRe) - - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - - if info.ID != rc2ID { - t.Fatalf("Expected info.ID to be %q, got %q", rc2ID, info.ID) - } - if info.IP == "" { - t.Fatalf("Expected a IP for the implicit route") - } - if info.IP != hp2 { - t.Fatalf("Expected IP Host of %s, got %s\n", hp2, info.IP) - } - - route2Expect(infoRe) - routeSend2("PING\r\n") - route2Expect(pongRe) - - // Now let's do a third. - rc3 := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc3.Close() - - rc3ID := "2226" - rc3Port := 26 - rc3Host := "127.0.0.1" - - routeSend3, _ := setupRouteEx(t, rc3, opts, rc3ID) - - // register ourselves via INFO - r3Info := server.Info{ID: rc3ID, Host: rc3Host, Port: rc3Port} - b, _ = json.Marshal(r3Info) - infoJSON = fmt.Sprintf(server.InfoProto, b) - routeSend3(infoJSON) - - // Now read back out the info from the seed route - buf = route1Expect(infoRe) - - info = server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - - if info.ID != rc3ID { - t.Fatalf("Expected info.ID to be %q, got %q", rc3ID, info.ID) - } - - // Now read back out the info from the seed route - buf = route2Expect(infoRe) - - info = server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - - if info.ID != rc3ID { - t.Fatalf("Expected info.ID to be %q, got %q", rc3ID, info.ID) - } -} - -func TestSeedSolicitWorks(t *testing.T) { - s1, opts := runSeedServer(t) - defer s1.Shutdown() - - // Create the routes string for others to connect to the seed. - routesStr := fmt.Sprintf("nats-route://%s:%d/", opts.Cluster.Host, opts.Cluster.Port) - - // Run Server #2 - s2Opts := nextServerOpts(opts) - s2Opts.Routes = server.RoutesFromStr(routesStr) - - s2 := RunServer(s2Opts) - defer s2.Shutdown() - - // Run Server #3 - s3Opts := nextServerOpts(s2Opts) - - s3 := RunServer(s3Opts) - defer s3.Shutdown() - - // Wait for a bit for graph to connect - time.Sleep(500 * time.Millisecond) - - // Grab Routez from monitor ports, make sure we are fully connected - url := fmt.Sprintf("http://%s:%d/", opts.Host, opts.HTTPPort) - rz := readHTTPRoutez(t, url) - ris := expectRids(t, rz, []string{s2.ID(), s3.ID()}) - if ris[s2.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - if ris[s3.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - - url = fmt.Sprintf("http://%s:%d/", s2Opts.Host, s2Opts.HTTPPort) - rz = readHTTPRoutez(t, url) - ris = expectRids(t, rz, []string{s1.ID(), s3.ID()}) - if !ris[s1.ID()].IsConfigured { - t.Fatalf("Expected seed server to be configured\n") - } - if ris[s3.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - - url = fmt.Sprintf("http://%s:%d/", s3Opts.Host, s3Opts.HTTPPort) - rz = readHTTPRoutez(t, url) - ris = expectRids(t, rz, []string{s1.ID(), s2.ID()}) - if !ris[s1.ID()].IsConfigured { - t.Fatalf("Expected seed server to be configured\n") - } - if ris[s2.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } -} - -type serverInfo struct { - server *server.Server - opts *server.Options -} - -func checkConnected(t *testing.T, servers []serverInfo, current int, oneSeed bool) error { - s := servers[current] - - // Grab Routez from monitor ports, make sure we are fully connected - url := fmt.Sprintf("http://%s:%d/", s.opts.Host, s.opts.HTTPPort) - rz := readHTTPRoutez(t, url) - total := len(servers) - var ids []string - for i := 0; i < total; i++ { - if i == current { - continue - } - ids = append(ids, servers[i].server.ID()) - } - ris, err := expectRidsNoFatal(t, true, rz, ids) - if err != nil { - return err - } - for i := 0; i < total; i++ { - if i == current { - continue - } - s := servers[i] - if current == 0 || ((oneSeed && i > 0) || (!oneSeed && (i != current-1))) { - if ris[s.server.ID()].IsConfigured { - return fmt.Errorf("Expected server %s:%d not to be configured", s.opts.Host, s.opts.Port) - } - } else if oneSeed || (i == current-1) { - if !ris[s.server.ID()].IsConfigured { - return fmt.Errorf("Expected server %s:%d to be configured", s.opts.Host, s.opts.Port) - } - } - } - return nil -} - -func TestStressSeedSolicitWorks(t *testing.T) { - s1, opts := runSeedServer(t) - defer s1.Shutdown() - - // Create the routes string for others to connect to the seed. - routesStr := fmt.Sprintf("nats-route://%s:%d/", opts.Cluster.Host, opts.Cluster.Port) - - s2Opts := nextServerOpts(opts) - s2Opts.Routes = server.RoutesFromStr(routesStr) - - s3Opts := nextServerOpts(s2Opts) - s4Opts := nextServerOpts(s3Opts) - - for i := 0; i < 10; i++ { - func() { - // Run these servers manually, because we want them to start and - // connect to s1 as fast as possible. - - s2 := server.New(s2Opts) - if s2 == nil { - panic("No NATS Server object returned.") - } - defer s2.Shutdown() - go s2.Start() - - s3 := server.New(s3Opts) - if s3 == nil { - panic("No NATS Server object returned.") - } - defer s3.Shutdown() - go s3.Start() - - s4 := server.New(s4Opts) - if s4 == nil { - panic("No NATS Server object returned.") - } - defer s4.Shutdown() - go s4.Start() - - serversInfo := []serverInfo{{s1, opts}, {s2, s2Opts}, {s3, s3Opts}, {s4, s4Opts}} - - checkFor(t, 5*time.Second, 100*time.Millisecond, func() error { - for j := 0; j < len(serversInfo); j++ { - if err := checkConnected(t, serversInfo, j, true); err != nil { - return err - } - } - return nil - }) - }() - checkNumRoutes(t, s1, 0) - } -} - -func TestChainedSolicitWorks(t *testing.T) { - s1, opts := runSeedServer(t) - defer s1.Shutdown() - - // Create the routes string for others to connect to the seed. - routesStr := fmt.Sprintf("nats-route://%s:%d/", opts.Cluster.Host, opts.Cluster.Port) - - // Run Server #2 - s2Opts := nextServerOpts(opts) - s2Opts.Routes = server.RoutesFromStr(routesStr) - - s2 := RunServer(s2Opts) - defer s2.Shutdown() - - // Run Server #3 - s3Opts := nextServerOpts(s2Opts) - // We will have s3 connect to s2, not the seed. - routesStr = fmt.Sprintf("nats-route://%s:%d/", s2Opts.Cluster.Host, s2Opts.Cluster.Port) - s3Opts.Routes = server.RoutesFromStr(routesStr) - - s3 := RunServer(s3Opts) - defer s3.Shutdown() - - // Wait for a bit for graph to connect - time.Sleep(500 * time.Millisecond) - - // Grab Routez from monitor ports, make sure we are fully connected - url := fmt.Sprintf("http://%s:%d/", opts.Host, opts.HTTPPort) - rz := readHTTPRoutez(t, url) - ris := expectRids(t, rz, []string{s2.ID(), s3.ID()}) - if ris[s2.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - if ris[s3.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - - url = fmt.Sprintf("http://%s:%d/", s2Opts.Host, s2Opts.HTTPPort) - rz = readHTTPRoutez(t, url) - ris = expectRids(t, rz, []string{s1.ID(), s3.ID()}) - if !ris[s1.ID()].IsConfigured { - t.Fatalf("Expected seed server to be configured\n") - } - if ris[s3.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - - url = fmt.Sprintf("http://%s:%d/", s3Opts.Host, s3Opts.HTTPPort) - rz = readHTTPRoutez(t, url) - ris = expectRids(t, rz, []string{s1.ID(), s2.ID()}) - if !ris[s2.ID()].IsConfigured { - t.Fatalf("Expected s2 server to be configured\n") - } - if ris[s1.ID()].IsConfigured { - t.Fatalf("Expected seed server not to be configured\n") - } -} - -func TestStressChainedSolicitWorks(t *testing.T) { - s1, opts := runSeedServer(t) - defer s1.Shutdown() - - // Create the routes string for s2 to connect to the seed - routesStr := fmt.Sprintf("nats-route://%s:%d/", opts.Cluster.Host, opts.Cluster.Port) - s2Opts := nextServerOpts(opts) - s2Opts.Routes = server.RoutesFromStr(routesStr) - - s3Opts := nextServerOpts(s2Opts) - // Create the routes string for s3 to connect to s2 - routesStr = fmt.Sprintf("nats-route://%s:%d/", s2Opts.Cluster.Host, s2Opts.Cluster.Port) - s3Opts.Routes = server.RoutesFromStr(routesStr) - - s4Opts := nextServerOpts(s3Opts) - // Create the routes string for s4 to connect to s3 - routesStr = fmt.Sprintf("nats-route://%s:%d/", s3Opts.Cluster.Host, s3Opts.Cluster.Port) - s4Opts.Routes = server.RoutesFromStr(routesStr) - - for i := 0; i < 10; i++ { - func() { - // Run these servers manually, because we want them to start and - // connect to s1 as fast as possible. - - s2 := server.New(s2Opts) - if s2 == nil { - panic("No NATS Server object returned.") - } - defer s2.Shutdown() - go s2.Start() - - s3 := server.New(s3Opts) - if s3 == nil { - panic("No NATS Server object returned.") - } - defer s3.Shutdown() - go s3.Start() - - s4 := server.New(s4Opts) - if s4 == nil { - panic("No NATS Server object returned.") - } - defer s4.Shutdown() - go s4.Start() - - serversInfo := []serverInfo{{s1, opts}, {s2, s2Opts}, {s3, s3Opts}, {s4, s4Opts}} - - checkFor(t, 5*time.Second, 100*time.Millisecond, func() error { - for j := 0; j < len(serversInfo); j++ { - if err := checkConnected(t, serversInfo, j, false); err != nil { - return err - } - } - return nil - }) - }() - checkNumRoutes(t, s1, 0) - } -} - -func TestAuthSeedSolicitWorks(t *testing.T) { - s1, opts := runAuthSeedServer(t) - defer s1.Shutdown() - - // Create the routes string for others to connect to the seed. - routesStr := fmt.Sprintf("nats-route://%s:%s@%s:%d/", opts.Cluster.Username, opts.Cluster.Password, opts.Cluster.Host, opts.Cluster.Port) - - // Run Server #2 - s2Opts := nextServerOpts(opts) - s2Opts.Routes = server.RoutesFromStr(routesStr) - - s2 := RunServer(s2Opts) - defer s2.Shutdown() - - // Run Server #3 - s3Opts := nextServerOpts(s2Opts) - - s3 := RunServer(s3Opts) - defer s3.Shutdown() - - // Wait for a bit for graph to connect - time.Sleep(500 * time.Millisecond) - - // Grab Routez from monitor ports, make sure we are fully connected - url := fmt.Sprintf("http://%s:%d/", opts.Host, opts.HTTPPort) - rz := readHTTPRoutez(t, url) - ris := expectRids(t, rz, []string{s2.ID(), s3.ID()}) - if ris[s2.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - if ris[s3.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - - url = fmt.Sprintf("http://%s:%d/", s2Opts.Host, s2Opts.HTTPPort) - rz = readHTTPRoutez(t, url) - ris = expectRids(t, rz, []string{s1.ID(), s3.ID()}) - if !ris[s1.ID()].IsConfigured { - t.Fatalf("Expected seed server to be configured\n") - } - if ris[s3.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } - - url = fmt.Sprintf("http://%s:%d/", s3Opts.Host, s3Opts.HTTPPort) - rz = readHTTPRoutez(t, url) - ris = expectRids(t, rz, []string{s1.ID(), s2.ID()}) - if !ris[s1.ID()].IsConfigured { - t.Fatalf("Expected seed server to be configured\n") - } - if ris[s2.ID()].IsConfigured { - t.Fatalf("Expected server not to be configured\n") - } -} - -// Helper to check for correct route memberships -func expectRids(t *testing.T, rz *server.Routez, rids []string) map[string]*server.RouteInfo { - ri, err := expectRidsNoFatal(t, false, rz, rids) - if err != nil { - t.Fatalf("%v", err) - } - return ri -} - -func expectRidsNoFatal(t *testing.T, direct bool, rz *server.Routez, rids []string) (map[string]*server.RouteInfo, error) { - caller := 1 - if !direct { - caller++ - } - if len(rids) != rz.NumRoutes { - _, fn, line, _ := runtime.Caller(caller) - return nil, fmt.Errorf("[%s:%d] Expecting %d routes, got %d\n", fn, line, len(rids), rz.NumRoutes) - } - set := make(map[string]bool) - for _, v := range rids { - set[v] = true - } - // Make result map for additional checking - ri := make(map[string]*server.RouteInfo) - for _, r := range rz.Routes { - if !set[r.RemoteID] { - _, fn, line, _ := runtime.Caller(caller) - return nil, fmt.Errorf("[%s:%d] Route with rid %s unexpected, expected %+v\n", fn, line, r.RemoteID, rids) - } - ri[r.RemoteID] = r - } - return ri, nil -} - -// Helper to easily grab routez info. -func readHTTPRoutez(t *testing.T, url string) *server.Routez { - resetPreviousHTTPConnections() - resp, err := http.Get(url + "routez") - if err != nil { - stackFatalf(t, "Expected no error: Got %v\n", err) - } - defer resp.Body.Close() - if resp.StatusCode != 200 { - stackFatalf(t, "Expected a 200 response, got %d\n", resp.StatusCode) - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - stackFatalf(t, "Got an error reading the body: %v\n", err) - } - r := server.Routez{} - if err := json.Unmarshal(body, &r); err != nil { - stackFatalf(t, "Got an error unmarshalling the body: %v\n", err) - } - return &r -} - -func TestSeedReturnIPInInfo(t *testing.T) { - s, opts := runSeedServer(t) - defer s.Shutdown() - - rc1 := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc1.Close() - - rc1ID := "2222" - rc1Port := 22 - rc1Host := "127.0.0.1" - - routeSend1, route1Expect := setupRouteEx(t, rc1, opts, rc1ID) - route1Expect(infoRe) - - // register ourselves via INFO - r1Info := server.Info{ID: rc1ID, Host: rc1Host, Port: rc1Port} - b, _ := json.Marshal(r1Info) - infoJSON := fmt.Sprintf(server.InfoProto, b) - routeSend1(infoJSON) - routeSend1("PING\r\n") - route1Expect(pongRe) - - rc2 := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc2.Close() - - rc2ID := "2224" - rc2Port := 24 - rc2Host := "127.0.0.1" - - routeSend2, _ := setupRouteEx(t, rc2, opts, rc2ID) - - // register ourselves via INFO - r2Info := server.Info{ID: rc2ID, Host: rc2Host, Port: rc2Port} - b, _ = json.Marshal(r2Info) - infoJSON = fmt.Sprintf(server.InfoProto, b) - routeSend2(infoJSON) - - // Now read info that route1 should have received from the seed - buf := route1Expect(infoRe) - - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - - if info.IP == "" { - t.Fatal("Expected to have IP in INFO") - } - rip, _, err := net.SplitHostPort(strings.TrimPrefix(info.IP, "nats-route://")) - if err != nil { - t.Fatalf("Error parsing url: %v", err) - } - addr, ok := rc1.RemoteAddr().(*net.TCPAddr) - if !ok { - t.Fatal("Unable to get IP address from route") - } - s1 := strings.ToLower(addr.IP.String()) - s2 := strings.ToLower(rip) - if s1 != s2 { - t.Fatalf("Expected IP %s, got %s", s1, s2) - } -} - -func TestImplicitRouteRetry(t *testing.T) { - srvSeed, optsSeed := runSeedServer(t) - defer srvSeed.Shutdown() - - optsA := nextServerOpts(optsSeed) - optsA.Routes = server.RoutesFromStr(fmt.Sprintf("nats://%s:%d", optsSeed.Cluster.Host, optsSeed.Cluster.Port)) - optsA.Cluster.ConnectRetries = 5 - srvA := RunServer(optsA) - defer srvA.Shutdown() - - optsB := nextServerOpts(optsA) - rcb := createRouteConn(t, optsSeed.Cluster.Host, optsSeed.Cluster.Port) - defer rcb.Close() - rcbID := "ServerB" - routeBSend, routeBExpect := setupRouteEx(t, rcb, optsB, rcbID) - routeBExpect(infoRe) - // register ourselves via INFO - rbInfo := server.Info{ID: rcbID, Host: optsB.Cluster.Host, Port: optsB.Cluster.Port} - b, _ := json.Marshal(rbInfo) - infoJSON := fmt.Sprintf(server.InfoProto, b) - routeBSend(infoJSON) - routeBSend("PING\r\n") - routeBExpect(pongRe) - - // srvA should try to connect. Wait to make sure that it fails. - time.Sleep(1200 * time.Millisecond) - - // Setup a fake route listen for routeB - rbListen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", optsB.Cluster.Host, optsB.Cluster.Port)) - if err != nil { - t.Fatalf("Error during listen: %v", err) - } - c, err := rbListen.Accept() - if err != nil { - t.Fatalf("Error during accept: %v", err) - } - defer c.Close() - - br := bufio.NewReaderSize(c, 32768) - // Consume CONNECT and INFO - for i := 0; i < 2; i++ { - c.SetReadDeadline(time.Now().Add(2 * time.Second)) - buf, _, err := br.ReadLine() - c.SetReadDeadline(time.Time{}) - if err != nil { - t.Fatalf("Error reading: %v", err) - } - if i == 0 { - continue - } - buf = buf[len("INFO "):] - info := &server.Info{} - if err := json.Unmarshal(buf, info); err != nil { - t.Fatalf("Error during unmarshal: %v", err) - } - // Check INFO is from server A. - if info.ID != srvA.ID() { - t.Fatalf("Expected CONNECT from %v, got CONNECT from %v", srvA.ID(), info.ID) - } - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/routes_test.go b/vendor/github.com/nats-io/gnatsd/test/routes_test.go deleted file mode 100644 index 3328f649..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/routes_test.go +++ /dev/null @@ -1,1052 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net" - "runtime" - "strconv" - "strings" - "sync" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" - "github.com/nats-io/go-nats" -) - -const clientProtoInfo = 1 - -func runRouteServer(t *testing.T) (*server.Server, *server.Options) { - return RunServerWithConfig("./configs/cluster.conf") -} - -func TestRouterListeningSocket(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - // Check that the cluster socket is able to be connected. - addr := fmt.Sprintf("%s:%d", opts.Cluster.Host, opts.Cluster.Port) - checkSocket(t, addr, 2*time.Second) -} - -func TestRouteGoServerShutdown(t *testing.T) { - base := runtime.NumGoroutine() - s, _ := runRouteServer(t) - s.Shutdown() - time.Sleep(50 * time.Millisecond) - delta := (runtime.NumGoroutine() - base) - if delta > 1 { - t.Fatalf("%d Go routines still exist post Shutdown()", delta) - } -} - -func TestSendRouteInfoOnConnect(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - rc := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc.Close() - - routeSend, routeExpect := setupRoute(t, rc, opts) - buf := routeExpect(infoRe) - - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - - if !info.AuthRequired { - t.Fatal("Expected to see AuthRequired") - } - if info.Port != opts.Cluster.Port { - t.Fatalf("Received wrong information for port, expected %d, got %d", - info.Port, opts.Cluster.Port) - } - - // Need to send a different INFO than the one received, otherwise the server - // will detect as a "cycle" and close the connection. - info.ID = "RouteID" - b, err := json.Marshal(info) - if err != nil { - t.Fatalf("Could not marshal test route info: %v", err) - } - infoJSON := fmt.Sprintf("INFO %s\r\n", b) - routeSend(infoJSON) - routeSend("PING\r\n") - routeExpect(pongRe) -} - -func TestRouteToSelf(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - rc := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc.Close() - - routeSend, routeExpect := setupRouteEx(t, rc, opts, s.ID()) - buf := routeExpect(infoRe) - - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - - if !info.AuthRequired { - t.Fatal("Expected to see AuthRequired") - } - if info.Port != opts.Cluster.Port { - t.Fatalf("Received wrong information for port, expected %d, got %d", - info.Port, opts.Cluster.Port) - } - - // Now send it back and that should be detected as a route to self and the - // connection closed. - routeSend(string(buf)) - routeSend("PING\r\n") - rc.SetReadDeadline(time.Now().Add(2 * time.Second)) - if _, err := rc.Read(buf); err == nil { - t.Fatal("Expected route connection to be closed") - } -} - -func TestSendRouteSubAndUnsub(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - c := createClientConn(t, opts.Host, opts.Port) - defer c.Close() - - send, _ := setupConn(t, c) - - // We connect to the route. - rc := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer rc.Close() - - expectAuthRequired(t, rc) - routeSend, routeExpect := setupRouteEx(t, rc, opts, "ROUTER:xyz") - routeSend("INFO {\"server_id\":\"ROUTER:xyz\"}\r\n") - - routeSend("PING\r\n") - routeExpect(pongRe) - - // Send SUB via client connection - send("SUB foo 22\r\n") - - // Make sure the SUB is broadcast via the route - buf := expectResult(t, rc, subRe) - matches := subRe.FindAllSubmatch(buf, -1) - rsid := string(matches[0][5]) - if !strings.HasPrefix(rsid, "RSID:") { - t.Fatalf("Got wrong RSID: %s\n", rsid) - } - - // Send UNSUB via client connection - send("UNSUB 22\r\n") - - // Make sure the SUB is broadcast via the route - buf = expectResult(t, rc, unsubRe) - matches = unsubRe.FindAllSubmatch(buf, -1) - rsid2 := string(matches[0][1]) - - if rsid2 != rsid { - t.Fatalf("Expected rsid's to match. %q vs %q\n", rsid, rsid2) - } - - // Explicitly shutdown the server, otherwise this test would - // cause following test to fail. - s.Shutdown() -} - -func TestSendRouteSolicit(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - // Listen for a connection from the server on the first route. - if len(opts.Routes) <= 0 { - t.Fatalf("Need an outbound solicted route for this test") - } - rURL := opts.Routes[0] - - conn := acceptRouteConn(t, rURL.Host, server.DEFAULT_ROUTE_CONNECT) - defer conn.Close() - - // We should receive a connect message right away due to auth. - buf := expectResult(t, conn, connectRe) - - // Check INFO follows. Could be inline, with first result, if not - // check follow-on buffer. - if !infoRe.Match(buf) { - expectResult(t, conn, infoRe) - } -} - -func TestRouteForwardsMsgFromClients(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - client := createClientConn(t, opts.Host, opts.Port) - defer client.Close() - - clientSend, clientExpect := setupConn(t, client) - - route := acceptRouteConn(t, opts.Routes[0].Host, server.DEFAULT_ROUTE_CONNECT) - defer route.Close() - - routeSend, routeExpect := setupRoute(t, route, opts) - expectMsgs := expectMsgsCommand(t, routeExpect) - - // Eat the CONNECT and INFO protos - buf := routeExpect(connectRe) - if !infoRe.Match(buf) { - routeExpect(infoRe) - } - - // Send SUB via route connection - routeSend("SUB foo RSID:2:22\r\n") - routeSend("PING\r\n") - routeExpect(pongRe) - - // Send PUB via client connection - clientSend("PUB foo 2\r\nok\r\n") - clientSend("PING\r\n") - clientExpect(pongRe) - - matches := expectMsgs(1) - checkMsg(t, matches[0], "foo", "RSID:2:22", "", "2", "ok") -} - -func TestRouteForwardsMsgToClients(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - client := createClientConn(t, opts.Host, opts.Port) - defer client.Close() - - clientSend, clientExpect := setupConn(t, client) - expectMsgs := expectMsgsCommand(t, clientExpect) - - route := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route.Close() - expectAuthRequired(t, route) - routeSend, _ := setupRoute(t, route, opts) - - // Subscribe to foo - clientSend("SUB foo 1\r\n") - // Use ping roundtrip to make sure its processed. - clientSend("PING\r\n") - clientExpect(pongRe) - - // Send MSG proto via route connection - routeSend("MSG foo 1 2\r\nok\r\n") - - matches := expectMsgs(1) - checkMsg(t, matches[0], "foo", "1", "", "2", "ok") -} - -func TestRouteOneHopSemantics(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - route := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route.Close() - - expectAuthRequired(t, route) - routeSend, _ := setupRoute(t, route, opts) - - // Express interest on this route for foo. - routeSend("SUB foo RSID:2:2\r\n") - - // Send MSG proto via route connection - routeSend("MSG foo 1 2\r\nok\r\n") - - // Make sure it does not come back! - expectNothing(t, route) -} - -func TestRouteOnlySendOnce(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - client := createClientConn(t, opts.Host, opts.Port) - defer client.Close() - - clientSend, clientExpect := setupConn(t, client) - - route := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route.Close() - - expectAuthRequired(t, route) - routeSend, routeExpect := setupRoute(t, route, opts) - expectMsgs := expectMsgsCommand(t, routeExpect) - - // Express multiple interest on this route for foo. - routeSend("SUB foo RSID:2:1\r\n") - routeSend("SUB foo RSID:2:2\r\n") - routeSend("PING\r\n") - routeExpect(pongRe) - - // Send PUB via client connection - clientSend("PUB foo 2\r\nok\r\n") - clientSend("PING\r\n") - clientExpect(pongRe) - - expectMsgs(1) - routeSend("PING\r\n") - routeExpect(pongRe) -} - -func TestRouteQueueSemantics(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - client := createClientConn(t, opts.Host, opts.Port) - clientSend, clientExpect := setupConn(t, client) - clientExpectMsgs := expectMsgsCommand(t, clientExpect) - - defer client.Close() - - route := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route.Close() - - expectAuthRequired(t, route) - routeSend, routeExpect := setupRouteEx(t, route, opts, "ROUTER:xyz") - routeSend("INFO {\"server_id\":\"ROUTER:xyz\"}\r\n") - expectMsgs := expectMsgsCommand(t, routeExpect) - - // Express multiple interest on this route for foo, queue group bar. - qrsid1 := "QRSID:1:1" - routeSend(fmt.Sprintf("SUB foo bar %s\r\n", qrsid1)) - qrsid2 := "QRSID:1:2" - routeSend(fmt.Sprintf("SUB foo bar %s\r\n", qrsid2)) - - // Use ping roundtrip to make sure its processed. - routeSend("PING\r\n") - routeExpect(pongRe) - - // Send PUB via client connection - clientSend("PUB foo 2\r\nok\r\n") - // Use ping roundtrip to make sure its processed. - clientSend("PING\r\n") - clientExpect(pongRe) - - // Only 1 - matches := expectMsgs(1) - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - - // Add normal Interest as well to route interest. - routeSend("SUB foo RSID:1:4\r\n") - - // Use ping roundtrip to make sure its processed. - routeSend("PING\r\n") - routeExpect(pongRe) - - // Send PUB via client connection - clientSend("PUB foo 2\r\nok\r\n") - // Use ping roundtrip to make sure its processed. - clientSend("PING\r\n") - clientExpect(pongRe) - - // Should be 2 now, 1 for all normal, and one for specific queue subscriber. - matches = expectMsgs(2) - - // Expect first to be the normal subscriber, next will be the queue one. - if string(matches[0][sidIndex]) != "RSID:1:4" && - string(matches[1][sidIndex]) != "RSID:1:4" { - t.Fatalf("Did not received routed sid\n") - } - checkMsg(t, matches[0], "foo", "", "", "2", "ok") - checkMsg(t, matches[1], "foo", "", "", "2", "ok") - - // Check the rsid to verify it is one of the queue group subscribers. - var rsid string - if matches[0][sidIndex][0] == 'Q' { - rsid = string(matches[0][sidIndex]) - } else { - rsid = string(matches[1][sidIndex]) - } - if rsid != qrsid1 && rsid != qrsid2 { - t.Fatalf("Expected a queue group rsid, got %s\n", rsid) - } - - // Now create a queue subscription for the client as well as a normal one. - clientSend("SUB foo 1\r\n") - // Use ping roundtrip to make sure its processed. - clientSend("PING\r\n") - clientExpect(pongRe) - routeExpect(subRe) - - clientSend("SUB foo bar 2\r\n") - // Use ping roundtrip to make sure its processed. - clientSend("PING\r\n") - clientExpect(pongRe) - routeExpect(subRe) - - // Deliver a MSG from the route itself, make sure the client receives both. - routeSend("MSG foo RSID:1:1 2\r\nok\r\n") - // Queue group one. - routeSend("MSG foo QRSID:1:2 2\r\nok\r\n") - // Invlaid queue sid. - routeSend("MSG foo QRSID 2\r\nok\r\n") // cid and sid missing - routeSend("MSG foo QRSID:1 2\r\nok\r\n") // cid not terminated with ':' - routeSend("MSG foo QRSID:1: 2\r\nok\r\n") // cid==1 but sid missing. It needs to be at least one character. - - // Use ping roundtrip to make sure its processed. - routeSend("PING\r\n") - routeExpect(pongRe) - - // Should be 2 now, 1 for all normal, and one for specific queue subscriber. - matches = clientExpectMsgs(2) - - // Expect first to be the normal subscriber, next will be the queue one. - checkMsg(t, matches[0], "foo", "1", "", "2", "ok") - checkMsg(t, matches[1], "foo", "2", "", "2", "ok") -} - -func TestSolicitRouteReconnect(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - rURL := opts.Routes[0] - - route := acceptRouteConn(t, rURL.Host, 2*server.DEFAULT_ROUTE_CONNECT) - - // Go ahead and close the Route. - route.Close() - - // We expect to get called back.. - route = acceptRouteConn(t, rURL.Host, 2*server.DEFAULT_ROUTE_CONNECT) - route.Close() -} - -func TestMultipleRoutesSameId(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - route1 := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route1.Close() - - expectAuthRequired(t, route1) - route1Send, _ := setupRouteEx(t, route1, opts, "ROUTE:2222") - - route2 := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route2.Close() - - expectAuthRequired(t, route2) - route2Send, _ := setupRouteEx(t, route2, opts, "ROUTE:2222") - - // Send SUB via route connections - sub := "SUB foo RSID:2:22\r\n" - route1Send(sub) - route2Send(sub) - - // Make sure we do not get anything on a MSG send to a router. - // Send MSG proto via route connection - route1Send("MSG foo 1 2\r\nok\r\n") - - expectNothing(t, route1) - expectNothing(t, route2) - - // Setup a client - client := createClientConn(t, opts.Host, opts.Port) - clientSend, clientExpect := setupConn(t, client) - defer client.Close() - - // Send PUB via client connection - clientSend("PUB foo 2\r\nok\r\n") - clientSend("PING\r\n") - clientExpect(pongRe) - - // We should only receive on one route, not both. - // Check both manually. - route1.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) - buf, _ := ioutil.ReadAll(route1) - route1.SetReadDeadline(time.Time{}) - if len(buf) <= 0 { - route2.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) - buf, _ = ioutil.ReadAll(route2) - route2.SetReadDeadline(time.Time{}) - if len(buf) <= 0 { - t.Fatal("Expected to get one message on a route, received none.") - } - } - - matches := msgRe.FindAllSubmatch(buf, -1) - if len(matches) != 1 { - t.Fatalf("Expected 1 msg, got %d\n", len(matches)) - } - checkMsg(t, matches[0], "foo", "", "", "2", "ok") -} - -func TestRouteResendsLocalSubsOnReconnect(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - client := createClientConn(t, opts.Host, opts.Port) - defer client.Close() - - clientSend, clientExpect := setupConn(t, client) - - // Setup a local subscription, make sure it reaches. - clientSend("SUB foo 1\r\n") - clientSend("PING\r\n") - clientExpect(pongRe) - - route := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route.Close() - routeSend, routeExpect := setupRouteEx(t, route, opts, "ROUTE:1234") - - // Expect to see the local sub echoed through after we send our INFO. - time.Sleep(50 * time.Millisecond) - buf := routeExpect(infoRe) - - // Generate our own INFO so we can send one to trigger the local subs. - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - t.Fatalf("Could not unmarshal route info: %v", err) - } - info.ID = "ROUTE:1234" - b, err := json.Marshal(info) - if err != nil { - t.Fatalf("Could not marshal test route info: %v", err) - } - infoJSON := fmt.Sprintf("INFO %s\r\n", b) - - // Trigger the send of local subs. - routeSend(infoJSON) - - routeExpect(subRe) - - // Close and then re-open - route.Close() - - route = createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - defer route.Close() - - routeSend, routeExpect = setupRouteEx(t, route, opts, "ROUTE:1234") - - routeExpect(infoRe) - - routeSend(infoJSON) - routeExpect(subRe) -} - -type ignoreLogger struct{} - -func (l *ignoreLogger) Fatalf(f string, args ...interface{}) {} -func (l *ignoreLogger) Errorf(f string, args ...interface{}) {} - -func TestRouteConnectOnShutdownRace(t *testing.T) { - s, opts := runRouteServer(t) - defer s.Shutdown() - - l := &ignoreLogger{} - - var wg sync.WaitGroup - - cQuit := make(chan bool, 1) - - wg.Add(1) - - go func() { - defer wg.Done() - for { - route := createRouteConn(l, opts.Cluster.Host, opts.Cluster.Port) - if route != nil { - setupRouteEx(l, route, opts, "ROUTE:1234") - route.Close() - } - select { - case <-cQuit: - return - default: - } - } - }() - - time.Sleep(5 * time.Millisecond) - s.Shutdown() - - cQuit <- true - - wg.Wait() -} - -func TestRouteSendAsyncINFOToClients(t *testing.T) { - f := func(opts *server.Options) { - s := RunServer(opts) - defer s.Shutdown() - - clientURL := net.JoinHostPort(opts.Host, strconv.Itoa(opts.Port)) - - oldClient := createClientConn(t, opts.Host, opts.Port) - defer oldClient.Close() - - oldClientSend, oldClientExpect := setupConn(t, oldClient) - oldClientSend("PING\r\n") - oldClientExpect(pongRe) - - newClient := createClientConn(t, opts.Host, opts.Port) - defer newClient.Close() - - newClientSend, newClientExpect := setupConnWithProto(t, newClient, clientProtoInfo) - newClientSend("PING\r\n") - newClientExpect(pongRe) - - // Check that even a new client does not receive an async INFO at this point - // since there is no route created yet. - expectNothing(t, newClient) - - routeID := "Server-B" - - createRoute := func() (net.Conn, sendFun, expectFun) { - rc := createRouteConn(t, opts.Cluster.Host, opts.Cluster.Port) - routeSend, routeExpect := setupRouteEx(t, rc, opts, routeID) - - buf := routeExpect(infoRe) - - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - stackFatalf(t, "Could not unmarshal route info: %v", err) - } - if opts.Cluster.NoAdvertise { - if len(info.ClientConnectURLs) != 0 { - stackFatalf(t, "Expected ClientConnectURLs to be empty, got %v", info.ClientConnectURLs) - } - } else { - if len(info.ClientConnectURLs) == 0 { - stackFatalf(t, "Expected a list of URLs, got none") - } - if info.ClientConnectURLs[0] != clientURL { - stackFatalf(t, "Expected ClientConnectURLs to be %q, got %q", clientURL, info.ClientConnectURLs[0]) - } - } - - return rc, routeSend, routeExpect - } - - sendRouteINFO := func(routeSend sendFun, routeExpect expectFun, urls []string) { - routeInfo := server.Info{} - routeInfo.ID = routeID - routeInfo.Host = "127.0.0.1" - routeInfo.Port = 5222 - routeInfo.ClientConnectURLs = urls - b, err := json.Marshal(routeInfo) - if err != nil { - stackFatalf(t, "Could not marshal test route info: %v", err) - } - infoJSON := fmt.Sprintf("INFO %s\r\n", b) - routeSend(infoJSON) - routeSend("PING\r\n") - routeExpect(pongRe) - } - - checkClientConnectURLS := func(urls, expected []string) { - // Order of array is not guaranteed. - ok := false - if len(urls) == len(expected) { - m := make(map[string]struct{}, len(expected)) - for _, url := range expected { - m[url] = struct{}{} - } - ok = true - for _, url := range urls { - if _, present := m[url]; !present { - ok = false - break - } - } - } - if !ok { - stackFatalf(t, "Expected ClientConnectURLs to be %v, got %v", expected, urls) - } - } - - checkINFOReceived := func(client net.Conn, clientExpect expectFun, expectedURLs []string) { - if opts.Cluster.NoAdvertise { - expectNothing(t, client) - return - } - buf := clientExpect(infoRe) - info := server.Info{} - if err := json.Unmarshal(buf[4:], &info); err != nil { - stackFatalf(t, "Could not unmarshal route info: %v", err) - } - checkClientConnectURLS(info.ClientConnectURLs, expectedURLs) - } - - // Create a route - rc, routeSend, routeExpect := createRoute() - defer rc.Close() - - // Send an INFO with single URL - routeClientConnectURLs := []string{"127.0.0.1:5222"} - sendRouteINFO(routeSend, routeExpect, routeClientConnectURLs) - - // Expect nothing for old clients - expectNothing(t, oldClient) - - // We expect to get the one from the server we connect to and the other route. - expectedURLs := []string{clientURL, routeClientConnectURLs[0]} - - // Expect new client to receive an INFO (unless disabled) - checkINFOReceived(newClient, newClientExpect, expectedURLs) - - // Disconnect the route - rc.Close() - - // Expect nothing for old clients - expectNothing(t, oldClient) - - // Expect new client to receive an INFO (unless disabled). - // The content will now have the disconnected route ClientConnectURLs - // removed from the INFO. So it should be the one from the server the - // client is connected to. - checkINFOReceived(newClient, newClientExpect, []string{clientURL}) - - // Reconnect the route. - rc, routeSend, routeExpect = createRoute() - defer rc.Close() - - // Resend the same route INFO json. The server will now send - // the INFO since the disconnected route ClientConnectURLs was - // removed in previous step. - sendRouteINFO(routeSend, routeExpect, routeClientConnectURLs) - - // Expect nothing for old clients - expectNothing(t, oldClient) - - // Expect new client to receive an INFO (unless disabled) - checkINFOReceived(newClient, newClientExpect, expectedURLs) - - // Now stop the route and restart with an additional URL - rc.Close() - - // On route disconnect, clients will receive an updated INFO - expectNothing(t, oldClient) - checkINFOReceived(newClient, newClientExpect, []string{clientURL}) - - rc, routeSend, routeExpect = createRoute() - defer rc.Close() - - // Create a client not sending the CONNECT until after route is added - clientNoConnect := createClientConn(t, opts.Host, opts.Port) - defer clientNoConnect.Close() - - // Create a client that does not send the first PING yet - clientNoPing := createClientConn(t, opts.Host, opts.Port) - defer clientNoPing.Close() - clientNoPingSend, clientNoPingExpect := setupConnWithProto(t, clientNoPing, clientProtoInfo) - - // The route now has an additional URL - routeClientConnectURLs = append(routeClientConnectURLs, "127.0.0.1:7777") - expectedURLs = append(expectedURLs, "127.0.0.1:7777") - // This causes the server to add the route and send INFO to clients - sendRouteINFO(routeSend, routeExpect, routeClientConnectURLs) - - // Expect nothing for old clients - expectNothing(t, oldClient) - - // Expect new client to receive an INFO, and verify content as expected. - checkINFOReceived(newClient, newClientExpect, expectedURLs) - - // Expect nothing yet for client that did not send the PING - expectNothing(t, clientNoPing) - - // Now send the first PING - clientNoPingSend("PING\r\n") - // Should receive PONG followed by INFO - // Receive PONG only first - pongBuf := make([]byte, len("PONG\r\n")) - clientNoPing.SetReadDeadline(time.Now().Add(2 * time.Second)) - n, err := clientNoPing.Read(pongBuf) - clientNoPing.SetReadDeadline(time.Time{}) - if n <= 0 && err != nil { - t.Fatalf("Error reading from conn: %v\n", err) - } - if !pongRe.Match(pongBuf) { - t.Fatalf("Response did not match expected: \n\tReceived:'%q'\n\tExpected:'%s'\n", pongBuf, pongRe) - } - checkINFOReceived(clientNoPing, clientNoPingExpect, expectedURLs) - - // Have the client that did not send the connect do it now - clientNoConnectSend, clientNoConnectExpect := setupConnWithProto(t, clientNoConnect, clientProtoInfo) - // Send the PING - clientNoConnectSend("PING\r\n") - // Should receive PONG followed by INFO - // Receive PONG only first - clientNoConnect.SetReadDeadline(time.Now().Add(2 * time.Second)) - n, err = clientNoConnect.Read(pongBuf) - clientNoConnect.SetReadDeadline(time.Time{}) - if n <= 0 && err != nil { - t.Fatalf("Error reading from conn: %v\n", err) - } - if !pongRe.Match(pongBuf) { - t.Fatalf("Response did not match expected: \n\tReceived:'%q'\n\tExpected:'%s'\n", pongBuf, pongRe) - } - checkINFOReceived(clientNoConnect, clientNoConnectExpect, expectedURLs) - - // Create a client connection and verify content of initial INFO contains array - // (but empty if no advertise option is set) - cli := createClientConn(t, opts.Host, opts.Port) - defer cli.Close() - buf := expectResult(t, cli, infoRe) - js := infoRe.FindAllSubmatch(buf, 1)[0][1] - var sinfo server.Info - err = json.Unmarshal(js, &sinfo) - if err != nil { - t.Fatalf("Could not unmarshal INFO json: %v\n", err) - } - if opts.Cluster.NoAdvertise { - if len(sinfo.ClientConnectURLs) != 0 { - t.Fatalf("Expected ClientConnectURLs to be empty, got %v", sinfo.ClientConnectURLs) - } - } else { - checkClientConnectURLS(sinfo.ClientConnectURLs, expectedURLs) - } - - // Add a new route - routeID = "Server-C" - rc2, route2Send, route2Expect := createRoute() - defer rc2.Close() - - // Send an INFO with single URL - rc2ConnectURLs := []string{"127.0.0.1:8888"} - sendRouteINFO(route2Send, route2Expect, rc2ConnectURLs) - - // This is the combined client connect URLs array - totalConnectURLs := expectedURLs - totalConnectURLs = append(totalConnectURLs, rc2ConnectURLs...) - - // Expect nothing for old clients - expectNothing(t, oldClient) - - // Expect new client to receive an INFO (unless disabled) - checkINFOReceived(newClient, newClientExpect, totalConnectURLs) - - // Make first route disconnect - rc.Close() - - // Expect nothing for old clients - expectNothing(t, oldClient) - - // Expect new client to receive an INFO (unless disabled) - // The content should be the server client is connected to and the last route - checkINFOReceived(newClient, newClientExpect, []string{"127.0.0.1:5242", "127.0.0.1:8888"}) - } - - opts := LoadConfig("./configs/cluster.conf") - // For this test, be explicit about listen spec. - opts.Host = "127.0.0.1" - opts.Port = 5242 - - f(opts) - opts.Cluster.NoAdvertise = true - f(opts) -} - -func TestRouteBasicPermissions(t *testing.T) { - srvA, optsA := RunServerWithConfig("./configs/srv_a_perms.conf") - defer srvA.Shutdown() - - srvB, optsB := RunServerWithConfig("./configs/srv_b.conf") - defer srvB.Shutdown() - - checkClusterFormed(t, srvA, srvB) - - // Create a connection to server B - ncb, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", optsB.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer ncb.Close() - ch := make(chan bool, 1) - cb := func(_ *nats.Msg) { - ch <- true - } - // Subscribe on on "bar" and "baz", which should be accepted by server A - subBbar, err := ncb.Subscribe("bar", cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - defer subBbar.Unsubscribe() - subBbaz, err := ncb.Subscribe("baz", cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - defer subBbaz.Unsubscribe() - ncb.Flush() - if err := checkExpectedSubs(2, srvA, srvB); err != nil { - t.Fatal(err.Error()) - } - - // Create a connection to server A - nca, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", optsA.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer nca.Close() - // Publish on bar and baz, messages should be received. - if err := nca.Publish("bar", []byte("hello")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - if err := nca.Publish("baz", []byte("hello")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - for i := 0; i < 2; i++ { - select { - case <-ch: - case <-time.After(time.Second): - t.Fatal("Did not get the messages") - } - } - - // From B, start a subscription on "foo", which server A should drop since - // it only exports on "bar" and "baz" - subBfoo, err := ncb.Subscribe("foo", cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - defer subBfoo.Unsubscribe() - ncb.Flush() - // B should have now 3 subs - if err := checkExpectedSubs(3, srvB); err != nil { - t.Fatal(err.Error()) - } - // and A still 2. - if err := checkExpectedSubs(2, srvA); err != nil { - t.Fatal(err.Error()) - } - // So producing on "foo" from A should not be forwarded to B. - if err := nca.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - select { - case <-ch: - t.Fatal("Message should not have been received") - case <-time.After(100 * time.Millisecond): - } - - // Now on A, create a subscription on something that A does not import, - // like "bat". - subAbat, err := nca.Subscribe("bat", cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - defer subAbat.Unsubscribe() - nca.Flush() - // A should have 3 subs - if err := checkExpectedSubs(3, srvA); err != nil { - t.Fatal(err.Error()) - } - // And from B, send a message on that subject and make sure it is not received. - if err := ncb.Publish("bat", []byte("hello")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - select { - case <-ch: - t.Fatal("Message should not have been received") - case <-time.After(100 * time.Millisecond): - } - - // Stop subscription on foo from B - subBfoo.Unsubscribe() - ncb.Flush() - // Back to 2 subs on B - if err := checkExpectedSubs(2, srvB); err != nil { - t.Fatal(err.Error()) - } - - // Create subscription on foo from A, this should be forwared to B. - subAfoo, err := nca.Subscribe("foo", cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - defer subAfoo.Unsubscribe() - // Create another one so that test the import permissions cache - subAfoo2, err := nca.Subscribe("foo", cb) - if err != nil { - t.Fatalf("Error on subscribe: %v", err) - } - defer subAfoo2.Unsubscribe() - nca.Flush() - // A should have 5 subs - if err := checkExpectedSubs(5, srvA); err != nil { - t.Fatal(err.Error()) - } - // B should have 4 - if err := checkExpectedSubs(4, srvB); err != nil { - t.Fatal(err.Error()) - } - // Send a message from B and check that it is received. - if err := ncb.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - for i := 0; i < 2; i++ { - select { - case <-ch: - case <-time.After(time.Second): - t.Fatal("Did not get the message") - } - } - - // Close connection from B, and restart server B too. - // We want to make sure that - ncb.Close() - srvB.Shutdown() - - // Since B had 2 local subs, A should go from 5 to 3 - if err := checkExpectedSubs(3, srvA); err != nil { - t.Fatal(err.Error()) - } - - // Restart server B - srvB, optsB = RunServerWithConfig("./configs/srv_b.conf") - defer srvB.Shutdown() - // Check that subs from A that can be sent to B are sent. - // That would be 2 (the 2 subscriptions on foo). - if err := checkExpectedSubs(2, srvB); err != nil { - t.Fatal(err.Error()) - } - - // Connect to B and send on "foo" and make sure we receive - ncb, err = nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", optsB.Port)) - if err != nil { - t.Fatalf("Error on connect: %v", err) - } - defer ncb.Close() - if err := ncb.Publish("foo", []byte("hello")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - for i := 0; i < 2; i++ { - select { - case <-ch: - case <-time.After(time.Second): - t.Fatal("Did not get the message") - } - } - - // Send on "bat" and make sure that this is not received. - if err := ncb.Publish("bat", []byte("hello")); err != nil { - t.Fatalf("Error on publish: %v", err) - } - select { - case <-ch: - t.Fatal("Message should not have been received") - case <-time.After(100 * time.Millisecond): - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/test.go b/vendor/github.com/nats-io/gnatsd/test/test.go deleted file mode 100644 index 43684528..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/test.go +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "crypto/rand" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "net" - "regexp" - "runtime" - "strings" - "time" - - "github.com/nats-io/gnatsd/server" -) - -// So we can pass tests and benchmarks.. -type tLogger interface { - Fatalf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) -} - -// DefaultTestOptions are default options for the unit tests. -var DefaultTestOptions = server.Options{ - Host: "127.0.0.1", - Port: 4222, - NoLog: true, - NoSigs: true, - MaxControlLine: 256, -} - -// RunDefaultServer starts a new Go routine based server using the default options -func RunDefaultServer() *server.Server { - return RunServer(&DefaultTestOptions) -} - -// RunServer starts a new Go routine based server -func RunServer(opts *server.Options) *server.Server { - if opts == nil { - opts = &DefaultTestOptions - } - s := server.New(opts) - if s == nil { - panic("No NATS Server object returned.") - } - - // Run server in Go routine. - go s.Start() - - // Wait for accept loop(s) to be started - if !s.ReadyForConnections(10 * time.Second) { - panic("Unable to start NATS Server in Go Routine") - } - return s -} - -// LoadConfig loads a configuration from a filename -func LoadConfig(configFile string) (opts *server.Options) { - opts, err := server.ProcessConfigFile(configFile) - if err != nil { - panic(fmt.Sprintf("Error processing configuration file: %v", err)) - } - opts.NoSigs, opts.NoLog = true, true - return -} - -// RunServerWithConfig starts a new Go routine based server with a configuration file. -func RunServerWithConfig(configFile string) (srv *server.Server, opts *server.Options) { - opts = LoadConfig(configFile) - srv = RunServer(opts) - return -} - -func stackFatalf(t tLogger, f string, args ...interface{}) { - lines := make([]string, 0, 32) - msg := fmt.Sprintf(f, args...) - lines = append(lines, msg) - - // Ignore ourselves - _, testFile, _, _ := runtime.Caller(0) - - // Generate the Stack of callers: - for i := 0; true; i++ { - _, file, line, ok := runtime.Caller(i) - if !ok { - break - } - if file == testFile { - continue - } - msg := fmt.Sprintf("%d - %s:%d", i, file, line) - lines = append(lines, msg) - } - - t.Fatalf("%s", strings.Join(lines, "\n")) -} - -func acceptRouteConn(t tLogger, host string, timeout time.Duration) net.Conn { - l, e := net.Listen("tcp", host) - if e != nil { - stackFatalf(t, "Error listening for route connection on %v: %v", host, e) - } - defer l.Close() - - tl := l.(*net.TCPListener) - tl.SetDeadline(time.Now().Add(timeout)) - conn, err := l.Accept() - tl.SetDeadline(time.Time{}) - - if err != nil { - stackFatalf(t, "Did not receive a route connection request: %v", err) - } - return conn -} - -func createRouteConn(t tLogger, host string, port int) net.Conn { - return createClientConn(t, host, port) -} - -func createClientConn(t tLogger, host string, port int) net.Conn { - addr := fmt.Sprintf("%s:%d", host, port) - c, err := net.DialTimeout("tcp", addr, 3*time.Second) - if err != nil { - stackFatalf(t, "Could not connect to server: %v\n", err) - } - return c -} - -func checkSocket(t tLogger, addr string, wait time.Duration) { - end := time.Now().Add(wait) - for time.Now().Before(end) { - conn, err := net.Dial("tcp", addr) - if err != nil { - // Retry after 50ms - time.Sleep(50 * time.Millisecond) - continue - } - conn.Close() - // Wait a bit to give a chance to the server to remove this - // "client" from its state, which may otherwise interfere with - // some tests. - time.Sleep(25 * time.Millisecond) - return - } - // We have failed to bind the socket in the time allowed. - t.Fatalf("Failed to connect to the socket: %q", addr) -} - -func checkInfoMsg(t tLogger, c net.Conn) server.Info { - buf := expectResult(t, c, infoRe) - js := infoRe.FindAllSubmatch(buf, 1)[0][1] - var sinfo server.Info - err := json.Unmarshal(js, &sinfo) - if err != nil { - stackFatalf(t, "Could not unmarshal INFO json: %v\n", err) - } - return sinfo -} - -func doConnect(t tLogger, c net.Conn, verbose, pedantic, ssl bool) { - checkInfoMsg(t, c) - cs := fmt.Sprintf("CONNECT {\"verbose\":%v,\"pedantic\":%v,\"tls_required\":%v}\r\n", verbose, pedantic, ssl) - sendProto(t, c, cs) -} - -func doDefaultConnect(t tLogger, c net.Conn) { - // Basic Connect - doConnect(t, c, false, false, false) -} - -const connectProto = "CONNECT {\"verbose\":false,\"user\":\"%s\",\"pass\":\"%s\",\"name\":\"%s\"}\r\n" - -func doRouteAuthConnect(t tLogger, c net.Conn, user, pass, id string) { - cs := fmt.Sprintf(connectProto, user, pass, id) - sendProto(t, c, cs) -} - -func setupRouteEx(t tLogger, c net.Conn, opts *server.Options, id string) (sendFun, expectFun) { - user := opts.Cluster.Username - pass := opts.Cluster.Password - doRouteAuthConnect(t, c, user, pass, id) - return sendCommand(t, c), expectCommand(t, c) -} - -func setupRoute(t tLogger, c net.Conn, opts *server.Options) (sendFun, expectFun) { - u := make([]byte, 16) - io.ReadFull(rand.Reader, u) - id := fmt.Sprintf("ROUTER:%s", hex.EncodeToString(u)) - return setupRouteEx(t, c, opts, id) -} - -func setupConn(t tLogger, c net.Conn) (sendFun, expectFun) { - doDefaultConnect(t, c) - return sendCommand(t, c), expectCommand(t, c) -} - -func setupConnWithProto(t tLogger, c net.Conn, proto int) (sendFun, expectFun) { - checkInfoMsg(t, c) - cs := fmt.Sprintf("CONNECT {\"verbose\":%v,\"pedantic\":%v,\"tls_required\":%v,\"protocol\":%d}\r\n", false, false, false, proto) - sendProto(t, c, cs) - return sendCommand(t, c), expectCommand(t, c) -} - -type sendFun func(string) -type expectFun func(*regexp.Regexp) []byte - -// Closure version for easier reading -func sendCommand(t tLogger, c net.Conn) sendFun { - return func(op string) { - sendProto(t, c, op) - } -} - -// Closure version for easier reading -func expectCommand(t tLogger, c net.Conn) expectFun { - return func(re *regexp.Regexp) []byte { - return expectResult(t, c, re) - } -} - -// Send the protocol command to the server. -func sendProto(t tLogger, c net.Conn, op string) { - n, err := c.Write([]byte(op)) - if err != nil { - stackFatalf(t, "Error writing command to conn: %v\n", err) - } - if n != len(op) { - stackFatalf(t, "Partial write: %d vs %d\n", n, len(op)) - } -} - -var ( - infoRe = regexp.MustCompile(`INFO\s+([^\r\n]+)\r\n`) - pingRe = regexp.MustCompile(`PING\r\n`) - pongRe = regexp.MustCompile(`PONG\r\n`) - msgRe = regexp.MustCompile(`(?:(?:MSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\s*\r\n([^\\r\\n]*?)\r\n)+?)`) - okRe = regexp.MustCompile(`\A\+OK\r\n`) - errRe = regexp.MustCompile(`\A\-ERR\s+([^\r\n]+)\r\n`) - subRe = regexp.MustCompile(`SUB\s+([^\s]+)((\s+)([^\s]+))?\s+([^\s]+)\r\n`) - unsubRe = regexp.MustCompile(`UNSUB\s+([^\s]+)(\s+(\d+))?\r\n`) - connectRe = regexp.MustCompile(`CONNECT\s+([^\r\n]+)\r\n`) -) - -const ( - subIndex = 1 - sidIndex = 2 - replyIndex = 4 - lenIndex = 5 - msgIndex = 6 -) - -// Test result from server against regexp -func expectResult(t tLogger, c net.Conn, re *regexp.Regexp) []byte { - expBuf := make([]byte, 32768) - // Wait for commands to be processed and results queued for read - c.SetReadDeadline(time.Now().Add(5 * time.Second)) - n, err := c.Read(expBuf) - c.SetReadDeadline(time.Time{}) - - if n <= 0 && err != nil { - stackFatalf(t, "Error reading from conn: %v\n", err) - } - buf := expBuf[:n] - - if !re.Match(buf) { - stackFatalf(t, "Response did not match expected: \n\tReceived:'%q'\n\tExpected:'%s'\n", buf, re) - } - return buf -} - -func expectNothing(t tLogger, c net.Conn) { - expBuf := make([]byte, 32) - c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) - n, err := c.Read(expBuf) - c.SetReadDeadline(time.Time{}) - if err == nil && n > 0 { - stackFatalf(t, "Expected nothing, received: '%q'\n", expBuf[:n]) - } -} - -// This will check that we got what we expected. -func checkMsg(t tLogger, m [][]byte, subject, sid, reply, len, msg string) { - if string(m[subIndex]) != subject { - stackFatalf(t, "Did not get correct subject: expected '%s' got '%s'\n", subject, m[subIndex]) - } - if sid != "" && string(m[sidIndex]) != sid { - stackFatalf(t, "Did not get correct sid: expected '%s' got '%s'\n", sid, m[sidIndex]) - } - if string(m[replyIndex]) != reply { - stackFatalf(t, "Did not get correct reply: expected '%s' got '%s'\n", reply, m[replyIndex]) - } - if string(m[lenIndex]) != len { - stackFatalf(t, "Did not get correct msg length: expected '%s' got '%s'\n", len, m[lenIndex]) - } - if string(m[msgIndex]) != msg { - stackFatalf(t, "Did not get correct msg: expected '%s' got '%s'\n", msg, m[msgIndex]) - } -} - -// Closure for expectMsgs -func expectMsgsCommand(t tLogger, ef expectFun) func(int) [][][]byte { - return func(expected int) [][][]byte { - buf := ef(msgRe) - matches := msgRe.FindAllSubmatch(buf, -1) - if len(matches) != expected { - stackFatalf(t, "Did not get correct # msgs: %d vs %d\n", len(matches), expected) - } - return matches - } -} - -// This will check that the matches include at least one of the sids. Useful for checking -// that we received messages on a certain queue group. -func checkForQueueSid(t tLogger, matches [][][]byte, sids []string) { - seen := make(map[string]int, len(sids)) - for _, sid := range sids { - seen[sid] = 0 - } - for _, m := range matches { - sid := string(m[sidIndex]) - if _, ok := seen[sid]; ok { - seen[sid]++ - } - } - // Make sure we only see one and exactly one. - total := 0 - for _, n := range seen { - total += n - } - if total != 1 { - stackFatalf(t, "Did not get a msg for queue sids group: expected 1 got %d\n", total) - } -} - -// This will check that the matches include all of the sids. Useful for checking -// that we received messages on all subscribers. -func checkForPubSids(t tLogger, matches [][][]byte, sids []string) { - seen := make(map[string]int, len(sids)) - for _, sid := range sids { - seen[sid] = 0 - } - for _, m := range matches { - sid := string(m[sidIndex]) - if _, ok := seen[sid]; ok { - seen[sid]++ - } - } - // Make sure we only see one and exactly one for each sid. - for sid, n := range seen { - if n != 1 { - stackFatalf(t, "Did not get a msg for sid[%s]: expected 1 got %d\n", sid, n) - - } - } -} - -// Helper function to generate next opts to make sure no port conflicts etc. -func nextServerOpts(opts *server.Options) *server.Options { - nopts := opts.Clone() - nopts.Port++ - nopts.Cluster.Port++ - nopts.HTTPPort++ - return nopts -} diff --git a/vendor/github.com/nats-io/gnatsd/test/test_test.go b/vendor/github.com/nats-io/gnatsd/test/test_test.go deleted file mode 100644 index ac041747..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/test_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "fmt" - "strings" - "sync" - "testing" - "time" -) - -func checkFor(t *testing.T, totalWait, sleepDur time.Duration, f func() error) { - t.Helper() - timeout := time.Now().Add(totalWait) - var err error - for time.Now().Before(timeout) { - err = f() - if err == nil { - return - } - time.Sleep(sleepDur) - } - if err != nil { - t.Fatal(err.Error()) - } -} - -type dummyLogger struct { - sync.Mutex - msg string -} - -func (d *dummyLogger) Fatalf(format string, args ...interface{}) { - d.Lock() - d.msg = fmt.Sprintf(format, args...) - d.Unlock() -} - -func (d *dummyLogger) Errorf(format string, args ...interface{}) { -} - -func (d *dummyLogger) Debugf(format string, args ...interface{}) { -} - -func (d *dummyLogger) Tracef(format string, args ...interface{}) { -} - -func (d *dummyLogger) Noticef(format string, args ...interface{}) { -} - -func TestStackFatal(t *testing.T) { - d := &dummyLogger{} - stackFatalf(d, "test stack %d", 1) - if !strings.HasPrefix(d.msg, "test stack 1") { - t.Fatalf("Unexpected start of stack: %v", d.msg) - } - if !strings.Contains(d.msg, "test_test.go") { - t.Fatalf("Unexpected stack: %v", d.msg) - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/tls_test.go b/vendor/github.com/nats-io/gnatsd/test/tls_test.go deleted file mode 100644 index 38a90f81..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/tls_test.go +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2015-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "bufio" - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "net" - "strings" - "sync" - "testing" - "time" - - "github.com/nats-io/gnatsd/server" - "github.com/nats-io/go-nats" -) - -func TestTLSConnection(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/tls.conf") - defer srv.Shutdown() - - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint) - nc, err := nats.Connect(nurl) - if err == nil { - nc.Close() - t.Fatalf("Expected error trying to connect to secure server") - } - - // Do simple SecureConnect - nc, err = nats.Connect(fmt.Sprintf("tls://%s/", endpoint)) - if err == nil { - nc.Close() - t.Fatalf("Expected error trying to connect to secure server with no auth") - } - - // Now do more advanced checking, verifying servername and using rootCA. - - nc, err = nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem")) - if err != nil { - t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err) - } - defer nc.Close() - - subj := "foo-tls" - sub, _ := nc.SubscribeSync(subj) - - nc.Publish(subj, []byte("We are Secure!")) - nc.Flush() - nmsgs, _ := sub.QueuedMsgs() - if nmsgs != 1 { - t.Fatalf("Expected to receive a message over the TLS connection") - } -} - -func TestTLSClientCertificate(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/tlsverify.conf") - defer srv.Shutdown() - - nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port) - - _, err := nats.Connect(nurl) - if err == nil { - t.Fatalf("Expected error trying to connect to secure server without a certificate") - } - - // Load client certificate to successfully connect. - certFile := "./configs/certs/client-cert.pem" - keyFile := "./configs/certs/client-key.pem" - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - t.Fatalf("error parsing X509 certificate/key pair: %v", err) - } - - // Load in root CA for server verification - rootPEM, err := ioutil.ReadFile("./configs/certs/ca.pem") - if err != nil || rootPEM == nil { - t.Fatalf("failed to read root certificate") - } - pool := x509.NewCertPool() - ok := pool.AppendCertsFromPEM([]byte(rootPEM)) - if !ok { - t.Fatalf("failed to parse root certificate") - } - - config := &tls.Config{ - Certificates: []tls.Certificate{cert}, - ServerName: opts.Host, - RootCAs: pool, - MinVersion: tls.VersionTLS12, - } - - copts := nats.GetDefaultOptions() - copts.Url = nurl - copts.Secure = true - copts.TLSConfig = config - - nc, err := copts.Connect() - if err != nil { - t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err) - } - nc.Flush() - defer nc.Close() -} - -func TestTLSVerifyClientCertificate(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/tlsverify_noca.conf") - defer srv.Shutdown() - - nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port) - - // The client is configured properly, but the server has no CA - // to verify the client certificate. Connection should fail. - nc, err := nats.Connect(nurl, - nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem"), - nats.RootCAs("./configs/certs/ca.pem")) - if err == nil { - nc.Close() - t.Fatal("Expected failure to connect, did not") - } -} - -func TestTLSConnectionTimeout(t *testing.T) { - opts := LoadConfig("./configs/tls.conf") - opts.TLSTimeout = 0.25 - - srv := RunServer(opts) - defer srv.Shutdown() - - // Dial with normal TCP - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - conn, err := net.Dial("tcp", endpoint) - if err != nil { - t.Fatalf("Could not connect to %q", endpoint) - } - defer conn.Close() - - // Read deadlines - conn.SetReadDeadline(time.Now().Add(2 * time.Second)) - - // Read the INFO string. - br := bufio.NewReader(conn) - info, err := br.ReadString('\n') - if err != nil { - t.Fatalf("Failed to read INFO - %v", err) - } - if !strings.HasPrefix(info, "INFO ") { - t.Fatalf("INFO response incorrect: %s\n", info) - } - wait := time.Duration(opts.TLSTimeout * float64(time.Second)) - time.Sleep(wait) - // Read deadlines - conn.SetReadDeadline(time.Now().Add(2 * time.Second)) - tlsErr, err := br.ReadString('\n') - if err == nil && !strings.Contains(tlsErr, "-ERR 'Secure Connection - TLS Required") { - t.Fatalf("TLS Timeout response incorrect: %q\n", tlsErr) - } -} - -// Ensure there is no race between authorization timeout and TLS handshake. -func TestTLSAuthorizationShortTimeout(t *testing.T) { - opts := LoadConfig("./configs/tls.conf") - opts.AuthTimeout = 0.001 - - srv := RunServer(opts) - defer srv.Shutdown() - - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint) - - // Expect an error here (no CA) but not a TLS oversized record error which - // indicates the authorization timeout fired too soon. - _, err := nats.Connect(nurl) - if err == nil { - t.Fatal("Expected error trying to connect to secure server") - } - if strings.Contains(err.Error(), "oversized record") { - t.Fatal("Corrupted TLS handshake:", err) - } -} - -func stressConnect(t *testing.T, wg *sync.WaitGroup, errCh chan error, url string, index int) { - defer wg.Done() - - subName := fmt.Sprintf("foo.%d", index) - - for i := 0; i < 33; i++ { - nc, err := nats.Connect(url, nats.RootCAs("./configs/certs/ca.pem")) - if err != nil { - errCh <- fmt.Errorf("Unable to create TLS connection: %v\n", err) - return - } - defer nc.Close() - - sub, err := nc.SubscribeSync(subName) - if err != nil { - errCh <- fmt.Errorf("Unable to subscribe on '%s': %v\n", subName, err) - return - } - - if err := nc.Publish(subName, []byte("secure data")); err != nil { - errCh <- fmt.Errorf("Unable to send on '%s': %v\n", subName, err) - } - - if _, err := sub.NextMsg(2 * time.Second); err != nil { - errCh <- fmt.Errorf("Unable to get next message: %v\n", err) - } - - nc.Close() - } - - errCh <- nil -} - -func TestTLSStressConnect(t *testing.T) { - opts, err := server.ProcessConfigFile("./configs/tls.conf") - if err != nil { - panic(fmt.Sprintf("Error processing configuration file: %v", err)) - } - opts.NoSigs, opts.NoLog = true, true - - // For this test, remove the authorization - opts.Username = "" - opts.Password = "" - - // Increase ssl timeout - opts.TLSTimeout = 2.0 - - srv := RunServer(opts) - defer srv.Shutdown() - - nurl := fmt.Sprintf("tls://%s:%d", opts.Host, opts.Port) - - threadCount := 3 - - errCh := make(chan error, threadCount) - - var wg sync.WaitGroup - wg.Add(threadCount) - - for i := 0; i < threadCount; i++ { - go stressConnect(t, &wg, errCh, nurl, i) - } - - wg.Wait() - - var lastError error - for i := 0; i < threadCount; i++ { - err := <-errCh - if err != nil { - lastError = err - } - } - - if lastError != nil { - t.Fatalf("%v\n", lastError) - } -} - -func TestTLSBadAuthError(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/tls.conf") - defer srv.Shutdown() - - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, "NOT_THE_PASSWORD", endpoint) - - _, err := nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem")) - if err == nil { - t.Fatalf("Expected error trying to connect to secure server") - } - if err.Error() != nats.ErrAuthorization.Error() { - t.Fatalf("Excpected and auth violation, got %v\n", err) - } -} - -func TestTLSConnectionCurvePref(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/tls_curve_pref.conf") - defer srv.Shutdown() - - if len(opts.TLSConfig.CurvePreferences) != 1 { - t.Fatal("Invalid curve preference loaded.") - } - - if opts.TLSConfig.CurvePreferences[0] != tls.CurveP256 { - t.Fatalf("Invalid curve preference loaded [%v].", opts.TLSConfig.CurvePreferences[0]) - } - - endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) - nurl := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint) - nc, err := nats.Connect(nurl) - if err == nil { - nc.Close() - t.Fatalf("Expected error trying to connect to secure server") - } - - // Do simple SecureConnect - nc, err = nats.Connect(fmt.Sprintf("tls://%s/", endpoint)) - if err == nil { - nc.Close() - t.Fatalf("Expected error trying to connect to secure server with no auth") - } - - // Now do more advanced checking, verifying servername and using rootCA. - - nc, err = nats.Connect(nurl, nats.RootCAs("./configs/certs/ca.pem")) - if err != nil { - t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err) - } - defer nc.Close() - - subj := "foo-tls" - sub, _ := nc.SubscribeSync(subj) - - nc.Publish(subj, []byte("We are Secure!")) - nc.Flush() - nmsgs, _ := sub.QueuedMsgs() - if nmsgs != 1 { - t.Fatalf("Expected to receive a message over the TLS connection") - } -} diff --git a/vendor/github.com/nats-io/gnatsd/test/user_authorization_test.go b/vendor/github.com/nats-io/gnatsd/test/user_authorization_test.go deleted file mode 100644 index 851c917d..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/user_authorization_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "regexp" - "testing" -) - -const DefaultPass = "foo" - -var permErrRe = regexp.MustCompile(`\A\-ERR\s+'Permissions Violation([^\r\n]+)\r\n`) - -func TestUserAuthorizationProto(t *testing.T) { - srv, opts := RunServerWithConfig("./configs/authorization.conf") - defer srv.Shutdown() - - // Alice can do anything, check a few for OK result. - c := createClientConn(t, opts.Host, opts.Port) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", "alice", DefaultPass) - expectResult(t, c, okRe) - sendProto(t, c, "PUB foo 2\r\nok\r\n") - expectResult(t, c, okRe) - sendProto(t, c, "SUB foo 1\r\n") - expectResult(t, c, okRe) - - // Check that we now reserve _SYS.> though for internal, so no clients. - sendProto(t, c, "PUB _SYS.HB 2\r\nok\r\n") - expectResult(t, c, permErrRe) - - // Check that _ is ok - sendProto(t, c, "PUB _ 2\r\nok\r\n") - expectResult(t, c, okRe) - - c.Close() - - // Bob is a requestor only, e.g. req.foo, req.bar for publish, subscribe only to INBOXes. - c = createClientConn(t, opts.Host, opts.Port) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", "bob", DefaultPass) - expectResult(t, c, okRe) - - // These should error. - sendProto(t, c, "SUB foo 1\r\n") - expectResult(t, c, permErrRe) - sendProto(t, c, "PUB foo 2\r\nok\r\n") - expectResult(t, c, permErrRe) - - // These should work ok. - sendProto(t, c, "SUB _INBOX.abcd 1\r\n") - expectResult(t, c, okRe) - sendProto(t, c, "PUB req.foo 2\r\nok\r\n") - expectResult(t, c, okRe) - sendProto(t, c, "PUB req.bar 2\r\nok\r\n") - expectResult(t, c, okRe) - c.Close() - - // Joe is a default user - c = createClientConn(t, opts.Host, opts.Port) - defer c.Close() - expectAuthRequired(t, c) - doAuthConnect(t, c, "", "joe", DefaultPass) - expectResult(t, c, okRe) - - // These should error. - sendProto(t, c, "SUB foo.bar.* 1\r\n") - expectResult(t, c, permErrRe) - sendProto(t, c, "PUB foo.bar.baz 2\r\nok\r\n") - expectResult(t, c, permErrRe) - - // These should work ok. - sendProto(t, c, "SUB _INBOX.abcd 1\r\n") - expectResult(t, c, okRe) - sendProto(t, c, "SUB PUBLIC.abcd 1\r\n") - expectResult(t, c, okRe) - - sendProto(t, c, "PUB SANDBOX.foo 2\r\nok\r\n") - expectResult(t, c, okRe) - sendProto(t, c, "PUB SANDBOX.bar 2\r\nok\r\n") - expectResult(t, c, okRe) - - // Since only PWC, this should fail (too many tokens). - sendProto(t, c, "PUB SANDBOX.foo.bar 2\r\nok\r\n") - expectResult(t, c, permErrRe) - - c.Close() -} diff --git a/vendor/github.com/nats-io/gnatsd/test/verbose_test.go b/vendor/github.com/nats-io/gnatsd/test/verbose_test.go deleted file mode 100644 index d370a2df..00000000 --- a/vendor/github.com/nats-io/gnatsd/test/verbose_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2012-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package test - -import ( - "testing" -) - -func TestVerbosePing(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - doConnect(t, c, true, false, false) - - send := sendCommand(t, c) - expect := expectCommand(t, c) - - expect(okRe) - - // Ping should still be same - send("PING\r\n") - expect(pongRe) -} - -func TestVerboseConnect(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - doConnect(t, c, true, false, false) - - send := sendCommand(t, c) - expect := expectCommand(t, c) - - expect(okRe) - - // Connect - send("CONNECT {\"verbose\":true,\"pedantic\":true,\"tls_required\":false}\r\n") - expect(okRe) -} - -func TestVerbosePubSub(t *testing.T) { - s := runProtoServer() - defer s.Shutdown() - - c := createClientConn(t, "127.0.0.1", PROTO_TEST_PORT) - defer c.Close() - - doConnect(t, c, true, false, false) - send := sendCommand(t, c) - expect := expectCommand(t, c) - - expect(okRe) - - // Pub - send("PUB foo 2\r\nok\r\n") - expect(okRe) - - // Sub - send("SUB foo 1\r\n") - expect(okRe) - - // UnSub - send("UNSUB 1\r\n") - expect(okRe) -} diff --git a/vendor/github.com/nats-io/gnatsd/util/gnatsd.service b/vendor/github.com/nats-io/gnatsd/util/gnatsd.service deleted file mode 100644 index 259196f4..00000000 --- a/vendor/github.com/nats-io/gnatsd/util/gnatsd.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=NATS messaging server -After=network.target - -[Service] -PrivateTmp=true -Type=simple -ExecStart=/usr/sbin/gnatsd -c /etc/gnatsd.conf -ExecReload=/bin/kill -s HUP $MAINPID -ExecStop=/bin/kill -s SIGINT $MAINPID -User=gnatsd -Group=gnatsd - -[Install] -WantedBy=multi-user.target - diff --git a/vendor/github.com/nats-io/gnatsd/util/tls_pre17.go b/vendor/github.com/nats-io/gnatsd/util/tls_pre17.go deleted file mode 100644 index 99ea32b4..00000000 --- a/vendor/github.com/nats-io/gnatsd/util/tls_pre17.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build go1.5,!go1.7 - -package util - -import ( - "crypto/tls" -) - -// CloneTLSConfig returns a copy of c. Only the exported fields are copied. -// This is temporary, until this is provided by the language. -// https://go-review.googlesource.com/#/c/28075/ -func CloneTLSConfig(c *tls.Config) *tls.Config { - return &tls.Config{ - Rand: c.Rand, - Time: c.Time, - Certificates: c.Certificates, - NameToCertificate: c.NameToCertificate, - GetCertificate: c.GetCertificate, - RootCAs: c.RootCAs, - NextProtos: c.NextProtos, - ServerName: c.ServerName, - ClientAuth: c.ClientAuth, - ClientCAs: c.ClientCAs, - InsecureSkipVerify: c.InsecureSkipVerify, - CipherSuites: c.CipherSuites, - PreferServerCipherSuites: c.PreferServerCipherSuites, - SessionTicketsDisabled: c.SessionTicketsDisabled, - SessionTicketKey: c.SessionTicketKey, - ClientSessionCache: c.ClientSessionCache, - MinVersion: c.MinVersion, - MaxVersion: c.MaxVersion, - CurvePreferences: c.CurvePreferences, - } -} diff --git a/vendor/github.com/nats-io/gnatsd/util/tls_pre18.go b/vendor/github.com/nats-io/gnatsd/util/tls_pre18.go deleted file mode 100644 index 7df47261..00000000 --- a/vendor/github.com/nats-io/gnatsd/util/tls_pre18.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build go1.7,!go1.8 - -package util - -import ( - "crypto/tls" -) - -// CloneTLSConfig returns a copy of c. Only the exported fields are copied. -// This is temporary, until this is provided by the language. -// https://go-review.googlesource.com/#/c/28075/ -func CloneTLSConfig(c *tls.Config) *tls.Config { - return &tls.Config{ - Rand: c.Rand, - Time: c.Time, - Certificates: c.Certificates, - NameToCertificate: c.NameToCertificate, - GetCertificate: c.GetCertificate, - RootCAs: c.RootCAs, - NextProtos: c.NextProtos, - ServerName: c.ServerName, - ClientAuth: c.ClientAuth, - ClientCAs: c.ClientCAs, - InsecureSkipVerify: c.InsecureSkipVerify, - CipherSuites: c.CipherSuites, - PreferServerCipherSuites: c.PreferServerCipherSuites, - SessionTicketsDisabled: c.SessionTicketsDisabled, - SessionTicketKey: c.SessionTicketKey, - ClientSessionCache: c.ClientSessionCache, - MinVersion: c.MinVersion, - MaxVersion: c.MaxVersion, - CurvePreferences: c.CurvePreferences, - DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, - Renegotiation: c.Renegotiation, - } -} diff --git a/vendor/github.com/nats-io/go-nats/.gitignore b/vendor/github.com/nats-io/go-nats/.gitignore new file mode 100644 index 00000000..3d5981fa --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/.gitignore @@ -0,0 +1,39 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +# Emacs +*~ +\#*\# +.\#* + +# vi/vim +.??*.swp + +# Mac +.DS_Store + +# Eclipse +.project +.settings/ + +# bin diff --git a/vendor/github.com/nats-io/go-nats/.travis.yml b/vendor/github.com/nats-io/go-nats/.travis.yml new file mode 100644 index 00000000..732e4753 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/.travis.yml @@ -0,0 +1,21 @@ +language: go +sudo: false +go: +- 1.11.x +- 1.10.x +- 1.9.x +install: +- go get -t ./... +- go get github.com/nats-io/gnatsd +- go get github.com/mattn/goveralls +- go get github.com/wadey/gocovmerge +- go get -u honnef.co/go/tools/cmd/megacheck +- go get -u github.com/client9/misspell/cmd/misspell +before_script: +- $(exit $(go fmt ./... | wc -l)) +- go vet ./... +- misspell -error -locale US . +- megacheck -ignore "$(cat staticcheck.ignore)" ./... +script: +- go test -i -race ./... +- if [[ "$TRAVIS_GO_VERSION" == 1.11.* ]]; then ./scripts/cov.sh TRAVIS; else go test -v -race ./...; fi diff --git a/vendor/github.com/nats-io/go-nats/README.md b/vendor/github.com/nats-io/go-nats/README.md index f109f3d2..933a1ef7 100644 --- a/vendor/github.com/nats-io/go-nats/README.md +++ b/vendor/github.com/nats-io/go-nats/README.md @@ -19,6 +19,7 @@ go get github.com/nats-io/gnatsd ```go +// Connect to a server nc, _ := nats.Connect(nats.DefaultURL) // Simple Publisher @@ -41,6 +42,9 @@ msg := <- ch // Unsubscribe sub.Unsubscribe() +// Drain +sub.Drain() + // Requests msg, err := nc.Request("help", []byte("help me"), 10*time.Millisecond) @@ -49,9 +53,12 @@ nc.Subscribe("help", func(m *Msg) { nc.Publish(m.Reply, []byte("I can help!")) }) +// Drain connection (Preferred for responders) +// Close() not needed if this is called. +nc.Drain() + // Close connection -nc, _ := nats.Connect("nats://localhost:4222") -nc.Close(); +nc.Close() ``` ## Encoded Connections diff --git a/vendor/github.com/nats-io/go-nats/bench/bench.go b/vendor/github.com/nats-io/go-nats/bench/bench.go new file mode 100644 index 00000000..2dc496bf --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/bench/bench.go @@ -0,0 +1,365 @@ +// Copyright 2016-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bench + +import ( + "bytes" + "encoding/csv" + "fmt" + "log" + "math" + "strconv" + "time" + + "github.com/nats-io/go-nats" + "github.com/nats-io/nuid" +) + +// A Sample for a particular client +type Sample struct { + JobMsgCnt int + MsgCnt uint64 + MsgBytes uint64 + IOBytes uint64 + Start time.Time + End time.Time +} + +// SampleGroup for a number of samples, the group is a Sample itself agregating the values the Samples +type SampleGroup struct { + Sample + Samples []*Sample +} + +// Benchmark to hold the various Samples organized by publishers and subscribers +type Benchmark struct { + Sample + Name string + RunID string + Pubs *SampleGroup + Subs *SampleGroup + subChannel chan *Sample + pubChannel chan *Sample +} + +// NewBenchmark initializes a Benchmark. After creating a bench call AddSubSample/AddPubSample. +// When done collecting samples, call EndBenchmark +func NewBenchmark(name string, subCnt, pubCnt int) *Benchmark { + bm := Benchmark{Name: name, RunID: nuid.Next()} + bm.Subs = NewSampleGroup() + bm.Pubs = NewSampleGroup() + bm.subChannel = make(chan *Sample, subCnt) + bm.pubChannel = make(chan *Sample, pubCnt) + return &bm +} + +// Close organizes collected Samples and calculates aggregates. After Close(), no more samples can be added. +func (bm *Benchmark) Close() { + close(bm.subChannel) + close(bm.pubChannel) + + for s := range bm.subChannel { + bm.Subs.AddSample(s) + } + for s := range bm.pubChannel { + bm.Pubs.AddSample(s) + } + + if bm.Subs.HasSamples() { + bm.Start = bm.Subs.Start + bm.End = bm.Subs.End + } else { + bm.Start = bm.Pubs.Start + bm.End = bm.Pubs.End + } + + if bm.Subs.HasSamples() && bm.Pubs.HasSamples() { + if bm.Start.After(bm.Subs.Start) { + bm.Start = bm.Subs.Start + } + if bm.Start.After(bm.Pubs.Start) { + bm.Start = bm.Pubs.Start + } + + if bm.End.Before(bm.Subs.End) { + bm.End = bm.Subs.End + } + if bm.End.Before(bm.Pubs.End) { + bm.End = bm.Pubs.End + } + } + + bm.MsgBytes = bm.Pubs.MsgBytes + bm.Subs.MsgBytes + bm.IOBytes = bm.Pubs.IOBytes + bm.Subs.IOBytes + bm.MsgCnt = bm.Pubs.MsgCnt + bm.Subs.MsgCnt + bm.JobMsgCnt = bm.Pubs.JobMsgCnt + bm.Subs.JobMsgCnt +} + +// AddSubSample to the benchmark +func (bm *Benchmark) AddSubSample(s *Sample) { + bm.subChannel <- s +} + +// AddPubSample to the benchmark +func (bm *Benchmark) AddPubSample(s *Sample) { + bm.pubChannel <- s +} + +// CSV generates a csv report of all the samples collected +func (bm *Benchmark) CSV() string { + var buffer bytes.Buffer + writer := csv.NewWriter(&buffer) + headers := []string{"#RunID", "ClientID", "MsgCount", "MsgBytes", "MsgsPerSec", "BytesPerSec", "DurationSecs"} + if err := writer.Write(headers); err != nil { + log.Fatalf("Error while serializing headers %q: %v", headers, err) + } + groups := []*SampleGroup{bm.Subs, bm.Pubs} + pre := "S" + for i, g := range groups { + if i == 1 { + pre = "P" + } + for j, c := range g.Samples { + r := []string{bm.RunID, fmt.Sprintf("%s%d", pre, j), fmt.Sprintf("%d", c.MsgCnt), fmt.Sprintf("%d", c.MsgBytes), fmt.Sprintf("%d", c.Rate()), fmt.Sprintf("%f", c.Throughput()), fmt.Sprintf("%f", c.Duration().Seconds())} + if err := writer.Write(r); err != nil { + log.Fatalf("Error while serializing %v: %v", c, err) + } + } + } + + writer.Flush() + return buffer.String() +} + +// NewSample creates a new Sample initialized to the provided values. The nats.Conn information captured +func NewSample(jobCount int, msgSize int, start, end time.Time, nc *nats.Conn) *Sample { + s := Sample{JobMsgCnt: jobCount, Start: start, End: end} + s.MsgBytes = uint64(msgSize * jobCount) + s.MsgCnt = nc.OutMsgs + nc.InMsgs + s.IOBytes = nc.OutBytes + nc.InBytes + return &s +} + +// Throughput of bytes per second +func (s *Sample) Throughput() float64 { + return float64(s.MsgBytes) / s.Duration().Seconds() +} + +// Rate of meessages in the job per second +func (s *Sample) Rate() int64 { + return int64(float64(s.JobMsgCnt) / s.Duration().Seconds()) +} + +func (s *Sample) String() string { + rate := commaFormat(s.Rate()) + throughput := HumanBytes(s.Throughput(), false) + return fmt.Sprintf("%s msgs/sec ~ %s/sec", rate, throughput) +} + +// Duration that the sample was active +func (s *Sample) Duration() time.Duration { + return s.End.Sub(s.Start) +} + +// Seconds that the sample or samples were active +func (s *Sample) Seconds() float64 { + return s.Duration().Seconds() +} + +// NewSampleGroup initializer +func NewSampleGroup() *SampleGroup { + s := new(SampleGroup) + s.Samples = make([]*Sample, 0) + return s +} + +// Statistics information of the sample group (min, average, max and standard deviation) +func (sg *SampleGroup) Statistics() string { + return fmt.Sprintf("min %s | avg %s | max %s | stddev %s msgs", commaFormat(sg.MinRate()), commaFormat(sg.AvgRate()), commaFormat(sg.MaxRate()), commaFormat(int64(sg.StdDev()))) +} + +// MinRate returns the smallest message rate in the SampleGroup +func (sg *SampleGroup) MinRate() int64 { + m := int64(0) + for i, s := range sg.Samples { + if i == 0 { + m = s.Rate() + } + m = min(m, s.Rate()) + } + return m +} + +// MaxRate returns the largest message rate in the SampleGroup +func (sg *SampleGroup) MaxRate() int64 { + m := int64(0) + for i, s := range sg.Samples { + if i == 0 { + m = s.Rate() + } + m = max(m, s.Rate()) + } + return m +} + +// AvgRate returns the average of all the message rates in the SampleGroup +func (sg *SampleGroup) AvgRate() int64 { + sum := uint64(0) + for _, s := range sg.Samples { + sum += uint64(s.Rate()) + } + return int64(sum / uint64(len(sg.Samples))) +} + +// StdDev returns the standard deviation the message rates in the SampleGroup +func (sg *SampleGroup) StdDev() float64 { + avg := float64(sg.AvgRate()) + sum := float64(0) + for _, c := range sg.Samples { + sum += math.Pow(float64(c.Rate())-avg, 2) + } + variance := sum / float64(len(sg.Samples)) + return math.Sqrt(variance) +} + +// AddSample adds a Sample to the SampleGroup. After adding a Sample it shouldn't be modified. +func (sg *SampleGroup) AddSample(e *Sample) { + sg.Samples = append(sg.Samples, e) + + if len(sg.Samples) == 1 { + sg.Start = e.Start + sg.End = e.End + } + sg.IOBytes += e.IOBytes + sg.JobMsgCnt += e.JobMsgCnt + sg.MsgCnt += e.MsgCnt + sg.MsgBytes += e.MsgBytes + + if e.Start.Before(sg.Start) { + sg.Start = e.Start + } + + if e.End.After(sg.End) { + sg.End = e.End + } +} + +// HasSamples returns true if the group has samples +func (sg *SampleGroup) HasSamples() bool { + return len(sg.Samples) > 0 +} + +// Report returns a human readable report of the samples taken in the Benchmark +func (bm *Benchmark) Report() string { + var buffer bytes.Buffer + + indent := "" + if !bm.Pubs.HasSamples() && !bm.Subs.HasSamples() { + return "No publisher or subscribers. Nothing to report." + } + + if bm.Pubs.HasSamples() && bm.Subs.HasSamples() { + buffer.WriteString(fmt.Sprintf("%s Pub/Sub stats: %s\n", bm.Name, bm)) + indent += " " + } + if bm.Pubs.HasSamples() { + buffer.WriteString(fmt.Sprintf("%sPub stats: %s\n", indent, bm.Pubs)) + if len(bm.Pubs.Samples) > 1 { + for i, stat := range bm.Pubs.Samples { + buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt)) + } + buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Pubs.Statistics())) + } + } + + if bm.Subs.HasSamples() { + buffer.WriteString(fmt.Sprintf("%sSub stats: %s\n", indent, bm.Subs)) + if len(bm.Subs.Samples) > 1 { + for i, stat := range bm.Subs.Samples { + buffer.WriteString(fmt.Sprintf("%s [%d] %v (%d msgs)\n", indent, i+1, stat, stat.JobMsgCnt)) + } + buffer.WriteString(fmt.Sprintf("%s %s\n", indent, bm.Subs.Statistics())) + } + } + return buffer.String() +} + +func commaFormat(n int64) string { + in := strconv.FormatInt(n, 10) + out := make([]byte, len(in)+(len(in)-2+int(in[0]/'0'))/3) + if in[0] == '-' { + in, out[0] = in[1:], '-' + } + for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 { + out[j] = in[i] + if i == 0 { + return string(out) + } + if k++; k == 3 { + j, k = j-1, 0 + out[j] = ',' + } + } +} + +// HumanBytes formats bytes as a human readable string +func HumanBytes(bytes float64, si bool) string { + var base = 1024 + pre := []string{"K", "M", "G", "T", "P", "E"} + var post = "B" + if si { + base = 1000 + pre = []string{"k", "M", "G", "T", "P", "E"} + post = "iB" + } + if bytes < float64(base) { + return fmt.Sprintf("%.2f B", bytes) + } + exp := int(math.Log(bytes) / math.Log(float64(base))) + index := exp - 1 + units := pre[index] + post + return fmt.Sprintf("%.2f %s", bytes/math.Pow(float64(base), float64(exp)), units) +} + +func min(x, y int64) int64 { + if x < y { + return x + } + return y +} + +func max(x, y int64) int64 { + if x > y { + return x + } + return y +} + +// MsgsPerClient divides the number of messages by the number of clients and tries to distribute them as evenly as possible +func MsgsPerClient(numMsgs, numClients int) []int { + var counts []int + if numClients == 0 || numMsgs == 0 { + return counts + } + counts = make([]int, numClients) + mc := numMsgs / numClients + for i := 0; i < numClients; i++ { + counts[i] = mc + } + extra := numMsgs % numClients + for i := 0; i < extra; i++ { + counts[i]++ + } + return counts +} diff --git a/vendor/github.com/nats-io/go-nats/bench/benchlib_test.go b/vendor/github.com/nats-io/go-nats/bench/benchlib_test.go new file mode 100644 index 00000000..2fa43a50 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/bench/benchlib_test.go @@ -0,0 +1,239 @@ +// Copyright 2016-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bench + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/nats-io/go-nats" +) + +const ( + MsgSize = 8 + Million = 1000 * 1000 +) + +var baseTime = time.Now() + +func millionMessagesSecondSample(seconds int) *Sample { + messages := Million * seconds + start := baseTime + end := start.Add(time.Second * time.Duration(seconds)) + nc := new(nats.Conn) + + s := NewSample(messages, MsgSize, start, end, nc) + s.MsgCnt = uint64(messages) + s.MsgBytes = uint64(messages * MsgSize) + s.IOBytes = s.MsgBytes + return s +} + +func TestDuration(t *testing.T) { + s := millionMessagesSecondSample(1) + duration := s.End.Sub(s.Start) + if duration != s.Duration() || duration != time.Second { + t.Fatal("Expected sample duration to be 1 second") + } +} + +func TestSeconds(t *testing.T) { + s := millionMessagesSecondSample(1) + seconds := s.End.Sub(s.Start).Seconds() + if seconds != s.Seconds() || seconds != 1.0 { + t.Fatal("Expected sample seconds to be 1 second") + } +} + +func TestRate(t *testing.T) { + s := millionMessagesSecondSample(60) + if s.Rate() != Million { + t.Fatal("Expected rate at 1 million msgs") + } +} + +func TestThoughput(t *testing.T) { + s := millionMessagesSecondSample(60) + if s.Throughput() != Million*MsgSize { + t.Fatalf("Expected throughput at %d million bytes/sec", MsgSize) + } +} + +func TestStrings(t *testing.T) { + s := millionMessagesSecondSample(60) + if len(s.String()) == 0 { + t.Fatal("Sample didn't provide a String") + } +} + +func TestGroupDuration(t *testing.T) { + sg := NewSampleGroup() + sg.AddSample(millionMessagesSecondSample(1)) + sg.AddSample(millionMessagesSecondSample(2)) + duration := sg.End.Sub(sg.Start) + if duration != sg.Duration() || duration != time.Duration(2)*time.Second { + t.Fatal("Expected aggregate duration to be 2.0 seconds") + } +} + +func TestGroupSeconds(t *testing.T) { + sg := NewSampleGroup() + sg.AddSample(millionMessagesSecondSample(1)) + sg.AddSample(millionMessagesSecondSample(2)) + sg.AddSample(millionMessagesSecondSample(3)) + seconds := sg.End.Sub(sg.Start).Seconds() + if seconds != sg.Seconds() || seconds != 3.0 { + t.Fatal("Expected aggregate seconds to be 3.0 seconds") + } +} + +func TestGroupRate(t *testing.T) { + sg := NewSampleGroup() + sg.AddSample(millionMessagesSecondSample(1)) + sg.AddSample(millionMessagesSecondSample(2)) + sg.AddSample(millionMessagesSecondSample(3)) + if sg.Rate() != Million*2 { + t.Fatal("Expected MsgRate at 2 million msg/sec") + } +} + +func TestGroupThoughput(t *testing.T) { + sg := NewSampleGroup() + sg.AddSample(millionMessagesSecondSample(1)) + sg.AddSample(millionMessagesSecondSample(2)) + sg.AddSample(millionMessagesSecondSample(3)) + if sg.Throughput() != 2*Million*MsgSize { + t.Fatalf("Expected througput at %d million bytes/sec", 2*MsgSize) + } +} + +func TestMinMaxRate(t *testing.T) { + sg := NewSampleGroup() + sg.AddSample(millionMessagesSecondSample(1)) + sg.AddSample(millionMessagesSecondSample(2)) + sg.AddSample(millionMessagesSecondSample(3)) + if sg.MinRate() != sg.MaxRate() { + t.Fatal("Expected MinRate == MaxRate") + } +} + +func TestAvgRate(t *testing.T) { + sg := NewSampleGroup() + sg.AddSample(millionMessagesSecondSample(1)) + sg.AddSample(millionMessagesSecondSample(2)) + sg.AddSample(millionMessagesSecondSample(3)) + if sg.MinRate() != sg.AvgRate() { + t.Fatal("Expected MinRate == AvgRate") + } +} + +func TestStdDev(t *testing.T) { + sg := NewSampleGroup() + sg.AddSample(millionMessagesSecondSample(1)) + sg.AddSample(millionMessagesSecondSample(2)) + sg.AddSample(millionMessagesSecondSample(3)) + if sg.StdDev() != 0.0 { + t.Fatal("Expected stddev to be zero") + } +} + +func TestBenchSetup(t *testing.T) { + bench := NewBenchmark("test", 1, 1) + bench.AddSubSample(millionMessagesSecondSample(1)) + bench.AddPubSample(millionMessagesSecondSample(1)) + bench.Close() + if len(bench.RunID) == 0 { + t.Fatal("Bench doesn't have a RunID") + } + if len(bench.Pubs.Samples) != 1 { + t.Fatal("Expected one publisher") + } + if len(bench.Subs.Samples) != 1 { + t.Fatal("Expected one subscriber") + } + if bench.MsgCnt != 2*Million { + t.Fatal("Expected 2 million msgs") + } + if bench.IOBytes != 2*Million*MsgSize { + t.Fatalf("Expected %d million bytes", 2*MsgSize) + } + if bench.Duration() != time.Second { + t.Fatal("Expected duration to be 1 second") + } +} + +func makeBench(subs, pubs int) *Benchmark { + bench := NewBenchmark("test", subs, pubs) + for i := 0; i < subs; i++ { + bench.AddSubSample(millionMessagesSecondSample(1)) + } + for i := 0; i < pubs; i++ { + bench.AddPubSample(millionMessagesSecondSample(1)) + } + bench.Close() + return bench +} + +func TestCsv(t *testing.T) { + bench := makeBench(1, 1) + csv := bench.CSV() + lines := strings.Split(csv, "\n") + if len(lines) != 4 { + t.Fatal("Expected 4 lines of output from the CSV string") + } + + fields := strings.Split(lines[1], ",") + if len(fields) != 7 { + t.Fatal("Expected 7 fields") + } +} + +func TestBenchStrings(t *testing.T) { + bench := makeBench(1, 1) + s := bench.Report() + lines := strings.Split(s, "\n") + if len(lines) != 4 { + t.Fatal("Expected 3 lines of output: header, pub, sub, empty") + } + + bench = makeBench(2, 2) + s = bench.Report() + lines = strings.Split(s, "\n") + if len(lines) != 10 { + fmt.Printf("%q\n", s) + + t.Fatal("Expected 11 lines of output: header, pub header, pub x 2, stats, sub headers, sub x 2, stats, empty") + } +} + +func TestMsgsPerClient(t *testing.T) { + zero := MsgsPerClient(0, 0) + if len(zero) != 0 { + t.Fatal("Expected 0 length for 0 clients") + } + onetwo := MsgsPerClient(1, 2) + if len(onetwo) != 2 || onetwo[0] != 1 || onetwo[1] != 0 { + t.Fatal("Expected uneven distribution") + } + twotwo := MsgsPerClient(2, 2) + if len(twotwo) != 2 || twotwo[0] != 1 || twotwo[1] != 1 { + t.Fatal("Expected even distribution") + } + threetwo := MsgsPerClient(3, 2) + if len(threetwo) != 2 || threetwo[0] != 2 || threetwo[1] != 1 { + t.Fatal("Expected uneven distribution") + } +} diff --git a/vendor/github.com/nats-io/go-nats/context.go b/vendor/github.com/nats-io/go-nats/context.go index 4f9ec67d..3d753564 100644 --- a/vendor/github.com/nats-io/go-nats/context.go +++ b/vendor/github.com/nats-io/go-nats/context.go @@ -31,6 +31,11 @@ func (nc *Conn) RequestWithContext(ctx context.Context, subj string, data []byte if nc == nil { return nil, ErrInvalidConnection } + // Check whether the context is done already before making + // the request. + if ctx.Err() != nil { + return nil, ctx.Err() + } nc.mu.Lock() // If user wants the old style. @@ -116,6 +121,9 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) { if s == nil { return nil, ErrBadSubscription } + if ctx.Err() != nil { + return nil, ctx.Err() + } s.mu.Lock() err := s.validateNextMsgState() @@ -124,7 +132,6 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) { return nil, err } - // snapshot mch := s.mch s.mu.Unlock() diff --git a/vendor/github.com/nats-io/go-nats/enc.go b/vendor/github.com/nats-io/go-nats/enc.go index d35b5b68..0f06acc1 100644 --- a/vendor/github.com/nats-io/go-nats/enc.go +++ b/vendor/github.com/nats-io/go-nats/enc.go @@ -254,6 +254,15 @@ func (c *EncodedConn) Close() { c.Conn.Close() } +// Drain will put a connection into a drain state. All subscriptions will +// immediately be put into a drain state. Upon completion, the publishers +// will be drained and can not publish any additional messages. Upon draining +// of the publishers, the connection will be closed. Use the ClosedCB() +// option to know when the connection has moved from draining to closed. +func (c *EncodedConn) Drain() error { + return c.Conn.Drain() +} + // LastError reports the last error encountered via the Connection. func (c *EncodedConn) LastError() error { return c.Conn.err diff --git a/vendor/github.com/nats-io/go-nats/examples/nats-bench.go b/vendor/github.com/nats-io/go-nats/examples/nats-bench.go new file mode 100644 index 00000000..d22e1f66 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/examples/nats-bench.go @@ -0,0 +1,165 @@ +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "strings" + "sync" + "time" + + "github.com/nats-io/go-nats" + "github.com/nats-io/go-nats/bench" +) + +// Some sane defaults +const ( + DefaultNumMsgs = 100000 + DefaultNumPubs = 1 + DefaultNumSubs = 0 + DefaultMessageSize = 128 +) + +func usage() { + log.Fatalf("Usage: nats-bench [-s server (%s)] [--tls] [-np NUM_PUBLISHERS] [-ns NUM_SUBSCRIBERS] [-n NUM_MSGS] [-ms MESSAGE_SIZE] [-csv csvfile] \n", nats.DefaultURL) +} + +var benchmark *bench.Benchmark + +func main() { + var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)") + var tls = flag.Bool("tls", false, "Use TLS Secure Connection") + var numPubs = flag.Int("np", DefaultNumPubs, "Number of Concurrent Publishers") + var numSubs = flag.Int("ns", DefaultNumSubs, "Number of Concurrent Subscribers") + var numMsgs = flag.Int("n", DefaultNumMsgs, "Number of Messages to Publish") + var msgSize = flag.Int("ms", DefaultMessageSize, "Size of the message.") + var csvFile = flag.String("csv", "", "Save bench data to csv file") + + log.SetFlags(0) + flag.Usage = usage + flag.Parse() + + args := flag.Args() + if len(args) != 1 { + usage() + } + + if *numMsgs <= 0 { + log.Fatal("Number of messages should be greater than zero.") + } + + // Setup the option block + opts := nats.GetDefaultOptions() + opts.Servers = strings.Split(*urls, ",") + for i, s := range opts.Servers { + opts.Servers[i] = strings.Trim(s, " ") + } + opts.Secure = *tls + + benchmark = bench.NewBenchmark("NATS", *numSubs, *numPubs) + + var startwg sync.WaitGroup + var donewg sync.WaitGroup + + donewg.Add(*numPubs + *numSubs) + + // Run Subscribers first + startwg.Add(*numSubs) + for i := 0; i < *numSubs; i++ { + go runSubscriber(&startwg, &donewg, opts, *numMsgs, *msgSize) + } + startwg.Wait() + + // Now Publishers + startwg.Add(*numPubs) + pubCounts := bench.MsgsPerClient(*numMsgs, *numPubs) + for i := 0; i < *numPubs; i++ { + go runPublisher(&startwg, &donewg, opts, pubCounts[i], *msgSize) + } + + log.Printf("Starting benchmark [msgs=%d, msgsize=%d, pubs=%d, subs=%d]\n", *numMsgs, *msgSize, *numPubs, *numSubs) + + startwg.Wait() + donewg.Wait() + + benchmark.Close() + + fmt.Print(benchmark.Report()) + + if len(*csvFile) > 0 { + csv := benchmark.CSV() + ioutil.WriteFile(*csvFile, []byte(csv), 0644) + fmt.Printf("Saved metric data in csv file %s\n", *csvFile) + } +} + +func runPublisher(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) { + nc, err := opts.Connect() + if err != nil { + log.Fatalf("Can't connect: %v\n", err) + } + defer nc.Close() + startwg.Done() + + args := flag.Args() + subj := args[0] + var msg []byte + if msgSize > 0 { + msg = make([]byte, msgSize) + } + + start := time.Now() + + for i := 0; i < numMsgs; i++ { + nc.Publish(subj, msg) + } + nc.Flush() + benchmark.AddPubSample(bench.NewSample(numMsgs, msgSize, start, time.Now(), nc)) + + donewg.Done() +} + +func runSubscriber(startwg, donewg *sync.WaitGroup, opts nats.Options, numMsgs int, msgSize int) { + nc, err := opts.Connect() + if err != nil { + log.Fatalf("Can't connect: %v\n", err) + } + + args := flag.Args() + subj := args[0] + + received := 0 + ch := make(chan time.Time, 2) + sub, _ := nc.Subscribe(subj, func(msg *nats.Msg) { + received++ + if received == 1 { + ch <- time.Now() + } + if received >= numMsgs { + ch <- time.Now() + } + }) + sub.SetPendingLimits(-1, -1) + nc.Flush() + startwg.Done() + + start := <-ch + end := <-ch + benchmark.AddSubSample(bench.NewSample(numMsgs, msgSize, start, end, nc)) + nc.Close() + donewg.Done() +} diff --git a/vendor/github.com/nats-io/go-nats/examples/nats-pub.go b/vendor/github.com/nats-io/go-nats/examples/nats-pub.go new file mode 100644 index 00000000..c129f504 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/examples/nats-pub.go @@ -0,0 +1,58 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build ignore + +package main + +import ( + "flag" + "log" + + "github.com/nats-io/go-nats" +) + +// NOTE: Use tls scheme for TLS, e.g. nats-pub -s tls://demo.nats.io:4443 foo hello +func usage() { + log.Fatalf("Usage: nats-pub [-s server (%s)] \n", nats.DefaultURL) +} + +func main() { + var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)") + + log.SetFlags(0) + flag.Usage = usage + flag.Parse() + + args := flag.Args() + if len(args) < 2 { + usage() + } + + nc, err := nats.Connect(*urls) + if err != nil { + log.Fatal(err) + } + defer nc.Close() + + subj, msg := args[0], []byte(args[1]) + + nc.Publish(subj, msg) + nc.Flush() + + if err := nc.LastError(); err != nil { + log.Fatal(err) + } else { + log.Printf("Published [%s] : '%s'\n", subj, msg) + } +} diff --git a/vendor/github.com/nats-io/go-nats/examples/nats-qsub.go b/vendor/github.com/nats-io/go-nats/examples/nats-qsub.go new file mode 100644 index 00000000..05e72671 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/examples/nats-qsub.go @@ -0,0 +1,72 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build ignore + +package main + +import ( + "flag" + "log" + "os" + "runtime" + + "github.com/nats-io/go-nats" +) + +// NOTE: Use tls scheme for TLS, e.g. nats-qsub -s tls://demo.nats.io:4443 foo +func usage() { + log.Fatalf("Usage: nats-qsub [-s server] [-t] \n") +} + +func printMsg(m *nats.Msg, i int) { + log.Printf("[#%d] Received on [%s] Queue[%s] Pid[%d]: '%s'\n", i, m.Subject, m.Sub.Queue, os.Getpid(), string(m.Data)) +} + +func main() { + var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)") + var showTime = flag.Bool("t", false, "Display timestamps") + + log.SetFlags(0) + flag.Usage = usage + flag.Parse() + + args := flag.Args() + if len(args) < 2 { + usage() + } + + nc, err := nats.Connect(*urls) + if err != nil { + log.Fatalf("Can't connect: %v\n", err) + } + + subj, queue, i := args[0], args[1], 0 + + nc.QueueSubscribe(subj, queue, func(msg *nats.Msg) { + i++ + printMsg(msg, i) + }) + nc.Flush() + + if err := nc.LastError(); err != nil { + log.Fatal(err) + } + + log.Printf("Listening on [%s]\n", subj) + if *showTime { + log.SetFlags(log.LstdFlags) + } + + runtime.Goexit() +} diff --git a/vendor/github.com/nats-io/go-nats/examples/nats-req.go b/vendor/github.com/nats-io/go-nats/examples/nats-req.go new file mode 100644 index 00000000..680d77bb --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/examples/nats-req.go @@ -0,0 +1,60 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build ignore + +package main + +import ( + "flag" + "log" + "time" + + "github.com/nats-io/go-nats" +) + +// NOTE: Use tls scheme for TLS, e.g. nats-req -s tls://demo.nats.io:4443 foo hello +func usage() { + log.Fatalf("Usage: nats-req [-s server (%s)] \n", nats.DefaultURL) +} + +func main() { + var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)") + + log.SetFlags(0) + flag.Usage = usage + flag.Parse() + + args := flag.Args() + if len(args) < 2 { + usage() + } + + nc, err := nats.Connect(*urls) + if err != nil { + log.Fatalf("Can't connect: %v\n", err) + } + defer nc.Close() + subj, payload := args[0], []byte(args[1]) + + msg, err := nc.Request(subj, []byte(payload), 100*time.Millisecond) + if err != nil { + if nc.LastError() != nil { + log.Fatalf("Error in Request: %v\n", nc.LastError()) + } + log.Fatalf("Error in Request: %v\n", err) + } + + log.Printf("Published [%s] : '%s'\n", subj, payload) + log.Printf("Received [%v] : '%s'\n", msg.Subject, string(msg.Data)) +} diff --git a/vendor/github.com/nats-io/go-nats/examples/nats-rply.go b/vendor/github.com/nats-io/go-nats/examples/nats-rply.go new file mode 100644 index 00000000..8ea09e23 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/examples/nats-rply.go @@ -0,0 +1,72 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build ignore + +package main + +import ( + "flag" + "log" + "runtime" + + "github.com/nats-io/go-nats" +) + +// NOTE: Use tls scheme for TLS, e.g. nats-rply -s tls://demo.nats.io:4443 foo hello +func usage() { + log.Fatalf("Usage: nats-rply [-s server][-t] \n") +} + +func printMsg(m *nats.Msg, i int) { + log.Printf("[#%d] Received on [%s]: '%s'\n", i, m.Subject, string(m.Data)) +} + +func main() { + var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)") + var showTime = flag.Bool("t", false, "Display timestamps") + + log.SetFlags(0) + flag.Usage = usage + flag.Parse() + + args := flag.Args() + if len(args) < 2 { + usage() + } + + nc, err := nats.Connect(*urls) + if err != nil { + log.Fatalf("Can't connect: %v\n", err) + } + + subj, reply, i := args[0], args[1], 0 + + nc.Subscribe(subj, func(msg *nats.Msg) { + i++ + printMsg(msg, i) + nc.Publish(msg.Reply, []byte(reply)) + }) + nc.Flush() + + if err := nc.LastError(); err != nil { + log.Fatal(err) + } + + log.Printf("Listening on [%s]\n", subj) + if *showTime { + log.SetFlags(log.LstdFlags) + } + + runtime.Goexit() +} diff --git a/vendor/github.com/nats-io/go-nats/examples/nats-sub.go b/vendor/github.com/nats-io/go-nats/examples/nats-sub.go new file mode 100644 index 00000000..68c19837 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/examples/nats-sub.go @@ -0,0 +1,71 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build ignore + +package main + +import ( + "flag" + "log" + "runtime" + + "github.com/nats-io/go-nats" +) + +// NOTE: Use tls scheme for TLS, e.g. nats-sub -s tls://demo.nats.io:4443 foo +func usage() { + log.Fatalf("Usage: nats-sub [-s server] [-t] \n") +} + +func printMsg(m *nats.Msg, i int) { + log.Printf("[#%d] Received on [%s]: '%s'\n", i, m.Subject, string(m.Data)) +} + +func main() { + var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)") + var showTime = flag.Bool("t", false, "Display timestamps") + + log.SetFlags(0) + flag.Usage = usage + flag.Parse() + + args := flag.Args() + if len(args) < 1 { + usage() + } + + nc, err := nats.Connect(*urls) + if err != nil { + log.Fatalf("Can't connect: %v\n", err) + } + + subj, i := args[0], 0 + + nc.Subscribe(subj, func(msg *nats.Msg) { + i += 1 + printMsg(msg, i) + }) + nc.Flush() + + if err := nc.LastError(); err != nil { + log.Fatal(err) + } + + log.Printf("Listening on [%s]\n", subj) + if *showTime { + log.SetFlags(log.LstdFlags) + } + + runtime.Goexit() +} diff --git a/vendor/github.com/nats-io/go-nats/nats.go b/vendor/github.com/nats-io/go-nats/nats.go index b2869a27..0b33a7aa 100644 --- a/vendor/github.com/nats-io/go-nats/nats.go +++ b/vendor/github.com/nats-io/go-nats/nats.go @@ -51,6 +51,7 @@ const ( DefaultMaxChanLen = 8192 // 8k DefaultReconnectBufSize = 8 * 1024 * 1024 // 8MB RequestChanLen = 8 + DefaultDrainTimeout = 30 * time.Second LangString = "go" ) @@ -65,31 +66,34 @@ const AUTHORIZATION_ERR = "authorization violation" // Errors var ( - ErrConnectionClosed = errors.New("nats: connection closed") - ErrSecureConnRequired = errors.New("nats: secure connection required") - ErrSecureConnWanted = errors.New("nats: secure connection not available") - ErrBadSubscription = errors.New("nats: invalid subscription") - ErrTypeSubscription = errors.New("nats: invalid subscription type") - ErrBadSubject = errors.New("nats: invalid subject") - ErrSlowConsumer = errors.New("nats: slow consumer, messages dropped") - ErrTimeout = errors.New("nats: timeout") - ErrBadTimeout = errors.New("nats: timeout invalid") - ErrAuthorization = errors.New("nats: authorization violation") - ErrNoServers = errors.New("nats: no servers available for connection") - ErrJsonParse = errors.New("nats: connect message, json parse error") - ErrChanArg = errors.New("nats: argument needs to be a channel type") - ErrMaxPayload = errors.New("nats: maximum payload exceeded") - ErrMaxMessages = errors.New("nats: maximum messages delivered") - ErrSyncSubRequired = errors.New("nats: illegal call on an async subscription") - ErrMultipleTLSConfigs = errors.New("nats: multiple tls.Configs not allowed") - ErrNoInfoReceived = errors.New("nats: protocol exception, INFO not received") - ErrReconnectBufExceeded = errors.New("nats: outbound buffer limit exceeded") - ErrInvalidConnection = errors.New("nats: invalid connection") - ErrInvalidMsg = errors.New("nats: invalid message or message nil") - ErrInvalidArg = errors.New("nats: invalid argument") - ErrInvalidContext = errors.New("nats: invalid context") - ErrNoEchoNotSupported = errors.New("nats: no echo option not supported by this server") - ErrStaleConnection = errors.New("nats: " + STALE_CONNECTION) + ErrConnectionClosed = errors.New("nats: connection closed") + ErrConnectionDraining = errors.New("nats: connection draining") + ErrDrainTimeout = errors.New("nats: draining connection timed out") + ErrConnectionReconnecting = errors.New("nats: connection reconnecting") + ErrSecureConnRequired = errors.New("nats: secure connection required") + ErrSecureConnWanted = errors.New("nats: secure connection not available") + ErrBadSubscription = errors.New("nats: invalid subscription") + ErrTypeSubscription = errors.New("nats: invalid subscription type") + ErrBadSubject = errors.New("nats: invalid subject") + ErrSlowConsumer = errors.New("nats: slow consumer, messages dropped") + ErrTimeout = errors.New("nats: timeout") + ErrBadTimeout = errors.New("nats: timeout invalid") + ErrAuthorization = errors.New("nats: authorization violation") + ErrNoServers = errors.New("nats: no servers available for connection") + ErrJsonParse = errors.New("nats: connect message, json parse error") + ErrChanArg = errors.New("nats: argument needs to be a channel type") + ErrMaxPayload = errors.New("nats: maximum payload exceeded") + ErrMaxMessages = errors.New("nats: maximum messages delivered") + ErrSyncSubRequired = errors.New("nats: illegal call on an async subscription") + ErrMultipleTLSConfigs = errors.New("nats: multiple tls.Configs not allowed") + ErrNoInfoReceived = errors.New("nats: protocol exception, INFO not received") + ErrReconnectBufExceeded = errors.New("nats: outbound buffer limit exceeded") + ErrInvalidConnection = errors.New("nats: invalid connection") + ErrInvalidMsg = errors.New("nats: invalid message or message nil") + ErrInvalidArg = errors.New("nats: invalid argument") + ErrInvalidContext = errors.New("nats: invalid context") + ErrNoEchoNotSupported = errors.New("nats: no echo option not supported by this server") + ErrStaleConnection = errors.New("nats: " + STALE_CONNECTION) ) // GetDefaultOptions returns default configuration options for the client. @@ -103,6 +107,7 @@ func GetDefaultOptions() Options { MaxPingsOut: DefaultMaxPingOut, SubChanLen: DefaultMaxChanLen, ReconnectBufSize: DefaultReconnectBufSize, + DrainTimeout: DefaultDrainTimeout, } } @@ -120,6 +125,8 @@ const ( CLOSED RECONNECTING CONNECTING + DRAINING_SUBS + DRAINING_PUBS ) // ConnHandler is used for asynchronous events such as @@ -209,6 +216,9 @@ type Options struct { // Timeout sets the timeout for a Dial operation on a connection. Timeout time.Duration + // DrainTimeout sets the timeout for a Drain Operation to complete. + DrainTimeout time.Duration + // FlusherTimeout is the maximum time to wait for the flusher loop // to be able to finish writing to the underlying connection. FlusherTimeout time.Duration @@ -287,6 +297,9 @@ const ( // NUID size nuidSize = 22 + + // Default port used if none is specified in given URL(s) + defaultPortString = "4222" ) // A Conn represents a bare connection to a nats-server. @@ -452,8 +465,10 @@ func Connect(url string, options ...Option) (*Conn, error) { opts := GetDefaultOptions() opts.Servers = processUrlString(url) for _, opt := range options { - if err := opt(&opts); err != nil { - return nil, err + if opt != nil { + if err := opt(&opts); err != nil { + return nil, err + } } } return opts.Connect() @@ -596,6 +611,14 @@ func Timeout(t time.Duration) Option { } } +// DrainTimeout is an Option to set the timeout for draining a connection. +func DrainTimeout(t time.Duration) Option { + return func(o *Options) error { + o.DrainTimeout = t + return nil + } +} + // DisconnectHandler is an Option to set the disconnected handler. func DisconnectHandler(cb ConnHandler) Option { return func(o *Options) error { @@ -924,9 +947,27 @@ func (nc *Conn) setupServerPool() error { // addURLToPool adds an entry to the server pool func (nc *Conn) addURLToPool(sURL string, implicit bool) error { - u, err := url.Parse(sURL) - if err != nil { - return err + if !strings.Contains(sURL, "://") { + sURL = "nats://" + sURL + } + var ( + u *url.URL + err error + ) + for i := 0; i < 2; i++ { + u, err = url.Parse(sURL) + if err != nil { + return err + } + if u.Port() != "" { + break + } + // In case given URL is of the form "localhost:", just add + // the port number at the end, otherwise, add ":4222". + if sURL[len(sURL)-1] != ':' { + sURL += ":" + } + sURL += defaultPortString } s := &srv{url: u, isImplicit: implicit} nc.srvPool = append(nc.srvPool, s) @@ -1161,7 +1202,8 @@ func (nc *Conn) checkForSecure() error { if o.Secure && !nc.info.TLSRequired { return ErrSecureConnWanted } else if nc.info.TLSRequired && !o.Secure { - return ErrSecureConnRequired + // Switch to Secure since server needs TLS. + o.Secure = true } // Need to rewrap with bufio @@ -1686,8 +1728,19 @@ func (nc *Conn) waitForMsgs(s *Subscription) { var closed bool var delivered, max uint64 + // Used to account for adjustments to sub.pBytes when we wrap back around. + msgLen := -1 + for { s.mu.Lock() + // Do accounting for last msg delivered here so we only lock once + // and drain state trips after callback has returned. + if msgLen >= 0 { + s.pMsgs-- + s.pBytes -= msgLen + msgLen = -1 + } + if s.pHead == nil && !s.closed { s.pCond.Wait() } @@ -1705,8 +1758,7 @@ func (nc *Conn) waitForMsgs(s *Subscription) { } continue } - s.pMsgs-- - s.pBytes -= len(m.Data) + msgLen = len(m.Data) } mcb := s.mcb max = s.max @@ -2117,6 +2169,16 @@ func (nc *Conn) publish(subj, reply string, data []byte) error { } nc.mu.Lock() + if nc.isClosed() { + nc.mu.Unlock() + return ErrConnectionClosed + } + + if nc.isDrainingPubs() { + nc.mu.Unlock() + return ErrConnectionDraining + } + // Proactively reject payloads over the threshold set by server. msgSize := int64(len(data)) if msgSize > nc.info.MaxPayload { @@ -2124,11 +2186,6 @@ func (nc *Conn) publish(subj, reply string, data []byte) error { return ErrMaxPayload } - if nc.isClosed() { - nc.mu.Unlock() - return ErrConnectionClosed - } - // Check if we are reconnecting, and if so check if // we have exceeded our reconnect outbound buffer limits. if nc.isReconnecting() { @@ -2358,26 +2415,30 @@ func respToken(respInbox string) string { // Subscribe will express interest in the given subject. The subject // can have wildcards (partial:*, full:>). Messages will be delivered -// to the associated MsgHandler. If no MsgHandler is given, the -// subscription is a synchronous subscription and can be polled via -// Subscription.NextMsg(). +// to the associated MsgHandler. func (nc *Conn) Subscribe(subj string, cb MsgHandler) (*Subscription, error) { return nc.subscribe(subj, _EMPTY_, cb, nil) } -// ChanSubscribe will place all messages received on the channel. +// ChanSubscribe will express interest in the given subject and place +// all messages received on the channel. // You should not close the channel until sub.Unsubscribe() has been called. func (nc *Conn) ChanSubscribe(subj string, ch chan *Msg) (*Subscription, error) { return nc.subscribe(subj, _EMPTY_, nil, ch) } -// ChanQueueSubscribe will place all messages received on the channel. +// ChanQueueSubscribe will express interest in the given subject. +// All subscribers with the same queue name will form the queue group +// and only one member of the group will be selected to receive any given message, +// which will be placed on the channel. // You should not close the channel until sub.Unsubscribe() has been called. +// Note: This is the same than QueueSubscribeSyncWithChan. func (nc *Conn) ChanQueueSubscribe(subj, group string, ch chan *Msg) (*Subscription, error) { return nc.subscribe(subj, group, nil, ch) } -// SubscribeSync is syntactic sugar for Subscribe(subject, nil). +// SubscribeSync will express interest on the given subject. Messages will +// be received synchronously using Subscription.NextMsg(). func (nc *Conn) SubscribeSync(subj string) (*Subscription, error) { if nc == nil { return nil, ErrInvalidConnection @@ -2401,7 +2462,7 @@ func (nc *Conn) QueueSubscribe(subj, queue string, cb MsgHandler) (*Subscription // QueueSubscribeSync creates a synchronous queue subscriber on the given // subject. All subscribers with the same queue name will form the queue // group and only one member of the group will be selected to receive any -// given message synchronously. +// given message synchronously using Subscription.NextMsg(). func (nc *Conn) QueueSubscribeSync(subj, queue string) (*Subscription, error) { mch := make(chan *Msg, nc.Opts.SubChanLen) s, e := nc.subscribe(subj, queue, nil, mch) @@ -2411,7 +2472,12 @@ func (nc *Conn) QueueSubscribeSync(subj, queue string) (*Subscription, error) { return s, e } -// QueueSubscribeSyncWithChan is syntactic sugar for ChanQueueSubscribe(subject, group, ch). +// QueueSubscribeSyncWithChan will express interest in the given subject. +// All subscribers with the same queue name will form the queue group +// and only one member of the group will be selected to receive any given message, +// which will be placed on the channel. +// You should not close the channel until sub.Unsubscribe() has been called. +// Note: This is the same than ChanQueueSubscribe. func (nc *Conn) QueueSubscribeSyncWithChan(subj, queue string, ch chan *Msg) (*Subscription, error) { return nc.subscribe(subj, queue, nil, ch) } @@ -2430,6 +2496,9 @@ func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg) (*Sub if nc.isClosed() { return nil, ErrConnectionClosed } + if nc.isDraining() { + return nil, ErrConnectionDraining + } if cb == nil && ch == nil { return nil, ErrBadSubscription @@ -2465,6 +2534,13 @@ func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg) (*Sub return sub, nil } +// NumSubscriptions returns active number of subscriptions. +func (nc *Conn) NumSubscriptions() int { + nc.mu.Lock() + defer nc.mu.Unlock() + return len(nc.subs) +} + // Lock for nc should be held here upon entry func (nc *Conn) removeSub(s *Subscription) { nc.subsMu.Lock() @@ -2519,6 +2595,21 @@ func (s *Subscription) IsValid() bool { return s.conn != nil } +// Drain will remove interest but continue callbacks until all messages +// have been processed. +func (s *Subscription) Drain() error { + if s == nil { + return ErrBadSubscription + } + s.mu.Lock() + conn := s.conn + s.mu.Unlock() + if conn == nil { + return ErrBadSubscription + } + return conn.unsubscribe(s, 0, true) +} + // Unsubscribe will remove interest in the given subject. func (s *Subscription) Unsubscribe() error { if s == nil { @@ -2530,13 +2621,53 @@ func (s *Subscription) Unsubscribe() error { if conn == nil { return ErrBadSubscription } - return conn.unsubscribe(s, 0) + if conn.IsDraining() { + return ErrConnectionDraining + } + return conn.unsubscribe(s, 0, false) +} + +// checkDrained will watch for a subscription to be fully drained +// and then remove it. +func (nc *Conn) checkDrained(sub *Subscription) { + if nc == nil || sub == nil { + return + } + + // This allows us to know that whatever we have in the client pending + // is correct and the server will not send additional information. + nc.Flush() + + // Once we are here we just wait for Pending to reach 0 or + // any other state to exit this go routine. + for { + // check connection is still valid. + if nc.IsClosed() { + return + } + + // Check subscription state + sub.mu.Lock() + conn := sub.conn + closed := sub.closed + pMsgs := sub.pMsgs + sub.mu.Unlock() + + if conn == nil || closed || pMsgs == 0 { + nc.mu.Lock() + nc.removeSub(sub) + nc.mu.Unlock() + return + } + + time.Sleep(100 * time.Millisecond) + } } // AutoUnsubscribe will issue an automatic Unsubscribe that is // processed by the server when max messages have been received. // This can be useful when sending a request to an unknown number -// of subscribers. Request() uses this functionality. +// of subscribers. func (s *Subscription) AutoUnsubscribe(max int) error { if s == nil { return ErrBadSubscription @@ -2547,12 +2678,12 @@ func (s *Subscription) AutoUnsubscribe(max int) error { if conn == nil { return ErrBadSubscription } - return conn.unsubscribe(s, max) + return conn.unsubscribe(s, max, false) } // unsubscribe performs the low level unsubscribe to the server. // Use Subscription.Unsubscribe() -func (nc *Conn) unsubscribe(sub *Subscription, max int) error { +func (nc *Conn) unsubscribe(sub *Subscription, max int, drainMode bool) error { nc.mu.Lock() // ok here, but defer is expensive defer nc.mu.Unlock() @@ -2574,9 +2705,14 @@ func (nc *Conn) unsubscribe(sub *Subscription, max int) error { if max > 0 { s.max = uint64(max) maxStr = strconv.Itoa(max) - } else { + } else if !drainMode { nc.removeSub(s) } + + if drainMode { + go nc.checkDrained(sub) + } + // We will send these for all subs when we reconnect // so that we can suppress here. if !nc.isReconnecting() { @@ -3089,6 +3225,98 @@ func (nc *Conn) IsConnected() bool { return nc.isConnected() } +// drainConnection will run in a separate Go routine and will +// flush all publishes and drain all active subscriptions. +func (nc *Conn) drainConnection() { + // Snapshot subs list. + nc.mu.Lock() + subs := make([]*Subscription, 0, len(nc.subs)) + for _, s := range nc.subs { + subs = append(subs, s) + } + errCB := nc.Opts.AsyncErrorCB + drainWait := nc.Opts.DrainTimeout + nc.mu.Unlock() + + // for pushing errors with context. + pushErr := func(err error) { + nc.mu.Lock() + if errCB != nil { + nc.ach.push(func() { errCB(nc, nil, err) }) + } + nc.mu.Unlock() + } + + // Do subs first + for _, s := range subs { + if err := s.Drain(); err != nil { + // We will notify about these but continue. + pushErr(err) + } + } + + // Wait for the subscriptions to drop to zero. + timeout := time.Now().Add(drainWait) + for time.Now().Before(timeout) { + if nc.NumSubscriptions() == 0 { + break + } + time.Sleep(10 * time.Millisecond) + } + + // Check if we timed out. + if nc.NumSubscriptions() != 0 { + pushErr(ErrDrainTimeout) + } + + // Flip State + nc.mu.Lock() + nc.status = DRAINING_PUBS + nc.mu.Unlock() + + // Do publish drain via Flush() call. + err := nc.Flush() + if err != nil { + pushErr(err) + nc.Close() + return + } + + // Move to closed state. + nc.Close() +} + +// Drain will put a connection into a drain state. All subscriptions will +// immediately be put into a drain state. Upon completion, the publishers +// will be drained and can not publish any additional messages. Upon draining +// of the publishers, the connection will be closed. Use the ClosedCB() +// option to know when the connection has moved from draining to closed. +func (nc *Conn) Drain() error { + nc.mu.Lock() + defer nc.mu.Unlock() + + if nc.isClosed() { + return ErrConnectionClosed + } + if nc.isConnecting() || nc.isReconnecting() { + return ErrConnectionReconnecting + } + if nc.isDraining() { + return nil + } + + nc.status = DRAINING_SUBS + go nc.drainConnection() + return nil +} + +// IsDraining tests if a Conn is in the draining state. +func (nc *Conn) IsDraining() bool { + nc.mu.Lock() + defer nc.mu.Unlock() + return nc.isDraining() +} + // caller must lock func (nc *Conn) getServers(implicitOnly bool) []string { poolSize := len(nc.srvPool) @@ -3146,7 +3374,17 @@ func (nc *Conn) isReconnecting() bool { // Test if Conn is connected or connecting. func (nc *Conn) isConnected() bool { - return nc.status == CONNECTED + return nc.status == CONNECTED || nc.isDraining() +} + +// Test if Conn is in the draining state. +func (nc *Conn) isDraining() bool { + return nc.status == DRAINING_SUBS || nc.status == DRAINING_PUBS +} + +// Test if Conn is in the draining state for pubs. +func (nc *Conn) isDrainingPubs() bool { + return nc.status == DRAINING_PUBS } // Stats will return a race safe copy of the Statistics section for the connection. diff --git a/vendor/github.com/nats-io/go-nats/nats_test.go b/vendor/github.com/nats-io/go-nats/nats_test.go index 680d72f0..e7bd146e 100644 --- a/vendor/github.com/nats-io/go-nats/nats_test.go +++ b/vendor/github.com/nats-io/go-nats/nats_test.go @@ -223,6 +223,50 @@ var testServers = []string{ "nats://localhost:1228", } +func TestSimplifiedURLs(t *testing.T) { + opts := GetDefaultOptions() + opts.NoRandomize = true + opts.Servers = []string{ + "nats://host1:1234", + "nats://host2:", + "nats://host3", + "host4:1234", + "host5:", + "host6", + "nats://[1:2:3:4]:1234", + "nats://[5:6:7:8]:", + "nats://[9:10:11:12]", + "[13:14:15:16]:", + "[17:18:19:20]:1234", + } + + // We expect the result in the server pool to be: + expected := []string{ + "nats://host1:1234", + "nats://host2:4222", + "nats://host3:4222", + "nats://host4:1234", + "nats://host5:4222", + "nats://host6:4222", + "nats://[1:2:3:4]:1234", + "nats://[5:6:7:8]:4222", + "nats://[9:10:11:12]:4222", + "nats://[13:14:15:16]:4222", + "nats://[17:18:19:20]:1234", + } + + nc := &Conn{Opts: opts} + if err := nc.setupServerPool(); err != nil { + t.Fatalf("Problem setting up Server Pool: %v\n", err) + } + // Check server pool directly + for i, u := range nc.srvPool { + if u.url.String() != expected[i] { + t.Fatalf("Expected url %q, got %q", expected[i], u.url.String()) + } + } +} + func TestServersRandomize(t *testing.T) { opts := GetDefaultOptions() opts.Servers = testServers @@ -613,7 +657,6 @@ func TestParserShouldFail(t *testing.T) { } func TestParserSplitMsg(t *testing.T) { - nc := &Conn{} nc.ps = &parseState{} diff --git a/vendor/github.com/nats-io/go-nats/scripts/cov.sh b/vendor/github.com/nats-io/go-nats/scripts/cov.sh new file mode 100755 index 00000000..8760dfab --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/scripts/cov.sh @@ -0,0 +1,19 @@ +#!/bin/bash -e +# Run from directory above via ./scripts/cov.sh + +rm -rf ./cov +mkdir cov +go test -v -race -covermode=atomic -coverprofile=./cov/nats.out +go test -v -race -covermode=atomic -coverprofile=./cov/test.out -coverpkg=github.com/nats-io/go-nats ./test +go test -v -race -covermode=atomic -coverprofile=./cov/builtin.out -coverpkg=github.com/nats-io/go-nats/encoders/builtin ./test -run EncBuiltin +go test -v -race -covermode=atomic -coverprofile=./cov/protobuf.out -coverpkg=github.com/nats-io/go-nats/encoders/protobuf ./test -run EncProto +gocovmerge ./cov/*.out > acc.out +rm -rf ./cov + +# If we have an arg, assume travis run and push to coveralls. Otherwise launch browser results +if [[ -n $1 ]]; then + $HOME/gopath/bin/goveralls -coverprofile=acc.out -service travis-ci + rm -rf ./acc.out +else + go tool cover -html=acc.out +fi diff --git a/vendor/github.com/nats-io/go-nats/test/auth_test.go b/vendor/github.com/nats-io/go-nats/test/auth_test.go new file mode 100644 index 00000000..aa606d76 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/auth_test.go @@ -0,0 +1,236 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "fmt" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/nats-io/gnatsd/server" + "github.com/nats-io/gnatsd/test" + "github.com/nats-io/go-nats" +) + +func TestAuth(t *testing.T) { + opts := test.DefaultTestOptions + opts.Port = 8232 + opts.Username = "derek" + opts.Password = "foo" + s := RunServerWithOptions(opts) + defer s.Shutdown() + + _, err := nats.Connect("nats://localhost:8232") + if err == nil { + t.Fatal("Should have received an error while trying to connect") + } + + // This test may be a bit too strict for the future, but for now makes + // sure that we correctly process the -ERR content on connect. + if err.Error() != nats.ErrAuthorization.Error() { + t.Fatalf("Expected error '%v', got '%v'", nats.ErrAuthorization, err) + } + + nc, err := nats.Connect("nats://derek:foo@localhost:8232") + if err != nil { + t.Fatal("Should have connected successfully with a token") + } + nc.Close() + + // Use Options + nc, err = nats.Connect("nats://localhost:8232", nats.UserInfo("derek", "foo")) + if err != nil { + t.Fatalf("Should have connected successfully with a token: %v", err) + } + nc.Close() + // Verify that credentials in URL take precedence. + nc, err = nats.Connect("nats://derek:foo@localhost:8232", nats.UserInfo("foo", "bar")) + if err != nil { + t.Fatalf("Should have connected successfully with a token: %v", err) + } + nc.Close() +} + +func TestAuthFailNoDisconnectCB(t *testing.T) { + opts := test.DefaultTestOptions + opts.Port = 8232 + opts.Username = "derek" + opts.Password = "foo" + s := RunServerWithOptions(opts) + defer s.Shutdown() + + copts := nats.GetDefaultOptions() + copts.Url = "nats://localhost:8232" + receivedDisconnectCB := int32(0) + copts.DisconnectedCB = func(nc *nats.Conn) { + atomic.AddInt32(&receivedDisconnectCB, 1) + } + + _, err := copts.Connect() + if err == nil { + t.Fatal("Should have received an error while trying to connect") + } + if atomic.LoadInt32(&receivedDisconnectCB) > 0 { + t.Fatal("Should not have received a disconnect callback on auth failure") + } +} + +func TestAuthFailAllowReconnect(t *testing.T) { + ts := RunServerOnPort(23232) + defer ts.Shutdown() + + var servers = []string{ + "nats://localhost:23232", + "nats://localhost:23233", + "nats://localhost:23234", + } + + ots2 := test.DefaultTestOptions + ots2.Port = 23233 + ots2.Username = "ivan" + ots2.Password = "foo" + ts2 := RunServerWithOptions(ots2) + defer ts2.Shutdown() + + ts3 := RunServerOnPort(23234) + defer ts3.Shutdown() + + reconnectch := make(chan bool) + + opts := nats.GetDefaultOptions() + opts.Servers = servers + opts.AllowReconnect = true + opts.NoRandomize = true + opts.MaxReconnect = 10 + opts.ReconnectWait = 100 * time.Millisecond + + opts.ReconnectedCB = func(_ *nats.Conn) { + reconnectch <- true + } + + // Connect + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + // Stop the server + ts.Shutdown() + + // The client will try to connect to the second server, and that + // should fail. It should then try to connect to the third and succeed. + + // Wait for the reconnect CB. + if e := Wait(reconnectch); e != nil { + t.Fatal("Reconnect callback should have been triggered") + } + + if nc.IsClosed() { + t.Fatal("Should have reconnected") + } + + if nc.ConnectedUrl() != servers[2] { + t.Fatalf("Should have reconnected to %s, reconnected to %s instead", servers[2], nc.ConnectedUrl()) + } +} + +func TestTokenAuth(t *testing.T) { + opts := test.DefaultTestOptions + opts.Port = 8232 + secret := "S3Cr3T0k3n!" + opts.Authorization = secret + s := RunServerWithOptions(opts) + defer s.Shutdown() + + _, err := nats.Connect("nats://localhost:8232") + if err == nil { + t.Fatal("Should have received an error while trying to connect") + } + + tokenURL := fmt.Sprintf("nats://%s@localhost:8232", secret) + nc, err := nats.Connect(tokenURL) + if err != nil { + t.Fatal("Should have connected successfully") + } + nc.Close() + + // Use Options + nc, err = nats.Connect("nats://localhost:8232", nats.Token(secret)) + if err != nil { + t.Fatalf("Should have connected successfully: %v", err) + } + nc.Close() + // Verify that token in the URL takes precedence. + nc, err = nats.Connect(tokenURL, nats.Token("badtoken")) + if err != nil { + t.Fatalf("Should have connected successfully: %v", err) + } + nc.Close() +} + +func TestPermViolation(t *testing.T) { + opts := test.DefaultTestOptions + opts.Port = 8232 + opts.Users = []*server.User{ + { + Username: "ivan", + Password: "pwd", + Permissions: &server.Permissions{ + Publish: &server.SubjectPermission{Allow: []string{"foo"}}, + Subscribe: &server.SubjectPermission{Allow: []string{"bar"}}, + }, + }, + } + s := RunServerWithOptions(opts) + defer s.Shutdown() + + errCh := make(chan error, 2) + errCB := func(_ *nats.Conn, _ *nats.Subscription, err error) { + errCh <- err + } + nc, err := nats.Connect( + fmt.Sprintf("nats://ivan:pwd@localhost:%d", opts.Port), + nats.ErrorHandler(errCB)) + if err != nil { + t.Fatalf("Error on connect: %v", err) + } + defer nc.Close() + + // Cause a publish error + nc.Publish("bar", []byte("fail")) + // Cause a subscribe error + nc.Subscribe("foo", func(_ *nats.Msg) {}) + + expectedErrorTypes := []string{"publish", "subscription"} + for _, expectedErr := range expectedErrorTypes { + select { + case e := <-errCh: + if !strings.Contains(e.Error(), nats.PERMISSIONS_ERR) { + t.Fatalf("Did not receive error about permissions") + } + if !strings.Contains(e.Error(), expectedErr) { + t.Fatalf("Did not receive error about %q, got %v", expectedErr, e.Error()) + } + case <-time.After(2 * time.Second): + t.Fatalf("Did not get the permission error") + } + } + // Make sure connection has not been closed + if nc.IsClosed() { + t.Fatal("Connection should be not be closed") + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/basic_test.go b/vendor/github.com/nats-io/go-nats/test/basic_test.go new file mode 100644 index 00000000..fe48e755 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/basic_test.go @@ -0,0 +1,908 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "bytes" + "fmt" + "math" + "regexp" + "runtime" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/nats-io/go-nats" +) + +func TestCloseLeakingGoRoutines(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + // Give time for things to settle before capturing the number of + // go routines + time.Sleep(500 * time.Millisecond) + + base := runtime.NumGoroutine() + + nc := NewDefaultConnection(t) + + nc.Flush() + nc.Close() + + // Give time for things to settle before capturing the number of + // go routines + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + delta := (runtime.NumGoroutine() - base) + if delta > 0 { + return fmt.Errorf("%d Go routines still exist post Close()", delta) + } + return nil + }) + + // Make sure we can call Close() multiple times + nc.Close() +} + +func TestLeakingGoRoutinesOnFailedConnect(t *testing.T) { + // Give time for things to settle before capturing the number of + // go routines + time.Sleep(500 * time.Millisecond) + + base := runtime.NumGoroutine() + + nc, err := nats.Connect(nats.DefaultURL) + if err == nil { + nc.Close() + t.Fatalf("Expected failure to connect") + } + + // Give time for things to settle before capturing the number of + // go routines + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + delta := (runtime.NumGoroutine() - base) + if delta > 0 { + return fmt.Errorf("%d Go routines still exist post Close()", delta) + } + return nil + }) +} + +func TestConnectedServer(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + u := nc.ConnectedUrl() + if u == "" || u != nats.DefaultURL { + t.Fatalf("Unexpected connected URL of %s\n", u) + } + srv := nc.ConnectedServerId() + if srv == "" { + t.Fatal("Expected a connected server id") + } + nc.Close() + u = nc.ConnectedUrl() + if u != "" { + t.Fatalf("Expected a nil connected URL, got %s\n", u) + } + srv = nc.ConnectedServerId() + if srv != "" { + t.Fatalf("Expected a nil connect server, got %s\n", srv) + } +} + +func TestMultipleClose(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + nc.Close() + wg.Done() + }() + } + wg.Wait() +} + +func TestBadOptionTimeoutConnect(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + opts := nats.GetDefaultOptions() + opts.Timeout = -1 + opts.Url = "nats://localhost:4222" + + _, err := opts.Connect() + if err == nil { + t.Fatal("Expected an error") + } + if err != nats.ErrNoServers { + t.Fatalf("Expected a ErrNoServers error: Got %v\n", err) + } +} + +func TestSimplePublish(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + if err := nc.Publish("foo", []byte("Hello World")); err != nil { + t.Fatal("Failed to publish string message: ", err) + } +} + +func TestSimplePublishNoData(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + if err := nc.Publish("foo", nil); err != nil { + t.Fatal("Failed to publish empty message: ", err) + } +} + +func TestPublishDoesNotFailOnSlowConsumer(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatalf("Unable to create subscription: %v", err) + } + + if err := sub.SetPendingLimits(1, 1000); err != nil { + t.Fatalf("Unable to set pending limits: %v", err) + } + + var pubErr error + + msg := []byte("Hello") + for i := 0; i < 10; i++ { + pubErr = nc.Publish("foo", msg) + if pubErr != nil { + break + } + nc.Flush() + } + + if pubErr != nil { + t.Fatalf("Publish() should not fail because of slow consumer. Got '%v'", pubErr) + } +} + +func TestAsyncSubscribe(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + omsg := []byte("Hello World") + ch := make(chan bool) + + // Callback is mandatory + if _, err := nc.Subscribe("foo", nil); err == nil { + t.Fatal("Creating subscription without callback should have failed") + } + + _, err := nc.Subscribe("foo", func(m *nats.Msg) { + if !bytes.Equal(m.Data, omsg) { + t.Fatal("Message received does not match") + } + if m.Sub == nil { + t.Fatal("Callback does not have a valid Subscription") + } + ch <- true + }) + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + nc.Publish("foo", omsg) + if e := Wait(ch); e != nil { + t.Fatal("Message not received for subscription") + } +} + +func TestAsyncSubscribeRoutineLeakOnUnsubscribe(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + ch := make(chan bool) + + // Give time for things to settle before capturing the number of + // go routines + time.Sleep(500 * time.Millisecond) + + // Take the base once the connection is established, but before + // the subscriber is created. + base := runtime.NumGoroutine() + + sub, err := nc.Subscribe("foo", func(m *nats.Msg) { ch <- true }) + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + + // Send to ourself + nc.Publish("foo", []byte("hello")) + + // This ensures that the async delivery routine is up and running. + if err := Wait(ch); err != nil { + t.Fatal("Failed to receive message") + } + + // Make sure to give it time to go back into wait + time.Sleep(200 * time.Millisecond) + + // Explicit unsubscribe + sub.Unsubscribe() + + // Give time for things to settle before capturing the number of + // go routines + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + delta := (runtime.NumGoroutine() - base) + if delta > 0 { + return fmt.Errorf("%d Go routines still exist post Unsubscribe()", delta) + } + return nil + }) +} + +func TestAsyncSubscribeRoutineLeakOnClose(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ch := make(chan bool) + + // Give time for things to settle before capturing the number of + // go routines + time.Sleep(500 * time.Millisecond) + + // Take the base before creating the connection, since we are going + // to close it before taking the delta. + base := runtime.NumGoroutine() + + nc := NewDefaultConnection(t) + defer nc.Close() + + _, err := nc.Subscribe("foo", func(m *nats.Msg) { ch <- true }) + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + + // Send to ourself + nc.Publish("foo", []byte("hello")) + + // This ensures that the async delivery routine is up and running. + if err := Wait(ch); err != nil { + t.Fatal("Failed to receive message") + } + + // Make sure to give it time to go back into wait + time.Sleep(200 * time.Millisecond) + + // Close connection without explicit unsubscribe + nc.Close() + + // Give time for things to settle before capturing the number of + // go routines + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + delta := (runtime.NumGoroutine() - base) + if delta > 0 { + return fmt.Errorf("%d Go routines still exist post Close()", delta) + } + return nil + }) +} + +func TestSyncSubscribe(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + omsg := []byte("Hello World") + nc.Publish("foo", omsg) + msg, err := sub.NextMsg(1 * time.Second) + if err != nil || !bytes.Equal(msg.Data, omsg) { + t.Fatal("Message received does not match") + } +} + +func TestPubSubWithReply(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + omsg := []byte("Hello World") + nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: "bar", Data: omsg}) + msg, err := sub.NextMsg(10 * time.Second) + if err != nil || !bytes.Equal(msg.Data, omsg) { + t.Fatal("Message received does not match") + } +} + +func TestFlush(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + omsg := []byte("Hello World") + for i := 0; i < 10000; i++ { + nc.Publish("flush", omsg) + } + if err := nc.FlushTimeout(0); err == nil { + t.Fatal("Calling FlushTimeout() with invalid timeout should fail") + } + if err := nc.Flush(); err != nil { + t.Fatalf("Received error from flush: %s\n", err) + } + if nb, _ := nc.Buffered(); nb > 0 { + t.Fatalf("Outbound buffer not empty: %d bytes\n", nb) + } + + nc.Close() + if _, err := nc.Buffered(); err == nil { + t.Fatal("Calling Buffered() on closed connection should fail") + } +} + +func TestQueueSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + s1, _ := nc.QueueSubscribeSync("foo", "bar") + s2, _ := nc.QueueSubscribeSync("foo", "bar") + omsg := []byte("Hello World") + nc.Publish("foo", omsg) + nc.Flush() + r1, _ := s1.QueuedMsgs() + r2, _ := s2.QueuedMsgs() + if (r1 + r2) != 1 { + t.Fatal("Received too many messages for multiple queue subscribers") + } + // Drain messages + s1.NextMsg(time.Second) + s2.NextMsg(time.Second) + + total := 1000 + for i := 0; i < total; i++ { + nc.Publish("foo", omsg) + } + nc.Flush() + v := uint(float32(total) * 0.15) + r1, _ = s1.QueuedMsgs() + r2, _ = s2.QueuedMsgs() + if r1+r2 != total { + t.Fatalf("Incorrect number of messages: %d vs %d", (r1 + r2), total) + } + expected := total / 2 + d1 := uint(math.Abs(float64(expected - r1))) + d2 := uint(math.Abs(float64(expected - r2))) + if d1 > v || d2 > v { + t.Fatalf("Too much variance in totals: %d, %d > %d", d1, d2, v) + } +} + +func TestReplyArg(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + ch := make(chan bool) + replyExpected := "bar" + + nc.Subscribe("foo", func(m *nats.Msg) { + if m.Reply != replyExpected { + t.Fatalf("Did not receive correct reply arg in callback: "+ + "('%s' vs '%s')", m.Reply, replyExpected) + } + ch <- true + }) + nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: replyExpected, Data: []byte("Hello")}) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive callback") + } +} + +func TestSyncReplyArg(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + replyExpected := "bar" + sub, _ := nc.SubscribeSync("foo") + nc.PublishMsg(&nats.Msg{Subject: "foo", Reply: replyExpected, Data: []byte("Hello")}) + msg, err := sub.NextMsg(1 * time.Second) + if err != nil { + t.Fatal("Received an err on NextMsg()") + } + if msg.Reply != replyExpected { + t.Fatalf("Did not receive correct reply arg in callback: "+ + "('%s' vs '%s')", msg.Reply, replyExpected) + } +} + +func TestUnsubscribe(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + received := int32(0) + max := int32(10) + ch := make(chan bool) + nc.Subscribe("foo", func(m *nats.Msg) { + atomic.AddInt32(&received, 1) + if received == max { + err := m.Sub.Unsubscribe() + if err != nil { + t.Fatal("Unsubscribe failed with err:", err) + } + ch <- true + } + }) + send := 20 + for i := 0; i < send; i++ { + nc.Publish("foo", []byte("hello")) + } + nc.Flush() + <-ch + + r := atomic.LoadInt32(&received) + if r != max { + t.Fatalf("Received wrong # of messages after unsubscribe: %d vs %d", + r, max) + } +} + +func TestDoubleUnsubscribe(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + if err = sub.Unsubscribe(); err != nil { + t.Fatal("Unsubscribe failed with err:", err) + } + if err = sub.Unsubscribe(); err == nil { + t.Fatal("Unsubscribe should have reported an error") + } +} + +func TestRequestTimeout(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + if _, err := nc.Request("foo", []byte("help"), 10*time.Millisecond); err == nil { + t.Fatalf("Expected to receive a timeout error") + } +} + +func TestOldRequest(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle()) + if err != nil { + t.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + + response := []byte("I will help you") + nc.Subscribe("foo", func(m *nats.Msg) { + nc.Publish(m.Reply, response) + }) + msg, err := nc.Request("foo", []byte("help"), 500*time.Millisecond) + if err != nil { + t.Fatalf("Received an error on Request test: %s", err) + } + if !bytes.Equal(msg.Data, response) { + t.Fatalf("Received invalid response") + } +} + +func TestRequest(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + response := []byte("I will help you") + nc.Subscribe("foo", func(m *nats.Msg) { + nc.Publish(m.Reply, response) + }) + msg, err := nc.Request("foo", []byte("help"), 500*time.Millisecond) + if err != nil { + t.Fatalf("Received an error on Request test: %s", err) + } + if !bytes.Equal(msg.Data, response) { + t.Fatalf("Received invalid response") + } +} + +func TestRequestNoBody(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + response := []byte("I will help you") + nc.Subscribe("foo", func(m *nats.Msg) { + nc.Publish(m.Reply, response) + }) + msg, err := nc.Request("foo", nil, 500*time.Millisecond) + if err != nil { + t.Fatalf("Received an error on Request test: %s", err) + } + if !bytes.Equal(msg.Data, response) { + t.Fatalf("Received invalid response") + } +} + +func TestSimultaneousRequests(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + response := []byte("I will help you") + nc.Subscribe("foo", func(m *nats.Msg) { + nc.Publish(m.Reply, response) + }) + + var wg sync.WaitGroup + for i := 0; i < 50; i++ { + wg.Add(1) + go func() { + if _, err := nc.Request("foo", nil, 2*time.Second); err != nil { + t.Fatalf("Expected to receive a timeout error") + } else { + wg.Done() + } + }() + } + wg.Wait() +} + +func TestRequestClose(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + time.Sleep(100 * time.Millisecond) + nc.Close() + }() + if _, err := nc.Request("foo", []byte("help"), 2*time.Second); err != nats.ErrInvalidConnection && err != nats.ErrConnectionClosed { + t.Fatalf("Expected connection error: got %v", err) + } + wg.Wait() +} + +func TestRequestCloseTimeout(t *testing.T) { + // Make sure we return a timeout when we close + // the connection even if response is queued. + + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + response := []byte("I will help you") + nc.Subscribe("foo", func(m *nats.Msg) { + nc.Publish(m.Reply, response) + nc.Close() + }) + if _, err := nc.Request("foo", nil, 1*time.Second); err == nil { + t.Fatalf("Expected to receive a timeout error") + } +} + +func TestFlushInCB(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + ch := make(chan bool) + + nc.Subscribe("foo", func(_ *nats.Msg) { + nc.Flush() + ch <- true + }) + nc.Publish("foo", []byte("Hello")) + if e := Wait(ch); e != nil { + t.Fatal("Flush did not return properly in callback") + } +} + +func TestReleaseFlush(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + + for i := 0; i < 1000; i++ { + nc.Publish("foo", []byte("Hello")) + } + go nc.Close() + nc.Flush() +} + +func TestInbox(t *testing.T) { + inbox := nats.NewInbox() + if matched, _ := regexp.Match(`_INBOX.\S`, []byte(inbox)); !matched { + t.Fatal("Bad INBOX format") + } +} + +func TestStats(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + data := []byte("The quick brown fox jumped over the lazy dog") + iter := 10 + + for i := 0; i < iter; i++ { + nc.Publish("foo", data) + } + + if nc.OutMsgs != uint64(iter) { + t.Fatalf("Not properly tracking OutMsgs: received %d, wanted %d\n", nc.OutMsgs, iter) + } + obb := uint64(iter * len(data)) + if nc.OutBytes != obb { + t.Fatalf("Not properly tracking OutBytes: received %d, wanted %d\n", nc.OutBytes, obb) + } + + // Clear outbound + nc.OutMsgs, nc.OutBytes = 0, 0 + + // Test both sync and async versions of subscribe. + nc.Subscribe("foo", func(_ *nats.Msg) {}) + nc.SubscribeSync("foo") + + for i := 0; i < iter; i++ { + nc.Publish("foo", data) + } + nc.Flush() + + if nc.InMsgs != uint64(2*iter) { + t.Fatalf("Not properly tracking InMsgs: received %d, wanted %d\n", nc.InMsgs, 2*iter) + } + + ibb := 2 * obb + if nc.InBytes != ibb { + t.Fatalf("Not properly tracking InBytes: received %d, wanted %d\n", nc.InBytes, ibb) + } +} + +func TestRaceSafeStats(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + go nc.Publish("foo", []byte("Hello World")) + time.Sleep(200 * time.Millisecond) + + stats := nc.Stats() + + if stats.OutMsgs != uint64(1) { + t.Fatalf("Not properly tracking OutMsgs: received %d, wanted %d\n", nc.OutMsgs, 1) + } +} + +func TestBadSubject(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + err := nc.Publish("", []byte("Hello World")) + if err == nil { + t.Fatalf("Expected an error on bad subject to publish") + } + if err != nats.ErrBadSubject { + t.Fatalf("Expected a ErrBadSubject error: Got %v\n", err) + } +} + +func TestOptions(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL, + nats.Name("myName"), + nats.MaxReconnects(2), + nats.ReconnectWait(50*time.Millisecond), + nats.PingInterval(20*time.Millisecond)) + if err != nil { + t.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + + rch := make(chan bool) + cch := make(chan bool) + + nc.SetReconnectHandler(func(_ *nats.Conn) { rch <- true }) + nc.SetClosedHandler(func(_ *nats.Conn) { cch <- true }) + + s.Shutdown() + + s = RunDefaultServer() + defer s.Shutdown() + + if err := Wait(rch); err != nil { + t.Fatal("Failed getting reconnected cb") + } + + nc.Close() + + if err := Wait(cch); err != nil { + t.Fatal("Failed getting closed cb") + } + + nc, err = nats.Connect(nats.DefaultURL, nats.NoReconnect()) + if err != nil { + t.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + + nc.SetReconnectHandler(func(_ *nats.Conn) { rch <- true }) + nc.SetClosedHandler(func(_ *nats.Conn) { cch <- true }) + + s.Shutdown() + + // We should not get a reconnect cb this time + if err := WaitTime(rch, time.Second); err == nil { + t.Fatal("Unexpected reconnect cb") + } + + nc.Close() + + if err := Wait(cch); err != nil { + t.Fatal("Failed getting closed cb") + } +} + +func TestNilConnection(t *testing.T) { + var nc *nats.Conn + data := []byte("ok") + + // Publish + if err := nc.Publish("foo", data); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + if err := nc.PublishMsg(nil); err == nil || err != nats.ErrInvalidMsg { + t.Fatalf("Expected ErrInvalidMsg error, got %v\n", err) + } + if err := nc.PublishMsg(&nats.Msg{}); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + if err := nc.PublishRequest("foo", "reply", data); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + + // Subscribe + if _, err := nc.Subscribe("foo", nil); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + if _, err := nc.SubscribeSync("foo"); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + if _, err := nc.QueueSubscribe("foo", "bar", nil); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + ch := make(chan *nats.Msg) + if _, err := nc.ChanSubscribe("foo", ch); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + if _, err := nc.ChanQueueSubscribe("foo", "bar", ch); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + if _, err := nc.QueueSubscribeSyncWithChan("foo", "bar", ch); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + + // Flush + if err := nc.Flush(); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + if err := nc.FlushTimeout(time.Millisecond); err == nil || err != nats.ErrInvalidConnection { + t.Fatalf("Expected ErrInvalidConnection error, got %v\n", err) + } + + // Nil Subscribers + var sub *nats.Subscription + if sub.Type() != nats.NilSubscription { + t.Fatalf("Got wrong type for nil subscription, %v\n", sub.Type()) + } + if sub.IsValid() { + t.Fatalf("Expected IsValid() to return false") + } + if err := sub.Unsubscribe(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected Unsubscribe to return proper error, got %v\n", err) + } + if err := sub.AutoUnsubscribe(1); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if _, err := sub.NextMsg(time.Millisecond); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if _, err := sub.QueuedMsgs(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if _, _, err := sub.Pending(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if _, _, err := sub.MaxPending(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if err := sub.ClearMaxPending(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if _, _, err := sub.PendingLimits(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if err := sub.SetPendingLimits(1, 1); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if _, err := sub.Delivered(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } + if _, err := sub.Dropped(); err == nil || err != nats.ErrBadSubscription { + t.Fatalf("Expected ErrBadSubscription error, got %v\n", err) + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/bench_test.go b/vendor/github.com/nats-io/go-nats/test/bench_test.go new file mode 100644 index 00000000..d97a1662 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/bench_test.go @@ -0,0 +1,166 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "sync/atomic" + "testing" + "time" + + "github.com/nats-io/go-nats" +) + +func BenchmarkPublishSpeed(b *testing.B) { + b.StopTimer() + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(b) + defer nc.Close() + b.StartTimer() + + msg := []byte("Hello World") + + for i := 0; i < b.N; i++ { + if err := nc.Publish("foo", msg); err != nil { + b.Fatalf("Error in benchmark during Publish: %v\n", err) + } + } + // Make sure they are all processed. + nc.Flush() + b.StopTimer() +} + +func BenchmarkPubSubSpeed(b *testing.B) { + b.StopTimer() + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(b) + defer nc.Close() + + ch := make(chan bool) + + nc.SetErrorHandler(func(nc *nats.Conn, s *nats.Subscription, err error) { + b.Fatalf("Error : %v\n", err) + }) + + received := int32(0) + + nc.Subscribe("foo", func(m *nats.Msg) { + if nr := atomic.AddInt32(&received, 1); nr >= int32(b.N) { + ch <- true + } + }) + + msg := []byte("Hello World") + + b.StartTimer() + + for i := 0; i < b.N; i++ { + if err := nc.Publish("foo", msg); err != nil { + b.Fatalf("Error in benchmark during Publish: %v\n", err) + } + // Don't overrun ourselves and be a slow consumer, server will cut us off + if int32(i)-atomic.LoadInt32(&received) > 32768 { + time.Sleep(100 * time.Nanosecond) + } + } + + // Make sure they are all processed. + err := WaitTime(ch, 10*time.Second) + if err != nil { + b.Fatal("Timed out waiting for messages") + } else if atomic.LoadInt32(&received) != int32(b.N) { + b.Fatalf("Received: %d, err:%v", received, nc.LastError()) + } + b.StopTimer() +} + +func BenchmarkAsyncSubscriptionCreationSpeed(b *testing.B) { + b.StopTimer() + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(b) + defer nc.Close() + b.StartTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + nc.Subscribe("foo", func(m *nats.Msg) {}) + } +} + +func BenchmarkSyncSubscriptionCreationSpeed(b *testing.B) { + b.StopTimer() + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(b) + defer nc.Close() + b.StartTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + nc.SubscribeSync("foo") + } +} + +func BenchmarkInboxCreation(b *testing.B) { + for i := 0; i < b.N; i++ { + nats.NewInbox() + } +} + +func BenchmarkRequest(b *testing.B) { + b.StopTimer() + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(b) + defer nc.Close() + ok := []byte("ok") + nc.Subscribe("req", func(m *nats.Msg) { + nc.Publish(m.Reply, ok) + }) + b.StartTimer() + b.ReportAllocs() + q := []byte("q") + for i := 0; i < b.N; i++ { + _, err := nc.Request("req", q, 1*time.Second) + if err != nil { + b.Fatalf("Err %v\n", err) + } + } +} + +func BenchmarkOldRequest(b *testing.B) { + b.StopTimer() + s := RunDefaultServer() + defer s.Shutdown() + nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle()) + if err != nil { + b.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + ok := []byte("ok") + nc.Subscribe("req", func(m *nats.Msg) { + nc.Publish(m.Reply, ok) + }) + b.StartTimer() + b.ReportAllocs() + q := []byte("q") + for i := 0; i < b.N; i++ { + _, err := nc.Request("req", q, 1*time.Second) + if err != nil { + b.Fatalf("Err %v\n", err) + } + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/cluster_test.go b/vendor/github.com/nats-io/go-nats/test/cluster_test.go new file mode 100644 index 00000000..13c1e182 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/cluster_test.go @@ -0,0 +1,859 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "fmt" + "math" + "net" + "runtime" + "strings" + "sync" + "testing" + "time" + + "github.com/nats-io/gnatsd/server" + "github.com/nats-io/gnatsd/test" + "github.com/nats-io/go-nats" +) + +var testServers = []string{ + "nats://localhost:1222", + "nats://localhost:1223", + "nats://localhost:1224", + "nats://localhost:1225", + "nats://localhost:1226", + "nats://localhost:1227", + "nats://localhost:1228", +} + +var servers = strings.Join(testServers, ",") + +func serverVersionAtLeast(major, minor, update int) error { + var ( + ma, mi, up int + ) + fmt.Sscanf(server.VERSION, "%d.%d.%d", &ma, &mi, &up) + if ma > major || (ma == major && mi > minor) || (ma == major && mi == minor && up >= update) { + return nil + } + return fmt.Errorf("Server version is %v, requires %d.%d.%d+", server.VERSION, major, minor, update) +} + +func TestServersOption(t *testing.T) { + opts := nats.GetDefaultOptions() + opts.NoRandomize = true + + _, err := opts.Connect() + if err != nats.ErrNoServers { + t.Fatalf("Wrong error: '%v'\n", err) + } + opts.Servers = testServers + _, err = opts.Connect() + if err == nil || err != nats.ErrNoServers { + t.Fatalf("Did not receive proper error: %v\n", err) + } + + // Make sure we can connect to first server if running + s1 := RunServerOnPort(1222) + // Do this in case some failure occurs before explicit shutdown + defer s1.Shutdown() + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Could not connect: %v\n", err) + } + if nc.ConnectedUrl() != "nats://localhost:1222" { + nc.Close() + t.Fatalf("Does not report correct connection: %s\n", + nc.ConnectedUrl()) + } + nc.Close() + s1.Shutdown() + + // Make sure we can connect to a non first server if running + s2 := RunServerOnPort(1223) + // Do this in case some failure occurs before explicit shutdown + defer s2.Shutdown() + + nc, err = opts.Connect() + if err != nil { + t.Fatalf("Could not connect: %v\n", err) + } + defer nc.Close() + if nc.ConnectedUrl() != "nats://localhost:1223" { + t.Fatalf("Does not report correct connection: %s\n", + nc.ConnectedUrl()) + } +} + +func TestNewStyleServersOption(t *testing.T) { + _, err := nats.Connect(nats.DefaultURL, nats.DontRandomize()) + if err != nats.ErrNoServers { + t.Fatalf("Wrong error: '%v'\n", err) + } + servers := strings.Join(testServers, ",") + + _, err = nats.Connect(servers, nats.DontRandomize()) + if err == nil || err != nats.ErrNoServers { + t.Fatalf("Did not receive proper error: %v\n", err) + } + + // Make sure we can connect to first server if running + s1 := RunServerOnPort(1222) + // Do this in case some failure occurs before explicit shutdown + defer s1.Shutdown() + + nc, err := nats.Connect(servers, nats.DontRandomize()) + if err != nil { + t.Fatalf("Could not connect: %v\n", err) + } + if nc.ConnectedUrl() != "nats://localhost:1222" { + nc.Close() + t.Fatalf("Does not report correct connection: %s\n", + nc.ConnectedUrl()) + } + nc.Close() + s1.Shutdown() + + // Make sure we can connect to a non-first server if running + s2 := RunServerOnPort(1223) + // Do this in case some failure occurs before explicit shutdown + defer s2.Shutdown() + + nc, err = nats.Connect(servers, nats.DontRandomize()) + if err != nil { + t.Fatalf("Could not connect: %v\n", err) + } + defer nc.Close() + if nc.ConnectedUrl() != "nats://localhost:1223" { + t.Fatalf("Does not report correct connection: %s\n", + nc.ConnectedUrl()) + } +} + +func TestAuthServers(t *testing.T) { + var plainServers = []string{ + "nats://localhost:1222", + "nats://localhost:1224", + } + + opts := test.DefaultTestOptions + opts.Username = "derek" + opts.Password = "foo" + + opts.Port = 1222 + as1 := RunServerWithOptions(opts) + defer as1.Shutdown() + opts.Port = 1224 + as2 := RunServerWithOptions(opts) + defer as2.Shutdown() + + pservers := strings.Join(plainServers, ",") + nc, err := nats.Connect(pservers, nats.DontRandomize(), nats.Timeout(5*time.Second)) + if err == nil { + nc.Close() + t.Fatalf("Expect Auth failure, got no error\n") + } + + if !strings.Contains(err.Error(), "authorization") { + t.Fatalf("Wrong error, wanted Auth failure, got '%s'\n", err) + } + + // Test that we can connect to a subsequent correct server. + var authServers = []string{ + "nats://localhost:1222", + "nats://derek:foo@localhost:1224", + } + aservers := strings.Join(authServers, ",") + nc, err = nats.Connect(aservers, nats.DontRandomize(), nats.Timeout(5*time.Second)) + if err != nil { + t.Fatalf("Expected to connect properly: %v\n", err) + } + defer nc.Close() + if nc.ConnectedUrl() != authServers[1] { + t.Fatalf("Does not report correct connection: %s\n", + nc.ConnectedUrl()) + } +} + +func TestBasicClusterReconnect(t *testing.T) { + s1 := RunServerOnPort(1222) + defer s1.Shutdown() + s2 := RunServerOnPort(1224) + defer s2.Shutdown() + + dch := make(chan bool) + rch := make(chan bool) + + dcbCalled := false + + opts := []nats.Option{nats.DontRandomize(), + nats.DisconnectHandler(func(nc *nats.Conn) { + // Suppress any additional callbacks + if dcbCalled { + return + } + dcbCalled = true + dch <- true + }), + nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true }), + } + + nc, err := nats.Connect(servers, opts...) + if err != nil { + t.Fatalf("Expected to connect, got err: %v\n", err) + } + defer nc.Close() + + s1.Shutdown() + + // wait for disconnect + if e := WaitTime(dch, 2*time.Second); e != nil { + t.Fatal("Did not receive a disconnect callback message") + } + + reconnectTimeStart := time.Now() + + // wait for reconnect + if e := WaitTime(rch, 2*time.Second); e != nil { + t.Fatal("Did not receive a reconnect callback message") + } + + if nc.ConnectedUrl() != testServers[2] { + t.Fatalf("Does not report correct connection: %s\n", + nc.ConnectedUrl()) + } + + // Make sure we did not wait on reconnect for default time. + // Reconnect should be fast since it will be a switch to the + // second server and not be dependent on server restart time. + + // On Windows, a failed connect takes more than a second, so + // account for that. + maxDuration := 100 * time.Millisecond + if runtime.GOOS == "windows" { + maxDuration = 1100 * time.Millisecond + } + reconnectTime := time.Since(reconnectTimeStart) + if reconnectTime > maxDuration { + t.Fatalf("Took longer than expected to reconnect: %v\n", reconnectTime) + } +} + +func TestHotSpotReconnect(t *testing.T) { + s1 := RunServerOnPort(1222) + defer s1.Shutdown() + + var srvrs string + if runtime.GOOS == "windows" { + srvrs = strings.Join(testServers[:5], ",") + } else { + srvrs = servers + } + + numClients := 32 + clients := []*nats.Conn{} + + wg := &sync.WaitGroup{} + wg.Add(numClients) + + opts := []nats.Option{ + nats.ReconnectWait(50 * time.Millisecond), + nats.ReconnectHandler(func(_ *nats.Conn) { wg.Done() }), + } + + for i := 0; i < numClients; i++ { + nc, err := nats.Connect(srvrs, opts...) + if err != nil { + t.Fatalf("Expected to connect, got err: %v\n", err) + } + defer nc.Close() + if nc.ConnectedUrl() != testServers[0] { + t.Fatalf("Connected to incorrect server: %v\n", nc.ConnectedUrl()) + } + clients = append(clients, nc) + } + + s2 := RunServerOnPort(1224) + defer s2.Shutdown() + s3 := RunServerOnPort(1226) + defer s3.Shutdown() + + s1.Shutdown() + + numServers := 2 + + // Wait on all reconnects + wg.Wait() + + // Walk the clients and calculate how many of each.. + cs := make(map[string]int) + for _, nc := range clients { + cs[nc.ConnectedUrl()]++ + nc.Close() + } + if len(cs) != numServers { + t.Fatalf("Wrong number of reported servers: %d vs %d\n", len(cs), numServers) + } + expected := numClients / numServers + v := uint(float32(expected) * 0.40) + + // Check that each item is within acceptable range + for s, total := range cs { + delta := uint(math.Abs(float64(expected - total))) + if delta > v { + t.Fatalf("Connected clients to server: %s out of range: %d\n", s, total) + } + } +} + +func TestProperReconnectDelay(t *testing.T) { + s1 := RunServerOnPort(1222) + defer s1.Shutdown() + + var srvs string + opts := nats.GetDefaultOptions() + if runtime.GOOS == "windows" { + srvs = strings.Join(testServers[:2], ",") + } else { + srvs = strings.Join(testServers, ",") + } + opts.NoRandomize = true + + dcbCalled := false + closedCbCalled := false + dch := make(chan bool) + + dcb := func(nc *nats.Conn) { + // Suppress any additional calls + if dcbCalled { + return + } + dcbCalled = true + dch <- true + } + + ccb := func(_ *nats.Conn) { + closedCbCalled = true + } + + nc, err := nats.Connect(srvs, nats.DontRandomize(), nats.DisconnectHandler(dcb), nats.ClosedHandler(ccb)) + if err != nil { + t.Fatalf("Expected to connect, got err: %v\n", err) + } + defer nc.Close() + + s1.Shutdown() + + // wait for disconnect + if e := WaitTime(dch, 2*time.Second); e != nil { + t.Fatal("Did not receive a disconnect callback message") + } + + // Wait, want to make sure we don't spin on reconnect to non-existent servers. + time.Sleep(1 * time.Second) + + // Make sure we are still reconnecting.. + if closedCbCalled { + t.Fatal("Closed CB was triggered, should not have been.") + } + if status := nc.Status(); status != nats.RECONNECTING { + t.Fatalf("Wrong status: %d\n", status) + } +} + +func TestProperFalloutAfterMaxAttempts(t *testing.T) { + s1 := RunServerOnPort(1222) + defer s1.Shutdown() + + opts := nats.GetDefaultOptions() + // Reduce the list of servers for Windows tests + if runtime.GOOS == "windows" { + opts.Servers = testServers[:2] + opts.MaxReconnect = 2 + } else { + opts.Servers = testServers + opts.MaxReconnect = 5 + } + opts.NoRandomize = true + opts.ReconnectWait = (25 * time.Millisecond) + + dch := make(chan bool) + opts.DisconnectedCB = func(_ *nats.Conn) { + dch <- true + } + + closedCbCalled := false + cch := make(chan bool) + + opts.ClosedCB = func(_ *nats.Conn) { + closedCbCalled = true + cch <- true + } + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected to connect, got err: %v\n", err) + } + defer nc.Close() + + s1.Shutdown() + + // On Windows, creating a TCP connection to a server not running takes more than + // a second. So be generous with the WaitTime. + + // wait for disconnect + if e := WaitTime(dch, 5*time.Second); e != nil { + t.Fatal("Did not receive a disconnect callback message") + } + + // Wait for ClosedCB + if e := WaitTime(cch, 5*time.Second); e != nil { + t.Fatal("Did not receive a closed callback message") + } + + // Make sure we are not still reconnecting.. + if !closedCbCalled { + t.Logf("%+v\n", nc) + t.Fatal("Closed CB was not triggered, should have been.") + } + + // Expect connection to be closed... + if !nc.IsClosed() { + t.Fatalf("Wrong status: %d\n", nc.Status()) + } +} + +func TestProperFalloutAfterMaxAttemptsWithAuthMismatch(t *testing.T) { + var myServers = []string{ + "nats://localhost:1222", + "nats://localhost:4443", + } + s1 := RunServerOnPort(1222) + defer s1.Shutdown() + + s2, _ := RunServerWithConfig("./configs/tlsverify.conf") + defer s2.Shutdown() + + opts := nats.GetDefaultOptions() + opts.Servers = myServers + opts.NoRandomize = true + if runtime.GOOS == "windows" { + opts.MaxReconnect = 2 + } else { + opts.MaxReconnect = 5 + } + opts.ReconnectWait = (25 * time.Millisecond) + + dch := make(chan bool) + opts.DisconnectedCB = func(_ *nats.Conn) { + dch <- true + } + + closedCbCalled := false + cch := make(chan bool) + + opts.ClosedCB = func(_ *nats.Conn) { + closedCbCalled = true + cch <- true + } + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected to connect, got err: %v\n", err) + } + defer nc.Close() + + s1.Shutdown() + + // On Windows, creating a TCP connection to a server not running takes more than + // a second. So be generous with the WaitTime. + + // wait for disconnect + if e := WaitTime(dch, 5*time.Second); e != nil { + t.Fatal("Did not receive a disconnect callback message") + } + + // Wait for ClosedCB + if e := WaitTime(cch, 5*time.Second); e != nil { + reconnects := nc.Stats().Reconnects + t.Fatalf("Did not receive a closed callback message, #reconnects: %v", reconnects) + } + + // Make sure we have not exceeded MaxReconnect + reconnects := nc.Stats().Reconnects + if reconnects != uint64(opts.MaxReconnect) { + t.Fatalf("Num reconnects was %v, expected %v", reconnects, opts.MaxReconnect) + } + + // Make sure we are not still reconnecting.. + if !closedCbCalled { + t.Logf("%+v\n", nc) + t.Fatal("Closed CB was not triggered, should have been.") + } + + // Expect connection to be closed... + if !nc.IsClosed() { + t.Fatalf("Wrong status: %d\n", nc.Status()) + } +} + +func TestTimeoutOnNoServers(t *testing.T) { + s1 := RunServerOnPort(1222) + defer s1.Shutdown() + + opts := nats.GetDefaultOptions() + if runtime.GOOS == "windows" { + opts.Servers = testServers[:2] + opts.MaxReconnect = 2 + opts.ReconnectWait = (100 * time.Millisecond) + } else { + opts.Servers = testServers + // 1 second total time wait + opts.MaxReconnect = 10 + opts.ReconnectWait = (100 * time.Millisecond) + } + opts.NoRandomize = true + + dch := make(chan bool) + opts.DisconnectedCB = func(nc *nats.Conn) { + // Suppress any additional calls + nc.SetDisconnectHandler(nil) + dch <- true + } + + cch := make(chan bool) + opts.ClosedCB = func(_ *nats.Conn) { + cch <- true + } + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected to connect, got err: %v\n", err) + } + defer nc.Close() + + s1.Shutdown() + + // On Windows, creating a connection to a non-running server takes + // more than a second. So be generous with WaitTime + + // wait for disconnect + if e := WaitTime(dch, 5*time.Second); e != nil { + t.Fatal("Did not receive a disconnect callback message") + } + + startWait := time.Now() + + // Wait for ClosedCB + if e := WaitTime(cch, 5*time.Second); e != nil { + t.Fatal("Did not receive a closed callback message") + } + + if runtime.GOOS != "windows" { + timeWait := time.Since(startWait) + + // Use 500ms as variable time delta + variable := (500 * time.Millisecond) + expected := (time.Duration(opts.MaxReconnect) * opts.ReconnectWait) + + if timeWait > (expected + variable) { + t.Fatalf("Waited too long for Closed state: %d\n", timeWait/time.Millisecond) + } + } +} + +func TestPingReconnect(t *testing.T) { + RECONNECTS := 4 + s1 := RunServerOnPort(1222) + defer s1.Shutdown() + + opts := nats.GetDefaultOptions() + opts.Servers = testServers + opts.NoRandomize = true + opts.ReconnectWait = 200 * time.Millisecond + opts.PingInterval = 50 * time.Millisecond + opts.MaxPingsOut = -1 + + var wg sync.WaitGroup + wg.Add(1) + rch := make(chan time.Time, RECONNECTS) + dch := make(chan time.Time, RECONNECTS) + + opts.DisconnectedCB = func(_ *nats.Conn) { + d := dch + select { + case d <- time.Now(): + default: + d = nil + } + } + + opts.ReconnectedCB = func(c *nats.Conn) { + r := rch + select { + case r <- time.Now(): + default: + r = nil + wg.Done() + } + } + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected to connect, got err: %v\n", err) + } + defer nc.Close() + + wg.Wait() + s1.Shutdown() + + <-dch + for i := 0; i < RECONNECTS-1; i++ { + disconnectedAt := <-dch + reconnectAt := <-rch + pingCycle := disconnectedAt.Sub(reconnectAt) + if pingCycle > 2*opts.PingInterval { + t.Fatalf("Reconnect due to ping took %s", pingCycle.String()) + } + } +} + +type checkPoolUpdatedDialer struct { + conn net.Conn + first, final bool + ra int +} + +func (d *checkPoolUpdatedDialer) Dial(network, address string) (net.Conn, error) { + doReal := false + if d.first { + d.first = false + doReal = true + } else if d.final { + d.ra++ + return nil, fmt.Errorf("On purpose") + } else { + d.ra++ + if d.ra == 15 { + d.ra = 0 + doReal = true + } + } + if doReal { + c, err := net.Dial(network, address) + if err != nil { + return nil, err + } + d.conn = c + return c, nil + } + return nil, fmt.Errorf("On purpose") +} + +func TestServerPoolUpdatedWhenRouteGoesAway(t *testing.T) { + if err := serverVersionAtLeast(1, 0, 7); err != nil { + t.Skipf(err.Error()) + } + s1Opts := test.DefaultTestOptions + s1Opts.Host = "127.0.0.1" + s1Opts.Port = 4222 + s1Opts.Cluster.Host = "127.0.0.1" + s1Opts.Cluster.Port = 6222 + s1Opts.Routes = server.RoutesFromStr("nats://127.0.0.1:6223,nats://127.0.0.1:6224") + s1 := test.RunServer(&s1Opts) + defer s1.Shutdown() + + s1Url := "nats://127.0.0.1:4222" + s2Url := "nats://127.0.0.1:4223" + s3Url := "nats://127.0.0.1:4224" + + ch := make(chan bool, 1) + chch := make(chan bool, 1) + connHandler := func(_ *nats.Conn) { + chch <- true + } + nc, err := nats.Connect(s1Url, + nats.ReconnectHandler(connHandler), + nats.DiscoveredServersHandler(func(_ *nats.Conn) { + ch <- true + })) + if err != nil { + t.Fatalf("Error on connect") + } + + s2Opts := test.DefaultTestOptions + s2Opts.Host = "127.0.0.1" + s2Opts.Port = s1Opts.Port + 1 + s2Opts.Cluster.Host = "127.0.0.1" + s2Opts.Cluster.Port = 6223 + s2Opts.Routes = server.RoutesFromStr("nats://127.0.0.1:6222,nats://127.0.0.1:6224") + s2 := test.RunServer(&s2Opts) + defer s2.Shutdown() + + // Wait to be notified + if err := Wait(ch); err != nil { + t.Fatal("New server callback was not invoked") + } + + checkPool := func(expected []string) { + // Don't use discovered here, but Servers to have the full list. + // Also, there may be cases where the mesh is not formed yet, + // so try again on failure. + var ( + ds []string + timeout = time.Now().Add(5 * time.Second) + ) + for time.Now().Before(timeout) { + ds = nc.Servers() + if len(ds) == len(expected) { + m := make(map[string]struct{}, len(ds)) + for _, url := range ds { + m[url] = struct{}{} + } + ok := true + for _, url := range expected { + if _, present := m[url]; !present { + ok = false + break + } + } + if ok { + return + } + } + time.Sleep(50 * time.Millisecond) + } + stackFatalf(t, "Expected %v, got %v", expected, ds) + } + // Verify that we now know about s2 + checkPool([]string{s1Url, s2Url}) + + s3Opts := test.DefaultTestOptions + s3Opts.Host = "127.0.0.1" + s3Opts.Port = s2Opts.Port + 1 + s3Opts.Cluster.Host = "127.0.0.1" + s3Opts.Cluster.Port = 6224 + s3Opts.Routes = server.RoutesFromStr("nats://127.0.0.1:6222,nats://127.0.0.1:6223") + s3 := test.RunServer(&s3Opts) + defer s3.Shutdown() + + // Wait to be notified + if err := Wait(ch); err != nil { + t.Fatal("New server callback was not invoked") + } + // Verify that we now know about s3 + checkPool([]string{s1Url, s2Url, s3Url}) + + // Stop s1. Since this was passed to the Connect() call, this one should + // still be present. + s1.Shutdown() + // Wait for reconnect + if err := Wait(chch); err != nil { + t.Fatal("Reconnect handler not invoked") + } + checkPool([]string{s1Url, s2Url, s3Url}) + + // Check the server we reconnected to. + reConnectedTo := nc.ConnectedUrl() + expected := []string{s1Url} + restartS2 := false + if reConnectedTo == s2Url { + restartS2 = true + s2.Shutdown() + expected = append(expected, s3Url) + } else if reConnectedTo == s3Url { + s3.Shutdown() + expected = append(expected, s2Url) + } else { + t.Fatalf("Unexpected server client has reconnected to: %v", reConnectedTo) + } + // Wait for reconnect + if err := Wait(chch); err != nil { + t.Fatal("Reconnect handler not invoked") + } + // The implicit server that we just shutdown should have been removed from the pool + checkPool(expected) + + // Restart the one that was shutdown and check that it is now back in the pool + if restartS2 { + s2 = test.RunServer(&s2Opts) + defer s2.Shutdown() + expected = append(expected, s2Url) + } else { + s3 = test.RunServer(&s3Opts) + defer s3.Shutdown() + expected = append(expected, s3Url) + } + // Since this is not a "new" server, the DiscoveredServersCB won't be invoked. + checkPool(expected) + + nc.Close() + + // Restart s1 + s1 = test.RunServer(&s1Opts) + defer s1.Shutdown() + + // We should have all 3 servers running now... + + // Create a client connection with special dialer. + d := &checkPoolUpdatedDialer{first: true} + nc, err = nats.Connect(s1Url, + nats.MaxReconnects(10), + nats.ReconnectWait(15*time.Millisecond), + nats.SetCustomDialer(d), + nats.ReconnectHandler(connHandler), + nats.ClosedHandler(connHandler)) + if err != nil { + t.Fatalf("Error on connect") + } + defer nc.Close() + + // Make sure that we have all 3 servers in the pool (this will wait if required) + checkPool(expected) + + // Cause disconnection between client and server. We are going to reconnect + // and we want to check that when we get the INFO again with the list of + // servers, we don't lose the knowledge of how many times we tried to + // reconnect. + d.conn.Close() + + // Wait for client to reconnect to a server + if err := Wait(chch); err != nil { + t.Fatal("Reconnect handler not invoked") + } + // At this point, we should have tried to reconnect 5 times to each server. + // For the one we reconnected to, its max reconnect attempts should have been + // cleared, not for the other ones. + + // Cause a disconnect again and ensure we won't reconnect. + d.final = true + d.conn.Close() + + // Wait for Close callback to be invoked. + if err := Wait(chch); err != nil { + t.Fatal("Close handler not invoked") + } + + // Since MaxReconnect is 10, after trying 5 more times on 2 of the servers, + // these should have been removed. We have still 5 more tries for the server + // we did previously reconnect to. + // So total of reconnect attempt should be: 2*5+1*10=20 + if d.ra != 20 { + t.Fatalf("Should have tried to reconnect 20 more times, got %v", d.ra) + } + + nc.Close() +} diff --git a/vendor/github.com/nats-io/go-nats/test/configs/certs/ca.pem b/vendor/github.com/nats-io/go-nats/test/configs/certs/ca.pem new file mode 100644 index 00000000..17447f94 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/configs/certs/ca.pem @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +MIIGjzCCBHegAwIBAgIJAKT2W9SKY7o4MA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy +MzA2MTdaFw0xOTExMDQyMzA2MTdaMIGLMQswCQYDVQQGEwJVUzELMAkGA1UECBMC +Q0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkFwY2VyYSBJbmMx +EDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxvY2FsaG9zdDEcMBoGCSqGSIb3 +DQEJARYNZGVyZWtAbmF0cy5pbzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAJOyBvFaREbmO/yaw8UD8u5vSk+Qrwdkfa0iHMo11nkcVtynHNKcgRUTkZBC +xEZILVsuPa+WSUcUc0ej0TmuimrtOjXGn+LD0TrDVz6dd6lBufLXjo1fbUnKUjml +TBYB2h7StDksrBPFnbEOVKN+qb1No4YxfvbJ6EK3xfnsm3dvamnetJugrmQ2EUlu +glPNZDIShu9Fcsiq2hjw+dJ2Erl8kx2/PE8nOdcDG9I4wAM71pw9L1dHGmMOnTsq +opLDVkMNjeIgMPxj5aIhvS8Tcnj16ZNi4h10587vld8fIdz+OgTDFMNi91PgZQmX +9puXraBGi5UEn0ly57IIY+aFkx74jPWgnVYz8w8G+W2GTFYQEVgHcPTJ4aIPjyRd +m/cLelV34TMNCoTXmpIKVBkJY01t2awUYN0AcauhmD1L+ihY2lVk330lxQR11ZQ/ +rjSRpG6jzb6diVK5wpNjsRRt5zJgZr6BMp0LYwJESGjt0sF0zZxixvHu8EctVle4 +zX6NHDic7mf4Wvo4rfnUyCGr7Y3OxB2vakq1fDZ1Di9OzpW/k8i/TE+mPRI5GTZt +lR+c8mBxdV595EKHDxj0gY7PCM3Pe35p3oScWtfbpesTX6a7IL801ZwKKtN+4DOV +mZhwiefztb/9IFPNXiuQnNh7mf7W2ob7SiGYct8iCLLjT64DAgMBAAGjgfMwgfAw +HQYDVR0OBBYEFPDMEiYb7Np2STbm8j9qNj1aAvz2MIHABgNVHSMEgbgwgbWAFPDM +EiYb7Np2STbm8j9qNj1aAvz2oYGRpIGOMIGLMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkFwY2VyYSBJ +bmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxvY2FsaG9zdDEcMBoGCSqG +SIb3DQEJARYNZGVyZWtAbmF0cy5pb4IJAKT2W9SKY7o4MAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggIBAIkoO+svWiudydr4sQNv/XhDvH0GiWMjaI738fAB +sGUKWXarXM9rsRtoQ78iwEBZmusEv0fmJ9hX275aZdduTJt4AnCBVptnSyMJS6K5 +RZF4ZQ3zqT3QOeWepLqszqRZHf+xNfl9JiXZc3pqNhoh1YXPubCgY+TY1XFSrL+u +Wmbs3n56Cede5+dKwMpT9SfQ7nL1pwKihx16vlBGTjjvJ0RE5Tx+0VRcDgbtIF52 +pNlvjg9DL+UqP3S1WR0PcsUss/ygiC1NDegZr+I/04/wEG9Drwk1yPSshWsH90W0 +7TmLDoWf5caAX62jOJtXbsA9JZ16RnIWy2iZYwg4YdE0rEeMbnDzrRucbyBahMX0 +mKc8C+rroW0TRTrqxYDQTE5gmAghCa9EixcwSTgMH/U6zsRbbY62m9WA5fKfu3n0 +z82+c36ijScHLgppTVosq+kkr/YE84ct56RMsg9esEKTxGxje812OSdHp/i2RzqW +J59yo7KUn1nX7HsFvBVh9D8147J5BxtPztc0GtCQTXFT73nQapJjAd5J+AC5AB4t +ShE+MRD+XIlPB/aMgtzz9Th8UCktVKoPOpFMC0SvFbbINWL/JO1QGhuZLMTKLjQN +QBzjrETAOA9PICpI5hcPtTXz172X+I8/tIEFrZfew0Fdt/oAVcnb659zKiR8EuAq ++Svp +-----END CERTIFICATE----- diff --git a/vendor/github.com/nats-io/go-nats/test/configs/certs/client-cert.pem b/vendor/github.com/nats-io/go-nats/test/configs/certs/client-cert.pem new file mode 100644 index 00000000..549c9b38 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/configs/certs/client-cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFPDCCAySgAwIBAgIJAO+k4G7bNTypMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy +MzEwNDdaFw0xOTExMDQyMzEwNDdaMBYxFDASBgNVBAMTC25hdHMtY2xpZW50MIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArgLxszD5/vDrDUwwIEgQx9I0 +J/H6MXPO0Tj9D2BnR+nwjCe9M03fsq4Il96BVzoaAiAQD1r4NyAX2adKydlnE3/m +bUFiSVHErJceEi9aSs+WlLdmKEgU2qrsIal9KzthlI786qtjb7OFSCxP14R4xYA5 +dlZXhJ9oUuFhVTdaVmRMzWuWj8RbBx8VptSZ0f7Q+Uv8GuB0kyiVkv6GYcH/IWuI +7jnM0QcVWBmxJfWmqd0yx/FLlX/LRXqdiyoFSIlMaP0VOwto3uEhAoBk83Z+/zrZ +Brymx1Nnz3qzTCf8/mdMjPuWibXDTLbo0/Kf6neHs6wxx8irb1ZfIwhn8grXTcgd +rg9bfcyyUOBey7QXiedpU0xFqoH26E+Aq+CV4R56i1sJKsSYEGu8O69H8zu5dgan +LZRhcCHcZhMe7Nbiu5BcuOW4r3rGDMTLXSugEX91iy5jJaYmRjtPN5imQIJtf+GK +Vq7YLv4MQV6R3xRiZXaocCae1qzIMc4kxCKvZTmxuJsvIUPjNnGumwbjV/a2fLFX +9tMqUKyEmiPtFtqNH/kmkHCQ5FGYIIj3wGuD5yWfK5Tr3iHOdNJoNNPgPBg9tMRw +j3+W8+uyBxc+FUEb8a9m3R4VmAYyiqgzCA0DWZBF1fOYLWfRnwS5OBKiP4OUlUEb +YZUEzfvDbLOwQrb123cCAwEAAaMXMBUwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJ +KoZIhvcNAQELBQADggIBACNKPbvaXwl5rRTqFw37Am1r6e+LkUg9dFogSwXDnuT/ +RRZJi5MHsC5MUOkHB28lTmPwkAogs+LBmKrM0Npzk6OPkT/LCgKqpVoz2Tc1nGMI +Jy8jxPYogMmDCOhoEoC7zsWABMLiX5KDAuKommk61w7AwKu4kK198ngwbfF2fzdH +1DUGID7iV4fyPGI+pCU3Ullv51c5xkhqjVy1JYdYc0+s6rFyVTibSABa7PfHE2ML +A+cNFWoKQhugVHQU7qYvuWvnEqZro2T6nmSmpK3oOaUgVnDuY2q4JwiMbZAtuyD7 +8LFwCim49WzgYcfs/BwKlUrTV/QBYurruHWjElZzwA39/ZlbnOjJJ85j/YqxR+4S +fK/KktegyrPJU3fxdl2+77zVlfgzxaQ//58vx5LgXWhl2KeHyakeD0jQFVn1R7GD +bynAlHlSOr+nGkwP2WVqXKf+l/gb/gUEY7bC8fCVRCctkcK+smEl+sIKH3O9JY8l +rBWjOXkMY91ZDh77hfTNni/s2/DGAoNrEft8rgu3/NPxhCTfQH3ranCryth9mF6I +qsOFr5/81WGKqU+Kec8st/RSU2vBjBp41HILAEEhUiB6prhc9B3+exwkvQSPz22W +PIvhkzqeOYRoEDE2bWGC1ukd818qvQp618eLBmJSvwGh4YfUcmgqHaEk2NjoPIMV +-----END CERTIFICATE----- diff --git a/vendor/github.com/nats-io/go-nats/test/configs/certs/client-key.pem b/vendor/github.com/nats-io/go-nats/test/configs/certs/client-key.pem new file mode 100644 index 00000000..bb44aa5a --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/configs/certs/client-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEArgLxszD5/vDrDUwwIEgQx9I0J/H6MXPO0Tj9D2BnR+nwjCe9 +M03fsq4Il96BVzoaAiAQD1r4NyAX2adKydlnE3/mbUFiSVHErJceEi9aSs+WlLdm +KEgU2qrsIal9KzthlI786qtjb7OFSCxP14R4xYA5dlZXhJ9oUuFhVTdaVmRMzWuW +j8RbBx8VptSZ0f7Q+Uv8GuB0kyiVkv6GYcH/IWuI7jnM0QcVWBmxJfWmqd0yx/FL +lX/LRXqdiyoFSIlMaP0VOwto3uEhAoBk83Z+/zrZBrymx1Nnz3qzTCf8/mdMjPuW +ibXDTLbo0/Kf6neHs6wxx8irb1ZfIwhn8grXTcgdrg9bfcyyUOBey7QXiedpU0xF +qoH26E+Aq+CV4R56i1sJKsSYEGu8O69H8zu5dganLZRhcCHcZhMe7Nbiu5BcuOW4 +r3rGDMTLXSugEX91iy5jJaYmRjtPN5imQIJtf+GKVq7YLv4MQV6R3xRiZXaocCae +1qzIMc4kxCKvZTmxuJsvIUPjNnGumwbjV/a2fLFX9tMqUKyEmiPtFtqNH/kmkHCQ +5FGYIIj3wGuD5yWfK5Tr3iHOdNJoNNPgPBg9tMRwj3+W8+uyBxc+FUEb8a9m3R4V +mAYyiqgzCA0DWZBF1fOYLWfRnwS5OBKiP4OUlUEbYZUEzfvDbLOwQrb123cCAwEA +AQKCAgAQUkBfYVGhgvFZDvNYo8nHJEU2FfE0oDsezqyVu6IUUbH5Q2TwofZAaShv +LjSNfOqhlmZLOmobqYvzI0jVg+myH4X6a26Pl/bNhWMRq5VZfP0Pt+ACGTizheKe +Caqu2mP9rie0zxyFhp4Ste1LNqapR6ycF98flmAPngomFwoHHmNBxTybAXzUPysl +ub0vwCnTqDfeQX1NrDnTTsJF+w82EEMIrS0z0elDmS1PdSoLtq6jqFNBk3n6a1TJ +j8htFEuxcUODhT9x4EXbWTWezFd/EwL2Kc2u1njfMhANLZcCOagpdROamQzXbjSK +ZLBxKoL07ErDBWRnDf/gZlJxlmi5QFgy3LFvmZ93sbedzRaTDsjXEpbTse/l36QY +6YCjSnb2zUX2AElKmyC/QwR8BZ9afRQM7x3eqLkE1q4jkLsk3+W3VroyaoOfQxiB +k+xtL5cxoa9SiTgETNHpFQhiTNyX7FlH1ykoJzTryLsbccTd1iP7DF5ZPt8DfgIZ +PLzwh7PDiK5cpitm8g6TdvuLA9FT+bEtd/78odN++VDhkcCmSQMWKk3Xt8wznNcY +8Ye5JC/4aHRueWCziWaJYJHi6ZNCt4CR5wzEGBmPlf0562UpQpfEuDOQDRX3FaMs +qYbCrRVeQL3wXcu3sVToj9zSES2R+kQfTwaqdypgS79y0Dp6eQKCAQEA2BAu0Cqn +xmjuqn/qpPXtW3kryHPP7eyzt53o8Xg7RqQ0oT+FNiO3o4aGoVlxkMjBW+NOpWo1 +VtsTrsB+RxIiuugb9/D2dy1z5BK2x4bvurxkyOovU3J2WHSNIUsbQ5FSN8w5sAcl ++1QFNcM5ooBa7VahRV2vJcGe9P+QFR75c4xSCvG6AOu8WzZNUNOw97s/N24NevU5 +26Ql20zwn+E0avd3yuFU7bKrvXh9v6lNqWhjkJePk8eTh/5O4cTuF/cB3wPcgjiC +24uyNI29lAVHS/+h0nVTdm0F1Fel8nwPkOLyRJUyEzWm8SX2rnwI3EegWaRyDohp +a1hmjHsCcpoxhQKCAQEAzizucnHqwxEQiMaJPUKBi3v3j+a/me3PfsY1760LdLVY +AcMuGr+wg2/e9d7jMvEIxlACng4aU2kKG0fOxS0G0e7AefB9DiwzexJ+pHu0R49p +PmkAoPl2+mAlfeqvwEJ4gQEH8hKoIEkU0XAPZfWMTlshCJgAyYYpsLlJl0f8ooa3 +4VRg3hjfWj+Z5pQryojN/Pfl4XRoM11xdaa79odvtptpN3KWxs9IhesM1o4mi4kC +Dd996iQpNau1bF6LHmEXJhbkEJ+SDXUDvEx6d3HYAFNPyWLe4DtJn38qb1gtuesZ +vGntToaAN12z4vJIj75vuduSJei8ceXcixYo1WZrywKCAQEAiz9avERRXpjwAChy +lB/++i4MnqKtBjy/0n3NzBndsfhQBwAGHU9FofkoOUKI43PO0iab4BWkDLciZ0Sd +3bX9dhHzPIcqgMJlZz78V3lKdUHHfokXOSOSzA1Ji4R5LMGyiE1xfFYPD3wl43FP +asBoWX+0bh0jrSStCl7OgB43TFXJ5k3Fv6Qt/2buy0GzUuV1p4ag33a99CVFVKGw +jom4m5ujs7gnYQ3+ixzlhilZ6O1jBaP4H5jHJyUpt22QuRczOISnj7FV/KJ6lk4n +OQdx3LQCmb2NrcwzrpdSVwXHjmwFEVhKLoEsd0wtQGSl3Tm4SS2naGBX+Ju/c5gv +iqZ/dQKCAQAzDJcByUkKgZgpdZcXjvcKdWhnvgek8mgVCLjkHmGexSQEU7J/twTa +loGLOWPiAiJdEASF5BIKoxB4jsAYvDxbEJWh27TrJHCewYaP7X1G1rCFXnRkZ0BZ +YCMIWWqo3Qx/TKUOACaWz+GStf9qDHFwGUpFmXVgcJK0Cjy5c36PM3ImHcFaXKg4 +7VSK7hclr9fpEexedXczeKiWK/GQahp0CWj07K9+jGZ1mix0l3/dvs++ZZ8EsW1u +t5RVP9eMbxfPO42+u/Pq1xVUs08DcjG8auRvhcaPmL5y+oakSR4RUa/uof+7GLx4 +eQAIalsjFFEPoNk//69hODvySEtWA2UfAoIBACGXYc0SuE9m2KxnxLiy4yEvDbw1 +3KO9Gwv+0iRaeCizdCTwaSu/weQrw9ddpfmeqdGhwsvH1S5WyFqtwsjS7abdj4cg +KJ3nuR1EDInFQcu9ii+T8MSTc64cPkJVIYHwYiwE2Whj+6F7KFc1mf33/zrivruT +6Mm1YJv11KkBDAaM4Bj37DQfCrYh6quxczCT827YX7Wuw9YGQZYZh/xzss0Tkfzm +LgHriX+8U7+rL24Fi+merhDhjO95NVkRSIDmg+pULaWkeDOyVxfLCIMmy7JByHW4 +fyDr/w1dfkx/yiV0xvkrfT+sOFmnMjfgMwmit3tfm7zkmkzNfmASugDPWjA= +-----END RSA PRIVATE KEY----- diff --git a/vendor/github.com/nats-io/go-nats/test/configs/certs/key.pem b/vendor/github.com/nats-io/go-nats/test/configs/certs/key.pem new file mode 100644 index 00000000..113a87e1 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/configs/certs/key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKgIBAAKCAgEAtgHLcgRjeSqV/mHa8S2T0IHhWe0AP55pVzdj3G4UcniTRJyy +PCtgfdhzBBbR8Ok5AIjTXTZihBPu08IFP6sLTDWYzzbRlIpL/LZIgr1wzosdaRRt +BxZ95ov67PYcHeNMSby2YQQVMsEkUxsylSy+MDkYuoZRGzCw2NgSXwz3BLUERPDZ +754IVrjDGr2gYen8OCHS9mCUfNAvmiwSlFy3VppCjo6NbNlzUKDHhLGYw6gxYXwF +DOU7tqKRtkQnGTTdMgU2mH9rMm3ua+Iyx5bvaY/5tf2yb/xuwg2JiAkwzYcDKMiA +VUxdfwBh8QULjCjNiWguqfTLL1N2OHIZuxSODTJN3iUD0uQYqugF1jV2s9J6Tk2P +1uvbtQYYZ9TZ10APnFgEh54Vj7eepJPzryghcH+bU/vWny2mSC6PH9Goqvee86oE +eLOahBpZmw8Ldf8lzg29UeKGm43M3+7UPmbEaHGzH5GqesiSFLQio2uiSCA9lrO6 +CYee133keBNvcmmNjdEYRhcBA2v6ZkZQJz4JW7SaEVfEAxlx9WnmcODiEoeJpG/Q +pxqoGaefwAHnDkWJOmnNRtE/TPPsaTCt26XBHpzYRvnvn7/TbZNuALHwH1IfjMlF +OPma2srnp4WBNye5cH5idZo/v/uqYohnPGt3dQO+fNpuGcyKIgru8vyqI5MCAwEA +AQKCAgEAl6zBNUAxAW2a2AYGZgx8bTt/Z+hY16uUz8jqIG1f/tE6sOgApKHlZJp3 +pwW5aRGCnk5oDfrfeH///Fpo81kALj9QHAbr+uSRVIU3wjRLCOTn2oTaIxj8TJ+E +ueqTHdko3x4zwn+bhtNsCRHWQnip+hfq4q5Ccu1Nwze1f56XUEXly+oHRGenPVX1 +yZgTSuWqecC+RPHRbH413T4zMY5efv5IzvI/K2G/doa2Hn+99fd5R2sJ7mguLhIm +agU7rAbg+ulbSRSOadUw5pj3hlrjI06HY8GK7UYpqu+LGGHIWM7VtCv6vprII6lW +9Xsl12S9fG/ky1+j38mm8H0tsjj78t2L6ZDS2Fb9usbM5VhdQfQpTBTSfAEZPeus +X2QTpTXnp5oHM7CzcQuGE25CruSHEJPy/Y0hTaunNBQ9VY6M/Pcq0sB0xAa0hN5H +PqOae1/fNKR/7iwdptesNGguZoLnNd1yeVBdZ55SZw7+9hjIPAjn3iLNqfieSpXL +5lG+Z0JEUMW0f1MRmU9AsR2x4Dlpvulrn39Oc5vgc0JP+r7+MMpY5BpWS5WhTxqm +tx1qh49yXFXIIEXqxjIIxQ3NO1del8QNDUGROnqlh5gFRADIcJpZMv8uAhSHEXm3 ++3PndJoCIfNv9gE8zNsB3r3PPgelG3wagy/eDe59PH0JvUmTWZkCggEBANxBkHAT +LB5hkp3hAwmop62HgkG8k6Ht11q2qGgkO/EhfsgsZXTpI3LZZ3Nrf+5IZiwStloW +iZwY/xocGL6tIFcuXHRqDDDPNRFUVxhSdcQd2mL7R6uin9eJ4ccQdaOXplQXOXFG +G7wAIhfGR7JnyzS1+eKItdFYrU63BeavPLltE4GV4pFJIFXEXc3v87j/Ba9uIop1 +/zytEn37yzDxdptH0HYtCm4Ve17n0STwvf9Le7b3ZFbs/cj3akAoSOTy/bYKNZl4 +EtaT0T7AGr8qJIaAlUYtva30+sQ2ytXHOdjkKD38xTN2oXoHgAfn7wIinzM+rbGi +d6FFIiARlp1g0O0CggEBANOLMJSvNeMxlM+8LJ0xo2J20Lk+1EGyb0+Ltp6jkrRW +SPCvnNC7Ww6L6tRfCvatnb0qTvfR/HfM1oE2e2Q2QL+hZoZyxXEiZHd/ERyAj398 +uImSz8bkRPWzPZU0wqYO621MEdY+fPcQfZDMBlcA25cFlvuiCRoeRQ1DIREDKMMG +Cnhbvv0f2J7e9rVAIqrTRtxKaRAIwU4YVIG2ymwWA+P/3/NFlYC344MGfoeum0NI +qazULaAVKE99jV3sYC2twcrGgXel/OSGCX33WCVsQKIhIOGDib1KzyJHTBr+D8Tu +rbO4fmyJtUpKC+XCIXto7ebbo0sVE2+7dp5ofBhCtn8CggEBALvBABkpnsA/OLZw +qyA+rsET9IuI7uhoUN25OxGbYaWJggOtJMdmPZuXi8It7x32hXIoeV2OPLvd6wgc +z1MrTZhDovhxtfadi4U8Ogo3sL//Grypq0y6EjuwA9CnTUCo81ZXfdX7h4TZMDbI +BTIlnGlQfrUHCMZuKz4gcl1VIBSI0Mn0NPDYP0IdZEE6vK4EZppG7hbNw0e72Tmf +vHP6QbrYmvFCL9PraAFc50HwHmZTuCAd/2DCIQyBLAeIz6qrIG9fgJVUb+qOkx5E +sAgpKn2lepoaP8jcPi+o7XsSm1MyGsPMh2X5SGk3n4IdyfYuATuzwGjeL9A/mHlx +xMxfTXkCggEAGYuTYEEQNtFD8Rn+ITVfT4KdjeEibJSJkIeEk/+YtaI9yKLMQwB8 +7HLE9sRLZKJui+tSAecfn6/ir1PO7rkGdJ2e7dlqMlE+5Jc5j8GOkoyTFDngUVo7 +YZg1dZEbeEYQ8+/dr4t4N7WMFDIvCc6WtdP8+YIFq1vAZuuWUKGbCIHwPbyGgbaY +yAaQsC6AgTRmOC/cJA2Kmk2h1tAl/YtjCONbPdtHRHXwSWA9Y1EYerWJl88/ezdS +2NaGfbMPojR7VGtIMxSeR1JQTx/RSyOZYnqxp8nkljE0diU58YCAkv1niG5dBepT +NBdg/GvG80omgFxBic2PvUxb9KEVazCTLQKCAQEAwx3aNk2lMovLzuMRqj2O7rqs +4usiHDllR1S7vAySUqhBaL8l+y1lsulgCDExClt3SQpsaM5xep1sK5jN8REzKsE9 +xBgXkNRgy+/1VGa1Tx0DR6xLoAIYT7Ttm27kellAFLE1tEFsSdZP9ZcfwjYKQEuu +Bsm4zf5duDb+hLraxK9ISqcc8ZUSlCLkj9GdhLwf+/8C81LXkS2ScR8Edumn8qe7 +IYqqWSYqKhaoqmx6sr8E0SIn6PKd7uXZnXTTxTf6AR1RNzFcStIL5lC06V6Savpa +tSX2voU3DgUIDYrYUhDweukR8i+0nrkR8wRUUjxaAeegUIRHN5ffpk57lQNaNg== +-----END RSA PRIVATE KEY----- diff --git a/vendor/github.com/nats-io/go-nats/test/configs/certs/server.pem b/vendor/github.com/nats-io/go-nats/test/configs/certs/server.pem new file mode 100644 index 00000000..46bc9133 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/configs/certs/server.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgIJAO+k4G7bNTyoMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR +BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy +MzA2MzRaFw0xOTExMDQyMzA2MzRaMBQxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALYBy3IEY3kqlf5h2vEtk9CB4Vnt +AD+eaVc3Y9xuFHJ4k0ScsjwrYH3YcwQW0fDpOQCI0102YoQT7tPCBT+rC0w1mM82 +0ZSKS/y2SIK9cM6LHWkUbQcWfeaL+uz2HB3jTEm8tmEEFTLBJFMbMpUsvjA5GLqG +URswsNjYEl8M9wS1BETw2e+eCFa4wxq9oGHp/Dgh0vZglHzQL5osEpRct1aaQo6O +jWzZc1Cgx4SxmMOoMWF8BQzlO7aikbZEJxk03TIFNph/azJt7mviMseW72mP+bX9 +sm/8bsINiYgJMM2HAyjIgFVMXX8AYfEFC4wozYloLqn0yy9TdjhyGbsUjg0yTd4l +A9LkGKroBdY1drPSek5Nj9br27UGGGfU2ddAD5xYBIeeFY+3nqST868oIXB/m1P7 +1p8tpkgujx/RqKr3nvOqBHizmoQaWZsPC3X/Jc4NvVHihpuNzN/u1D5mxGhxsx+R +qnrIkhS0IqNrokggPZazugmHntd95HgTb3JpjY3RGEYXAQNr+mZGUCc+CVu0mhFX +xAMZcfVp5nDg4hKHiaRv0KcaqBmnn8AB5w5FiTppzUbRP0zz7GkwrdulwR6c2Eb5 +75+/022TbgCx8B9SH4zJRTj5mtrK56eFgTcnuXB+YnWaP7/7qmKIZzxrd3UDvnza +bhnMiiIK7vL8qiOTAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA +ATANBgkqhkiG9w0BAQsFAAOCAgEAOrh8XfW6quwBAcCxHf6/uvu/iNq4yHCg2qH6 +VtWs/x38I2t3BRSNsLsJGieh6yLlZDzOus+XYui4uDE50XmcwaIsY0VcXnvdyZVZ +w9+lMyfp00kRF1o3B6eVxq0pRE5VB0cai7XI7tyfpRwGzA+oNLF4vBvxAHm9Ony5 +Q57DC/HFzyUogdkMYciO/kd9oa4HosDEXwaE8UvZUL8OVl/dptMXLL/GGwzZsUAE +1sLAbgm044YChLUDzgBAtDTkB/HNkcPzSKwULuskhe7ndoaEQNXVZuP7quGiZ/W1 +1lE59gnmnyG8ySFCL05jHrKLtFAJe88gQjgDK65ZJv4W/k7ocmT+HhCxWyQWcX6v +abJ0EssqeSQuzRMuZebMJJ8s46d6RcYuMdIX3RDXq+1moJDFopE7lgNrlRhWgaky +Og8f/u8s1j75tk1YaYcY9uBKjKk7f681R9wMumkd6IEmEvkUwHNFsctxi4fGI7h1 +PRdKL0DlhVmnpHlKs6Kvm2sJ3twSAGSrC4u0LuxACeR3XbiBfyhFV/291LSuw/y1 +JtWOW5koh0g1k9xtkiu3/ePVdG/CLp796IyRhdB1jP/vD7W5RLLG/VAlomfjsPsB +AnwFYbVZ8KrmMKYUpTJOH31CRzFdOB6nWqXu5tk3nOtLKo1nIOuVtmp9XLz3VtHe +NiZPnqA= +-----END CERTIFICATE----- diff --git a/vendor/github.com/nats-io/go-nats/test/configs/tls.conf b/vendor/github.com/nats-io/go-nats/test/configs/tls.conf new file mode 100644 index 00000000..02754ec8 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/configs/tls.conf @@ -0,0 +1,16 @@ +# Simple TLS config file + +port: 4443 +net: localhost # net interface + +tls { + cert_file: "./configs/certs/server.pem" + key_file: "./configs/certs/key.pem" + timeout: 2 +} + +authorization { + user: derek + password: buckley + timeout: 1 +} diff --git a/vendor/github.com/nats-io/go-nats/test/configs/tlsverify.conf b/vendor/github.com/nats-io/go-nats/test/configs/tlsverify.conf new file mode 100644 index 00000000..12c904c1 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/configs/tlsverify.conf @@ -0,0 +1,16 @@ +# Simple TLS config file + +port: 4443 +net: localhost + +tls { + cert_file: "./configs/certs/server.pem" + key_file: "./configs/certs/key.pem" + timeout: 2 + + # Optional certificate authority for clients + ca_file: "./configs/certs/ca.pem" + + # Require a client certificate + verify: true +} diff --git a/vendor/github.com/nats-io/go-nats/test/conn_test.go b/vendor/github.com/nats-io/go-nats/test/conn_test.go new file mode 100644 index 00000000..06c09e44 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/conn_test.go @@ -0,0 +1,2053 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "bufio" + "bytes" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "net" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/nats-io/gnatsd/server" + "github.com/nats-io/gnatsd/test" + "github.com/nats-io/go-nats" +) + +func TestDefaultConnection(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + nc.Close() +} + +func TestConnectionStatus(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + if nc.Status() != nats.CONNECTED { + t.Fatal("Should have status set to CONNECTED") + } + if !nc.IsConnected() { + t.Fatal("Should have status set to CONNECTED") + } + nc.Close() + if nc.Status() != nats.CLOSED { + t.Fatal("Should have status set to CLOSED") + } + if !nc.IsClosed() { + t.Fatal("Should have status set to CLOSED") + } +} + +func TestConnClosedCB(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ch := make(chan bool) + o := nats.GetDefaultOptions() + o.Url = nats.DefaultURL + o.ClosedCB = func(_ *nats.Conn) { + ch <- true + } + nc, err := o.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + nc.Close() + if e := Wait(ch); e != nil { + t.Fatalf("Closed callback not triggered\n") + } +} + +func TestCloseDisconnectedCB(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ch := make(chan bool) + o := nats.GetDefaultOptions() + o.Url = nats.DefaultURL + o.AllowReconnect = false + o.DisconnectedCB = func(_ *nats.Conn) { + ch <- true + } + nc, err := o.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + nc.Close() + if e := Wait(ch); e != nil { + t.Fatal("Disconnected callback not triggered") + } +} + +func TestServerStopDisconnectedCB(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ch := make(chan bool) + o := nats.GetDefaultOptions() + o.Url = nats.DefaultURL + o.AllowReconnect = false + o.DisconnectedCB = func(nc *nats.Conn) { + ch <- true + } + nc, err := o.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + s.Shutdown() + if e := Wait(ch); e != nil { + t.Fatalf("Disconnected callback not triggered\n") + } +} + +func TestServerSecureConnections(t *testing.T) { + s, opts := RunServerWithConfig("./configs/tls.conf") + defer s.Shutdown() + + endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) + secureURL := fmt.Sprintf("nats://%s:%s@%s/", opts.Username, opts.Password, endpoint) + + // Make sure this succeeds + nc, err := nats.Connect(secureURL, nats.Secure()) + if err != nil { + t.Fatalf("Failed to create secure (TLS) connection: %v", err) + } + defer nc.Close() + + omsg := []byte("Hello World") + checkRecv := make(chan bool) + + received := 0 + nc.Subscribe("foo", func(m *nats.Msg) { + received++ + if !bytes.Equal(m.Data, omsg) { + t.Fatal("Message received does not match") + } + checkRecv <- true + }) + err = nc.Publish("foo", omsg) + if err != nil { + t.Fatalf("Failed to publish on secure (TLS) connection: %v", err) + } + nc.Flush() + + if err := Wait(checkRecv); err != nil { + t.Fatal("Failed receiving message") + } + + nc.Close() + + // Server required, but not specified in Connect(), should switch automatically + nc, err = nats.Connect(secureURL) + if err != nil { + t.Fatalf("Failed to create secure (TLS) connection: %v", err) + } + nc.Close() + + // Test flag mismatch + // Wanted but not available.. + ds := RunDefaultServer() + defer ds.Shutdown() + + nc, err = nats.Connect(nats.DefaultURL, nats.Secure()) + if err == nil || nc != nil || err != nats.ErrSecureConnWanted { + if nc != nil { + nc.Close() + } + t.Fatalf("Should have failed to create connection: %v", err) + } + + // Let's be more TLS correct and verify servername, endpoint etc. + // Now do more advanced checking, verifying servername and using rootCA. + // Setup our own TLSConfig using RootCA from our self signed cert. + rootPEM, err := ioutil.ReadFile("./configs/certs/ca.pem") + if err != nil || rootPEM == nil { + t.Fatalf("failed to read root certificate") + } + pool := x509.NewCertPool() + ok := pool.AppendCertsFromPEM([]byte(rootPEM)) + if !ok { + t.Fatal("failed to parse root certificate") + } + + tls1 := &tls.Config{ + ServerName: opts.Host, + RootCAs: pool, + MinVersion: tls.VersionTLS12, + } + + nc, err = nats.Connect(secureURL, nats.Secure(tls1)) + if err != nil { + t.Fatalf("Got an error on Connect with Secure Options: %+v\n", err) + } + defer nc.Close() + + tls2 := &tls.Config{ + ServerName: "OtherHostName", + RootCAs: pool, + MinVersion: tls.VersionTLS12, + } + + nc2, err := nats.Connect(secureURL, nats.Secure(tls1, tls2)) + if err == nil { + nc2.Close() + t.Fatal("Was expecting an error!") + } +} + +func TestClientCertificate(t *testing.T) { + + s, opts := RunServerWithConfig("./configs/tlsverify.conf") + defer s.Shutdown() + + endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) + secureURL := fmt.Sprintf("nats://%s", endpoint) + + // Make sure this fails + nc, err := nats.Connect(secureURL, nats.Secure()) + if err == nil { + nc.Close() + t.Fatal("Should have failed (TLS) connection without client certificate") + } + + // Check parameters validity + nc, err = nats.Connect(secureURL, nats.ClientCert("", "")) + if err == nil { + nc.Close() + t.Fatal("Should have failed due to invalid parameters") + } + + // Should fail because wrong key + nc, err = nats.Connect(secureURL, + nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/key.pem")) + if err == nil { + nc.Close() + t.Fatal("Should have failed due to invalid key") + } + + // Should fail because no CA + nc, err = nats.Connect(secureURL, + nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem")) + if err == nil { + nc.Close() + t.Fatal("Should have failed due to missing ca") + } + + nc, err = nats.Connect(secureURL, + nats.RootCAs("./configs/certs/ca.pem"), + nats.ClientCert("./configs/certs/client-cert.pem", "./configs/certs/client-key.pem")) + if err != nil { + t.Fatalf("Failed to create (TLS) connection: %v", err) + } + defer nc.Close() + + omsg := []byte("Hello!") + checkRecv := make(chan bool) + + received := 0 + nc.Subscribe("foo", func(m *nats.Msg) { + received++ + if !bytes.Equal(m.Data, omsg) { + t.Fatal("Message received does not match") + } + checkRecv <- true + }) + err = nc.Publish("foo", omsg) + if err != nil { + t.Fatalf("Failed to publish on secure (TLS) connection: %v", err) + } + nc.Flush() + + if err := Wait(checkRecv); err != nil { + t.Fatal("Failed to receive message") + } +} + +func TestServerTLSHintConnections(t *testing.T) { + s, opts := RunServerWithConfig("./configs/tls.conf") + defer s.Shutdown() + + endpoint := fmt.Sprintf("%s:%d", opts.Host, opts.Port) + secureURL := fmt.Sprintf("tls://%s:%s@%s/", opts.Username, opts.Password, endpoint) + + nc, err := nats.Connect(secureURL, nats.RootCAs("./configs/certs/badca.pem")) + if err == nil { + nc.Close() + t.Fatal("Expected an error from bad RootCA file") + } + + nc, err = nats.Connect(secureURL, nats.RootCAs("./configs/certs/ca.pem")) + if err != nil { + t.Fatalf("Failed to create secure (TLS) connection: %v", err) + } + defer nc.Close() +} + +func TestClosedConnections(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, _ := nc.SubscribeSync("foo") + if sub == nil { + t.Fatal("Failed to create valid subscription") + } + + // Test all API endpoints do the right thing with a closed connection. + nc.Close() + if err := nc.Publish("foo", nil); err != nats.ErrConnectionClosed { + t.Fatalf("Publish on closed conn did not fail properly: %v\n", err) + } + if err := nc.PublishMsg(&nats.Msg{Subject: "foo"}); err != nats.ErrConnectionClosed { + t.Fatalf("PublishMsg on closed conn did not fail properly: %v\n", err) + } + if err := nc.Flush(); err != nats.ErrConnectionClosed { + t.Fatalf("Flush on closed conn did not fail properly: %v\n", err) + } + _, err := nc.Subscribe("foo", nil) + if err != nats.ErrConnectionClosed { + t.Fatalf("Subscribe on closed conn did not fail properly: %v\n", err) + } + _, err = nc.SubscribeSync("foo") + if err != nats.ErrConnectionClosed { + t.Fatalf("SubscribeSync on closed conn did not fail properly: %v\n", err) + } + _, err = nc.QueueSubscribe("foo", "bar", nil) + if err != nats.ErrConnectionClosed { + t.Fatalf("QueueSubscribe on closed conn did not fail properly: %v\n", err) + } + _, err = nc.Request("foo", []byte("help"), 10*time.Millisecond) + if err != nats.ErrConnectionClosed { + t.Fatalf("Request on closed conn did not fail properly: %v\n", err) + } + if _, err = sub.NextMsg(10); err != nats.ErrConnectionClosed { + t.Fatalf("NextMessage on closed conn did not fail properly: %v\n", err) + } + if err = sub.Unsubscribe(); err != nats.ErrConnectionClosed { + t.Fatalf("Unsubscribe on closed conn did not fail properly: %v\n", err) + } +} + +func TestErrOnConnectAndDeadlock(t *testing.T) { + // We will hand run a fake server that will timeout and not return a proper + // INFO proto. This is to test that we do not deadlock. Issue #18 + + l, e := net.Listen("tcp", ":0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + + go func() { + conn, err := l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + defer conn.Close() + // Send back a mal-formed INFO. + conn.Write([]byte("INFOZ \r\n")) + }() + + // Used to synchronize + ch := make(chan bool) + + go func() { + natsURL := fmt.Sprintf("nats://localhost:%d/", addr.Port) + nc, err := nats.Connect(natsURL) + if err == nil { + nc.Close() + t.Fatal("Expected bad INFO err, got none") + } + ch <- true + }() + + // Setup a timer to watch for deadlock + select { + case <-ch: + break + case <-time.After(time.Second): + t.Fatalf("Connect took too long, deadlock?") + } +} + +func TestMoreErrOnConnect(t *testing.T) { + l, e := net.Listen("tcp", "127.0.0.1:0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + + done := make(chan bool) + case1 := make(chan bool) + case2 := make(chan bool) + case3 := make(chan bool) + case4 := make(chan bool) + + go func() { + for i := 0; i < 5; i++ { + conn, err := l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + switch i { + case 0: + // Send back a partial INFO and close the connection. + conn.Write([]byte("INFO")) + case 1: + // Send just INFO + conn.Write([]byte("INFO\r\n")) + // Stick around a bit + <-case1 + case 2: + info := fmt.Sprintf("INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":1048576}\r\n", addr.IP, addr.Port) + // Send complete INFO + conn.Write([]byte(info)) + // Read connect and ping commands sent from the client + br := bufio.NewReaderSize(conn, 1024) + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected CONNECT from client, got: %s", err) + } + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected PING from client, got: %s", err) + } + // Client expect +OK, send it but then something else than PONG + conn.Write([]byte("+OK\r\n")) + // Stick around a bit + <-case2 + case 3: + info := fmt.Sprintf("INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":1048576}\r\n", addr.IP, addr.Port) + // Send complete INFO + conn.Write([]byte(info)) + // Read connect and ping commands sent from the client + br := bufio.NewReaderSize(conn, 1024) + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected CONNECT from client, got: %s", err) + } + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected PING from client, got: %s", err) + } + // Client expect +OK, send it but then something else than PONG + conn.Write([]byte("+OK\r\nXXX\r\n")) + // Stick around a bit + <-case3 + case 4: + info := fmt.Sprintf("INFO {'x'}\r\n") + // Send INFO with JSON marshall error + conn.Write([]byte(info)) + // Stick around a bit + <-case4 + } + + conn.Close() + } + + // Hang around until asked to quit + <-done + }() + + natsURL := fmt.Sprintf("nats://localhost:%d", addr.Port) + + if nc, err := nats.Connect(natsURL, nats.Timeout(20*time.Millisecond)); err == nil { + nc.Close() + t.Fatal("Expected error, got none") + } + + if nc, err := nats.Connect(natsURL, nats.Timeout(20*time.Millisecond)); err == nil { + close(case1) + nc.Close() + t.Fatal("Expected error, got none") + } + + close(case1) + + opts := nats.GetDefaultOptions() + opts.Servers = []string{natsURL} + opts.Timeout = 20 * time.Millisecond + opts.Verbose = true + + if nc, err := opts.Connect(); err == nil { + close(case2) + nc.Close() + t.Fatal("Expected error, got none") + } + + close(case2) + + if nc, err := opts.Connect(); err == nil { + close(case3) + nc.Close() + t.Fatal("Expected error, got none") + } + + close(case3) + + if nc, err := opts.Connect(); err == nil { + close(case4) + nc.Close() + t.Fatal("Expected error, got none") + } + + close(case4) + + close(done) +} + +func TestErrOnMaxPayloadLimit(t *testing.T) { + expectedMaxPayload := int64(10) + serverInfo := "INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":%d}\r\n" + + l, e := net.Listen("tcp", "127.0.0.1:0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + + // Send back an INFO message with custom max payload size on connect. + var conn net.Conn + var err error + + go func() { + conn, err = l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + defer conn.Close() + info := fmt.Sprintf(serverInfo, addr.IP, addr.Port, expectedMaxPayload) + conn.Write([]byte(info)) + + // Read connect and ping commands sent from the client + line := make([]byte, 111) + _, err := conn.Read(line) + if err != nil { + t.Fatalf("Expected CONNECT and PING from client, got: %s", err) + } + conn.Write([]byte("PONG\r\n")) + // Hang around a bit to not err on EOF in client. + time.Sleep(250 * time.Millisecond) + }() + + // Wait for server mock to start + time.Sleep(100 * time.Millisecond) + + natsURL := fmt.Sprintf("nats://%s:%d", addr.IP, addr.Port) + opts := nats.GetDefaultOptions() + opts.Servers = []string{natsURL} + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected INFO message with custom max payload, got: %s", err) + } + defer nc.Close() + + got := nc.MaxPayload() + if got != expectedMaxPayload { + t.Fatalf("Expected MaxPayload to be %d, got: %d", expectedMaxPayload, got) + } + err = nc.Publish("hello", []byte("hello world")) + if err != nats.ErrMaxPayload { + t.Fatalf("Expected to fail trying to send more than max payload, got: %s", err) + } + err = nc.Publish("hello", []byte("a")) + if err != nil { + t.Fatalf("Expected to succeed trying to send less than max payload, got: %s", err) + } +} + +func TestConnectVerbose(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + o := nats.GetDefaultOptions() + o.Verbose = true + + nc, err := o.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + nc.Close() +} + +func getStacks(all bool) string { + var ( + stacks []byte + stacksSize = 10000 + n int + ) + for { + stacks = make([]byte, stacksSize) + n = runtime.Stack(stacks, all) + if n == stacksSize { + stacksSize *= 2 + continue + } + break + } + return string(stacks[:n]) +} + +func isRunningInAsyncCBDispatcher() error { + strStacks := getStacks(false) + if strings.Contains(strStacks, "asyncCBDispatcher") { + return nil + } + return fmt.Errorf("callback not executed from dispatcher:\n %s", strStacks) +} + +func isAsyncDispatcherRunning() bool { + strStacks := getStacks(true) + return strings.Contains(strStacks, "asyncCBDispatcher") +} + +func TestCallbacksOrder(t *testing.T) { + authS, authSOpts := RunServerWithConfig("./configs/tls.conf") + defer authS.Shutdown() + + s := RunDefaultServer() + defer s.Shutdown() + + firstDisconnect := true + dtime1 := time.Time{} + dtime2 := time.Time{} + rtime := time.Time{} + atime1 := time.Time{} + atime2 := time.Time{} + ctime := time.Time{} + + cbErrors := make(chan error, 20) + + reconnected := make(chan bool) + closed := make(chan bool) + asyncErr := make(chan bool, 2) + recvCh := make(chan bool, 2) + recvCh1 := make(chan bool) + recvCh2 := make(chan bool) + + dch := func(nc *nats.Conn) { + if err := isRunningInAsyncCBDispatcher(); err != nil { + cbErrors <- err + return + } + time.Sleep(100 * time.Millisecond) + if firstDisconnect { + firstDisconnect = false + dtime1 = time.Now() + } else { + dtime2 = time.Now() + } + } + + rch := func(nc *nats.Conn) { + if err := isRunningInAsyncCBDispatcher(); err != nil { + cbErrors <- err + reconnected <- true + return + } + time.Sleep(50 * time.Millisecond) + rtime = time.Now() + reconnected <- true + } + + ech := func(nc *nats.Conn, sub *nats.Subscription, err error) { + if err := isRunningInAsyncCBDispatcher(); err != nil { + cbErrors <- err + asyncErr <- true + return + } + if sub.Subject == "foo" { + time.Sleep(20 * time.Millisecond) + atime1 = time.Now() + } else { + atime2 = time.Now() + } + asyncErr <- true + } + + cch := func(nc *nats.Conn) { + if err := isRunningInAsyncCBDispatcher(); err != nil { + cbErrors <- err + closed <- true + return + } + ctime = time.Now() + closed <- true + } + + url := net.JoinHostPort(authSOpts.Host, strconv.Itoa(authSOpts.Port)) + url = "nats://" + url + "," + nats.DefaultURL + + nc, err := nats.Connect(url, + nats.DisconnectHandler(dch), + nats.ReconnectHandler(rch), + nats.ClosedHandler(cch), + nats.ErrorHandler(ech), + nats.ReconnectWait(50*time.Millisecond), + nats.DontRandomize()) + + if err != nil { + t.Fatalf("Unable to connect: %v\n", err) + } + defer nc.Close() + + ncp, err := nats.Connect(nats.DefaultURL, + nats.ReconnectWait(50*time.Millisecond)) + if err != nil { + t.Fatalf("Unable to connect: %v\n", err) + } + defer ncp.Close() + + // Wait to make sure that if we have closed (incorrectly) the + // asyncCBDispatcher during the connect process, this is caught here. + time.Sleep(time.Second) + + s.Shutdown() + + s = RunDefaultServer() + defer s.Shutdown() + + if err := Wait(reconnected); err != nil { + t.Fatal("Did not get the reconnected callback") + } + + var sub1 *nats.Subscription + var sub2 *nats.Subscription + + recv := func(m *nats.Msg) { + // Signal that one message is received + recvCh <- true + + // We will now block + if m.Subject == "foo" { + <-recvCh1 + } else { + <-recvCh2 + } + m.Sub.Unsubscribe() + } + + sub1, err = nc.Subscribe("foo", recv) + if err != nil { + t.Fatalf("Unable to create subscription: %v\n", err) + } + sub1.SetPendingLimits(1, 100000) + + sub2, err = nc.Subscribe("bar", recv) + if err != nil { + t.Fatalf("Unable to create subscription: %v\n", err) + } + sub2.SetPendingLimits(1, 100000) + + nc.Flush() + + ncp.Publish("foo", []byte("test")) + ncp.Publish("bar", []byte("test")) + ncp.Flush() + + // Wait notification that message were received + err = Wait(recvCh) + if err == nil { + err = Wait(recvCh) + } + if err != nil { + t.Fatal("Did not receive message") + } + + for i := 0; i < 2; i++ { + ncp.Publish("foo", []byte("test")) + ncp.Publish("bar", []byte("test")) + } + ncp.Flush() + + if err := Wait(asyncErr); err != nil { + t.Fatal("Did not get the async callback") + } + if err := Wait(asyncErr); err != nil { + t.Fatal("Did not get the async callback") + } + + close(recvCh1) + close(recvCh2) + + nc.Close() + + if err := Wait(closed); err != nil { + t.Fatal("Did not get the close callback") + } + + if len(cbErrors) > 0 { + t.Fatalf("%v", <-cbErrors) + } + + if (dtime1 == time.Time{}) || (dtime2 == time.Time{}) || (rtime == time.Time{}) || (atime1 == time.Time{}) || (atime2 == time.Time{}) || (ctime == time.Time{}) { + t.Fatalf("Some callbacks did not fire:\n%v\n%v\n%v\n%v\n%v\n%v", dtime1, rtime, atime1, atime2, dtime2, ctime) + } + + if rtime.Before(dtime1) || dtime2.Before(rtime) || atime2.Before(atime1) || ctime.Before(atime2) { + t.Fatalf("Wrong callback order:\n%v\n%v\n%v\n%v\n%v\n%v", dtime1, rtime, atime1, atime2, dtime2, ctime) + } + + // Close the other connection + ncp.Close() + + // Check that the go routine is gone. Allow plenty of time + // to avoid flappers. + timeout := time.Now().Add(5 * time.Second) + for time.Now().Before(timeout) { + if !isAsyncDispatcherRunning() { + // Good, we are done! + return + } + time.Sleep(50 * time.Millisecond) + } + t.Fatalf("The async callback dispatcher(s) should have stopped") +} + +func TestFlushReleaseOnClose(t *testing.T) { + serverInfo := "INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":1048576}\r\n" + + l, e := net.Listen("tcp", "127.0.0.1:0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + done := make(chan bool) + + go func() { + conn, err := l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + defer conn.Close() + info := fmt.Sprintf(serverInfo, addr.IP, addr.Port) + conn.Write([]byte(info)) + + // Read connect and ping commands sent from the client + br := bufio.NewReaderSize(conn, 1024) + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected CONNECT from client, got: %s", err) + } + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected PING from client, got: %s", err) + } + conn.Write([]byte("PONG\r\n")) + + // Hang around until asked to quit + <-done + }() + + // Wait for server mock to start + time.Sleep(100 * time.Millisecond) + + natsURL := fmt.Sprintf("nats://%s:%d", addr.IP, addr.Port) + opts := nats.GetDefaultOptions() + opts.AllowReconnect = false + opts.Servers = []string{natsURL} + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected INFO message with custom max payload, got: %s", err) + } + defer nc.Close() + + // First try a FlushTimeout() and make sure we timeout + if err := nc.FlushTimeout(50 * time.Millisecond); err == nil || err != nats.ErrTimeout { + t.Fatalf("Expected a timeout error, got: %v", err) + } + + go func() { + time.Sleep(50 * time.Millisecond) + nc.Close() + }() + + if err := nc.Flush(); err == nil { + t.Fatal("Expected error on Flush() released by Close()") + } + + close(done) +} + +func TestMaxPendingOut(t *testing.T) { + serverInfo := "INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":1048576}\r\n" + + l, e := net.Listen("tcp", "127.0.0.1:0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + done := make(chan bool) + cch := make(chan bool) + + go func() { + conn, err := l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + defer conn.Close() + info := fmt.Sprintf(serverInfo, addr.IP, addr.Port) + conn.Write([]byte(info)) + + // Read connect and ping commands sent from the client + br := bufio.NewReaderSize(conn, 1024) + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected CONNECT from client, got: %s", err) + } + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected PING from client, got: %s", err) + } + conn.Write([]byte("PONG\r\n")) + + // Hang around until asked to quit + <-done + }() + + // Wait for server mock to start + time.Sleep(100 * time.Millisecond) + + natsURL := fmt.Sprintf("nats://%s:%d", addr.IP, addr.Port) + opts := nats.GetDefaultOptions() + opts.PingInterval = 20 * time.Millisecond + opts.MaxPingsOut = 2 + opts.AllowReconnect = false + opts.ClosedCB = func(_ *nats.Conn) { cch <- true } + opts.Servers = []string{natsURL} + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected INFO message with custom max payload, got: %s", err) + } + defer nc.Close() + + // After 60 ms, we should have closed the connection + time.Sleep(100 * time.Millisecond) + + if err := Wait(cch); err != nil { + t.Fatal("Failed to get ClosedCB") + } + if nc.LastError() != nats.ErrStaleConnection { + t.Fatalf("Expected to get %v, got %v", nats.ErrStaleConnection, nc.LastError()) + } + + close(done) +} + +func TestErrInReadLoop(t *testing.T) { + serverInfo := "INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":1048576}\r\n" + + l, e := net.Listen("tcp", "127.0.0.1:0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + done := make(chan bool) + cch := make(chan bool) + + go func() { + conn, err := l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + defer conn.Close() + info := fmt.Sprintf(serverInfo, addr.IP, addr.Port) + conn.Write([]byte(info)) + + // Read connect and ping commands sent from the client + br := bufio.NewReaderSize(conn, 1024) + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected CONNECT from client, got: %s", err) + } + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected PING from client, got: %s", err) + } + conn.Write([]byte("PONG\r\n")) + + // Read (and ignore) the SUB from the client + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected SUB from client, got: %s", err) + } + + // Send something that should make the subscriber fail. + conn.Write([]byte("Ivan")) + + // Hang around until asked to quit + <-done + }() + + // Wait for server mock to start + time.Sleep(100 * time.Millisecond) + + natsURL := fmt.Sprintf("nats://%s:%d", addr.IP, addr.Port) + opts := nats.GetDefaultOptions() + opts.AllowReconnect = false + opts.ClosedCB = func(_ *nats.Conn) { cch <- true } + opts.Servers = []string{natsURL} + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected INFO message with custom max payload, got: %s", err) + } + defer nc.Close() + + received := int64(0) + + nc.Subscribe("foo", func(_ *nats.Msg) { + atomic.AddInt64(&received, 1) + }) + + if err := Wait(cch); err != nil { + t.Fatal("Failed to get ClosedCB") + } + + recv := int(atomic.LoadInt64(&received)) + if recv != 0 { + t.Fatalf("Should not have received messages, got: %d", recv) + } + + close(done) +} + +func TestErrStaleConnection(t *testing.T) { + serverInfo := "INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":1048576}\r\n" + + l, e := net.Listen("tcp", "127.0.0.1:0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + done := make(chan bool) + dch := make(chan bool) + rch := make(chan bool) + cch := make(chan bool) + sch := make(chan bool) + + firstDisconnect := true + + go func() { + for i := 0; i < 2; i++ { + conn, err := l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + defer conn.Close() + info := fmt.Sprintf(serverInfo, addr.IP, addr.Port) + conn.Write([]byte(info)) + + // Read connect and ping commands sent from the client + br := bufio.NewReaderSize(conn, 1024) + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected CONNECT from client, got: %s", err) + } + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected PING from client, got: %s", err) + } + conn.Write([]byte("PONG\r\n")) + + if i == 0 { + // Wait a tiny, and simulate a Stale Connection + time.Sleep(50 * time.Millisecond) + conn.Write([]byte("-ERR 'Stale Connection'\r\n")) + + // The client should try to reconnect. When getting the + // disconnected callback, it will close this channel. + <-sch + + // Close the connection and go back to accept the new + // connection. + conn.Close() + } else { + // Hang around a bit + <-done + } + } + }() + + // Wait for server mock to start + time.Sleep(100 * time.Millisecond) + + natsURL := fmt.Sprintf("nats://%s:%d", addr.IP, addr.Port) + opts := nats.GetDefaultOptions() + opts.AllowReconnect = true + opts.DisconnectedCB = func(_ *nats.Conn) { + // Interested only in the first disconnect cb + if firstDisconnect { + firstDisconnect = false + close(sch) + dch <- true + } + } + opts.ReconnectedCB = func(_ *nats.Conn) { rch <- true } + opts.ClosedCB = func(_ *nats.Conn) { cch <- true } + opts.ReconnectWait = 20 * time.Millisecond + opts.MaxReconnect = 100 + opts.Servers = []string{natsURL} + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected INFO message with custom max payload, got: %s", err) + } + defer nc.Close() + + // We should first gets disconnected + if err := Wait(dch); err != nil { + t.Fatal("Failed to get DisconnectedCB") + } + + // Then reconneted.. + if err := Wait(rch); err != nil { + t.Fatal("Failed to get ReconnectedCB") + } + + // Now close the connection + nc.Close() + + // We should get the closed cb + if err := Wait(cch); err != nil { + t.Fatal("Failed to get ClosedCB") + } + + close(done) +} + +func TestServerErrorClosesConnection(t *testing.T) { + serverInfo := "INFO {\"server_id\":\"foobar\",\"host\":\"%s\",\"port\":%d,\"auth_required\":false,\"tls_required\":false,\"max_payload\":1048576}\r\n" + + l, e := net.Listen("tcp", "127.0.0.1:0") + if e != nil { + t.Fatal("Could not listen on an ephemeral port") + } + tl := l.(*net.TCPListener) + defer tl.Close() + + addr := tl.Addr().(*net.TCPAddr) + done := make(chan bool) + dch := make(chan bool) + cch := make(chan bool) + + serverSentError := "Any Error" + reconnected := int64(0) + + go func() { + conn, err := l.Accept() + if err != nil { + t.Fatalf("Error accepting client connection: %v\n", err) + } + defer conn.Close() + info := fmt.Sprintf(serverInfo, addr.IP, addr.Port) + conn.Write([]byte(info)) + + // Read connect and ping commands sent from the client + br := bufio.NewReaderSize(conn, 1024) + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected CONNECT from client, got: %s", err) + } + if _, err := br.ReadString('\n'); err != nil { + t.Fatalf("Expected PING from client, got: %s", err) + } + conn.Write([]byte("PONG\r\n")) + + // Wait a tiny, and simulate a Stale Connection + time.Sleep(50 * time.Millisecond) + conn.Write([]byte("-ERR '" + serverSentError + "'\r\n")) + + // Hang around a bit + <-done + }() + + // Wait for server mock to start + time.Sleep(100 * time.Millisecond) + + natsURL := fmt.Sprintf("nats://%s:%d", addr.IP, addr.Port) + opts := nats.GetDefaultOptions() + opts.AllowReconnect = true + opts.DisconnectedCB = func(_ *nats.Conn) { dch <- true } + opts.ReconnectedCB = func(_ *nats.Conn) { atomic.AddInt64(&reconnected, 1) } + opts.ClosedCB = func(_ *nats.Conn) { cch <- true } + opts.ReconnectWait = 20 * time.Millisecond + opts.MaxReconnect = 100 + opts.Servers = []string{natsURL} + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Expected INFO message with custom max payload, got: %s", err) + } + defer nc.Close() + + // The server sends an error that should cause the client to simply close + // the connection. + + // We should first gets disconnected + if err := Wait(dch); err != nil { + t.Fatal("Failed to get DisconnectedCB") + } + + // We should get the closed cb + if err := Wait(cch); err != nil { + t.Fatal("Failed to get ClosedCB") + } + + // We should not have been reconnected + if atomic.LoadInt64(&reconnected) != 0 { + t.Fatal("ReconnectedCB should not have been invoked") + } + + // Check LastError(), it should be "nats: " + lastErr := nc.LastError().Error() + expectedErr := "nats: " + strings.ToLower(serverSentError) + if lastErr != expectedErr { + t.Fatalf("Expected error: '%v', got '%v'", expectedErr, lastErr) + } + + close(done) +} + +func TestUseDefaultTimeout(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + opts := &nats.Options{ + Servers: []string{nats.DefaultURL}, + } + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Unexpected error on connect: %v", err) + } + defer nc.Close() + if nc.Opts.Timeout != nats.DefaultTimeout { + t.Fatalf("Expected Timeout to be set to %v, got %v", nats.DefaultTimeout, nc.Opts.Timeout) + } +} + +func TestNoRaceOnLastError(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + // Access LastError in disconnection and closed handlers to make sure + // that there is no race. It is possible in some cases that + // nc.LastError() returns a non nil error. We don't care here about the + // returned value. + dch := func(c *nats.Conn) { + c.LastError() + } + closedCh := make(chan struct{}) + cch := func(c *nats.Conn) { + c.LastError() + closedCh <- struct{}{} + } + nc, err := nats.Connect(nats.DefaultURL, + nats.DisconnectHandler(dch), + nats.ClosedHandler(cch), + nats.MaxReconnects(-1), + nats.ReconnectWait(5*time.Millisecond)) + if err != nil { + t.Fatalf("Unable to connect: %v\n", err) + } + defer nc.Close() + + // Restart the server several times to trigger a reconnection. + for i := 0; i < 10; i++ { + s.Shutdown() + time.Sleep(10 * time.Millisecond) + s = RunDefaultServer() + } + nc.Close() + s.Shutdown() + select { + case <-closedCh: + case <-time.After(5 * time.Second): + t.Fatal("Timeout waiting for the closed callback") + } +} + +type customDialer struct { + ch chan bool +} + +func (cd *customDialer) Dial(network, address string) (net.Conn, error) { + cd.ch <- true + return nil, fmt.Errorf("on purpose") +} + +func TestUseCustomDialer(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + dialer := &net.Dialer{ + Timeout: 10 * time.Second, + DualStack: true, + } + opts := &nats.Options{ + Servers: []string{nats.DefaultURL}, + Dialer: dialer, + } + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Unexpected error on connect: %v", err) + } + defer nc.Close() + if nc.Opts.Dialer != dialer { + t.Fatalf("Expected Dialer to be set to %v, got %v", dialer, nc.Opts.Dialer) + } + + // Should be possible to set via variadic func based Option setter + dialer2 := &net.Dialer{ + Timeout: 5 * time.Second, + DualStack: true, + } + nc2, err := nats.Connect(nats.DefaultURL, nats.Dialer(dialer2)) + if err != nil { + t.Fatalf("Unexpected error on connect: %v", err) + } + defer nc2.Close() + if !nc2.Opts.Dialer.DualStack { + t.Fatalf("Expected for dialer to be customized to use dual stack support") + } + + // By default, dialer still uses the DefaultTimeout + nc3, err := nats.Connect(nats.DefaultURL) + if err != nil { + t.Fatalf("Unexpected error on connect: %v", err) + } + defer nc3.Close() + if nc3.Opts.Dialer.Timeout != nats.DefaultTimeout { + t.Fatalf("Expected DialTimeout to be set to %v, got %v", nats.DefaultTimeout, nc.Opts.Dialer.Timeout) + } + + // Create custom dialer that return error on Dial(). + cdialer := &customDialer{ch: make(chan bool, 1)} + + // When both Dialer and CustomDialer are set, CustomDialer + // should take precedence. That means that the connection + // should fail for these two set of options. + options := []*nats.Options{ + {Dialer: dialer, CustomDialer: cdialer}, + {CustomDialer: cdialer}, + } + for _, o := range options { + o.Servers = []string{nats.DefaultURL} + nc, err := o.Connect() + // As of now, Connect() would not return the actual dialer error, + // instead it returns "no server available for connections". + // So use go channel to ensure that custom dialer's Dial() method + // was invoked. + if err == nil { + if nc != nil { + nc.Close() + } + t.Fatal("Expected error, got none") + } + if err := Wait(cdialer.ch); err != nil { + t.Fatal("Did not get our notification") + } + } + // Same with variadic + foptions := [][]nats.Option{ + {nats.Dialer(dialer), nats.SetCustomDialer(cdialer)}, + {nats.SetCustomDialer(cdialer)}, + } + for _, fos := range foptions { + nc, err := nats.Connect(nats.DefaultURL, fos...) + if err == nil { + if nc != nil { + nc.Close() + } + t.Fatal("Expected error, got none") + } + if err := Wait(cdialer.ch); err != nil { + t.Fatal("Did not get our notification") + } + } +} + +func TestDefaultOptionsDialer(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + opts1 := nats.DefaultOptions + opts2 := nats.DefaultOptions + + nc1, err := opts1.Connect() + if err != nil { + t.Fatalf("Unexpected error on connect: %v", err) + } + defer nc1.Close() + + nc2, err := opts2.Connect() + if err != nil { + t.Fatalf("Unexpected error on connect: %v", err) + } + defer nc2.Close() + + if nc1.Opts.Dialer == nc2.Opts.Dialer { + t.Fatalf("Expected each connection to have its own dialer") + } +} + +func TestCustomFlusherTimeout(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + opts := &nats.Options{ + Servers: []string{nats.DefaultURL}, + + // Reasonably large flusher timeout will not induce errors + // when we can flush fast + FlusherTimeout: 10 * time.Second, + } + nc1, err := opts.Connect() + if err != nil { + t.Fatalf("Expected to be able to connect, got: %s", err) + } + doneCh := make(chan struct{}) + payload := "" + for i := 0; i < 8192; i++ { + payload += "A" + } + payloadBytes := []byte(payload) + + go func() { + for { + select { + case <-time.After(200 * time.Millisecond): + err := nc1.Publish("hello", payloadBytes) + if err != nil { + t.Errorf("Error during publish: %s", err) + } + case <-time.After(5 * time.Second): + t.Errorf("Timeout publishing messages") + return + case <-doneCh: + return + } + } + }() + defer nc1.Close() + + opts = &nats.Options{ + Servers: []string{nats.DefaultURL}, + + // Use short flusher timeout to trigger the error + FlusherTimeout: 1 * time.Microsecond, + + // Upon failure to be able to exercice ping pong interval + // then we will hit this timeout and disconnect + PingInterval: 500 * time.Millisecond, + } + + opts.DisconnectedCB = func(nc *nats.Conn) { + // Ping loops that test is done + doneCh <- struct{}{} + } + + nc2, err := opts.Connect() + if err != nil { + t.Fatalf("Expected to be able to connect, got: %s", err) + } + defer nc2.Close() + + // Consume messages to make the reading loop work + _, err = nc2.Subscribe(">", func(_ *nats.Msg) {}) + if err != nil { + t.Fatalf("Expected to be able to create subscription, got: %s", err) + } + + for { + select { + case <-time.After(100 * time.Millisecond): + // Some of the publishes will succeed and others fail with i/o timeout error + // but eventually ping interval will fail and close the connection. + err = nc2.Publish("world", payloadBytes) + if err == nats.ErrConnectionClosed { + return + } + case <-time.After(5 * time.Second): + t.Errorf("Timeout publishing messages") + return + } + } +} + +func TestNewServers(t *testing.T) { + s1Opts := test.DefaultTestOptions + s1Opts.Host = "127.0.0.1" + s1Opts.Port = 4222 + s1Opts.Cluster.Host = "localhost" + s1Opts.Cluster.Port = 6222 + s1 := test.RunServer(&s1Opts) + defer s1.Shutdown() + + s2Opts := test.DefaultTestOptions + s2Opts.Host = "127.0.0.1" + s2Opts.Port = 4223 + s2Opts.Port = s1Opts.Port + 1 + s2Opts.Cluster.Host = "localhost" + s2Opts.Cluster.Port = 6223 + s2Opts.Routes = server.RoutesFromStr("nats://localhost:6222") + s2 := test.RunServer(&s2Opts) + defer s2.Shutdown() + + ch := make(chan bool) + cb := func(_ *nats.Conn) { + ch <- true + } + url := fmt.Sprintf("nats://%s:%d", s1Opts.Host, s1Opts.Port) + nc1, err := nats.Connect(url, nats.DiscoveredServersHandler(cb)) + if err != nil { + t.Fatalf("Error on connect: %v", err) + } + defer nc1.Close() + + nc2, err := nats.Connect(url) + if err != nil { + t.Fatalf("Error on connect: %v", err) + } + defer nc2.Close() + nc2.SetDiscoveredServersHandler(cb) + + opts := nats.GetDefaultOptions() + opts.Url = nats.DefaultURL + opts.DiscoveredServersCB = cb + nc3, err := opts.Connect() + if err != nil { + t.Fatalf("Error on connect: %v", err) + } + defer nc3.Close() + + // Make sure that handler is not invoked on initial connect. + select { + case <-ch: + t.Fatalf("Handler should not have been invoked") + case <-time.After(500 * time.Millisecond): + } + + // Start a new server. + s3Opts := test.DefaultTestOptions + s1Opts.Host = "127.0.0.1" + s1Opts.Port = 4224 + s3Opts.Port = s2Opts.Port + 1 + s3Opts.Cluster.Host = "localhost" + s3Opts.Cluster.Port = 6224 + s3Opts.Routes = server.RoutesFromStr("nats://localhost:6222") + s3 := test.RunServer(&s3Opts) + defer s3.Shutdown() + + // The callbacks should have been invoked + if err := Wait(ch); err != nil { + t.Fatal("Did not get our callback") + } + if err := Wait(ch); err != nil { + t.Fatal("Did not get our callback") + } + if err := Wait(ch); err != nil { + t.Fatal("Did not get our callback") + } +} + +func TestBarrier(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + pubMsgs := int32(0) + ch := make(chan bool, 1) + + sub1, err := nc.Subscribe("pub", func(_ *nats.Msg) { + atomic.AddInt32(&pubMsgs, 1) + time.Sleep(250 * time.Millisecond) + }) + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + + sub2, err := nc.Subscribe("close", func(_ *nats.Msg) { + // The "close" message was sent/received lat, but + // because we are dealing with different subscriptions, + // which are dispatched by different dispatchers, and + // because the "pub" subscription is delayed, this + // callback is likely to be invoked before the sub1's + // second callback is invoked. Using the Barrier call + // here will ensure that the given function will be invoked + // after the preceding messages have been dispatched. + nc.Barrier(func() { + res := atomic.LoadInt32(&pubMsgs) == 2 + ch <- res + }) + }) + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + + // Send 2 "pub" messages followed by a "close" message + for i := 0; i < 2; i++ { + if err := nc.Publish("pub", []byte("pub msg")); err != nil { + t.Fatalf("Error on publish: %v", err) + } + } + if err := nc.Publish("close", []byte("closing")); err != nil { + t.Fatalf("Error on publish: %v", err) + } + + select { + case ok := <-ch: + if !ok { + t.Fatal("The barrier function was invoked before the second message") + } + case <-time.After(2 * time.Second): + t.Fatal("Waited for too long...") + } + + // Remove all subs + sub1.Unsubscribe() + sub2.Unsubscribe() + + // Barrier should be invoked in place. Since we use buffered channel + // we are ok. + nc.Barrier(func() { ch <- true }) + if err := Wait(ch); err != nil { + t.Fatal("Barrier function was not invoked") + } + + if _, err := nc.Subscribe("foo", func(m *nats.Msg) { + // To check that the Barrier() function works if the subscription + // is unsubscribed after the call was made, sleep a bit here. + time.Sleep(250 * time.Millisecond) + m.Sub.Unsubscribe() + }); err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + if err := nc.Publish("foo", []byte("hello")); err != nil { + t.Fatalf("Error on publish: %v", err) + } + // We need to Flush here to make sure that message has been received + // and posted to subscription's internal queue before calling Barrier. + if err := nc.Flush(); err != nil { + t.Fatalf("Error on flush: %v", err) + } + nc.Barrier(func() { ch <- true }) + if err := Wait(ch); err != nil { + t.Fatal("Barrier function was not invoked") + } + + // Test with AutoUnsubscribe now... + sub1, err = nc.Subscribe("foo", func(m *nats.Msg) { + // Since we auto-unsubscribe with 1, there should not be another + // invocation of this callback, but the Barrier should still be + // invoked. + nc.Barrier(func() { ch <- true }) + + }) + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + sub1.AutoUnsubscribe(1) + // Send 2 messages and flush + for i := 0; i < 2; i++ { + if err := nc.Publish("foo", []byte("hello")); err != nil { + t.Fatalf("Error on publish: %v", err) + } + } + if err := nc.Flush(); err != nil { + t.Fatalf("Error on flush: %v", err) + } + // Check barrier was invoked + if err := Wait(ch); err != nil { + t.Fatal("Barrier function was not invoked") + } + + // Check that Barrier only affects asynchronous subscriptions + sub1, err = nc.Subscribe("foo", func(m *nats.Msg) { + nc.Barrier(func() { ch <- true }) + }) + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + syncSub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + msgChan := make(chan *nats.Msg, 1) + chanSub, err := nc.ChanSubscribe("foo", msgChan) + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + if err := nc.Publish("foo", []byte("hello")); err != nil { + t.Fatalf("Error on publish: %v", err) + } + if err := nc.Flush(); err != nil { + t.Fatalf("Error on flush: %v", err) + } + // Check barrier was invoked even if we did not yet consume + // from the 2 other type of subscriptions + if err := Wait(ch); err != nil { + t.Fatal("Barrier function was not invoked") + } + if _, err := syncSub.NextMsg(time.Second); err != nil { + t.Fatalf("Sync sub did not receive the message") + } + select { + case <-msgChan: + case <-time.After(time.Second): + t.Fatal("Chan sub did not receive the message") + } + chanSub.Unsubscribe() + syncSub.Unsubscribe() + sub1.Unsubscribe() + + atomic.StoreInt32(&pubMsgs, 0) + // Check barrier does not prevent new messages to be delivered. + sub1, err = nc.Subscribe("foo", func(_ *nats.Msg) { + if pm := atomic.AddInt32(&pubMsgs, 1); pm == 1 { + nc.Barrier(func() { + nc.Publish("foo", []byte("second")) + nc.Flush() + }) + } else if pm == 2 { + ch <- true + } + }) + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + if err := nc.Publish("foo", []byte("first")); err != nil { + t.Fatalf("Error on publish: %v", err) + } + if err := Wait(ch); err != nil { + t.Fatal("Barrier function was not invoked") + } + sub1.Unsubscribe() + + // Check that barrier works if called before connection + // is closed. + if _, err := nc.Subscribe("bar", func(_ *nats.Msg) { + nc.Barrier(func() { ch <- true }) + nc.Close() + }); err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + if err := nc.Publish("bar", []byte("hello")); err != nil { + t.Fatalf("Error on publish: %v", err) + } + // This could fail if the connection is closed before we get + // here. + nc.Flush() + if err := Wait(ch); err != nil { + t.Fatal("Barrier function was not invoked") + } + + // Finally, check that if connection is closed, Barrier returns + // an error. + if err := nc.Barrier(func() { ch <- true }); err != nats.ErrConnectionClosed { + t.Fatalf("Expected error %v, got %v", nats.ErrConnectionClosed, err) + } + + // Check that one can call connection methods from Barrier + // when there is no async subscriptions + nc = NewDefaultConnection(t) + defer nc.Close() + + if err := nc.Barrier(func() { + ch <- nc.TLSRequired() + }); err != nil { + t.Fatalf("Error on Barrier: %v", err) + } + if err := Wait(ch); err != nil { + t.Fatal("Barrier was blocked") + } +} + +func TestReceiveInfoRightAfterFirstPong(t *testing.T) { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Error on listen: %v", err) + } + tl := l.(*net.TCPListener) + defer tl.Close() + addr := tl.Addr().(*net.TCPAddr) + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + + c, err := tl.Accept() + if err != nil { + return + } + defer c.Close() + // Send the initial INFO + c.Write([]byte("INFO {}\r\n")) + buf := make([]byte, 0, 100) + b := make([]byte, 100) + for { + n, err := c.Read(b) + if err != nil { + return + } + buf = append(buf, b[:n]...) + if bytes.Contains(buf, []byte("PING\r\n")) { + break + } + } + // Send PONG and following INFO in one go (or at least try). + // The processing of PONG in sendConnect() should leave the + // rest for the readLoop to process. + c.Write([]byte(fmt.Sprintf("PONG\r\nINFO {\"connect_urls\":[\"127.0.0.1:%d\", \"me:1\"]}\r\n", addr.Port))) + // Wait for client to disconnect + for { + if _, err := c.Read(buf); err != nil { + return + } + } + }() + + nc, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", addr.Port)) + if err != nil { + t.Fatalf("Error on connect: %v", err) + } + defer nc.Close() + var ( + ds []string + timeout = time.Now().Add(2 * time.Second) + ok = false + ) + for time.Now().Before(timeout) { + ds = nc.DiscoveredServers() + if len(ds) == 1 && ds[0] == "nats://me:1" { + ok = true + break + } + time.Sleep(50 * time.Millisecond) + } + nc.Close() + wg.Wait() + if !ok { + t.Fatalf("Unexpected discovered servers: %v", ds) + } +} + +func TestReceiveInfoWithEmptyConnectURLs(t *testing.T) { + ready := make(chan bool, 2) + ch := make(chan bool, 1) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + + ports := []int{4222, 4223} + for i := 0; i < 2; i++ { + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", ports[i])) + if err != nil { + t.Fatalf("Error on listen: %v", err) + } + tl := l.(*net.TCPListener) + defer tl.Close() + + ready <- true + + c, err := tl.Accept() + if err != nil { + return + } + defer c.Close() + + // Send the initial INFO + c.Write([]byte(fmt.Sprintf("INFO {\"server_id\":\"server%d\"}\r\n", (i + 1)))) + buf := make([]byte, 0, 100) + b := make([]byte, 100) + for { + n, err := c.Read(b) + if err != nil { + return + } + buf = append(buf, b[:n]...) + if bytes.Contains(buf, []byte("PING\r\n")) { + break + } + } + if i == 0 { + // Send PONG and following INFO in one go (or at least try). + // The processing of PONG in sendConnect() should leave the + // rest for the readLoop to process. + c.Write([]byte("PONG\r\nINFO {\"server_id\":\"server1\",\"connect_urls\":[\"127.0.0.1:4222\", \"127.0.0.1:4223\", \"127.0.0.1:4224\"]}\r\n")) + // Wait for the notication + <-ch + // Close the connection in our side and go back into accept + c.Close() + } else { + // Send no connect ULRs (as if this was an older server that could in some cases + // send an empty array) + c.Write([]byte(fmt.Sprintf("PONG\r\nINFO {\"server_id\":\"server2\"}\r\n"))) + // Wait for client to disconnect + for { + if _, err := c.Read(buf); err != nil { + return + } + } + } + } + }() + + // Wait for listener to be up and running + if err := Wait(ready); err != nil { + t.Fatal("Listener not ready") + } + + rch := make(chan bool) + nc, err := nats.Connect("nats://127.0.0.1:4222", + nats.ReconnectWait(50*time.Millisecond), + nats.ReconnectHandler(func(_ *nats.Conn) { + rch <- true + })) + if err != nil { + t.Fatalf("Error on connect: %v", err) + } + defer nc.Close() + var ( + ds []string + timeout = time.Now().Add(2 * time.Second) + ok = false + ) + for time.Now().Before(timeout) { + ds = nc.DiscoveredServers() + if len(ds) == 2 { + if (ds[0] == "nats://127.0.0.1:4223" && ds[1] == "nats://127.0.0.1:4224") || + (ds[0] == "nats://127.0.0.1:4224" && ds[1] == "nats://127.0.0.1:4223") { + ok = true + break + } + } + time.Sleep(50 * time.Millisecond) + } + if !ok { + t.Fatalf("Unexpected discovered servers: %v", ds) + } + // Make the server close our connection + ch <- true + // Wait for the reconnect + if err := Wait(rch); err != nil { + t.Fatal("Did not reconnect") + } + // Discovered servers should still contain nats://me:1 + ds = nc.DiscoveredServers() + if len(ds) != 2 || + !((ds[0] == "nats://127.0.0.1:4223" && ds[1] == "nats://127.0.0.1:4224") || + (ds[0] == "nats://127.0.0.1:4224" && ds[1] == "nats://127.0.0.1:4223")) { + t.Fatalf("Unexpected discovered servers list: %v", ds) + } + nc.Close() + wg.Wait() +} + +func TestConnectWithSimplifiedURLs(t *testing.T) { + urls := []string{ + "nats://127.0.0.1:4222", + "nats://127.0.0.1:", + "nats://127.0.0.1", + "127.0.0.1:", + "127.0.0.1", + } + + connect := func(t *testing.T, url string) { + t.Helper() + nc, err := nats.Connect(url) + if err != nil { + t.Fatalf("URL %q expected to connect, got %v", url, err) + } + nc.Close() + } + + // Start a server that listens on default port 4222. + s := RunDefaultServer() + defer s.Shutdown() + + // Try for every connection in the urls array. + for _, u := range urls { + connect(t, u) + } + + s.Shutdown() + + // Use this to build the options for us... + s, opts := RunServerWithConfig("configs/tls.conf") + s.Shutdown() + // Now change listen port to 4222 and remove auth + opts.Port = 4222 + opts.Username = "" + opts.Password = "" + // and restart the server + s = RunServerWithOptions(*opts) + defer s.Shutdown() + + // Test again against a server that wants TLS and check + // that we automatically switch to Secure. + for _, u := range urls { + connect(t, u) + } +} + +func TestNilOpts(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + // Test a single nil option + var o1, o2, o3 nats.Option + _, err := nats.Connect(nats.DefaultURL, o1) + if err != nil { + t.Fatalf("Unexpected error with one nil option: %v", err) + } + + // Test nil, opt, nil + o2 = nats.ReconnectBufSize(2222) + nc, err := nats.Connect(nats.DefaultURL, o1, o2, o3) + if err != nil { + t.Fatalf("Unexpected error with multiple nil options: %v", err) + } + // check that the opt was set + if nc.Opts.ReconnectBufSize != 2222 { + t.Fatal("Unexpected error: option not set.") + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/context_test.go b/vendor/github.com/nats-io/go-nats/test/context_test.go new file mode 100644 index 00000000..453984f8 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/context_test.go @@ -0,0 +1,1011 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build go1.7 + +package test + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/nats-io/go-nats" +) + +func TestContextRequestWithNilConnection(t *testing.T) { + var nc *nats.Conn + + ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + _, err := nc.RequestWithContext(ctx, "fast", []byte("")) + if err == nil { + t.Fatal("Expected request with context and nil connection to fail") + } + if err != nats.ErrInvalidConnection { + t.Fatalf("Expected nats.ErrInvalidConnection, got %v\n", err) + } +} + +func testContextRequestWithTimeout(t *testing.T, nc *nats.Conn) { + nc.Subscribe("slow", func(m *nats.Msg) { + // Simulates latency into the client so that timeout is hit. + time.Sleep(200 * time.Millisecond) + nc.Publish(m.Reply, []byte("NG")) + }) + nc.Subscribe("fast", func(m *nats.Msg) { + nc.Publish(m.Reply, []byte("OK")) + }) + + ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + // Fast request should not fail at this point. + resp, err := nc.RequestWithContext(ctx, "fast", []byte("")) + if err != nil { + t.Fatalf("Expected request with context to not fail on fast response: %s", err) + } + got := string(resp.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + + // Slow request hits timeout so expected to fail. + _, err = nc.RequestWithContext(ctx, "slow", []byte("world")) + if err == nil { + t.Fatal("Expected request with timeout context to fail") + } + + // Reported error is "context deadline exceeded" from Context package, + // which implements net.Error interface. + type timeoutError interface { + Timeout() bool + } + timeoutErr, ok := err.(timeoutError) + if !ok || !timeoutErr.Timeout() { + t.Error("Expected to have a timeout error") + } + expected = `context deadline exceeded` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } + + // 2nd request should fail again even if they would be fast because context + // has already timed out. + _, err = nc.RequestWithContext(ctx, "fast", []byte("world")) + if err == nil { + t.Fatal("Expected request with context to fail") + } +} + +func TestContextRequestWithTimeout(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + testContextRequestWithTimeout(t, nc) +} + +func TestOldContextRequestWithTimeout(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle()) + if err != nil { + t.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + + testContextRequestWithTimeout(t, nc) +} + +func testContextRequestWithTimeoutCanceled(t *testing.T, nc *nats.Conn) { + ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancelCB() + + nc.Subscribe("fast", func(m *nats.Msg) { + nc.Publish(m.Reply, []byte("OK")) + }) + + // Fast request should not fail + resp, err := nc.RequestWithContext(ctx, "fast", []byte("")) + if err != nil { + t.Fatalf("Expected request with context to not fail on fast response: %s", err) + } + got := string(resp.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + + // Cancel the context already so that rest of requests fail. + cancelCB() + + // Context is already canceled so requests should immediately fail. + _, err = nc.RequestWithContext(ctx, "fast", []byte("world")) + if err == nil { + t.Fatal("Expected request with timeout context to fail") + } + + // Reported error is "context canceled" from Context package, + // which is not a timeout error. + type timeoutError interface { + Timeout() bool + } + if _, ok := err.(timeoutError); ok { + t.Errorf("Expected to not have a timeout error") + } + expected = `context canceled` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } + + // 2nd request should fail again even if fast because context has already been canceled + _, err = nc.RequestWithContext(ctx, "fast", []byte("world")) + if err == nil { + t.Fatal("Expected request with context to fail") + } +} + +func TestContextRequestWithTimeoutCanceled(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + testContextRequestWithTimeoutCanceled(t, nc) +} + +func TestOldContextRequestWithTimeoutCanceled(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle()) + if err != nil { + t.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + + testContextRequestWithTimeoutCanceled(t, nc) +} + +func testContextRequestWithCancel(t *testing.T, nc *nats.Conn) { + ctx, cancelCB := context.WithCancel(context.Background()) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + // timer which cancels the context though can also be arbitrarily extended + expirationTimer := time.AfterFunc(100*time.Millisecond, func() { + cancelCB() + }) + + nc.Subscribe("slow", func(m *nats.Msg) { + // simulates latency into the client so that timeout is hit. + time.Sleep(40 * time.Millisecond) + nc.Publish(m.Reply, []byte("OK")) + }) + nc.Subscribe("slower", func(m *nats.Msg) { + // we know this request will take longer so extend the timeout + expirationTimer.Reset(100 * time.Millisecond) + + // slower reply which would have hit original timeout + time.Sleep(90 * time.Millisecond) + + nc.Publish(m.Reply, []byte("Also OK")) + }) + + for i := 0; i < 2; i++ { + resp, err := nc.RequestWithContext(ctx, "slow", []byte("")) + if err != nil { + t.Fatalf("Expected request with context to not fail: %s", err) + } + got := string(resp.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + } + + // A third request with latency would make the context + // get canceled, but these reset the timer so deadline + // gets extended: + for i := 0; i < 10; i++ { + resp, err := nc.RequestWithContext(ctx, "slower", []byte("")) + if err != nil { + t.Fatalf("Expected request with context to not fail: %s", err) + } + got := string(resp.Data) + expected := "Also OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + } + + // One more slow request will expire the timer and cause an error... + _, err := nc.RequestWithContext(ctx, "slow", []byte("")) + if err == nil { + t.Fatal("Expected request with cancellation context to fail") + } + + // ...though reported error is "context canceled" from Context package, + // which is not a timeout error. + type timeoutError interface { + Timeout() bool + } + if _, ok := err.(timeoutError); ok { + t.Errorf("Expected to not have a timeout error") + } + expected := `context canceled` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextRequestWithCancel(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + testContextRequestWithCancel(t, nc) +} + +func TestOldContextRequestWithCancel(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle()) + if err != nil { + t.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + + testContextRequestWithCancel(t, nc) +} + +func testContextRequestWithDeadline(t *testing.T, nc *nats.Conn) { + deadline := time.Now().Add(100 * time.Millisecond) + ctx, cancelCB := context.WithDeadline(context.Background(), deadline) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + nc.Subscribe("slow", func(m *nats.Msg) { + // simulates latency into the client so that timeout is hit. + time.Sleep(40 * time.Millisecond) + nc.Publish(m.Reply, []byte("OK")) + }) + + for i := 0; i < 2; i++ { + resp, err := nc.RequestWithContext(ctx, "slow", []byte("")) + if err != nil { + t.Fatalf("Expected request with context to not fail: %s", err) + } + got := string(resp.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + } + + // A third request with latency would make the context + // reach the deadline. + _, err := nc.RequestWithContext(ctx, "slow", []byte("")) + if err == nil { + t.Fatal("Expected request with context to reach deadline") + } + + // Reported error is "context deadline exceeded" from Context package, + // which implements net.Error Timeout interface. + type timeoutError interface { + Timeout() bool + } + timeoutErr, ok := err.(timeoutError) + if !ok || !timeoutErr.Timeout() { + t.Errorf("Expected to have a timeout error") + } + expected := `context deadline exceeded` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextRequestWithDeadline(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + testContextRequestWithDeadline(t, nc) +} + +func TestOldContextRequestWithDeadline(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL, nats.UseOldRequestStyle()) + if err != nil { + t.Fatalf("Failed to connect: %v", err) + } + defer nc.Close() + + testContextRequestWithDeadline(t, nc) +} + +func TestContextSubNextMsgWithTimeout(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + sub, err := nc.SubscribeSync("slow") + if err != nil { + t.Fatalf("Expected to be able to subscribe: %s", err) + } + + for i := 0; i < 2; i++ { + err := nc.Publish("slow", []byte("OK")) + if err != nil { + t.Fatalf("Expected publish to not fail: %s", err) + } + // Enough time to get a couple of messages + time.Sleep(40 * time.Millisecond) + + msg, err := sub.NextMsgWithContext(ctx) + if err != nil { + t.Fatalf("Expected to receive message: %s", err) + } + got := string(msg.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + } + + // Third message will fail because the context will be canceled by now + _, err = sub.NextMsgWithContext(ctx) + if err == nil { + t.Fatal("Expected to fail receiving a message") + } + + // Reported error is "context deadline exceeded" from Context package, + // which implements net.Error Timeout interface. + type timeoutError interface { + Timeout() bool + } + timeoutErr, ok := err.(timeoutError) + if !ok || !timeoutErr.Timeout() { + t.Errorf("Expected to have a timeout error") + } + expected := `context deadline exceeded` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextSubNextMsgWithTimeoutCanceled(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + sub, err := nc.SubscribeSync("fast") + if err != nil { + t.Fatalf("Expected to be able to subscribe: %s", err) + } + + for i := 0; i < 2; i++ { + err := nc.Publish("fast", []byte("OK")) + if err != nil { + t.Fatalf("Expected publish to not fail: %s", err) + } + // Enough time to get a couple of messages + time.Sleep(40 * time.Millisecond) + + msg, err := sub.NextMsgWithContext(ctx) + if err != nil { + t.Fatalf("Expected to receive message: %s", err) + } + got := string(msg.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + } + + // Cancel the context already so that rest of NextMsg calls fail. + cancelCB() + + _, err = sub.NextMsgWithContext(ctx) + if err == nil { + t.Fatal("Expected request with timeout context to fail") + } + + // Reported error is "context canceled" from Context package, + // which is not a timeout error. + type timeoutError interface { + Timeout() bool + } + if _, ok := err.(timeoutError); ok { + t.Errorf("Expected to not have a timeout error") + } + expected := `context canceled` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextSubNextMsgWithCancel(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + ctx, cancelCB := context.WithCancel(context.Background()) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + // timer which cancels the context though can also be arbitrarily extended + time.AfterFunc(100*time.Millisecond, func() { + cancelCB() + }) + + sub1, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatalf("Expected to be able to subscribe: %s", err) + } + sub2, err := nc.SubscribeSync("bar") + if err != nil { + t.Fatalf("Expected to be able to subscribe: %s", err) + } + + for i := 0; i < 2; i++ { + err := nc.Publish("foo", []byte("OK")) + if err != nil { + t.Fatalf("Expected publish to not fail: %s", err) + } + resp, err := sub1.NextMsgWithContext(ctx) + if err != nil { + t.Fatalf("Expected request with context to not fail: %s", err) + } + got := string(resp.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + } + err = nc.Publish("bar", []byte("Also OK")) + if err != nil { + t.Fatalf("Expected publish to not fail: %s", err) + } + + resp, err := sub2.NextMsgWithContext(ctx) + if err != nil { + t.Fatalf("Expected request with context to not fail: %s", err) + } + got := string(resp.Data) + expected := "Also OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + + // We do not have another message pending so timer will + // cancel the context. + _, err = sub2.NextMsgWithContext(ctx) + if err == nil { + t.Fatal("Expected request with context to fail") + } + + // Reported error is "context canceled" from Context package, + // which is not a timeout error. + type timeoutError interface { + Timeout() bool + } + if _, ok := err.(timeoutError); ok { + t.Errorf("Expected to not have a timeout error") + } + expected = `context canceled` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextSubNextMsgWithDeadline(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + deadline := time.Now().Add(100 * time.Millisecond) + ctx, cancelCB := context.WithDeadline(context.Background(), deadline) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + sub, err := nc.SubscribeSync("slow") + if err != nil { + t.Fatalf("Expected to be able to subscribe: %s", err) + } + + for i := 0; i < 2; i++ { + err := nc.Publish("slow", []byte("OK")) + if err != nil { + t.Fatalf("Expected publish to not fail: %s", err) + } + // Enough time to get a couple of messages + time.Sleep(40 * time.Millisecond) + + msg, err := sub.NextMsgWithContext(ctx) + if err != nil { + t.Fatalf("Expected to receive message: %s", err) + } + got := string(msg.Data) + expected := "OK" + if got != expected { + t.Errorf("Expected to receive %s, got: %s", expected, got) + } + } + + // Third message will fail because the context will be canceled by now + _, err = sub.NextMsgWithContext(ctx) + if err == nil { + t.Fatal("Expected to fail receiving a message") + } + + // Reported error is "context deadline exceeded" from Context package, + // which implements net.Error Timeout interface. + type timeoutError interface { + Timeout() bool + } + timeoutErr, ok := err.(timeoutError) + if !ok || !timeoutErr.Timeout() { + t.Errorf("Expected to have a timeout error") + } + expected := `context deadline exceeded` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextEncodedRequestWithTimeout(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER) + if err != nil { + t.Fatalf("Unable to create encoded connection: %v", err) + } + defer c.Close() + + deadline := time.Now().Add(100 * time.Millisecond) + ctx, cancelCB := context.WithDeadline(context.Background(), deadline) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + type request struct { + Message string `json:"message"` + } + type response struct { + Code int `json:"code"` + } + c.Subscribe("slow", func(_, reply string, req *request) { + got := req.Message + expected := "Hello" + if got != expected { + t.Errorf("Expected to receive request with %q, got %q", got, expected) + } + + // simulates latency into the client so that timeout is hit. + time.Sleep(40 * time.Millisecond) + c.Publish(reply, &response{Code: 200}) + }) + + for i := 0; i < 2; i++ { + req := &request{Message: "Hello"} + resp := &response{} + err := c.RequestWithContext(ctx, "slow", req, resp) + if err != nil { + t.Fatalf("Expected encoded request with context to not fail: %s", err) + } + got := resp.Code + expected := 200 + if got != expected { + t.Errorf("Expected to receive %v, got: %v", expected, got) + } + } + + // A third request with latency would make the context + // reach the deadline. + req := &request{Message: "Hello"} + resp := &response{} + err = c.RequestWithContext(ctx, "slow", req, resp) + if err == nil { + t.Fatal("Expected request with context to reach deadline") + } + + // Reported error is "context deadline exceeded" from Context package, + // which implements net.Error Timeout interface. + type timeoutError interface { + Timeout() bool + } + timeoutErr, ok := err.(timeoutError) + if !ok || !timeoutErr.Timeout() { + t.Errorf("Expected to have a timeout error") + } + expected := `context deadline exceeded` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextEncodedRequestWithTimeoutCanceled(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER) + if err != nil { + t.Fatalf("Unable to create encoded connection: %v", err) + } + defer c.Close() + + ctx, cancelCB := context.WithTimeout(context.Background(), 100*time.Millisecond) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + type request struct { + Message string `json:"message"` + } + type response struct { + Code int `json:"code"` + } + + c.Subscribe("fast", func(_, reply string, req *request) { + got := req.Message + expected := "Hello" + if got != expected { + t.Errorf("Expected to receive request with %q, got %q", got, expected) + } + + // simulates latency into the client so that timeout is hit. + time.Sleep(40 * time.Millisecond) + + c.Publish(reply, &response{Code: 200}) + }) + + // Fast request should not fail + req := &request{Message: "Hello"} + resp := &response{} + c.RequestWithContext(ctx, "fast", req, resp) + expectedCode := 200 + if resp.Code != expectedCode { + t.Errorf("Expected to receive %d, got: %d", expectedCode, resp.Code) + } + + // Cancel the context already so that rest of requests fail. + cancelCB() + + err = c.RequestWithContext(ctx, "fast", req, resp) + if err == nil { + t.Fatal("Expected request with timeout context to fail") + } + + // Reported error is "context canceled" from Context package, + // which is not a timeout error. + type timeoutError interface { + Timeout() bool + } + if _, ok := err.(timeoutError); ok { + t.Errorf("Expected to not have a timeout error") + } + expected := `context canceled` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } + + // 2nd request should fail again even if fast because context has already been canceled + err = c.RequestWithContext(ctx, "fast", req, resp) + if err == nil { + t.Fatal("Expected request with timeout context to fail") + } +} + +func TestContextEncodedRequestWithCancel(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER) + if err != nil { + t.Fatalf("Unable to create encoded connection: %v", err) + } + defer c.Close() + + ctx, cancelCB := context.WithCancel(context.Background()) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + // timer which cancels the context though can also be arbitrarily extended + expirationTimer := time.AfterFunc(100*time.Millisecond, func() { + cancelCB() + }) + + type request struct { + Message string `json:"message"` + } + type response struct { + Code int `json:"code"` + } + c.Subscribe("slow", func(_, reply string, req *request) { + got := req.Message + expected := "Hello" + if got != expected { + t.Errorf("Expected to receive request with %q, got %q", got, expected) + } + + // simulates latency into the client so that timeout is hit. + time.Sleep(40 * time.Millisecond) + c.Publish(reply, &response{Code: 200}) + }) + c.Subscribe("slower", func(_, reply string, req *request) { + got := req.Message + expected := "World" + if got != expected { + t.Errorf("Expected to receive request with %q, got %q", got, expected) + } + + // we know this request will take longer so extend the timeout + expirationTimer.Reset(100 * time.Millisecond) + + // slower reply which would have hit original timeout + time.Sleep(90 * time.Millisecond) + c.Publish(reply, &response{Code: 200}) + }) + + for i := 0; i < 2; i++ { + req := &request{Message: "Hello"} + resp := &response{} + err := c.RequestWithContext(ctx, "slow", req, resp) + if err != nil { + t.Fatalf("Expected encoded request with context to not fail: %s", err) + } + got := resp.Code + expected := 200 + if got != expected { + t.Errorf("Expected to receive %v, got: %v", expected, got) + } + } + + // A third request with latency would make the context + // get canceled, but these reset the timer so deadline + // gets extended: + for i := 0; i < 10; i++ { + req := &request{Message: "World"} + resp := &response{} + err := c.RequestWithContext(ctx, "slower", req, resp) + if err != nil { + t.Fatalf("Expected request with context to not fail: %s", err) + } + got := resp.Code + expected := 200 + if got != expected { + t.Errorf("Expected to receive %d, got: %d", expected, got) + } + } + + req := &request{Message: "Hello"} + resp := &response{} + + // One more slow request will expire the timer and cause an error... + err = c.RequestWithContext(ctx, "slow", req, resp) + if err == nil { + t.Fatal("Expected request with cancellation context to fail") + } + + // ...though reported error is "context canceled" from Context package, + // which is not a timeout error. + type timeoutError interface { + Timeout() bool + } + if _, ok := err.(timeoutError); ok { + t.Errorf("Expected to not have a timeout error") + } + expected := `context canceled` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextEncodedRequestWithDeadline(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER) + if err != nil { + t.Fatalf("Unable to create encoded connection: %v", err) + } + defer c.Close() + + deadline := time.Now().Add(100 * time.Millisecond) + ctx, cancelCB := context.WithDeadline(context.Background(), deadline) + defer cancelCB() // should always be called, not discarded, to prevent context leak + + type request struct { + Message string `json:"message"` + } + type response struct { + Code int `json:"code"` + } + c.Subscribe("slow", func(_, reply string, req *request) { + got := req.Message + expected := "Hello" + if got != expected { + t.Errorf("Expected to receive request with %q, got %q", got, expected) + } + + // simulates latency into the client so that timeout is hit. + time.Sleep(40 * time.Millisecond) + c.Publish(reply, &response{Code: 200}) + }) + + for i := 0; i < 2; i++ { + req := &request{Message: "Hello"} + resp := &response{} + err := c.RequestWithContext(ctx, "slow", req, resp) + if err != nil { + t.Fatalf("Expected encoded request with context to not fail: %s", err) + } + got := resp.Code + expected := 200 + if got != expected { + t.Errorf("Expected to receive %v, got: %v", expected, got) + } + } + + // A third request with latency would make the context + // reach the deadline. + req := &request{Message: "Hello"} + resp := &response{} + err = c.RequestWithContext(ctx, "slow", req, resp) + if err == nil { + t.Fatal("Expected request with context to reach deadline") + } + + // Reported error is "context deadline exceeded" from Context package, + // which implements net.Error Timeout interface. + type timeoutError interface { + Timeout() bool + } + timeoutErr, ok := err.(timeoutError) + if !ok || !timeoutErr.Timeout() { + t.Errorf("Expected to have a timeout error") + } + expected := `context deadline exceeded` + if !strings.Contains(err.Error(), expected) { + t.Errorf("Expected %q error, got: %q", expected, err.Error()) + } +} + +func TestContextRequestConnClosed(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + ctx, cancelCB := context.WithCancel(context.Background()) + defer cancelCB() + + time.AfterFunc(100*time.Millisecond, func() { + cancelCB() + }) + + nc.Close() + _, err := nc.RequestWithContext(ctx, "foo", []byte("")) + if err == nil { + t.Fatal("Expected request to fail with error") + } + if err != nats.ErrConnectionClosed { + t.Errorf("Expected request to fail with connection closed error: %s", err) + } +} + +func TestContextBadSubscription(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + ctx, cancelCB := context.WithCancel(context.Background()) + defer cancelCB() + time.AfterFunc(100*time.Millisecond, func() { + cancelCB() + }) + + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {}) + if err != nil { + t.Fatalf("Expected to be able to subscribe: %s", err) + } + + err = sub.Unsubscribe() + if err != nil { + t.Fatalf("Expected to be able to unsubscribe: %s", err) + } + + _, err = sub.NextMsgWithContext(ctx) + if err == nil { + t.Fatal("Expected to fail getting next message with context") + } + + if err != nats.ErrBadSubscription { + t.Errorf("Expected request to fail with connection closed error: %s", err) + } +} + +func TestContextInvalid(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + c, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER) + if err != nil { + t.Fatalf("Unable to create encoded connection: %v", err) + } + defer c.Close() + + _, err = nc.RequestWithContext(nil, "foo", []byte("")) + if err == nil { + t.Fatal("Expected request to fail with error") + } + if err != nats.ErrInvalidContext { + t.Errorf("Expected request to fail with connection closed error: %s", err) + } + + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {}) + if err != nil { + t.Fatalf("Expected to be able to subscribe: %s", err) + } + + _, err = sub.NextMsgWithContext(nil) + if err == nil { + t.Fatal("Expected request to fail with error") + } + if err != nats.ErrInvalidContext { + t.Errorf("Expected request to fail with connection closed error: %s", err) + } + + type request struct { + Message string `json:"message"` + } + type response struct { + Code int `json:"code"` + } + req := &request{Message: "Hello"} + resp := &response{} + err = c.RequestWithContext(nil, "slow", req, resp) + if err == nil { + t.Fatal("Expected request with context to reach deadline") + } + if err != nats.ErrInvalidContext { + t.Errorf("Expected request to fail with connection closed error: %s", err) + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/drain_test.go b/vendor/github.com/nats-io/go-nats/test/drain_test.go new file mode 100644 index 00000000..96d9a9a3 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/drain_test.go @@ -0,0 +1,396 @@ +// Copyright 2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "fmt" + "sync/atomic" + "testing" + "time" + + nats "github.com/nats-io/go-nats" +) + +// Drain can be very useful for graceful shutdown of subscribers. +// Especially queue subscribers. +func TestDrain(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + done := make(chan bool) + received := int32(0) + expected := int32(100) + + cb := func(_ *nats.Msg) { + // Allow this to back up. + time.Sleep(time.Millisecond) + rcvd := atomic.AddInt32(&received, 1) + if rcvd >= expected { + done <- true + } + } + + sub, err := nc.Subscribe("foo", cb) + if err != nil { + t.Fatalf("Error creating subscription; %v", err) + } + + for i := int32(0); i < expected; i++ { + nc.Publish("foo", []byte("Don't forget about me")) + } + + // Drain it and make sure we receive all messages. + sub.Drain() + select { + case <-done: + break + case <-time.After(2 * time.Second): + r := atomic.LoadInt32(&received) + if r != expected { + t.Fatalf("Did not receive all messages: %d of %d", r, expected) + } + } +} + +func TestDrainQueueSub(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + done := make(chan bool) + received := int32(0) + expected := int32(4096) + numSubs := int32(32) + + checkDone := func() int32 { + rcvd := atomic.AddInt32(&received, 1) + if rcvd >= expected { + done <- true + } + return rcvd + } + + callback := func(m *nats.Msg) { + rcvd := checkDone() + // Randomly replace this sub from time to time. + if rcvd%3 == 0 { + m.Sub.Drain() + // Create a new one that we will not drain. + nc.QueueSubscribe("foo", "bar", func(m *nats.Msg) { checkDone() }) + } + } + + for i := int32(0); i < numSubs; i++ { + _, err := nc.QueueSubscribe("foo", "bar", callback) + if err != nil { + t.Fatalf("Error creating subscription; %v", err) + } + } + + for i := int32(0); i < expected; i++ { + nc.Publish("foo", []byte("Don't forget about me")) + } + + select { + case <-done: + break + case <-time.After(5 * time.Second): + r := atomic.LoadInt32(&received) + if r != expected { + t.Fatalf("Did not receive all messages: %d of %d", r, expected) + } + } +} + +func waitFor(t *testing.T, totalWait, sleepDur time.Duration, f func() error) { + t.Helper() + timeout := time.Now().Add(totalWait) + var err error + for time.Now().Before(timeout) { + err = f() + if err == nil { + return + } + time.Sleep(sleepDur) + } + if err != nil { + t.Fatal(err.Error()) + } +} + +func TestDrainUnSubs(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + num := 100 + subs := make([]*nats.Subscription, num) + + // Normal Unsubscribe + for i := 0; i < num; i++ { + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {}) + if err != nil { + t.Fatalf("Error creating subscription; %v", err) + } + subs[i] = sub + } + + if numSubs := nc.NumSubscriptions(); numSubs != num { + t.Fatalf("Expected %d subscriptions, got %d", num, numSubs) + } + for i := 0; i < num; i++ { + subs[i].Unsubscribe() + } + if numSubs := nc.NumSubscriptions(); numSubs != 0 { + t.Fatalf("Expected no subscriptions, got %d", numSubs) + } + + // Drain version + for i := 0; i < num; i++ { + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) {}) + if err != nil { + t.Fatalf("Error creating subscription; %v", err) + } + subs[i] = sub + } + + if numSubs := nc.NumSubscriptions(); numSubs != num { + t.Fatalf("Expected %d subscriptions, got %d", num, numSubs) + } + for i := 0; i < num; i++ { + subs[i].Drain() + } + // Should happen quickly that we get to zero, so do not need to wait long. + waitFor(t, 2*time.Second, 10*time.Millisecond, func() error { + if numSubs := nc.NumSubscriptions(); numSubs != 0 { + return fmt.Errorf("Expected no subscriptions, got %d\n", numSubs) + } + return nil + }) +} + +func TestDrainSlowSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + nc := NewDefaultConnection(t) + defer nc.Close() + + received := int32(0) + + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) { + atomic.AddInt32(&received, 1) + time.Sleep(100 * time.Millisecond) + }) + if err != nil { + t.Fatalf("Error creating subscription; %v", err) + } + + total := 10 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Slow Slow")) + } + nc.Flush() + + pmsgs, _, _ := sub.Pending() + if pmsgs != total && pmsgs != total-1 { + t.Fatalf("Expected most messages to be pending, but got %d vs %d", pmsgs, total) + } + sub.Drain() + + // Should take a second or so to drain away. + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + // Wait for it to become invalid. Once drained it is unsubscribed. + _, _, err := sub.Pending() + if err != nats.ErrBadSubscription { + return fmt.Errorf("Still valid") + } + r := int(atomic.LoadInt32(&received)) + if r != total { + t.Fatalf("Did not receive all messages, got %d vs %d", r, total) + } + return nil + }) +} + +func TestDrainConnection(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + done := make(chan bool) + rdone := make(chan bool) + + closed := func(nc *nats.Conn) { + done <- true + } + + url := fmt.Sprintf("nats://127.0.0.1:%d", nats.DefaultPort) + nc, err := nats.Connect(url, nats.ClosedHandler(closed)) + if err != nil { + t.Fatalf("Failed to create default connection: %v", err) + } + defer nc.Close() + + nc2, err := nats.Connect(url) + if err != nil { + t.Fatalf("Failed to create default connection: %v", err) + } + defer nc2.Close() + + received := int32(0) + responses := int32(0) + expected := int32(50) + sleep := 10 * time.Millisecond + + // Create the listner for responses on "bar" + _, err = nc2.Subscribe("bar", func(_ *nats.Msg) { + r := atomic.AddInt32(&responses, 1) + if r == expected { + rdone <- true + } + }) + if err != nil { + t.Fatalf("Error creating subscription for responses: %v", err) + } + + // Create a slow subscriber for the responder + sub, err := nc.Subscribe("foo", func(m *nats.Msg) { + time.Sleep(sleep) + atomic.AddInt32(&received, 1) + err := nc.Publish(m.Reply, []byte("Stop bugging me")) + if err != nil { + t.Errorf("Publisher received an error sending response: %v\n", err) + } + }) + if err != nil { + t.Fatalf("Error creating subscription; %v", err) + } + + // Publish some messages + for i := int32(0); i < expected; i++ { + nc.PublishRequest("foo", "bar", []byte("Slow Slow")) + } + + drainStart := time.Now() + nc.Drain() + + // Sub should be disabled immediately + if err := sub.Unsubscribe(); err == nil { + t.Fatalf("Expected to receive an error on Unsubscribe after drain") + } + // Also can not create any new subs + if _, err := nc.Subscribe("foo", func(_ *nats.Msg) {}); err == nil { + t.Fatalf("Expected to receive an error on new Subscription after drain") + } + + // Make sure we can still publish, this is for any responses. + if err := nc.Publish("baz", []byte("Slow Slow")); err != nil { + t.Fatalf("Expected to not receive an error on Publish after drain, got %v", err) + } + + // Wait for the closed state from nc + select { + case <-done: + if time.Since(drainStart) < (sleep * time.Duration(expected)) { + t.Fatalf("Drain exited too soon\n") + } + r := atomic.LoadInt32(&received) + if r != expected { + t.Fatalf("Did not receive all messages from Drain, %d vs %d", r, expected) + } + break + case <-time.After(2 * time.Second): + t.Fatalf("Timeout waiting for closed state for connection") + } + + // Now make sure all responses were received. + select { + case <-rdone: + r := atomic.LoadInt32(&responses) + if r != expected { + t.Fatalf("Did not receive all responses, %d vs %d", r, expected) + } + break + case <-time.After(2 * time.Second): + t.Fatalf("Timeout waiting for all the responses") + } +} + +func TestDrainConnectionAutoUnsub(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + errors := int32(0) + received := int32(0) + expected := int32(10) + + done := make(chan bool) + + closed := func(nc *nats.Conn) { + done <- true + } + + errCb := func(nc *nats.Conn, s *nats.Subscription, err error) { + atomic.AddInt32(&errors, 1) + } + + url := fmt.Sprintf("nats://127.0.0.1:%d", nats.DefaultPort) + nc, err := nats.Connect(url, nats.ErrorHandler(errCb), nats.ClosedHandler(closed)) + if err != nil { + t.Fatalf("Failed to create default connection: %v", err) + } + defer nc.Close() + + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) { + // So they back up a bit in client and allow drain to do its thing. + time.Sleep(10 * time.Millisecond) + atomic.AddInt32(&received, 1) + + }) + if err != nil { + t.Fatalf("Error creating subscription; %v", err) + } + + sub.AutoUnsubscribe(int(expected)) + + // Publish some messages + for i := 0; i < 50; i++ { + nc.Publish("foo", []byte("Only 10 please!")) + } + // Flush here so messages coming back into client. + nc.Flush() + + // Now add drain state. + time.Sleep(10 * time.Millisecond) + nc.Drain() + + // Wait for the closed state from nc + select { + case <-done: + errs := atomic.LoadInt32(&errors) + if errs > 0 { + t.Fatalf("Did not expect any errors, got %d", errs) + } + r := atomic.LoadInt32(&received) + if r != expected { + t.Fatalf("Did not receive all messages from Drain, %d vs %d", r, expected) + } + break + case <-time.After(2 * time.Second): + t.Fatalf("Timeout waiting for closed state for connection") + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/enc_test.go b/vendor/github.com/nats-io/go-nats/test/enc_test.go new file mode 100644 index 00000000..a2a2ebe6 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/enc_test.go @@ -0,0 +1,470 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "bytes" + "testing" + "time" + + "github.com/nats-io/go-nats" + "github.com/nats-io/go-nats/encoders/builtin" +) + +const TEST_PORT = 8168 + +func NewDefaultEConn(t *testing.T) *nats.EncodedConn { + ec, err := nats.NewEncodedConn(NewConnection(t, TEST_PORT), nats.DEFAULT_ENCODER) + if err != nil { + t.Fatalf("Failed to create an encoded connection: %v\n", err) + } + return ec +} + +func TestEncBuiltinConstructorErrs(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + c := NewConnection(t, TEST_PORT) + _, err := nats.NewEncodedConn(nil, "default") + if err == nil { + t.Fatal("Expected err for nil connection") + } + _, err = nats.NewEncodedConn(c, "foo22") + if err == nil { + t.Fatal("Expected err for bad encoder") + } + c.Close() + _, err = nats.NewEncodedConn(c, "default") + if err == nil { + t.Fatal("Expected err for closed connection") + } + +} + +func TestEncBuiltinMarshalString(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + + testString := "Hello World!" + + ec.Subscribe("enc_string", func(s string) { + if s != testString { + t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString) + } + ch <- true + }) + ec.Publish("enc_string", testString) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinMarshalBytes(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + + testBytes := []byte("Hello World!") + + ec.Subscribe("enc_bytes", func(b []byte) { + if !bytes.Equal(b, testBytes) { + t.Fatalf("Received test bytes of '%s', wanted '%s'\n", b, testBytes) + } + ch <- true + }) + ec.Publish("enc_bytes", testBytes) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinMarshalInt(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + + testN := 22 + + ec.Subscribe("enc_int", func(n int) { + if n != testN { + t.Fatalf("Received test number of %d, wanted %d\n", n, testN) + } + ch <- true + }) + ec.Publish("enc_int", testN) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinMarshalInt32(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + + testN := 22 + + ec.Subscribe("enc_int", func(n int32) { + if n != int32(testN) { + t.Fatalf("Received test number of %d, wanted %d\n", n, testN) + } + ch <- true + }) + ec.Publish("enc_int", testN) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinMarshalInt64(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + + testN := 22 + + ec.Subscribe("enc_int", func(n int64) { + if n != int64(testN) { + t.Fatalf("Received test number of %d, wanted %d\n", n, testN) + } + ch <- true + }) + ec.Publish("enc_int", testN) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinMarshalFloat32(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + + testN := float32(22) + + ec.Subscribe("enc_float", func(n float32) { + if n != testN { + t.Fatalf("Received test number of %f, wanted %f\n", n, testN) + } + ch <- true + }) + ec.Publish("enc_float", testN) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinMarshalFloat64(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + + testN := float64(22.22) + + ec.Subscribe("enc_float", func(n float64) { + if n != testN { + t.Fatalf("Received test number of %f, wanted %f\n", n, testN) + } + ch <- true + }) + ec.Publish("enc_float", testN) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinMarshalBool(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + ch := make(chan bool) + expected := make(chan bool, 1) + + ec.Subscribe("enc_bool", func(b bool) { + val := <-expected + if b != val { + t.Fatal("Boolean values did not match") + } + ch <- true + }) + + expected <- false + ec.Publish("enc_bool", false) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } + + expected <- true + ec.Publish("enc_bool", true) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinExtendedSubscribeCB(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + + ch := make(chan bool) + + testString := "Hello World!" + subject := "cb_args" + + ec.Subscribe(subject, func(subj, s string) { + if s != testString { + t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString) + } + if subj != subject { + t.Fatalf("Received subject of '%s', wanted '%s'\n", subj, subject) + } + ch <- true + }) + ec.Publish(subject, testString) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinExtendedSubscribeCB2(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + + ch := make(chan bool) + + testString := "Hello World!" + oSubj := "cb_args" + oReply := "foobar" + + ec.Subscribe(oSubj, func(subj, reply, s string) { + if s != testString { + t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString) + } + if subj != oSubj { + t.Fatalf("Received subject of '%s', wanted '%s'\n", subj, oSubj) + } + if reply != oReply { + t.Fatalf("Received reply of '%s', wanted '%s'\n", reply, oReply) + } + ch <- true + }) + ec.PublishRequest(oSubj, oReply, testString) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinRawMsgSubscribeCB(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + + ch := make(chan bool) + + testString := "Hello World!" + oSubj := "cb_args" + oReply := "foobar" + + ec.Subscribe(oSubj, func(m *nats.Msg) { + s := string(m.Data) + if s != testString { + t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString) + } + if m.Subject != oSubj { + t.Fatalf("Received subject of '%s', wanted '%s'\n", m.Subject, oSubj) + } + if m.Reply != oReply { + t.Fatalf("Received reply of '%s', wanted '%s'\n", m.Reply, oReply) + } + ch <- true + }) + ec.PublishRequest(oSubj, oReply, testString) + if e := Wait(ch); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinRequest(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + + expectedResp := "I can help!" + + ec.Subscribe("help", func(subj, reply, req string) { + ec.Publish(reply, expectedResp) + }) + + var resp string + + err := ec.Request("help", "help me", &resp, 1*time.Second) + if err != nil { + t.Fatalf("Failed at receiving proper response: %v\n", err) + } + if resp != expectedResp { + t.Fatalf("Received reply '%s', wanted '%s'\n", resp, expectedResp) + } +} + +func TestEncBuiltinRequestReceivesMsg(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + + expectedResp := "I can help!" + + ec.Subscribe("help", func(subj, reply, req string) { + ec.Publish(reply, expectedResp) + }) + + var resp nats.Msg + + err := ec.Request("help", "help me", &resp, 1*time.Second) + if err != nil { + t.Fatalf("Failed at receiving proper response: %v\n", err) + } + if string(resp.Data) != expectedResp { + t.Fatalf("Received reply '%s', wanted '%s'\n", string(resp.Data), expectedResp) + } +} + +func TestEncBuiltinAsyncMarshalErr(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + defer ec.Close() + + ch := make(chan bool) + + testString := "Hello World!" + subject := "err_marshall" + + ec.Subscribe(subject, func(subj, num int) { + // This will never get called. + }) + + ec.Conn.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, err error) { + ch <- true + } + + ec.Publish(subject, testString) + if e := Wait(ch); e != nil { + t.Fatalf("Did not receive the message: %s", e) + } +} + +func TestEncBuiltinEncodeNil(t *testing.T) { + de := &builtin.DefaultEncoder{} + _, err := de.Encode("foo", nil) + if err != nil { + t.Fatalf("Expected no error encoding nil: %v", err) + } +} + +func TestEncBuiltinDecodeDefault(t *testing.T) { + de := &builtin.DefaultEncoder{} + b, err := de.Encode("foo", 22) + if err != nil { + t.Fatalf("Expected no error encoding number: %v", err) + } + var c chan bool + err = de.Decode("foo", b, &c) + if err == nil { + t.Fatalf("Expected an error decoding") + } +} + +func TestEncDrainSupported(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewDefaultEConn(t) + err := ec.Drain() + if err != nil { + t.Fatalf("Expected no error calling Drain(), got %v", err) + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/gob_test.go b/vendor/github.com/nats-io/go-nats/test/gob_test.go new file mode 100644 index 00000000..72992f86 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/gob_test.go @@ -0,0 +1,139 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "reflect" + "testing" + + "github.com/nats-io/go-nats" +) + +func NewGobEncodedConn(tl TestLogger) *nats.EncodedConn { + ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), nats.GOB_ENCODER) + if err != nil { + tl.Fatalf("Failed to create an encoded connection: %v\n", err) + } + return ec +} + +func TestEncBuiltinGobMarshalString(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewGobEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + testString := "Hello World!" + + ec.Subscribe("gob_string", func(s string) { + if s != testString { + t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString) + } + ch <- true + }) + ec.Publish("gob_string", testString) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinGobMarshalInt(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewGobEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + testN := 22 + + ec.Subscribe("gob_int", func(n int) { + if n != testN { + t.Fatalf("Received test int of '%d', wanted '%d'\n", n, testN) + } + ch <- true + }) + ec.Publish("gob_int", testN) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinGobMarshalStruct(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewGobEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*person) + + me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + me.Assets = make(map[string]uint) + me.Assets["house"] = 1000 + me.Assets["car"] = 100 + + ec.Subscribe("gob_struct", func(p *person) { + if !reflect.DeepEqual(p, me) { + t.Fatalf("Did not receive the correct struct response") + } + ch <- true + }) + + ec.Publish("gob_struct", me) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func BenchmarkPublishGobStruct(b *testing.B) { + // stop benchmark for set-up + b.StopTimer() + + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewGobEncodedConn(b) + defer ec.Close() + ch := make(chan bool) + + me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*person) + + me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + ec.Subscribe("gob_struct", func(p *person) { + if !reflect.DeepEqual(p, me) { + b.Fatalf("Did not receive the correct struct response") + } + ch <- true + }) + + // resume benchmark + b.StartTimer() + + for n := 0; n < b.N; n++ { + ec.Publish("gob_struct", me) + if e := Wait(ch); e != nil { + b.Fatal("Did not receive the message") + } + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/helper_test.go b/vendor/github.com/nats-io/go-nats/test/helper_test.go new file mode 100644 index 00000000..e7775742 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/helper_test.go @@ -0,0 +1,123 @@ +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "errors" + "fmt" + "runtime" + "strings" + "time" + + "github.com/nats-io/gnatsd/server" + "github.com/nats-io/go-nats" + + gnatsd "github.com/nats-io/gnatsd/test" +) + +// So that we can pass tests and benchmarks... +type tLogger interface { + Fatalf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) +} + +// TestLogger +type TestLogger tLogger + +// Dumb wait program to sync on callbacks, etc... Will timeout +func Wait(ch chan bool) error { + return WaitTime(ch, 5*time.Second) +} + +// Wait for a chan with a timeout. +func WaitTime(ch chan bool, timeout time.Duration) error { + select { + case <-ch: + return nil + case <-time.After(timeout): + } + return errors.New("timeout") +} + +func stackFatalf(t tLogger, f string, args ...interface{}) { + lines := make([]string, 0, 32) + msg := fmt.Sprintf(f, args...) + lines = append(lines, msg) + + // Generate the Stack of callers: Skip us and verify* frames. + for i := 1; true; i++ { + _, file, line, ok := runtime.Caller(i) + if !ok { + break + } + msg := fmt.Sprintf("%d - %s:%d", i, file, line) + lines = append(lines, msg) + } + t.Fatalf("%s", strings.Join(lines, "\n")) +} + +//////////////////////////////////////////////////////////////////////////////// +// Creating client connections +//////////////////////////////////////////////////////////////////////////////// + +// NewDefaultConnection +func NewDefaultConnection(t tLogger) *nats.Conn { + return NewConnection(t, nats.DefaultPort) +} + +// NewConnection forms connection on a given port. +func NewConnection(t tLogger, port int) *nats.Conn { + url := fmt.Sprintf("nats://localhost:%d", port) + nc, err := nats.Connect(url) + if err != nil { + t.Fatalf("Failed to create default connection: %v\n", err) + return nil + } + return nc +} + +// NewEConn +func NewEConn(t tLogger) *nats.EncodedConn { + ec, err := nats.NewEncodedConn(NewDefaultConnection(t), nats.DEFAULT_ENCODER) + if err != nil { + t.Fatalf("Failed to create an encoded connection: %v\n", err) + } + return ec +} + +//////////////////////////////////////////////////////////////////////////////// +// Running gnatsd server in separate Go routines +//////////////////////////////////////////////////////////////////////////////// + +// RunDefaultServer will run a server on the default port. +func RunDefaultServer() *server.Server { + return RunServerOnPort(nats.DefaultPort) +} + +// RunServerOnPort will run a server on the given port. +func RunServerOnPort(port int) *server.Server { + opts := gnatsd.DefaultTestOptions + opts.Port = port + return RunServerWithOptions(opts) +} + +// RunServerWithOptions will run a server with the given options. +func RunServerWithOptions(opts server.Options) *server.Server { + return gnatsd.RunServer(&opts) +} + +// RunServerWithConfig will run a server with the given configuration file. +func RunServerWithConfig(configFile string) (*server.Server, *server.Options) { + return gnatsd.RunServerWithConfig(configFile) +} diff --git a/vendor/github.com/nats-io/go-nats/test/json_test.go b/vendor/github.com/nats-io/go-nats/test/json_test.go new file mode 100644 index 00000000..64dbd91b --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/json_test.go @@ -0,0 +1,330 @@ +// Copyright 2012-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "reflect" + "testing" + "time" + + "github.com/nats-io/go-nats" + "github.com/nats-io/go-nats/encoders/builtin" +) + +func NewJsonEncodedConn(tl TestLogger) *nats.EncodedConn { + ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), nats.JSON_ENCODER) + if err != nil { + tl.Fatalf("Failed to create an encoded connection: %v\n", err) + } + return ec +} + +func TestEncBuiltinJsonMarshalString(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + testString := "Hello World!" + + ec.Subscribe("json_string", func(s string) { + if s != testString { + t.Fatalf("Received test string of '%s', wanted '%s'\n", s, testString) + } + ch <- true + }) + ec.Publish("json_string", testString) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinJsonMarshalEmptyString(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + ec.Subscribe("json_empty_string", func(s string) { + if s != "" { + t.Fatalf("Received test of '%v', wanted empty string\n", s) + } + ch <- true + }) + ec.Publish("json_empty_string", "") + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinJsonMarshalInt(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + testN := 22 + + ec.Subscribe("json_int", func(n int) { + if n != testN { + t.Fatalf("Received test int of '%d', wanted '%d'\n", n, testN) + } + ch <- true + }) + ec.Publish("json_int", testN) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinJsonMarshalBool(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + ec.Subscribe("json_bool", func(b bool) { + if !b { + t.Fatalf("Received test of '%v', wanted 'true'\n", b) + } + ch <- true + }) + ec.Publish("json_bool", true) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinJsonMarshalNull(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + + type TestType struct{} + ch := make(chan bool) + + var testValue *TestType + + ec.Subscribe("json_null", func(i interface{}) { + if i != nil { + t.Fatalf("Received test of '%v', wanted 'nil'\n", i) + } + ch <- true + }) + ec.Publish("json_null", testValue) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinJsonMarshalArray(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + + ch := make(chan bool) + + var a = []string{"a", "b", "c"} + + ec.Subscribe("json_array", func(v []string) { + if !reflect.DeepEqual(v, a) { + t.Fatalf("Received test of '%v', wanted '%v'\n", v, a) + } + ch <- true + }) + ec.Publish("json_array", a) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncBuiltinJsonMarshalEmptyArray(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + + ch := make(chan bool) + + var a []string + + ec.Subscribe("json_empty_array", func(v []string) { + if !reflect.DeepEqual(v, a) { + t.Fatalf("Received test of '%v', wanted '%v'\n", v, a) + } + ch <- true + }) + ec.Publish("json_empty_array", a) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +type person struct { + Name string + Address string + Age int + Children map[string]*person + Assets map[string]uint +} + +func TestEncBuiltinJsonMarshalStruct(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*person) + + me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + me.Assets = make(map[string]uint) + me.Assets["house"] = 1000 + me.Assets["car"] = 100 + + ec.Subscribe("json_struct", func(p *person) { + if !reflect.DeepEqual(p, me) { + t.Fatal("Did not receive the correct struct response") + } + ch <- true + }) + + ec.Publish("json_struct", me) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func BenchmarkJsonMarshalStruct(b *testing.B) { + me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*person) + + me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + encoder := &builtin.JsonEncoder{} + for n := 0; n < b.N; n++ { + if _, err := encoder.Encode("protobuf_test", me); err != nil { + b.Fatal("Couldn't serialize object", err) + } + } +} + +func BenchmarkPublishJsonStruct(b *testing.B) { + // stop benchmark for set-up + b.StopTimer() + + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(b) + defer ec.Close() + ch := make(chan bool) + + me := &person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*person) + + me.Children["sam"] = &person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + ec.Subscribe("json_struct", func(p *person) { + if !reflect.DeepEqual(p, me) { + b.Fatalf("Did not receive the correct struct response") + } + ch <- true + }) + + // resume benchmark + b.StartTimer() + + for n := 0; n < b.N; n++ { + ec.Publish("json_struct", me) + if e := Wait(ch); e != nil { + b.Fatal("Did not receive the message") + } + } + +} + +func TestEncBuiltinNotMarshableToJson(t *testing.T) { + je := &builtin.JsonEncoder{} + ch := make(chan bool) + _, err := je.Encode("foo", ch) + if err == nil { + t.Fatal("Expected an error when failing encoding") + } +} + +func TestEncBuiltinFailedEncodedPublish(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewJsonEncodedConn(t) + defer ec.Close() + + ch := make(chan bool) + err := ec.Publish("foo", ch) + if err == nil { + t.Fatal("Expected an error trying to publish a channel") + } + err = ec.PublishRequest("foo", "bar", ch) + if err == nil { + t.Fatal("Expected an error trying to publish a channel") + } + var cr chan bool + err = ec.Request("foo", ch, &cr, 1*time.Second) + if err == nil { + t.Fatal("Expected an error trying to publish a channel") + } + err = ec.LastError() + if err != nil { + t.Fatalf("Expected LastError to be nil: %q ", err) + } +} + +func TestEncBuiltinDecodeConditionals(t *testing.T) { + je := &builtin.JsonEncoder{} + + b, err := je.Encode("foo", 22) + if err != nil { + t.Fatalf("Expected no error when encoding, got %v\n", err) + } + var foo string + var bar []byte + err = je.Decode("foo", b, &foo) + if err != nil { + t.Fatalf("Expected no error when decoding, got %v\n", err) + } + err = je.Decode("foo", b, &bar) + if err != nil { + t.Fatalf("Expected no error when decoding, got %v\n", err) + } +} diff --git a/vendor/github.com/nats-io/gnatsd/util/tls.go b/vendor/github.com/nats-io/go-nats/test/main.go similarity index 74% rename from vendor/github.com/nats-io/gnatsd/util/tls.go rename to vendor/github.com/nats-io/go-nats/test/main.go index 87907eeb..3277158a 100644 --- a/vendor/github.com/nats-io/gnatsd/util/tls.go +++ b/vendor/github.com/nats-io/go-nats/test/main.go @@ -11,15 +11,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -// +build go1.8 - -package util +package test import ( - "crypto/tls" + "os" + "testing" ) -// CloneTLSConfig returns a copy of c. -func CloneTLSConfig(c *tls.Config) *tls.Config { - return c.Clone() +// TestMain runs all tests. Added since tests were moved to a separate package. +func TestMain(m *testing.M) { + // call flag.Parse() here if TestMain uses flags + os.Exit(m.Run()) } diff --git a/vendor/github.com/nats-io/go-nats/test/netchan_test.go b/vendor/github.com/nats-io/go-nats/test/netchan_test.go new file mode 100644 index 00000000..ff84499b --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/netchan_test.go @@ -0,0 +1,368 @@ +// Copyright 2013-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "fmt" + "runtime" + "testing" + "time" + + "github.com/nats-io/go-nats" +) + +func TestBadChan(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + if err := ec.BindSendChan("foo", "not a chan"); err == nil { + t.Fatalf("Expected an Error when sending a non-channel\n") + } + + if _, err := ec.BindRecvChan("foo", "not a chan"); err == nil { + t.Fatalf("Expected an Error when sending a non-channel\n") + } + + if err := ec.BindSendChan("foo", "not a chan"); err != nats.ErrChanArg { + t.Fatalf("Expected an ErrChanArg when sending a non-channel\n") + } + + if _, err := ec.BindRecvChan("foo", "not a chan"); err != nats.ErrChanArg { + t.Fatalf("Expected an ErrChanArg when sending a non-channel\n") + } +} + +func TestSimpleSendChan(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + recv := make(chan bool) + + numSent := int32(22) + ch := make(chan int32) + + if err := ec.BindSendChan("foo", ch); err != nil { + t.Fatalf("Failed to bind to a send channel: %v\n", err) + } + + ec.Subscribe("foo", func(num int32) { + if num != numSent { + t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent) + } + recv <- true + }) + + // Send to 'foo' + ch <- numSent + + if e := Wait(recv); e != nil { + if ec.LastError() != nil { + e = ec.LastError() + } + t.Fatalf("Did not receive the message: %s", e) + } + close(ch) +} + +func TestFailedChannelSend(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + nc := ec.Conn + ch := make(chan bool) + wch := make(chan bool) + + nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) { + wch <- true + } + + if err := ec.BindSendChan("foo", ch); err != nil { + t.Fatalf("Failed to bind to a receive channel: %v\n", err) + } + + nc.Flush() + + go func() { + time.Sleep(100 * time.Millisecond) + nc.Close() + }() + + func() { + for { + select { + case ch <- true: + case <-wch: + return + case <-time.After(time.Second): + t.Fatal("Failed to get async error cb") + } + } + }() + + ec = NewEConn(t) + defer ec.Close() + + nc = ec.Conn + bch := make(chan []byte) + + nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) { + wch <- true + } + + if err := ec.BindSendChan("foo", bch); err != nil { + t.Fatalf("Failed to bind to a receive channel: %v\n", err) + } + + buf := make([]byte, 2*1024*1024) + bch <- buf + + if e := Wait(wch); e != nil { + t.Fatal("Failed to call async err handler") + } +} + +func TestSimpleRecvChan(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + numSent := int32(22) + ch := make(chan int32) + + if _, err := ec.BindRecvChan("foo", ch); err != nil { + t.Fatalf("Failed to bind to a receive channel: %v\n", err) + } + + ec.Publish("foo", numSent) + + // Receive from 'foo' + select { + case num := <-ch: + if num != numSent { + t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent) + } + case <-time.After(1 * time.Second): + t.Fatalf("Failed to receive a value, timed-out\n") + } + close(ch) +} + +func TestQueueRecvChan(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + numSent := int32(22) + ch := make(chan int32) + + if _, err := ec.BindRecvQueueChan("foo", "bar", ch); err != nil { + t.Fatalf("Failed to bind to a queue receive channel: %v\n", err) + } + + ec.Publish("foo", numSent) + + // Receive from 'foo' + select { + case num := <-ch: + if num != numSent { + t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent) + } + case <-time.After(1 * time.Second): + t.Fatalf("Failed to receive a value, timed-out\n") + } + close(ch) +} + +func TestDecoderErrRecvChan(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + nc := ec.Conn + wch := make(chan bool) + + nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) { + wch <- true + } + + ch := make(chan *int32) + + if _, err := ec.BindRecvChan("foo", ch); err != nil { + t.Fatalf("Failed to bind to a send channel: %v\n", err) + } + + ec.Publish("foo", "Hello World") + + if e := Wait(wch); e != nil { + t.Fatal("Failed to call async err handler") + } +} + +func TestRecvChanPanicOnClosedChan(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + ch := make(chan int) + + if _, err := ec.BindRecvChan("foo", ch); err != nil { + t.Fatalf("Failed to bind to a send channel: %v\n", err) + } + + close(ch) + ec.Publish("foo", 22) + ec.Flush() +} + +func TestRecvChanAsyncLeakGoRoutines(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + // Call this to make sure that we have everything setup connection wise + ec.Flush() + + before := runtime.NumGoroutine() + + ch := make(chan int) + + if _, err := ec.BindRecvChan("foo", ch); err != nil { + t.Fatalf("Failed to bind to a send channel: %v\n", err) + } + + // Close the receive Channel + close(ch) + + // The publish will trigger the close and shutdown of the Go routines + ec.Publish("foo", 22) + ec.Flush() + + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + delta := (runtime.NumGoroutine() - before) + if delta > 0 { + return fmt.Errorf("Leaked Go routine(s) : %d, closing channel should have closed them", delta) + } + return nil + }) +} + +func TestRecvChanLeakGoRoutines(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + // Call this to make sure that we have everything setup connection wise + ec.Flush() + + before := runtime.NumGoroutine() + + ch := make(chan int) + + sub, err := ec.BindRecvChan("foo", ch) + if err != nil { + t.Fatalf("Failed to bind to a send channel: %v\n", err) + } + sub.Unsubscribe() + + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + delta := (runtime.NumGoroutine() - before) + if delta > 0 { + return fmt.Errorf("Leaked Go routine(s) : %d, closing channel should have closed them", delta) + } + return nil + }) +} + +func TestRecvChanMultipleMessages(t *testing.T) { + // Make sure we can receive more than one message. + // In response to #25, which is a bug from fixing #22. + + s := RunDefaultServer() + defer s.Shutdown() + + ec := NewEConn(t) + defer ec.Close() + + // Num to send, should == len of messages queued. + size := 10 + + ch := make(chan int, size) + + if _, err := ec.BindRecvChan("foo", ch); err != nil { + t.Fatalf("Failed to bind to a send channel: %v\n", err) + } + + for i := 0; i < size; i++ { + ec.Publish("foo", 22) + } + ec.Flush() + time.Sleep(10 * time.Millisecond) + + if lch := len(ch); lch != size { + t.Fatalf("Expected %d messages queued, got %d.", size, lch) + } +} + +func BenchmarkPublishSpeedViaChan(b *testing.B) { + b.StopTimer() + + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL) + if err != nil { + b.Fatalf("Could not connect: %v\n", err) + } + ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER) + if err != nil { + b.Fatalf("Failed creating encoded connection: %v\n", err) + } + defer ec.Close() + + ch := make(chan int32, 1024) + if err := ec.BindSendChan("foo", ch); err != nil { + b.Fatalf("Failed to bind to a send channel: %v\n", err) + } + + b.StartTimer() + + num := int32(22) + + for i := 0; i < b.N; i++ { + ch <- num + } + // Make sure they are all processed. + nc.Flush() + b.StopTimer() +} diff --git a/vendor/github.com/nats-io/go-nats/test/protobuf_test.go b/vendor/github.com/nats-io/go-nats/test/protobuf_test.go new file mode 100644 index 00000000..03c95d59 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/protobuf_test.go @@ -0,0 +1,139 @@ +// Copyright 2015-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "reflect" + "testing" + "time" + + "github.com/nats-io/go-nats" + + "github.com/nats-io/go-nats/encoders/protobuf" + pb "github.com/nats-io/go-nats/encoders/protobuf/testdata" +) + +func NewProtoEncodedConn(tl TestLogger) *nats.EncodedConn { + ec, err := nats.NewEncodedConn(NewConnection(tl, TEST_PORT), protobuf.PROTOBUF_ENCODER) + if err != nil { + tl.Fatalf("Failed to create an encoded connection: %v\n", err) + } + return ec +} + +func TestEncProtoMarshalStruct(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewProtoEncodedConn(t) + defer ec.Close() + ch := make(chan bool) + + me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*pb.Person) + + me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + ec.Subscribe("protobuf_test", func(p *pb.Person) { + if !reflect.DeepEqual(p, me) { + t.Fatal("Did not receive the correct protobuf response") + } + ch <- true + }) + + ec.Publish("protobuf_test", me) + if e := Wait(ch); e != nil { + t.Fatal("Did not receive the message") + } +} + +func TestEncProtoNilRequest(t *testing.T) { + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewProtoEncodedConn(t) + defer ec.Close() + + testPerson := &pb.Person{Name: "Anatolii", Age: 25, Address: "Ukraine, Nikolaev"} + + //Subscribe with empty interface shouldn't failed on empty message + ec.Subscribe("nil_test", func(_, reply string, _ interface{}) { + ec.Publish(reply, testPerson) + }) + + resp := new(pb.Person) + + //Request with nil argument shouldn't failed with nil argument + err := ec.Request("nil_test", nil, resp, 100*time.Millisecond) + ec.Flush() + + if err != nil { + t.Error("Fail to send empty message via encoded proto connection") + } + + if !reflect.DeepEqual(testPerson, resp) { + t.Error("Fail to receive encoded response") + } +} + +func BenchmarkProtobufMarshalStruct(b *testing.B) { + me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*pb.Person) + + me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + encoder := &protobuf.ProtobufEncoder{} + for n := 0; n < b.N; n++ { + if _, err := encoder.Encode("protobuf_test", me); err != nil { + b.Fatal("Couldn't serialize object", err) + } + } +} + +func BenchmarkPublishProtobufStruct(b *testing.B) { + // stop benchmark for set-up + b.StopTimer() + + s := RunServerOnPort(TEST_PORT) + defer s.Shutdown() + + ec := NewProtoEncodedConn(b) + defer ec.Close() + ch := make(chan bool) + + me := &pb.Person{Name: "derek", Age: 22, Address: "140 New Montgomery St"} + me.Children = make(map[string]*pb.Person) + + me.Children["sam"] = &pb.Person{Name: "sam", Age: 19, Address: "140 New Montgomery St"} + me.Children["meg"] = &pb.Person{Name: "meg", Age: 17, Address: "140 New Montgomery St"} + + ec.Subscribe("protobuf_test", func(p *pb.Person) { + if !reflect.DeepEqual(p, me) { + b.Fatalf("Did not receive the correct protobuf response") + } + ch <- true + }) + + // resume benchmark + b.StartTimer() + + for n := 0; n < b.N; n++ { + ec.Publish("protobuf_test", me) + if e := Wait(ch); e != nil { + b.Fatal("Did not receive the message") + } + } +} diff --git a/vendor/github.com/nats-io/go-nats/test/reconnect_test.go b/vendor/github.com/nats-io/go-nats/test/reconnect_test.go new file mode 100644 index 00000000..ed779113 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/reconnect_test.go @@ -0,0 +1,651 @@ +// Copyright 2013-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/nats-io/gnatsd/server" + "github.com/nats-io/go-nats" +) + +func startReconnectServer(t *testing.T) *server.Server { + return RunServerOnPort(22222) +} + +func TestReconnectTotalTime(t *testing.T) { + opts := nats.GetDefaultOptions() + totalReconnectTime := time.Duration(opts.MaxReconnect) * opts.ReconnectWait + if totalReconnectTime < (2 * time.Minute) { + t.Fatalf("Total reconnect time should be at least 2 mins: Currently %v\n", + totalReconnectTime) + } +} + +func TestReconnectDisallowedFlags(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + ch := make(chan bool) + opts := nats.GetDefaultOptions() + opts.Url = "nats://localhost:22222" + opts.AllowReconnect = false + opts.ClosedCB = func(_ *nats.Conn) { + ch <- true + } + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + ts.Shutdown() + + if e := Wait(ch); e != nil { + t.Fatal("Did not trigger ClosedCB correctly") + } +} + +func TestReconnectAllowedFlags(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + ch := make(chan bool) + dch := make(chan bool) + opts := nats.GetDefaultOptions() + opts.Url = "nats://localhost:22222" + opts.AllowReconnect = true + opts.MaxReconnect = 2 + opts.ReconnectWait = 1 * time.Second + + opts.ClosedCB = func(_ *nats.Conn) { + ch <- true + } + opts.DisconnectedCB = func(_ *nats.Conn) { + dch <- true + } + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + ts.Shutdown() + + // We want wait to timeout here, and the connection + // should not trigger the Close CB. + if e := WaitTime(ch, 500*time.Millisecond); e == nil { + t.Fatal("Triggered ClosedCB incorrectly") + } + + // We should wait to get the disconnected callback to ensure + // that we are in the process of reconnecting. + if e := Wait(dch); e != nil { + t.Fatal("DisconnectedCB should have been triggered") + } + + if !nc.IsReconnecting() { + t.Fatal("Expected to be in a reconnecting state") + } + + // clear the CloseCB since ch will block + nc.Opts.ClosedCB = nil +} + +var reconnectOpts = nats.Options{ + Url: "nats://localhost:22222", + AllowReconnect: true, + MaxReconnect: 10, + ReconnectWait: 100 * time.Millisecond, + Timeout: nats.DefaultTimeout, +} + +func TestConnCloseBreaksReconnectLoop(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + cch := make(chan bool) + + opts := reconnectOpts + // Bump the max reconnect attempts + opts.MaxReconnect = 100 + opts.ClosedCB = func(_ *nats.Conn) { + cch <- true + } + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + nc.Flush() + + // Shutdown the server + ts.Shutdown() + + // Wait a second, then close the connection + time.Sleep(time.Second) + + // Close the connection, this should break the reconnect loop. + // Do this in a go routine since the issue was that Close() + // would block until the reconnect loop is done. + go nc.Close() + + // Even on Windows (where a createConn takes more than a second) + // we should be able to break the reconnect loop with the following + // timeout. + if err := WaitTime(cch, 3*time.Second); err != nil { + t.Fatal("Did not get a closed callback") + } +} + +func TestBasicReconnectFunctionality(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + ch := make(chan bool) + dch := make(chan bool) + + opts := reconnectOpts + + opts.DisconnectedCB = func(_ *nats.Conn) { + dch <- true + } + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v\n", err) + } + defer nc.Close() + ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER) + if err != nil { + t.Fatalf("Failed to create an encoded connection: %v\n", err) + } + + testString := "bar" + ec.Subscribe("foo", func(s string) { + if s != testString { + t.Fatal("String doesn't match") + } + ch <- true + }) + ec.Flush() + + ts.Shutdown() + // server is stopped here... + + if err := Wait(dch); err != nil { + t.Fatalf("Did not get the disconnected callback on time\n") + } + + if err := ec.Publish("foo", testString); err != nil { + t.Fatalf("Failed to publish message: %v\n", err) + } + + ts = startReconnectServer(t) + defer ts.Shutdown() + + if err := ec.FlushTimeout(5 * time.Second); err != nil { + t.Fatalf("Error on Flush: %v", err) + } + + if e := Wait(ch); e != nil { + t.Fatal("Did not receive our message") + } + + expectedReconnectCount := uint64(1) + reconnectCount := ec.Conn.Stats().Reconnects + + if reconnectCount != expectedReconnectCount { + t.Fatalf("Reconnect count incorrect: %d vs %d\n", + reconnectCount, expectedReconnectCount) + } +} + +func TestExtendedReconnectFunctionality(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + opts := reconnectOpts + dch := make(chan bool) + opts.DisconnectedCB = func(_ *nats.Conn) { + dch <- true + } + rch := make(chan bool) + opts.ReconnectedCB = func(_ *nats.Conn) { + rch <- true + } + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER) + if err != nil { + t.Fatalf("Failed to create an encoded connection: %v\n", err) + } + testString := "bar" + received := int32(0) + + ec.Subscribe("foo", func(s string) { + atomic.AddInt32(&received, 1) + }) + + sub, _ := ec.Subscribe("foobar", func(s string) { + atomic.AddInt32(&received, 1) + }) + + ec.Publish("foo", testString) + ec.Flush() + + ts.Shutdown() + // server is stopped here.. + + // wait for disconnect + if e := WaitTime(dch, 2*time.Second); e != nil { + t.Fatal("Did not receive a disconnect callback message") + } + + // Sub while disconnected + ec.Subscribe("bar", func(s string) { + atomic.AddInt32(&received, 1) + }) + + // Unsub foobar while disconnected + sub.Unsubscribe() + + if err = ec.Publish("foo", testString); err != nil { + t.Fatalf("Received an error after disconnect: %v\n", err) + } + + if err = ec.Publish("bar", testString); err != nil { + t.Fatalf("Received an error after disconnect: %v\n", err) + } + + ts = startReconnectServer(t) + defer ts.Shutdown() + + // server is restarted here.. + // wait for reconnect + if e := WaitTime(rch, 2*time.Second); e != nil { + t.Fatal("Did not receive a reconnect callback message") + } + + if err = ec.Publish("foobar", testString); err != nil { + t.Fatalf("Received an error after server restarted: %v\n", err) + } + + if err = ec.Publish("foo", testString); err != nil { + t.Fatalf("Received an error after server restarted: %v\n", err) + } + + ch := make(chan bool) + ec.Subscribe("done", func(b bool) { + ch <- true + }) + ec.Publish("done", true) + + if e := Wait(ch); e != nil { + t.Fatal("Did not receive our message") + } + + // Sleep a bit to guarantee scheduler runs and process all subs. + time.Sleep(50 * time.Millisecond) + + if atomic.LoadInt32(&received) != 4 { + t.Fatalf("Received != %d, equals %d\n", 4, received) + } +} + +func TestQueueSubsOnReconnect(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + opts := reconnectOpts + + // Allow us to block on reconnect complete. + reconnectsDone := make(chan bool) + opts.ReconnectedCB = func(nc *nats.Conn) { + reconnectsDone <- true + } + + // Create connection + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v\n", err) + } + defer nc.Close() + + ec, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER) + if err != nil { + t.Fatalf("Failed to create an encoded connection: %v\n", err) + } + + // To hold results. + results := make(map[int]int) + var mu sync.Mutex + + // Make sure we got what we needed, 1 msg only and all seqnos accounted for.. + checkResults := func(numSent int) { + mu.Lock() + defer mu.Unlock() + + for i := 0; i < numSent; i++ { + if results[i] != 1 { + t.Fatalf("Received incorrect number of messages, [%d] for seq: %d\n", results[i], i) + } + } + + // Auto reset results map + results = make(map[int]int) + } + + subj := "foo.bar" + qgroup := "workers" + + cb := func(seqno int) { + mu.Lock() + defer mu.Unlock() + results[seqno] = results[seqno] + 1 + } + + // Create Queue Subscribers + ec.QueueSubscribe(subj, qgroup, cb) + ec.QueueSubscribe(subj, qgroup, cb) + + ec.Flush() + + // Helper function to send messages and check results. + sendAndCheckMsgs := func(numToSend int) { + for i := 0; i < numToSend; i++ { + ec.Publish(subj, i) + } + // Wait for processing. + ec.Flush() + time.Sleep(50 * time.Millisecond) + + // Check Results + checkResults(numToSend) + } + + // Base Test + sendAndCheckMsgs(10) + + // Stop and restart server + ts.Shutdown() + ts = startReconnectServer(t) + defer ts.Shutdown() + + if err := Wait(reconnectsDone); err != nil { + t.Fatal("Did not get the ReconnectedCB!") + } + + // Reconnect Base Test + sendAndCheckMsgs(10) +} + +func TestIsClosed(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + nc := NewConnection(t, 22222) + defer nc.Close() + + if nc.IsClosed() { + t.Fatalf("IsClosed returned true when the connection is still open.") + } + ts.Shutdown() + if nc.IsClosed() { + t.Fatalf("IsClosed returned true when the connection is still open.") + } + ts = startReconnectServer(t) + defer ts.Shutdown() + if nc.IsClosed() { + t.Fatalf("IsClosed returned true when the connection is still open.") + } + nc.Close() + if !nc.IsClosed() { + t.Fatalf("IsClosed returned false after Close() was called.") + } +} + +func TestIsReconnectingAndStatus(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + disconnectedch := make(chan bool) + reconnectch := make(chan bool) + opts := nats.GetDefaultOptions() + opts.Url = "nats://localhost:22222" + opts.AllowReconnect = true + opts.MaxReconnect = 10000 + opts.ReconnectWait = 100 * time.Millisecond + + opts.DisconnectedCB = func(_ *nats.Conn) { + disconnectedch <- true + } + opts.ReconnectedCB = func(_ *nats.Conn) { + reconnectch <- true + } + + // Connect, verify initial reconnecting state check, then stop the server + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + if nc.IsReconnecting() { + t.Fatalf("IsReconnecting returned true when the connection is still open.") + } + if status := nc.Status(); status != nats.CONNECTED { + t.Fatalf("Status returned %d when connected instead of CONNECTED", status) + } + ts.Shutdown() + + // Wait until we get the disconnected callback + if e := Wait(disconnectedch); e != nil { + t.Fatalf("Disconnect callback wasn't triggered: %v", e) + } + if !nc.IsReconnecting() { + t.Fatalf("IsReconnecting returned false when the client is reconnecting.") + } + if status := nc.Status(); status != nats.RECONNECTING { + t.Fatalf("Status returned %d when reconnecting instead of CONNECTED", status) + } + + ts = startReconnectServer(t) + defer ts.Shutdown() + + // Wait until we get the reconnect callback + if e := Wait(reconnectch); e != nil { + t.Fatalf("Reconnect callback wasn't triggered: %v", e) + } + if nc.IsReconnecting() { + t.Fatalf("IsReconnecting returned true after the connection was reconnected.") + } + if status := nc.Status(); status != nats.CONNECTED { + t.Fatalf("Status returned %d when reconnected instead of CONNECTED", status) + } + + // Close the connection, reconnecting should still be false + nc.Close() + if nc.IsReconnecting() { + t.Fatalf("IsReconnecting returned true after Close() was called.") + } + if status := nc.Status(); status != nats.CLOSED { + t.Fatalf("Status returned %d after Close() was called instead of CLOSED", status) + } +} + +func TestFullFlushChanDuringReconnect(t *testing.T) { + ts := startReconnectServer(t) + defer ts.Shutdown() + + reconnectch := make(chan bool) + + opts := nats.GetDefaultOptions() + opts.Url = "nats://localhost:22222" + opts.AllowReconnect = true + opts.MaxReconnect = 10000 + opts.ReconnectWait = 100 * time.Millisecond + + opts.ReconnectedCB = func(_ *nats.Conn) { + reconnectch <- true + } + + // Connect + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + // Channel used to make the go routine sending messages to stop. + stop := make(chan bool) + + // While connected, publish as fast as we can + go func() { + for i := 0; ; i++ { + _ = nc.Publish("foo", []byte("hello")) + + // Make sure we are sending at least flushChanSize (1024) messages + // before potentially pausing. + if i%2000 == 0 { + select { + case <-stop: + return + default: + time.Sleep(100 * time.Millisecond) + } + } + } + }() + + // Send a bit... + time.Sleep(500 * time.Millisecond) + + // Shut down the server + ts.Shutdown() + + // Continue sending while we are disconnected + time.Sleep(time.Second) + + // Restart the server + ts = startReconnectServer(t) + defer ts.Shutdown() + + // Wait for the reconnect CB to be invoked (but not for too long) + if e := WaitTime(reconnectch, 5*time.Second); e != nil { + t.Fatalf("Reconnect callback wasn't triggered: %v", e) + } +} + +func TestReconnectVerbose(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + o := nats.GetDefaultOptions() + o.Verbose = true + rch := make(chan bool) + o.ReconnectedCB = func(_ *nats.Conn) { + rch <- true + } + + nc, err := o.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + err = nc.Flush() + if err != nil { + t.Fatalf("Error during flush: %v", err) + } + + s.Shutdown() + s = RunDefaultServer() + defer s.Shutdown() + + if e := Wait(rch); e != nil { + t.Fatal("Should have reconnected ok") + } + + err = nc.Flush() + if err != nil { + t.Fatalf("Error during flush: %v", err) + } +} + +func TestReconnectBufSizeOption(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect("nats://localhost:4222", nats.ReconnectBufSize(32)) + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + if nc.Opts.ReconnectBufSize != 32 { + t.Fatalf("ReconnectBufSize should be 32 but it is %d", nc.Opts.ReconnectBufSize) + } +} + +func TestReconnectBufSize(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + o := nats.GetDefaultOptions() + o.ReconnectBufSize = 32 // 32 bytes + + dch := make(chan bool) + o.DisconnectedCB = func(_ *nats.Conn) { + dch <- true + } + + nc, err := o.Connect() + if err != nil { + t.Fatalf("Should have connected ok: %v", err) + } + defer nc.Close() + + err = nc.Flush() + if err != nil { + t.Fatalf("Error during flush: %v", err) + } + + // Force disconnected state. + s.Shutdown() + + if e := Wait(dch); e != nil { + t.Fatal("DisconnectedCB should have been triggered") + } + + msg := []byte("food") // 4 bytes paylaod, total proto is 16 bytes + // These should work, 2X16 = 32 + if err := nc.Publish("foo", msg); err != nil { + t.Fatalf("Failed to publish message: %v\n", err) + } + if err := nc.Publish("foo", msg); err != nil { + t.Fatalf("Failed to publish message: %v\n", err) + } + + // This should fail since we have exhausted the backing buffer. + if err := nc.Publish("foo", msg); err == nil { + t.Fatalf("Expected to fail to publish message: got no error\n") + } + nc.Buffered() +} diff --git a/vendor/github.com/nats-io/go-nats/test/sub_test.go b/vendor/github.com/nats-io/go-nats/test/sub_test.go new file mode 100644 index 00000000..6c6a8f39 --- /dev/null +++ b/vendor/github.com/nats-io/go-nats/test/sub_test.go @@ -0,0 +1,1495 @@ +// Copyright 2013-2018 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "fmt" + "runtime" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/nats-io/go-nats" +) + +// More advanced tests on subscriptions + +func TestServerAutoUnsub(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + received := int32(0) + max := int32(10) + + // Call this to make sure that we have everything setup connection wise + nc.Flush() + + base := runtime.NumGoroutine() + + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) { + atomic.AddInt32(&received, 1) + }) + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + sub.AutoUnsubscribe(int(max)) + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + nc.Flush() + time.Sleep(100 * time.Millisecond) + + if atomic.LoadInt32(&received) != max { + t.Fatalf("Received %d msgs, wanted only %d\n", received, max) + } + if sub.IsValid() { + t.Fatal("Expected subscription to be invalid after hitting max") + } + if err := sub.AutoUnsubscribe(10); err == nil { + t.Fatal("Calling AutoUnsubscribe() on closed subscription should fail") + } + waitFor(t, 2*time.Second, 100*time.Millisecond, func() error { + delta := (runtime.NumGoroutine() - base) + if delta > 0 { + return fmt.Errorf("%d Go routines still exist post max subscriptions hit", delta) + } + return nil + }) +} + +func TestClientSyncAutoUnsub(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + received := 0 + max := 10 + sub, _ := nc.SubscribeSync("foo") + sub.AutoUnsubscribe(max) + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + nc.Flush() + for { + _, err := sub.NextMsg(10 * time.Millisecond) + if err != nil { + if err != nats.ErrMaxMessages { + t.Fatalf("Expected '%v', but got: '%v'\n", nats.ErrBadSubscription, err.Error()) + } + break + } + received++ + } + if received != max { + t.Fatalf("Received %d msgs, wanted only %d\n", received, max) + } + if sub.IsValid() { + t.Fatal("Expected subscription to be invalid after hitting max") + } + if err := sub.AutoUnsubscribe(10); err == nil { + t.Fatal("Calling AutoUnsubscribe() ob closed subscription should fail") + } +} + +func TestClientASyncAutoUnsub(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + received := int32(0) + max := int32(10) + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) { + atomic.AddInt32(&received, 1) + }) + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + sub.AutoUnsubscribe(int(max)) + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + nc.Flush() + time.Sleep(10 * time.Millisecond) + + if atomic.LoadInt32(&received) != max { + t.Fatalf("Received %d msgs, wanted only %d\n", received, max) + } + if err := sub.AutoUnsubscribe(10); err == nil { + t.Fatal("Calling AutoUnsubscribe() on closed subscription should fail") + } +} + +func TestAutoUnsubAndReconnect(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + rch := make(chan bool) + + nc, err := nats.Connect(nats.DefaultURL, + nats.ReconnectWait(50*time.Millisecond), + nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true })) + if err != nil { + t.Fatalf("Unable to connect: %v", err) + } + defer nc.Close() + + received := int32(0) + max := int32(10) + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) { + atomic.AddInt32(&received, 1) + }) + if err != nil { + t.Fatalf("Failed to subscribe: %v", err) + } + sub.AutoUnsubscribe(int(max)) + + // Send less than the max + total := int(max / 2) + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + nc.Flush() + + // Restart the server + s.Shutdown() + s = RunDefaultServer() + defer s.Shutdown() + + // and wait to reconnect + if err := Wait(rch); err != nil { + t.Fatal("Failed to get the reconnect cb") + } + + // Now send more than the total max. + total = int(3 * max) + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + nc.Flush() + + // Wait a bit before checking. + time.Sleep(50 * time.Millisecond) + + // We should have received only up-to-max messages. + if atomic.LoadInt32(&received) != max { + t.Fatalf("Received %d msgs, wanted only %d\n", received, max) + } +} + +func TestAutoUnsubWithParallelNextMsgCalls(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + rch := make(chan bool, 1) + + nc, err := nats.Connect(nats.DefaultURL, + nats.ReconnectWait(50*time.Millisecond), + nats.ReconnectHandler(func(_ *nats.Conn) { rch <- true })) + if err != nil { + t.Fatalf("Unable to connect: %v", err) + } + defer nc.Close() + + numRoutines := 3 + max := 100 + total := max * 2 + received := int64(0) + + var wg sync.WaitGroup + + sub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatalf("Failed to subscribe: %v", err) + } + sub.AutoUnsubscribe(int(max)) + nc.Flush() + + wg.Add(numRoutines) + + for i := 0; i < numRoutines; i++ { + go func(s *nats.Subscription, idx int) { + for { + // The first to reach the max delivered will cause the + // subscription to be removed, which will kick out all + // other calls to NextMsg. So don't be afraid of the long + // timeout. + _, err := s.NextMsg(3 * time.Second) + if err != nil { + break + } + atomic.AddInt64(&received, 1) + } + wg.Done() + }(sub, i) + } + + msg := []byte("Hello") + for i := 0; i < max/2; i++ { + nc.Publish("foo", msg) + } + nc.Flush() + + s.Shutdown() + s = RunDefaultServer() + defer s.Shutdown() + + // Make sure we got the reconnected cb + if err := Wait(rch); err != nil { + t.Fatal("Failed to get reconnected cb") + } + + for i := 0; i < total; i++ { + nc.Publish("foo", msg) + } + nc.Flush() + + wg.Wait() + if atomic.LoadInt64(&received) != int64(max) { + t.Fatalf("Wrong number of received msg: %v instead of %v", atomic.LoadInt64(&received), max) + } +} + +func TestAutoUnsubscribeFromCallback(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc, err := nats.Connect(nats.DefaultURL) + if err != nil { + t.Fatalf("Unable to connect: %v", err) + } + defer nc.Close() + + max := 10 + resetUnsubMark := int64(max / 2) + limit := int64(100) + received := int64(0) + + msg := []byte("Hello") + + // Auto-unsubscribe within the callback with a value lower + // than what was already received. + + sub, err := nc.Subscribe("foo", func(m *nats.Msg) { + r := atomic.AddInt64(&received, 1) + if r == resetUnsubMark { + m.Sub.AutoUnsubscribe(int(r - 1)) + nc.Flush() + } + if r == limit { + // Something went wrong... fail now + t.Fatal("Got more messages than expected") + } + nc.Publish("foo", msg) + }) + if err != nil { + t.Fatalf("Failed to subscribe: %v", err) + } + sub.AutoUnsubscribe(int(max)) + nc.Flush() + + // Trigger the first message, the other are sent from the callback. + nc.Publish("foo", msg) + nc.Flush() + + waitFor(t, time.Second, 100*time.Millisecond, func() error { + recv := atomic.LoadInt64(&received) + if recv != resetUnsubMark { + return fmt.Errorf("Wrong number of received messages. Original max was %v reset to %v, actual received: %v", + max, resetUnsubMark, recv) + } + return nil + }) + + // Now check with AutoUnsubscribe with higher value than original + received = int64(0) + newMax := int64(2 * max) + + sub, err = nc.Subscribe("foo", func(m *nats.Msg) { + r := atomic.AddInt64(&received, 1) + if r == resetUnsubMark { + m.Sub.AutoUnsubscribe(int(newMax)) + nc.Flush() + } + if r == limit { + // Something went wrong... fail now + t.Fatal("Got more messages than expected") + } + nc.Publish("foo", msg) + }) + if err != nil { + t.Fatalf("Failed to subscribe: %v", err) + } + sub.AutoUnsubscribe(int(max)) + nc.Flush() + + // Trigger the first message, the other are sent from the callback. + nc.Publish("foo", msg) + nc.Flush() + + waitFor(t, time.Second, 100*time.Millisecond, func() error { + recv := atomic.LoadInt64(&received) + if recv != newMax { + return fmt.Errorf("Wrong number of received messages. Original max was %v reset to %v, actual received: %v", + max, newMax, recv) + } + return nil + }) +} + +func TestCloseSubRelease(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, _ := nc.SubscribeSync("foo") + start := time.Now() + go func() { + time.Sleep(5 * time.Millisecond) + nc.Close() + }() + _, err := sub.NextMsg(50 * time.Millisecond) + if err == nil { + t.Fatalf("Expected an error from NextMsg") + } + elapsed := time.Since(start) + + // On Windows, the minimum waitTime is at least 15ms. + if elapsed > 20*time.Millisecond { + t.Fatalf("Too much time has elapsed to release NextMsg: %dms", + (elapsed / time.Millisecond)) + } +} + +func TestIsValidSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatalf("Error on subscribe: %v", err) + } + if !sub.IsValid() { + t.Fatalf("Subscription should be valid") + } + for i := 0; i < 10; i++ { + nc.Publish("foo", []byte("Hello")) + } + nc.Flush() + _, err = sub.NextMsg(200 * time.Millisecond) + if err != nil { + t.Fatalf("NextMsg returned an error") + } + sub.Unsubscribe() + _, err = sub.NextMsg(200 * time.Millisecond) + if err == nil { + t.Fatalf("NextMsg should have returned an error") + } +} + +func TestSlowSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, _ := nc.SubscribeSync("foo") + sub.SetPendingLimits(100, 1024) + + for i := 0; i < 200; i++ { + nc.Publish("foo", []byte("Hello")) + } + timeout := 5 * time.Second + start := time.Now() + nc.FlushTimeout(timeout) + elapsed := time.Since(start) + if elapsed >= timeout { + t.Fatalf("Flush did not return before timeout: %d > %d", elapsed, timeout) + } + // Make sure NextMsg returns an error to indicate slow consumer + _, err := sub.NextMsg(200 * time.Millisecond) + if err == nil { + t.Fatalf("NextMsg did not return an error") + } +} + +func TestSlowChanSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + ch := make(chan *nats.Msg, 64) + sub, _ := nc.ChanSubscribe("foo", ch) + sub.SetPendingLimits(100, 1024) + + for i := 0; i < 200; i++ { + nc.Publish("foo", []byte("Hello")) + } + timeout := 5 * time.Second + start := time.Now() + nc.FlushTimeout(timeout) + elapsed := time.Since(start) + if elapsed >= timeout { + t.Fatalf("Flush did not return before timeout: %d > %d", elapsed, timeout) + } +} + +func TestSlowAsyncSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + bch := make(chan bool) + + sub, _ := nc.Subscribe("foo", func(_ *nats.Msg) { + // block to back us up.. + <-bch + }) + // Make sure these are the defaults + pm, pb, _ := sub.PendingLimits() + if pm != nats.DefaultSubPendingMsgsLimit { + t.Fatalf("Pending limit for number of msgs incorrect, expected %d, got %d\n", nats.DefaultSubPendingMsgsLimit, pm) + } + if pb != nats.DefaultSubPendingBytesLimit { + t.Fatalf("Pending limit for number of bytes incorrect, expected %d, got %d\n", nats.DefaultSubPendingBytesLimit, pb) + } + + // Set new limits + pml := 100 + pbl := 1024 * 1024 + + sub.SetPendingLimits(pml, pbl) + + // Make sure the set is correct + pm, pb, _ = sub.PendingLimits() + if pm != pml { + t.Fatalf("Pending limit for number of msgs incorrect, expected %d, got %d\n", pml, pm) + } + if pb != pbl { + t.Fatalf("Pending limit for number of bytes incorrect, expected %d, got %d\n", pbl, pb) + } + + for i := 0; i < (int(pml) + 100); i++ { + nc.Publish("foo", []byte("Hello")) + } + + timeout := 5 * time.Second + start := time.Now() + err := nc.FlushTimeout(timeout) + elapsed := time.Since(start) + if elapsed >= timeout { + t.Fatalf("Flush did not return before timeout") + } + // We want flush to work, so expect no error for it. + if err != nil { + t.Fatalf("Expected no error from Flush()\n") + } + if nc.LastError() != nats.ErrSlowConsumer { + t.Fatal("Expected LastError to indicate slow consumer") + } + // release the sub + bch <- true +} + +func TestAsyncErrHandler(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + opts := nats.GetDefaultOptions() + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Could not connect to server: %v\n", err) + } + defer nc.Close() + + subj := "async_test" + bch := make(chan bool) + + sub, err := nc.Subscribe(subj, func(_ *nats.Msg) { + // block to back us up.. + <-bch + }) + if err != nil { + t.Fatalf("Could not subscribe: %v\n", err) + } + + limit := 10 + toSend := 100 + + // Limit internal subchan length to trip condition easier. + sub.SetPendingLimits(limit, 1024) + + ch := make(chan bool) + + aeCalled := int64(0) + + nc.SetErrorHandler(func(c *nats.Conn, s *nats.Subscription, e error) { + atomic.AddInt64(&aeCalled, 1) + + if s != sub { + t.Fatal("Did not receive proper subscription") + } + if e != nats.ErrSlowConsumer { + t.Fatalf("Did not receive proper error: %v vs %v", e, nats.ErrSlowConsumer) + } + // Suppress additional calls + if atomic.LoadInt64(&aeCalled) == 1 { + // release the sub + defer close(bch) + // release the test + ch <- true + } + }) + + b := []byte("Hello World!") + // First one trips the ch wait in subscription callback. + nc.Publish(subj, b) + nc.Flush() + for i := 0; i < toSend; i++ { + nc.Publish(subj, b) + } + if err := nc.Flush(); err != nil { + t.Fatalf("Got an error on Flush:%v", err) + } + + if e := Wait(ch); e != nil { + t.Fatal("Failed to call async err handler") + } + // Make sure dropped stats is correct. + if d, _ := sub.Dropped(); d != toSend-limit+1 { + t.Fatalf("Expected Dropped to be %d, got %d", toSend-limit+1, d) + } + if ae := atomic.LoadInt64(&aeCalled); ae != 1 { + t.Fatalf("Expected err handler to be called only once, got %d", ae) + } + + sub.Unsubscribe() + if _, err := sub.Dropped(); err == nil { + t.Fatal("Calling Dropped() on closed subscription should fail") + } +} + +func TestAsyncErrHandlerChanSubscription(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + opts := nats.GetDefaultOptions() + + nc, err := opts.Connect() + if err != nil { + t.Fatalf("Could not connect to server: %v", err) + } + defer nc.Close() + + subj := "chan_test" + + limit := 10 + toSend := 100 + + // Create our own channel. + mch := make(chan *nats.Msg, limit) + sub, err := nc.ChanSubscribe(subj, mch) + if err != nil { + t.Fatalf("Could not subscribe: %v", err) + } + ch := make(chan bool) + aeCalled := int64(0) + + nc.SetErrorHandler(func(c *nats.Conn, s *nats.Subscription, e error) { + atomic.AddInt64(&aeCalled, 1) + if e != nats.ErrSlowConsumer { + t.Fatalf("Did not receive proper error: %v vs %v", + e, nats.ErrSlowConsumer) + } + // Suppress additional calls + if atomic.LoadInt64(&aeCalled) == 1 { + // release the test + ch <- true + } + }) + + b := []byte("Hello World!") + for i := 0; i < toSend; i++ { + nc.Publish(subj, b) + } + nc.Flush() + + if e := Wait(ch); e != nil { + t.Fatal("Failed to call async err handler") + } + // Make sure dropped stats is correct. + if d, _ := sub.Dropped(); d != toSend-limit { + t.Fatalf("Expected Dropped to be %d, go %d", toSend-limit, d) + } + if ae := atomic.LoadInt64(&aeCalled); ae != 1 { + t.Fatalf("Expected err handler to be called once, got %d", ae) + } + + sub.Unsubscribe() + if _, err := sub.Dropped(); err == nil { + t.Fatal("Calling Dropped() on closed subscription should fail") + } +} + +// Test to make sure that we can send and async receive messages on +// different subjects within a callback. +func TestAsyncSubscriberStarvation(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Helper + nc.Subscribe("helper", func(m *nats.Msg) { + nc.Publish(m.Reply, []byte("Hello")) + }) + + ch := make(chan bool) + + // Kickoff + nc.Subscribe("start", func(m *nats.Msg) { + // Helper Response + response := nats.NewInbox() + nc.Subscribe(response, func(_ *nats.Msg) { + ch <- true + }) + nc.PublishRequest("helper", response, []byte("Help Me!")) + }) + + nc.Publish("start", []byte("Begin")) + nc.Flush() + + if e := Wait(ch); e != nil { + t.Fatal("Was stalled inside of callback waiting on another callback") + } +} + +func TestAsyncSubscribersOnClose(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + toSend := 10 + callbacks := int32(0) + ch := make(chan bool, toSend) + + nc.Subscribe("foo", func(_ *nats.Msg) { + atomic.AddInt32(&callbacks, 1) + <-ch + }) + + for i := 0; i < toSend; i++ { + nc.Publish("foo", []byte("Hello World!")) + } + nc.Flush() + time.Sleep(10 * time.Millisecond) + nc.Close() + + // Release callbacks + for i := 1; i < toSend; i++ { + ch <- true + } + + // Wait for some time. + time.Sleep(10 * time.Millisecond) + seen := atomic.LoadInt32(&callbacks) + if seen != 1 { + t.Fatalf("Expected only one callback, received %d callbacks", seen) + } +} + +func TestNextMsgCallOnAsyncSub(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + sub, err := nc.Subscribe("foo", func(_ *nats.Msg) { + }) + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + _, err = sub.NextMsg(time.Second) + if err == nil { + t.Fatal("Expected an error call NextMsg() on AsyncSubscriber") + } +} + +func TestNextMsgCallOnClosedSub(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + sub, err := nc.SubscribeSync("foo") + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + + if err = sub.Unsubscribe(); err != nil { + t.Fatal("Unsubscribe failed with err:", err) + } + + _, err = sub.NextMsg(time.Second) + if err == nil { + t.Fatal("Expected an error calling NextMsg() on closed subscription") + } else if err != nats.ErrBadSubscription { + t.Fatalf("Expected '%v', but got: '%v'", nats.ErrBadSubscription, err.Error()) + } +} + +func TestChanSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Create our own channel. + ch := make(chan *nats.Msg, 128) + + // Channel is mandatory + if _, err := nc.ChanSubscribe("foo", nil); err == nil { + t.Fatal("Creating subscription without channel should have failed") + } + + _, err := nc.ChanSubscribe("foo", ch) + if err != nil { + t.Fatal("Failed to subscribe: ", err) + } + + // Send some messages to ourselves. + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + + received := 0 + tm := time.NewTimer(5 * time.Second) + defer tm.Stop() + + // Go ahead and receive + for { + select { + case _, ok := <-ch: + if !ok { + t.Fatalf("Got an error reading from channel") + } + case <-tm.C: + t.Fatalf("Timed out waiting on messages") + } + received++ + if received >= total { + return + } + } +} + +func TestChanQueueSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Create our own channel. + ch1 := make(chan *nats.Msg, 64) + ch2 := make(chan *nats.Msg, 64) + + nc.ChanQueueSubscribe("foo", "bar", ch1) + nc.ChanQueueSubscribe("foo", "bar", ch2) + + // Send some messages to ourselves. + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + + received := 0 + tm := time.NewTimer(5 * time.Second) + defer tm.Stop() + + chk := func(ok bool) { + if !ok { + t.Fatalf("Got an error reading from channel") + } else { + received++ + } + } + + // Go ahead and receive + for { + select { + case _, ok := <-ch1: + chk(ok) + case _, ok := <-ch2: + chk(ok) + case <-tm.C: + t.Fatalf("Timed out waiting on messages") + } + if received >= total { + return + } + } +} + +func TestChanSubscriberPendingLimits(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + ncp := NewDefaultConnection(t) + defer ncp.Close() + + // There was a defect that prevented to receive more than + // the default pending message limit. Trying to send more + // than this limit. + total := nats.DefaultSubPendingMsgsLimit + 100 + + for typeSubs := 0; typeSubs < 3; typeSubs++ { + + func() { + // Create our own channel. + ch := make(chan *nats.Msg, total) + + var err error + var sub *nats.Subscription + switch typeSubs { + case 0: + sub, err = nc.ChanSubscribe("foo", ch) + case 1: + sub, err = nc.ChanQueueSubscribe("foo", "bar", ch) + case 2: + sub, err = nc.QueueSubscribeSyncWithChan("foo", "bar", ch) + } + if err != nil { + t.Fatalf("Unexpected error on subscribe: %v", err) + } + defer sub.Unsubscribe() + nc.Flush() + + // Send some messages + for i := 0; i < total; i++ { + if err := ncp.Publish("foo", []byte("Hello")); err != nil { + t.Fatalf("Unexpected error on publish: %v", err) + } + } + + received := 0 + tm := time.NewTimer(10 * time.Second) + defer tm.Stop() + + chk := func(ok bool) { + if !ok { + t.Fatalf("Got an error reading from channel") + } else { + received++ + } + } + + // Go ahead and receive + for { + select { + case _, ok := <-ch: + chk(ok) + if received >= total { + return + } + case <-tm.C: + t.Fatalf("Timed out waiting on messages for test %d, received %d", typeSubs, received) + } + } + }() + } +} + +func TestQueueChanQueueSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Create our own channel. + ch1 := make(chan *nats.Msg, 64) + ch2 := make(chan *nats.Msg, 64) + + nc.QueueSubscribeSyncWithChan("foo", "bar", ch1) + nc.QueueSubscribeSyncWithChan("foo", "bar", ch2) + + // Send some messages to ourselves. + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + + recv1 := 0 + recv2 := 0 + tm := time.NewTimer(5 * time.Second) + defer tm.Stop() + runTimer := time.NewTimer(500 * time.Millisecond) + defer runTimer.Stop() + + chk := func(ok bool, which int) { + if !ok { + t.Fatalf("Got an error reading from channel") + } else { + if which == 1 { + recv1++ + } else { + recv2++ + } + } + } + + // Go ahead and receive +recvLoop: + for { + select { + case _, ok := <-ch1: + chk(ok, 1) + case _, ok := <-ch2: + chk(ok, 2) + case <-tm.C: + t.Fatalf("Timed out waiting on messages") + case <-runTimer.C: + break recvLoop + } + } + + if recv1+recv2 > total { + t.Fatalf("Received more messages than expected: %v vs %v", (recv1 + recv2), total) + } +} + +func TestUnsubscribeChanOnSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Create our own channel. + ch := make(chan *nats.Msg, 8) + sub, _ := nc.ChanSubscribe("foo", ch) + + // Send some messages to ourselves. + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + + sub.Unsubscribe() + for len(ch) > 0 { + <-ch + } + // Make sure we can send to the channel still. + // Test that we do not close it. + ch <- &nats.Msg{} +} + +func TestCloseChanOnSubscriber(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Create our own channel. + ch := make(chan *nats.Msg, 8) + nc.ChanSubscribe("foo", ch) + + // Send some messages to ourselves. + total := 100 + for i := 0; i < total; i++ { + nc.Publish("foo", []byte("Hello")) + } + + nc.Close() + for len(ch) > 0 { + <-ch + } + // Make sure we can send to the channel still. + // Test that we do not close it. + ch <- &nats.Msg{} +} + +func TestAsyncSubscriptionPending(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Send some messages to ourselves. + total := 100 + msg := []byte("0123456789") + + inCb := make(chan bool) + block := make(chan bool) + + sub, _ := nc.Subscribe("foo", func(_ *nats.Msg) { + inCb <- true + <-block + }) + defer sub.Unsubscribe() + + for i := 0; i < total; i++ { + nc.Publish("foo", msg) + } + nc.Flush() + + // Wait that a message is received, so checks are safe + if err := Wait(inCb); err != nil { + t.Fatal("No message received") + } + + // Test old way + q, _ := sub.QueuedMsgs() + if q != total && q != total-1 { + t.Fatalf("Expected %d or %d, got %d", total, total-1, q) + } + + // New way, make sure the same and check bytes. + m, b, _ := sub.Pending() + mlen := len(msg) + totalSize := total * mlen + + if m != total && m != total-1 { + t.Fatalf("Expected msgs of %d or %d, got %d", total, total-1, m) + } + if b != totalSize && b != totalSize-mlen { + t.Fatalf("Expected bytes of %d or %d, got %d", + totalSize, totalSize-mlen, b) + } + + // Make sure max has been set. Since we block after the first message is + // received, MaxPending should be >= total - 1 and <= total + mm, bm, _ := sub.MaxPending() + if mm < total-1 || mm > total { + t.Fatalf("Expected max msgs (%d) to be between %d and %d", + mm, total-1, total) + } + if bm < totalSize-mlen || bm > totalSize { + t.Fatalf("Expected max bytes (%d) to be between %d and %d", + bm, totalSize, totalSize-mlen) + } + // Check that clear works. + sub.ClearMaxPending() + mm, bm, _ = sub.MaxPending() + if mm != 0 { + t.Fatalf("Expected max msgs to be 0 vs %d after clearing", mm) + } + if bm != 0 { + t.Fatalf("Expected max bytes to be 0 vs %d after clearing", bm) + } + + close(block) + sub.Unsubscribe() + + // These calls should fail once the subscription is closed. + if _, _, err := sub.Pending(); err == nil { + t.Fatal("Calling Pending() on closed subscription should fail") + } + if _, _, err := sub.MaxPending(); err == nil { + t.Fatal("Calling MaxPending() on closed subscription should fail") + } + if err := sub.ClearMaxPending(); err == nil { + t.Fatal("Calling ClearMaxPending() on closed subscription should fail") + } +} + +func TestAsyncSubscriptionPendingDrain(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Send some messages to ourselves. + total := 100 + msg := []byte("0123456789") + + sub, _ := nc.Subscribe("foo", func(_ *nats.Msg) {}) + defer sub.Unsubscribe() + + for i := 0; i < total; i++ { + nc.Publish("foo", msg) + } + nc.Flush() + + // Wait for all delivered. + for d, _ := sub.Delivered(); d != int64(total); d, _ = sub.Delivered() { + time.Sleep(10 * time.Millisecond) + } + + m, b, _ := sub.Pending() + if m != 0 { + t.Fatalf("Expected msgs of 0, got %d", m) + } + if b != 0 { + t.Fatalf("Expected bytes of 0, got %d", b) + } + + sub.Unsubscribe() + if _, err := sub.Delivered(); err == nil { + t.Fatal("Calling Delivered() on closed subscription should fail") + } +} + +func TestSyncSubscriptionPendingDrain(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + // Send some messages to ourselves. + total := 100 + msg := []byte("0123456789") + + sub, _ := nc.SubscribeSync("foo") + defer sub.Unsubscribe() + + for i := 0; i < total; i++ { + nc.Publish("foo", msg) + } + nc.Flush() + + // Wait for all delivered. + for d, _ := sub.Delivered(); d != int64(total); d, _ = sub.Delivered() { + sub.NextMsg(10 * time.Millisecond) + } + + m, b, _ := sub.Pending() + if m != 0 { + t.Fatalf("Expected msgs of 0, got %d", m) + } + if b != 0 { + t.Fatalf("Expected bytes of 0, got %d", b) + } + + sub.Unsubscribe() + if _, err := sub.Delivered(); err == nil { + t.Fatal("Calling Delivered() on closed subscription should fail") + } +} + +func TestSyncSubscriptionPending(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, _ := nc.SubscribeSync("foo") + defer sub.Unsubscribe() + + // Send some messages to ourselves. + total := 100 + msg := []byte("0123456789") + for i := 0; i < total; i++ { + nc.Publish("foo", msg) + } + nc.Flush() + + // Test old way + q, _ := sub.QueuedMsgs() + if q != total && q != total-1 { + t.Fatalf("Expected %d or %d, got %d", total, total-1, q) + } + + // New way, make sure the same and check bytes. + m, b, _ := sub.Pending() + mlen := len(msg) + + if m != total { + t.Fatalf("Expected msgs of %d, got %d", total, m) + } + if b != total*mlen { + t.Fatalf("Expected bytes of %d, got %d", total*mlen, b) + } + + // Now drain some down and make sure pending is correct + for i := 0; i < total-1; i++ { + sub.NextMsg(10 * time.Millisecond) + } + m, b, _ = sub.Pending() + if m != 1 { + t.Fatalf("Expected msgs of 1, got %d", m) + } + if b != mlen { + t.Fatalf("Expected bytes of %d, got %d", mlen, b) + } +} + +func TestSetPendingLimits(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + payload := []byte("hello") + payloadLen := len(payload) + toSend := 100 + + var sub *nats.Subscription + + // Check for invalid values + invalid := func() error { + if err := sub.SetPendingLimits(0, 1); err == nil { + return fmt.Errorf("Setting limit with 0 should fail") + } + if err := sub.SetPendingLimits(1, 0); err == nil { + return fmt.Errorf("Setting limit with 0 should fail") + } + return nil + } + // function to send messages + send := func(subject string, count int) { + for i := 0; i < count; i++ { + if err := nc.Publish(subject, payload); err != nil { + t.Fatalf("Unexpected error on publish: %v", err) + } + } + nc.Flush() + } + + // Check pending vs expected values + var limitCount, limitBytes int + var expectedCount, expectedBytes int + checkPending := func() error { + lc, lb, err := sub.PendingLimits() + if err != nil { + return err + } + if lc != limitCount || lb != limitBytes { + return fmt.Errorf("Unexpected limits, expected %v msgs %v bytes, got %v msgs %v bytes", + limitCount, limitBytes, lc, lb) + } + msgs, bytes, err := sub.Pending() + if err != nil { + return fmt.Errorf("Unexpected error getting pending counts: %v", err) + } + if (msgs != expectedCount && msgs != expectedCount-1) || + (bytes != expectedBytes && bytes != expectedBytes-payloadLen) { + return fmt.Errorf("Unexpected counts, expected %v msgs %v bytes, got %v msgs %v bytes", + expectedCount, expectedBytes, msgs, bytes) + } + return nil + } + + recv := make(chan bool) + block := make(chan bool) + cb := func(m *nats.Msg) { + recv <- true + <-block + m.Sub.Unsubscribe() + } + subj := "foo" + sub, err := nc.Subscribe(subj, cb) + if err != nil { + t.Fatalf("Unexpected error on subscribe: %v", err) + } + defer sub.Unsubscribe() + if err := invalid(); err != nil { + t.Fatalf("%v", err) + } + // Check we apply limit only for size + limitCount = -1 + limitBytes = (toSend / 2) * payloadLen + if err := sub.SetPendingLimits(limitCount, limitBytes); err != nil { + t.Fatalf("Unexpected error setting limits: %v", err) + } + // Send messages + send(subj, toSend) + // Wait for message to be received + if err := Wait(recv); err != nil { + t.Fatal("Did not get our message") + } + expectedBytes = limitBytes + expectedCount = limitBytes / payloadLen + if err := checkPending(); err != nil { + t.Fatalf("%v", err) + } + // Release callback + block <- true + + subj = "bar" + sub, err = nc.Subscribe(subj, cb) + if err != nil { + t.Fatalf("Unexpected error on subscribe: %v", err) + } + defer sub.Unsubscribe() + // Check we apply limit only for count + limitCount = toSend / 4 + limitBytes = -1 + if err := sub.SetPendingLimits(limitCount, limitBytes); err != nil { + t.Fatalf("Unexpected error setting limits: %v", err) + } + // Send messages + send(subj, toSend) + // Wait for message to be received + if err := Wait(recv); err != nil { + t.Fatal("Did not get our message") + } + expectedCount = limitCount + expectedBytes = limitCount * payloadLen + if err := checkPending(); err != nil { + t.Fatalf("%v", err) + } + // Release callback + block <- true + + subj = "baz" + sub, err = nc.SubscribeSync(subj) + if err != nil { + t.Fatalf("Unexpected error on subscribe: %v", err) + } + defer sub.Unsubscribe() + if err := invalid(); err != nil { + t.Fatalf("%v", err) + } + // Check we apply limit only for size + limitCount = -1 + limitBytes = (toSend / 2) * payloadLen + if err := sub.SetPendingLimits(limitCount, limitBytes); err != nil { + t.Fatalf("Unexpected error setting limits: %v", err) + } + // Send messages + send(subj, toSend) + expectedBytes = limitBytes + expectedCount = limitBytes / payloadLen + if err := checkPending(); err != nil { + t.Fatalf("%v", err) + } + sub.Unsubscribe() + nc.Flush() + + subj = "boz" + sub, err = nc.SubscribeSync(subj) + if err != nil { + t.Fatalf("Unexpected error on subscribe: %v", err) + } + defer sub.Unsubscribe() + // Check we apply limit only for count + limitCount = toSend / 4 + limitBytes = -1 + if err := sub.SetPendingLimits(limitCount, limitBytes); err != nil { + t.Fatalf("Unexpected error setting limits: %v", err) + } + // Send messages + send(subj, toSend) + expectedCount = limitCount + expectedBytes = limitCount * payloadLen + if err := checkPending(); err != nil { + t.Fatalf("%v", err) + } + sub.Unsubscribe() + nc.Flush() +} + +func TestSubscriptionTypes(t *testing.T) { + s := RunDefaultServer() + defer s.Shutdown() + + nc := NewDefaultConnection(t) + defer nc.Close() + + sub, _ := nc.Subscribe("foo", func(_ *nats.Msg) {}) + defer sub.Unsubscribe() + if st := sub.Type(); st != nats.AsyncSubscription { + t.Fatalf("Expected AsyncSubscription, got %v", st) + } + // Check Pending + if err := sub.SetPendingLimits(1, 100); err != nil { + t.Fatalf("We should be able to SetPendingLimits()") + } + if _, _, err := sub.Pending(); err != nil { + t.Fatalf("We should be able to call Pending()") + } + sub.Unsubscribe() + if err := sub.SetPendingLimits(1, 100); err == nil { + t.Fatal("Calling SetPendingLimits() on closed subscription should fail") + } + if _, _, err := sub.PendingLimits(); err == nil { + t.Fatal("Calling PendingLimits() on closed subscription should fail") + } + + sub, _ = nc.SubscribeSync("foo") + defer sub.Unsubscribe() + if st := sub.Type(); st != nats.SyncSubscription { + t.Fatalf("Expected SyncSubscription, got %v", st) + } + // Check Pending + if err := sub.SetPendingLimits(1, 100); err != nil { + t.Fatalf("We should be able to SetPendingLimits()") + } + if _, _, err := sub.Pending(); err != nil { + t.Fatalf("We should be able to call Pending()") + } + sub.Unsubscribe() + if err := sub.SetPendingLimits(1, 100); err == nil { + t.Fatal("Calling SetPendingLimits() on closed subscription should fail") + } + if _, _, err := sub.PendingLimits(); err == nil { + t.Fatal("Calling PendingLimits() on closed subscription should fail") + } + + sub, _ = nc.ChanSubscribe("foo", make(chan *nats.Msg)) + defer sub.Unsubscribe() + if st := sub.Type(); st != nats.ChanSubscription { + t.Fatalf("Expected ChanSubscription, got %v", st) + } + // Check Pending + if err := sub.SetPendingLimits(1, 100); err == nil { + t.Fatalf("We should NOT be able to SetPendingLimits() on ChanSubscriber") + } + if _, _, err := sub.Pending(); err == nil { + t.Fatalf("We should NOT be able to call Pending() on ChanSubscriber") + } + if _, _, err := sub.MaxPending(); err == nil { + t.Fatalf("We should NOT be able to call MaxPending() on ChanSubscriber") + } + if err := sub.ClearMaxPending(); err == nil { + t.Fatalf("We should NOT be able to call ClearMaxPending() on ChanSubscriber") + } + if _, _, err := sub.PendingLimits(); err == nil { + t.Fatalf("We should NOT be able to call PendingLimits() on ChanSubscriber") + } + +} diff --git a/vendor/github.com/nats-io/nuid/.gitignore b/vendor/github.com/nats-io/nuid/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/vendor/github.com/nats-io/nuid/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/nats-io/nuid/.travis.yml b/vendor/github.com/nats-io/nuid/.travis.yml new file mode 100644 index 00000000..d1024beb --- /dev/null +++ b/vendor/github.com/nats-io/nuid/.travis.yml @@ -0,0 +1,16 @@ +language: go +sudo: false +go: +- 1.5 + +install: +- go get -t ./... +- go get github.com/mattn/goveralls + +script: +- go fmt ./... +- go vet ./... +- go test -v +- go test -v --race +- go test -v -covermode=count -coverprofile=coverage.out +- $HOME/gopath/bin/goveralls -coverprofile coverage.out -service travis-ci diff --git a/vendor/github.com/nats-io/nuid/GOVERNANCE.md b/vendor/github.com/nats-io/nuid/GOVERNANCE.md deleted file mode 100644 index 01aee70d..00000000 --- a/vendor/github.com/nats-io/nuid/GOVERNANCE.md +++ /dev/null @@ -1,3 +0,0 @@ -# NATS NUID Governance - -NATS NUID is part of the NATS project and is subject to the [NATS Governance](https://github.com/nats-io/nats-general/blob/master/GOVERNANCE.md). \ No newline at end of file diff --git a/vendor/github.com/nats-io/nuid/LICENSE b/vendor/github.com/nats-io/nuid/LICENSE index 261eeb9e..cadc3a49 100644 --- a/vendor/github.com/nats-io/nuid/LICENSE +++ b/vendor/github.com/nats-io/nuid/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +The MIT License (MIT) - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Copyright (c) 2012-2016 Apcera Inc. - 1. Definitions. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/nats-io/nuid/MAINTAINERS.md b/vendor/github.com/nats-io/nuid/MAINTAINERS.md deleted file mode 100644 index 6d0ed3e3..00000000 --- a/vendor/github.com/nats-io/nuid/MAINTAINERS.md +++ /dev/null @@ -1,6 +0,0 @@ -# Maintainers - -Maintainership is on a per project basis. - -### Core-maintainers - - Derek Collison [@derekcollison](https://github.com/derekcollison) \ No newline at end of file diff --git a/vendor/github.com/nats-io/nuid/README.md b/vendor/github.com/nats-io/nuid/README.md index 16d8a735..73d42e14 100644 --- a/vendor/github.com/nats-io/nuid/README.md +++ b/vendor/github.com/nats-io/nuid/README.md @@ -1,6 +1,6 @@ # NUID -[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![License MIT](https://img.shields.io/npm/l/express.svg)](http://opensource.org/licenses/MIT) [![ReportCard](http://goreportcard.com/badge/nats-io/nuid)](http://goreportcard.com/report/nats-io/nuid) [![Build Status](https://travis-ci.org/nats-io/nuid.svg?branch=master)](http://travis-ci.org/nats-io/nuid) [![Release](https://img.shields.io/badge/release-v1.0.0-1eb0fc.svg)](https://github.com/nats-io/nuid/releases/tag/v1.0.0) @@ -35,13 +35,32 @@ NUID needs to be very fast to generate and be truly unique, all while being entr NUID uses 12 bytes of crypto generated data (entropy draining), and 10 bytes of pseudo-random sequential data that increments with a pseudo-random increment. -Total length of a NUID string is 22 bytes of base 62 ascii text, so 62^22 or -2707803647802660400290261537185326956544 possibilities. +Total length of a NUID string is 22 bytes of base 36 ascii text, so 36^22 or +17324272922341479351919144385642496 possibilities. NUID can generate identifiers as fast as 60ns, or ~16 million per second. There is an associated benchmark you can use to test performance on your own hardware. ## License -Unless otherwise noted, the NATS source files are distributed -under the Apache Version 2.0 license found in the LICENSE file. +(The MIT License) + +Copyright (c) 2016 Apcera Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/vendor/github.com/nats-io/nuid/nuid.go b/vendor/github.com/nats-io/nuid/nuid.go index d79e9ce1..1fda3770 100644 --- a/vendor/github.com/nats-io/nuid/nuid.go +++ b/vendor/github.com/nats-io/nuid/nuid.go @@ -1,15 +1,4 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Copyright 2016 Apcera Inc. All rights reserved. // A unique identifier generator that is high performance, very fast, and tries to be entropy pool friendly. package nuid diff --git a/vendor/github.com/nats-io/nuid/nuid_test.go b/vendor/github.com/nats-io/nuid/nuid_test.go index 671a5539..c59677f2 100644 --- a/vendor/github.com/nats-io/nuid/nuid_test.go +++ b/vendor/github.com/nats-io/nuid/nuid_test.go @@ -1,16 +1,3 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package nuid import ( diff --git a/vendor/github.com/nats-io/nuid/unique_test.go b/vendor/github.com/nats-io/nuid/unique_test.go index df979015..6714a333 100644 --- a/vendor/github.com/nats-io/nuid/unique_test.go +++ b/vendor/github.com/nats-io/nuid/unique_test.go @@ -1,16 +1,3 @@ -// Copyright 2016-2018 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - // +build !race package nuid diff --git a/vendor/github.com/tidwall/geojson/LICENSE b/vendor/github.com/tidwall/geojson/LICENSE new file mode 100644 index 00000000..1d9d7baf --- /dev/null +++ b/vendor/github.com/tidwall/geojson/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/tidwall/geojson/README.md b/vendor/github.com/tidwall/geojson/README.md new file mode 100644 index 00000000..dc99e1b6 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/README.md @@ -0,0 +1,9 @@ +# `GeoJSON` + +[![GoDoc](https://godoc.org/github.com/tidwall/geojson?status.svg)](https://godoc.org/github.com/tidwall/geojson) + +This package provides GeoJSON utilties for Go. It's designed for [Tile38](https://github.com/tidwall/tile38). + +## License + +`GeoJSON` source code is available under the MIT License. diff --git a/vendor/github.com/tidwall/geojson/circle.go b/vendor/github.com/tidwall/geojson/circle.go new file mode 100644 index 00000000..5a1e5c82 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/circle.go @@ -0,0 +1,72 @@ +package geojson + +import ( + "strconv" + + "github.com/tidwall/geojson/geo" + "github.com/tidwall/geojson/geometry" +) + +// Circle ... +type Circle struct { + Object + center geometry.Point + meters float64 + steps int + km bool + extra *extra +} + +// NewCircle returns an circle object +func NewCircle(center geometry.Point, meters float64, steps int) *Circle { + if steps < 3 { + steps = 3 + } + g := new(Circle) + g.center = center + g.meters = meters + g.steps = steps + if meters <= 0 { + g.Object = NewPoint(center) + } else { + var points []geometry.Point + step := 360.0 / float64(steps) + i := 0 + for deg := 360.0; deg > 0; deg -= step { + lat, lon := geo.DestinationPoint(center.Y, center.X, meters, deg) + points = append(points, geometry.Point{X: lon, Y: lat}) + i++ + } + // TODO: account for the pole and antimerdian. In most cases only a polygon + // is needed, but when the circle bounds passes the 90/180 lines, we need + // to create a multipolygon + points = append(points, points[0]) + g.Object = NewPolygon( + geometry.NewPoly(points, nil, geometry.DefaultIndex), + ) + } + return g +} + +// AppendJSON ... +func (g *Circle) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"Feature","geometry":`...) + dst = append(dst, `{"type":"Point","coordinates":[`...) + dst = strconv.AppendFloat(dst, g.center.X, 'f', -1, 64) + dst = append(dst, ',') + dst = strconv.AppendFloat(dst, g.center.Y, 'f', -1, 64) + dst = append(dst, `]},"properties":{"type":"Circle","radius":`...) + dst = strconv.AppendFloat(dst, g.meters, 'f', -1, 64) + dst = append(dst, `",radius_units":"m"}}`...) + return dst +} + +// JSON ... +func (g *Circle) JSON() string { + return string(g.AppendJSON(nil)) +} + +// String ... +func (g *Circle) String() string { + return string(g.AppendJSON(nil)) +} diff --git a/vendor/github.com/tidwall/geojson/circle_test.go b/vendor/github.com/tidwall/geojson/circle_test.go new file mode 100644 index 00000000..859cd832 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/circle_test.go @@ -0,0 +1,18 @@ +package geojson + +import "testing" + +func TestCircle(t *testing.T) { + g, err := Parse(`{ + "type":"Feature", + "geometry":{"type":"Point","coordinates":[-112.2693,33.5123]}, + "properties": { + "type": "Circle", + "radius": 1000 + } + }`, nil) + if err != nil { + t.Fatal(err) + } + println(g.Contains(PO(-112.26, 33.51))) +} diff --git a/vendor/github.com/tidwall/geojson/collection.go b/vendor/github.com/tidwall/geojson/collection.go new file mode 100644 index 00000000..ab9497c0 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/collection.go @@ -0,0 +1,326 @@ +package geojson + +import ( + "github.com/tidwall/boxtree/d2" + "github.com/tidwall/geojson/geometry" +) + +type collection struct { + children []Object + extra *extra + tree *d2.BoxTree + prect geometry.Rect + pempty bool +} + +func (g *collection) Indexed() bool { + return g.tree != nil +} + +func (g *collection) Children() []Object { + return g.children +} + +// ForEach ... +func (g *collection) ForEach(iter func(geom Object) bool) bool { + for _, child := range g.children { + if !child.ForEach(iter) { + return false + } + } + return true +} + +// Base ... +func (g *collection) Base() []Object { + return g.children +} + +func (g *collection) Search(rect geometry.Rect, iter func(child Object) bool) { + if g.tree != nil { + g.tree.Search( + []float64{rect.Min.X, rect.Min.Y}, + []float64{rect.Max.X, rect.Max.Y}, + func(_, _ []float64, value interface{}) bool { + return iter(value.(Object)) + }, + ) + } else { + for _, child := range g.children { + if child.Empty() { + continue + } + if child.Rect().IntersectsRect(rect) { + if !iter(child) { + break + } + } + } + } +} + +// Empty ... +func (g *collection) Empty() bool { + return g.pempty +} + +// Rect ... +func (g *collection) Rect() geometry.Rect { + return g.prect +} + +// Center ... +func (g *collection) Center() geometry.Point { + return g.Rect().Center() +} + +// AppendJSON ... +func (g *collection) AppendJSON(dst []byte) []byte { + // this should never be called + return append(dst, "null"...) +} + +// JSON ... +func (g *collection) JSON() string { + return string(g.AppendJSON(nil)) +} + +// String ... +func (g *collection) String() string { + return string(g.AppendJSON(nil)) +} + +// Within ... +func (g *collection) Within(obj Object) bool { + return obj.Contains(g) +} + +// Contains ... +func (g *collection) Contains(obj Object) bool { + if g.Empty() { + return false + } + // all of obj must be contained by any number of the collection children + var objContained bool + obj.ForEach(func(geom Object) bool { + if geom.Empty() { + // ignore empties + return true + } + var geomContained bool + g.Search(geom.Rect(), func(child Object) bool { + if child.Contains(geom) { + // found a child object that contains geom, end inner loop + geomContained = true + return false + } + return true + }) + if !geomContained { + // unmark and quit the loop + objContained = false + return false + } + // mark that at least one geom is contained + objContained = true + return true + }) + return objContained +} + +func (g *collection) Spatial() Spatial { return g } + +func (g *collection) WithinRect(rect geometry.Rect) bool { + if g.Empty() { + return false + } + var withinCount int + g.Search(rect, func(child Object) bool { + if child.Spatial().WithinRect(rect) { + withinCount++ + return true + } + return false + }) + return withinCount == len(g.children) +} + +func (g *collection) WithinPoint(point geometry.Point) bool { + if g.Empty() { + return false + } + var withinCount int + g.Search(point.Rect(), func(child Object) bool { + if child.Spatial().WithinPoint(point) { + withinCount++ + return true + } + return false + }) + return withinCount == len(g.children) +} + +func (g *collection) WithinLine(line *geometry.Line) bool { + if g.Empty() { + return false + } + var withinCount int + g.Search(line.Rect(), func(child Object) bool { + if child.Spatial().WithinLine(line) { + withinCount++ + return true + } + return false + }) + return withinCount == len(g.children) +} + +func (g *collection) WithinPoly(poly *geometry.Poly) bool { + if g.Empty() { + return false + } + var withinCount int + g.Search(poly.Rect(), func(child Object) bool { + if child.Spatial().WithinPoly(poly) { + withinCount++ + return true + } + return false + }) + return withinCount == len(g.children) +} + +// Intersects ... +func (g *collection) Intersects(obj Object) bool { + // check if any of obj intersects with any of collection + var intersects bool + obj.ForEach(func(geom Object) bool { + if geom.Empty() { + // ignore the empties + return true + } + g.Search(geom.Rect(), func(child Object) bool { + if child.Intersects(geom) { + intersects = true + return false + } + return true + }) + if intersects { + return false + } + return true + }) + return intersects +} + +func (g *collection) IntersectsPoint(point geometry.Point) bool { + var intersects bool + g.Search(point.Rect(), func(child Object) bool { + if child.Spatial().IntersectsPoint(point) { + intersects = true + return false + } + return true + }) + return intersects +} + +func (g *collection) IntersectsRect(rect geometry.Rect) bool { + var intersects bool + g.Search(rect, func(child Object) bool { + if child.Spatial().IntersectsRect(rect) { + intersects = true + return false + } + return true + }) + return intersects +} + +func (g *collection) IntersectsLine(line *geometry.Line) bool { + var intersects bool + g.Search(line.Rect(), func(child Object) bool { + if child.Spatial().IntersectsLine(line) { + intersects = true + return false + } + return true + }) + return intersects +} + +func (g *collection) IntersectsPoly(poly *geometry.Poly) bool { + var intersects bool + g.Search(poly.Rect(), func(child Object) bool { + if child.Spatial().IntersectsPoly(poly) { + intersects = true + return false + } + return true + }) + return intersects +} + +// NumPoints ... +func (g *collection) NumPoints() int { + var n int + for _, child := range g.children { + n += child.NumPoints() + } + return n +} + +func (g *collection) parseInitRectIndex(opts *ParseOptions) { + g.pempty = true + var count int + for _, child := range g.children { + if child.Empty() { + continue + } + if g.pempty && !child.Empty() { + g.pempty = false + } + if count == 0 { + g.prect = child.Rect() + } else { + if len(g.children) == 1 { + g.prect = child.Rect() + } else { + g.prect = unionRects(g.prect, child.Rect()) + } + } + count++ + } + if count > 0 && opts.IndexChildren != 0 && count >= opts.IndexChildren { + g.tree = new(d2.BoxTree) + for _, child := range g.children { + if child.Empty() { + continue + } + rect := child.Rect() + g.tree.Insert( + []float64{rect.Min.X, rect.Min.Y}, + []float64{rect.Max.X, rect.Max.Y}, + child, + ) + } + } +} + +// Distance ... +func (g *collection) Distance(obj Object) float64 { + return obj.Spatial().DistancePoint(g.Center()) +} +func (g *collection) DistancePoint(point geometry.Point) float64 { + return geoDistancePoints(g.Center(), point) +} +func (g *collection) DistanceRect(rect geometry.Rect) float64 { + return geoDistancePoints(g.Center(), rect.Center()) +} +func (g *collection) DistanceLine(line *geometry.Line) float64 { + return geoDistancePoints(g.Center(), line.Rect().Center()) +} +func (g *collection) DistancePoly(poly *geometry.Poly) float64 { + return geoDistancePoints(g.Center(), poly.Rect().Center()) +} diff --git a/vendor/github.com/tidwall/geojson/collection_test.go b/vendor/github.com/tidwall/geojson/collection_test.go new file mode 100644 index 00000000..0dd99743 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/collection_test.go @@ -0,0 +1,136 @@ +package geojson + +import ( + "fmt" + "io/ioutil" + "math/rand" + "os" + "sort" + "strings" + "testing" + "time" + + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/lotsa" +) + +func parseCollection(t *testing.T, data string, index bool) Collection { + t.Helper() + opts := *DefaultParseOptions + if index { + opts.IndexChildren = 1 + opts.IndexGeometry = 1 + } else { + opts.IndexChildren = 0 + opts.IndexGeometry = 0 + } + g, err := Parse(string(data), &opts) + if err != nil { + t.Fatal(err) + } + c, ok := g.(Collection) + if !ok { + t.Fatal("not searchable") + } + return c +} + +func testCollectionSubset( + t *testing.T, json string, subsetRect geometry.Rect, +) []Collection { + t.Helper() + expectJSON(t, json, nil) + start := time.Now() + c := parseCollection(t, json, true) + dur := time.Since(start) + fmt.Printf("%d children in %s\n", len(c.Children()), dur) + cs := make([]Collection, 2) + ts := make([]time.Duration, 2) + start = time.Now() + cs[0] = parseCollection(t, json, false) + ts[0] = time.Since(start) + start = time.Now() + cs[1] = parseCollection(t, json, true) + ts[1] = time.Since(start) + var lastSubset string + for i, c := range cs { + var children []Object + start := time.Now() + c.Search(subsetRect, func(child Object) bool { + children = append(children, child) + return true + }) + dur := time.Since(start) + if c.Indexed() { + fmt.Printf("Indexed: %v (build: %v)\n", dur, ts[i]) + } else { + fmt.Printf("Simple: %v (build: %v)\n", dur, ts[i]) + } + var childrenJSONs []string + for _, child := range children { + childrenJSONs = append(childrenJSONs, string(child.AppendJSON(nil))) + } + sort.Strings(childrenJSONs) + subset := `{"type":"GeometryCollection","geometries":[` + + strings.Join(childrenJSONs, ",") + `]}` + if i > 0 { + if subset != lastSubset { + t.Fatal("mismatch") + } + } + lastSubset = subset + } + return cs +} + +func TestCollectionBostonSubset(t *testing.T) { + data, err := ioutil.ReadFile("test_files/boston_subset.geojson") + expect(t, err == nil) + cs := testCollectionSubset(t, string(data), + R(-71.474046, 42.492479, -71.466321, 42.497415), + ) + expect(t, cs[0].(Object).Intersects(PO(-71.46723, 42.49432))) + expect(t, cs[1].(Object).Intersects(PO(-71.46723, 42.49432))) + expect(t, !cs[0].(*FeatureCollection).Intersects(PO(-71.46713, 42.49431))) + expect(t, !cs[1].(Object).Intersects(PO(-71.46713, 42.49431))) +} + +func TestCollectionUSASubset(t *testing.T) { + data, err := ioutil.ReadFile("test_files/usa.geojson") + if err == nil { + fmt.Printf("test_files/usa.geojson missing\n") + return + } + + expect(t, err == nil) + cs := testCollectionSubset(t, string(data), + R(-90, -45, 90, 45), + ) + expect(t, cs[0].(Object).Intersects(PO(-91.09863, 30.03105))) + expect(t, cs[1].(Object).Intersects(PO(-91.09863, 30.03105))) + expect(t, !cs[0].(Object).Intersects(PO(-86.87988, 28.439713))) + expect(t, !cs[1].(Object).Intersects(PO(-86.87988, 28.439713))) + + N := 10000 + T := 6 + + // 34 is the lower 48 + rect := cs[0].Children()[34].(Object).Rect() + points := make([]Object, N) + for i := 0; i < N; i++ { + points[i] = PO( + (rect.Max.X-rect.Min.X)*rand.Float64()+rect.Min.X, + (rect.Max.Y-rect.Min.Y)*rand.Float64()+rect.Min.Y, + ) + } + lotsa.Output = os.Stdout + cs0 := cs[0].(Object) + cs1 := cs[1].(Object) + lotsa.Ops(N, T, func(i, _ int) { + cs0.Intersects(points[i]) + }) + lotsa.Ops(N, T, func(i, _ int) { + cs1.Intersects(points[i]) + }) + +} diff --git a/vendor/github.com/tidwall/geojson/feature.go b/vendor/github.com/tidwall/geojson/feature.go new file mode 100644 index 00000000..ceebdc5d --- /dev/null +++ b/vendor/github.com/tidwall/geojson/feature.go @@ -0,0 +1,214 @@ +package geojson + +import ( + "strings" + + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" + "github.com/tidwall/pretty" + "github.com/tidwall/sjson" +) + +// Feature ... +type Feature struct { + base Object + extra *extra +} + +// NewFeature returns a new GeoJSON Feature. +// The members must be a valid json object such as +// `{"id":"391","properties":{}}`, or it must be an empty string. It should not +// contain a "feature" member. +func NewFeature(geometry Object, members string) *Feature { + g := new(Feature) + g.base = geometry + members = strings.TrimSpace(members) + if members != "" && members != "{}" { + if gjson.Valid(members) && gjson.Parse(members).IsObject() { + if gjson.Get(members, "feature").Exists() { + members, _ = sjson.Delete(members, "feature") + } + g.extra = new(extra) + g.extra.members = string(pretty.UglyInPlace([]byte(members))) + } + } + return g +} + +// ForEach ... +func (g *Feature) ForEach(iter func(geom Object) bool) bool { + return g.base.ForEach(iter) +} + +// Empty ... +func (g *Feature) Empty() bool { + return g.base.Empty() +} + +// Rect ... +func (g *Feature) Rect() geometry.Rect { + return g.base.Rect() +} + +// Center ... +func (g *Feature) Center() geometry.Point { + return g.Rect().Center() +} + +// Base ... +func (g *Feature) Base() Object { + return g.base +} + +// Members ... +func (g *Feature) Members() string { + if g.extra != nil { + return g.extra.members + } + return "" +} + +// AppendJSON ... +func (g *Feature) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"Feature","geometry":`...) + dst = g.base.AppendJSON(dst) + dst = g.extra.appendJSONExtra(dst) + dst = append(dst, '}') + return dst + +} + +// String ... +func (g *Feature) String() string { + return string(g.AppendJSON(nil)) +} + +// JSON ... +func (g *Feature) JSON() string { + return string(g.AppendJSON(nil)) +} + +// Spatial ... +func (g *Feature) Spatial() Spatial { + return g +} + +// Within ... +func (g *Feature) Within(obj Object) bool { + return obj.Contains(g) +} + +// Contains ... +func (g *Feature) Contains(obj Object) bool { + return obj.Within(g.base) +} + +// WithinRect ... +func (g *Feature) WithinRect(rect geometry.Rect) bool { + return g.base.Spatial().WithinRect(rect) +} + +// WithinPoint ... +func (g *Feature) WithinPoint(point geometry.Point) bool { + return g.base.Spatial().WithinPoint(point) +} + +// WithinLine ... +func (g *Feature) WithinLine(line *geometry.Line) bool { + return g.base.Spatial().WithinLine(line) +} + +// WithinPoly ... +func (g *Feature) WithinPoly(poly *geometry.Poly) bool { + return g.base.Spatial().WithinPoly(poly) +} + +// Intersects ... +func (g *Feature) Intersects(obj Object) bool { + return obj.Intersects(g.base) +} + +// IntersectsPoint ... +func (g *Feature) IntersectsPoint(point geometry.Point) bool { + return g.base.Spatial().IntersectsPoint(point) +} + +// IntersectsRect ... +func (g *Feature) IntersectsRect(rect geometry.Rect) bool { + return g.base.Spatial().IntersectsRect(rect) +} + +// IntersectsLine ... +func (g *Feature) IntersectsLine(line *geometry.Line) bool { + return g.base.Spatial().IntersectsLine(line) +} + +// IntersectsPoly ... +func (g *Feature) IntersectsPoly(poly *geometry.Poly) bool { + return g.base.Spatial().IntersectsPoly(poly) +} + +// NumPoints ... +func (g *Feature) NumPoints() int { + return g.base.NumPoints() +} + +// parseJSONFeature will return a valid GeoJSON object. +func parseJSONFeature(keys *parseKeys, opts *ParseOptions) (Object, error) { + var g Feature + if !keys.rGeometry.Exists() { + return nil, errGeometryMissing + } + var err error + g.base, err = Parse(keys.rGeometry.Raw, opts) + if err != nil { + return nil, err + } + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + if point, ok := g.base.(*Point); ok { + if g.extra != nil { + members := g.extra.members + if gjson.Get(members, "properties.type").String() == "Circle" { + // Circle + radius := gjson.Get(members, "properties.radius").Float() + units := gjson.Get(members, "properties.radius_units").String() + switch units { + case "", "m": + case "km": + radius *= 1000 + default: + return nil, errCircleRadiusUnitsInvalid + } + return NewCircle(point.base, radius, 64), nil + } + } + } + return &g, nil +} + +// Distance ... +func (g *Feature) Distance(obj Object) float64 { + return g.base.Distance(obj) +} + +// DistancePoint ... +func (g *Feature) DistancePoint(point geometry.Point) float64 { + return g.base.Spatial().DistancePoint(point) +} + +// DistanceRect ... +func (g *Feature) DistanceRect(rect geometry.Rect) float64 { + return g.base.Spatial().DistanceRect(rect) +} + +// DistanceLine ... +func (g *Feature) DistanceLine(line *geometry.Line) float64 { + return g.base.Spatial().DistanceLine(line) +} + +// DistancePoly ... +func (g *Feature) DistancePoly(poly *geometry.Poly) float64 { + return g.base.Spatial().DistancePoly(poly) +} diff --git a/vendor/github.com/tidwall/geojson/feature_test.go b/vendor/github.com/tidwall/geojson/feature_test.go new file mode 100644 index 00000000..5ba83c42 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/feature_test.go @@ -0,0 +1,39 @@ +package geojson + +import "testing" + +func TestFeatureParse(t *testing.T) { + p := expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]}}`, nil) + expect(t, p.Center() == P(1, 2)) + expectJSON(t, `{"type":"Feature"}`, errGeometryMissing) + expectJSON(t, `{"type":"Feature","geometry":null}`, errDataInvalid) + expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":null}`, nil) + expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"id":[4,true]}`, nil) + expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"id":"15","properties":{"a":"b"}}`, nil) + expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":[1,2,3,4]}`, nil) + expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2],"bbox":[1,2,3,4]},"id":[4,true]}`, nil) +} + +func TestFeatureVarious(t *testing.T) { + var g = expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]}}`, nil) + expect(t, string(g.AppendJSON(nil)) == `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]}}`) + expect(t, g.Rect() == R(1, 2, 1, 2)) + expect(t, g.Center() == P(1, 2)) + expect(t, !g.Empty()) + + g = expectJSONOpts(t, + `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"bbox":[1,2,3,4]}`, + nil, nil) + expect(t, !g.Empty()) + expect(t, g.Rect() == R(1, 2, 1, 2)) + expect(t, g.Center() == P(1, 2)) + +} + +// func TestFeaturePoly(t *testing.T) { +// p := expectJSON(t, `{"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]}}`, nil) +// expect(t, p.Intersects(PO(1, 2))) +// expect(t, p.Contains(PO(1, 2))) +// expect(t, p.Within(PO(1, 2))) + +// } diff --git a/vendor/github.com/tidwall/geojson/featurecollection.go b/vendor/github.com/tidwall/geojson/featurecollection.go new file mode 100644 index 00000000..87ce6a87 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/featurecollection.go @@ -0,0 +1,76 @@ +package geojson + +import ( + "strings" + + "github.com/tidwall/gjson" +) + +// FeatureCollection ... +type FeatureCollection struct{ collection } + +// NewFeatureCollection ... +func NewFeatureCollection(features []Object) *FeatureCollection { + g := new(FeatureCollection) + g.children = features + g.parseInitRectIndex(DefaultParseOptions) + return g +} + +// AppendJSON appends the GeoJSON reprensentation to dst +func (g *FeatureCollection) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"FeatureCollection","features":[`...) + for i := 0; i < len(g.children); i++ { + if i > 0 { + dst = append(dst, ',') + } + dst = g.children[i].AppendJSON(dst) + } + dst = append(dst, ']') + if g.extra != nil { + dst = g.extra.appendJSONExtra(dst) + } + dst = append(dst, '}') + strings.Index("", " ") + return dst +} + +// String ... +func (g *FeatureCollection) String() string { + return string(g.AppendJSON(nil)) +} + +// JSON ... +func (g *FeatureCollection) JSON() string { + return string(g.AppendJSON(nil)) +} + +func parseJSONFeatureCollection( + keys *parseKeys, opts *ParseOptions, +) (Object, error) { + var g FeatureCollection + if !keys.rFeatures.Exists() { + return nil, errFeaturesMissing + } + if !keys.rFeatures.IsArray() { + return nil, errFeaturesInvalid + } + var err error + keys.rFeatures.ForEach(func(key, value gjson.Result) bool { + var f Object + f, err = Parse(value.Raw, opts) + if err != nil { + return false + } + g.children = append(g.children, f) + return true + }) + if err != nil { + return nil, err + } + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + g.parseInitRectIndex(opts) + return &g, nil +} diff --git a/vendor/github.com/tidwall/geojson/featurecollection_test.go b/vendor/github.com/tidwall/geojson/featurecollection_test.go new file mode 100644 index 00000000..0673c484 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/featurecollection_test.go @@ -0,0 +1,20 @@ +package geojson + +import "testing" + +func TestFeatureCollection(t *testing.T) { + p := expectJSON(t, `{"type":"FeatureCollection","features":[{"type":"Point","coordinates":[1,2,3]}]}`, nil) + if p.Center() != P(1, 2) { + t.Fatalf("expected '%v', got '%v'", P(1, 2), p.Center()) + } + expectJSON(t, `{"type":"FeatureCollection"}`, errFeaturesMissing) + expectJSON(t, `{"type":"FeatureCollection","features":null}`, errFeaturesInvalid) + expectJSON(t, `{"type":"FeatureCollection","features":[{"type":"Point","coordinates":[1,2,3]}],"bbox":null}`, nil) + expectJSON(t, `{"type":"FeatureCollection","features":[{"type":"Point"}]}`, errCoordinatesMissing) +} + +func TestFeatureCollectionPoly(t *testing.T) { + p := expectJSON(t, `{"type":"FeatureCollection","features":[{"type":"Point","coordinates":[1,2]}]}`, nil) + expect(t, p.Intersects(PO(1, 2))) + expect(t, p.Contains(PO(1, 2))) +} diff --git a/vendor/github.com/tidwall/geojson/geo/geo.go b/vendor/github.com/tidwall/geojson/geo/geo.go new file mode 100644 index 00000000..c47f6d31 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geo/geo.go @@ -0,0 +1,95 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geo + +import ( + "math" +) + +const ( + earthRadius = 6371e3 + radians = math.Pi / 180 + degrees = 180 / math.Pi +) + +// DistanceTo return the distance in meteres between two point. +func DistanceTo(latA, lonA, latB, lonB float64) (meters float64) { + φ1 := latA * radians + λ1 := lonA * radians + φ2 := latB * radians + λ2 := lonB * radians + Δφ := φ2 - φ1 + Δλ := λ2 - λ1 + a := math.Sin(Δφ/2)*math.Sin(Δφ/2) + + math.Cos(φ1)*math.Cos(φ2)*math.Sin(Δλ/2)*math.Sin(Δλ/2) + c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) + return earthRadius * c +} + +// DestinationPoint return the destination from a point based on a +// distance and bearing. +func DestinationPoint(lat, lon, meters, bearingDegrees float64) ( + destLat, destLon float64, +) { + // see http://williams.best.vwh.net/avform.htm#LL + δ := meters / earthRadius // angular distance in radians + θ := bearingDegrees * radians + φ1 := lat * radians + λ1 := lon * radians + φ2 := math.Asin(math.Sin(φ1)*math.Cos(δ) + + math.Cos(φ1)*math.Sin(δ)*math.Cos(θ)) + λ2 := λ1 + math.Atan2(math.Sin(θ)*math.Sin(δ)*math.Cos(φ1), + math.Cos(δ)-math.Sin(φ1)*math.Sin(φ2)) + λ2 = math.Mod(λ2+3*math.Pi, 2*math.Pi) - math.Pi // normalise to -180..+180° + return φ2 * degrees, λ2 * degrees +} + +// BearingTo returns the (initial) bearing from point 'A' to point 'B'. +func BearingTo(latA, lonA, latB, lonB float64) float64 { + // tanθ = sinΔλ⋅cosφ2 / cosφ1⋅sinφ2 − sinφ1⋅cosφ2⋅cosΔλ + // see mathforum.org/library/drmath/view/55417.html for derivation + + φ1 := latA * radians + φ2 := latB * radians + Δλ := (lonB - lonA) * radians + y := math.Sin(Δλ) * math.Cos(φ2) + x := math.Cos(φ1)*math.Sin(φ2) - math.Sin(φ1)*math.Cos(φ2)*math.Cos(Δλ) + θ := math.Atan2(y, x) + + return math.Mod(θ*degrees+360, 360) +} + +// // SegmentIntersectsCircle ... +// func SegmentIntersectsCircle( +// startLat, startLon, endLat, endLon, centerLat, centerLon, meters float64, +// ) bool { +// // These are faster checks. +// // If they succeed there's no need do complicate things. +// if DistanceTo(startLat, startLon, centerLat, centerLon) <= meters { +// return true +// } +// if DistanceTo(endLat, endLon, centerLat, centerLon) <= meters { +// return true +// } + +// // Distance between start and end +// l := DistanceTo(startLat, startLon, endLat, endLon) + +// // Unit direction vector +// dLat := (endLat - startLat) / l +// dLon := (endLon - startLon) / l + +// // Point of the line closest to the center +// t := dLon*(centerLon-startLon) + dLat*(centerLat-startLat) +// pLat := t*dLat + startLat +// pLon := t*dLon + startLon +// if pLon < startLon || pLon > endLon || pLat < startLat || pLat > endLat { +// // closest point is outside the segment +// return false +// } + +// // Distance from the closest point to the center +// return DistanceTo(centerLat, centerLon, pLat, pLon) <= meters +// } diff --git a/vendor/github.com/tidwall/geojson/geo/geo_test.go b/vendor/github.com/tidwall/geojson/geo/geo_test.go new file mode 100644 index 00000000..681c60e7 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geo/geo_test.go @@ -0,0 +1,32 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geo + +import "testing" + +func TestGeoCalc(t *testing.T) { + dist := 172853.26908429610193707048892974853515625 + bearing := 320.8560640269032546711969189345836639404296875 + latA, lonA := 33.112, -112.123 + latB, lonB := 34.312, -113.311 + // DistanceTo + value := DistanceTo(latA, lonA, latB, lonB) + if value != dist { + t.Fatalf("expected '%v', got '%v'", dist, value) + } + // BearingTo + value = BearingTo(latA, lonA, latB, lonB) + if value != bearing { + t.Fatalf("expected '%v', got '%v'", bearing, value) + } + // DestinationPoint + value1, value2 := DestinationPoint(latA, lonA, dist, bearing) + if value1 != latB { + t.Fatalf("expected '%v', got '%v'", latB, value1) + } + if value2 != lonB { + t.Fatalf("expected '%v', got '%v'", lonB, value2) + } +} diff --git a/vendor/github.com/tidwall/geojson/geometry/geometry.go b/vendor/github.com/tidwall/geojson/geometry/geometry.go new file mode 100644 index 00000000..9862f35a --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/geometry.go @@ -0,0 +1,22 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +// Geometry is a standard geometry +type Geometry interface { + Rect() Rect + Empty() bool + ContainsPoint(point Point) bool + IntersectsPoint(point Point) bool + ContainsRect(rect Rect) bool + IntersectsRect(rect Rect) bool + ContainsLine(line *Line) bool + IntersectsLine(line *Line) bool + ContainsPoly(poly *Poly) bool + IntersectsPoly(poly *Poly) bool +} + +// require conformance +var _ = []Geometry{Point{}, Rect{}, &Line{}, &Poly{}} diff --git a/vendor/github.com/tidwall/geojson/geometry/geometry_test.go b/vendor/github.com/tidwall/geojson/geometry/geometry_test.go new file mode 100644 index 00000000..22573d93 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/geometry_test.go @@ -0,0 +1,88 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "math/rand" + "os" + "testing" + "time" +) + +func init() { + seed := time.Now().UnixNano() + println(seed) + rand.Seed(seed) + if os.Getenv("PIPBENCH") != "1" { + println("use PIPBENCH=1 for point-in-polygon benchmarks") + } +} + +func S(ax, ay, bx, by float64) Segment { + return Segment{Point{ax, ay}, Point{bx, by}} +} +func R(minX, minY, maxX, maxY float64) Rect { + return Rect{Point{minX, minY}, Point{maxX, maxY}} +} +func P(x, y float64) Point { + return Point{x, y} +} +func L(points ...Point) *Line { + return NewLine(points, DefaultIndex) +} + +var ( + // rings + rectangle = []Point{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}} + pentagon = []Point{{2, 2}, {8, 0}, {10, 6}, {5, 10}, {0, 6}, {2, 2}} + triangle = []Point{{0, 0}, {10, 0}, {5, 10}, {0, 0}} + trapezoid = []Point{{0, 0}, {10, 0}, {8, 10}, {2, 10}, {0, 0}} + octagon = []Point{ + {3, 0}, {7, 0}, {10, 3}, {10, 7}, + {7, 10}, {3, 10}, {0, 7}, {0, 3}, {3, 0}, + } + concave1 = []Point{{5, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 5}, {5, 5}, {5, 0}} + concave2 = []Point{{0, 0}, {5, 0}, {5, 5}, {10, 5}, {10, 10}, {0, 10}, {0, 0}} + concave3 = []Point{{0, 0}, {10, 0}, {10, 5}, {5, 5}, {5, 10}, {0, 10}, {0, 0}} + concave4 = []Point{{0, 0}, {10, 0}, {10, 10}, {5, 10}, {5, 5}, {0, 5}, {0, 0}} + bowtie = []Point{{0, 0}, {5, 4}, {10, 0}, {10, 10}, {5, 6}, {0, 10}, {0, 0}} + notClosed = []Point{{0, 0}, {10, 0}, {10, 10}, {0, 10}} + + // lines + u1 = []Point{{0, 10}, {0, 0}, {10, 0}, {10, 10}} + u2 = []Point{{0, 0}, {10, 0}, {10, 10}, {0, 10}} + u3 = []Point{{10, 0}, {10, 10}, {0, 10}, {0, 0}} + u4 = []Point{{10, 10}, {0, 10}, {0, 0}, {10, 0}} + + v1 = []Point{{0, 10}, {5, 0}, {10, 10}} + v2 = []Point{{0, 0}, {10, 5}, {0, 10}} + v3 = []Point{{10, 0}, {5, 10}, {0, 0}} + v4 = []Point{{10, 10}, {0, 5}, {10, 0}} +) + +func expect(t testing.TB, what bool) { + t.Helper() + if !what { + t.Fatal("expection failure") + } +} + +// func TestRectVarious(t *testing.T) { +// expect(t, R(0, 0, 10, 10).ContainsRing(newRingSimple2(octagon))) +// expect(t, !R(5, 0, 15, 10).ContainsRing(newRingSimple2(octagon))) +// expect(t, R(5, 0, 15, 10).IntersectsRing(newRingSimple2(octagon))) +// expect(t, R(0, 0, 10, 10).Center() == P(5, 5)) +// } + +func TestRaycastBounds(t *testing.T) { + expect(t, S(0, 0, 10, 10).Raycast(P(20, -1)) == RaycastResult{false, false}) + expect(t, S(10, 10, 0, 0).Raycast(P(-1, 20)) == RaycastResult{false, false}) + expect(t, S(0, 0, 0, 0).Raycast(P(0, 0)) == RaycastResult{false, true}) + expect(t, S(0, 0, 0, 0).Raycast(P(0, 1)) == RaycastResult{false, false}) + expect(t, S(0, 0, 1, 0).Raycast(P(1, 0)) == RaycastResult{false, true}) + expect(t, S(1, 0, 0, 0).Raycast(P(1, 0)) == RaycastResult{false, true}) + expect(t, S(0, 1, 0, 0).Raycast(P(0, 1)) == RaycastResult{false, true}) + expect(t, S(0, 0, 0, 1).Raycast(P(0, 1)) == RaycastResult{false, true}) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/issue_test.go b/vendor/github.com/tidwall/geojson/geometry/issue_test.go new file mode 100644 index 00000000..d20dbd59 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/issue_test.go @@ -0,0 +1,138 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import "testing" + +// Below are various issues filed on the Tile38 Github page + +func TestIssue362(t *testing.T) { + rect := Rect{ + Point{-122.4434208869934, 37.73138728181471}, + Point{-122.43711233139038, 37.73579951265516}, + } + box := []Point{ + {-122.4434208869934, 37.73138728181471}, + {-122.43711233139038, 37.73138728181471}, + {-122.43711233139038, 37.73579951265516}, + {-122.4434208869934, 37.73579951265516}, + {-122.4434208869934, 37.73138728181471}, + } + shape := []Point{ + {-122.4475622177124, 37.73590133026304}, + {-122.44369983673094, 37.72904529863455}, + {-122.44052410125731, 37.732778859926555}, + {-122.43713378906249, 37.729079240948714}, + {-122.43292808532715, 37.73865035274667}, + {-122.43962287902832, 37.735154664553534}, + {-122.44850635528563, 37.73885397998102}, + {-122.4475622177124, 37.7359013302630}, + } + expect(t, !NewPoly(shape, nil, DefaultIndex).ContainsPoly( + NewPoly(box, nil, DefaultIndex), + )) + expect(t, !NewPoly(shape, nil, DefaultIndex).ContainsRect(rect)) +} + +func TestIssue245(t *testing.T) { + poly := NewPoly([]Point{ + {113.840391, 23.36073}, {113.840837, 23.360618}, {113.841198, 23.360528}, {113.84295, 23.360252}, {113.844386, 23.359914}, {113.844674, 23.359847}, {113.845843, 23.359466}, {113.846787, 23.358753}, {113.84679, 23.358751}, {113.847128, 23.358254}, {113.847571, 23.357602}, {113.847573, 23.357598}, {113.848175, 23.356521}, {113.848177, 23.356517}, {113.848187, 23.3565}, {113.848189, 23.356497}, {113.84847, 23.355449}, {113.848471, 23.355445}, {113.848475, 23.355126}, {113.848486, 23.354203}, {113.848373, 23.353516}, {113.848122, 23.352467}, {113.847432, 23.351258}, {113.846102, 23.349542}, {113.845853, 23.349288}, {113.84494, 23.348358}, {113.844435, 23.347908}, + {113.843638, 23.347198}, {113.840895, 23.345392}, {113.836433, 23.342373}, {113.835963, 23.342033}, {113.83469, 23.34111}, {113.833801, 23.340465}, {113.831238, 23.338917}, {113.830698, 23.33835}, {113.830644, 23.337639}, {113.830642, 23.337639}, {113.825764, 23.337862}, {113.82575, 23.337897}, {113.82575, 23.337899}, {113.825749, 23.337901}, {113.825748, 23.337905}, {113.825747, 23.337907}, {113.825746, 23.337909}, {113.825746, 23.337912}, {113.825745, 23.337914}, {113.825744, 23.337916}, {113.825743, 23.337919}, {113.825742, 23.337921}, {113.825741, 23.337923}, {113.82574, 23.337926}, {113.825739, 23.337928}, {113.825738, 23.33793}, {113.825738, 23.337933}, + {113.825737, 23.337935}, {113.825736, 23.337937}, {113.825735, 23.33794}, {113.825734, 23.337942}, {113.825733, 23.337944}, {113.825732, 23.337947}, {113.825731, 23.337949}, {113.82573, 23.337951}, {113.825729, 23.337954}, {113.825728, 23.337956}, {113.825727, 23.337959}, {113.825726, 23.337962}, {113.825725, 23.337964}, {113.825725, 23.337966}, {113.825724, 23.337969}, {113.825723, 23.337971}, {113.825722, 23.337973}, {113.825721, 23.337975}, {113.82572, 23.337978}, {113.825719, 23.33798}, {113.825718, 23.337982}, {113.825717, 23.337985}, {113.825717, 23.337987}, {113.825716, 23.337989}, {113.825715, 23.337992}, {113.825715, 23.337994}, {113.825714, 23.337996}, + {113.825713, 23.337999}, {113.825712, 23.338001}, {113.825711, 23.338003}, {113.82571, 23.338006}, {113.825709, 23.338008}, {113.825708, 23.33801}, {113.825707, 23.338013}, {113.825706, 23.338015}, {113.825705, 23.338018}, {113.825705, 23.338021}, {113.825704, 23.338023}, {113.825703, 23.338025}, {113.825702, 23.338028}, {113.825701, 23.33803}, {113.8257, 23.338032}, {113.825699, 23.338035}, {113.825698, 23.338037}, {113.825697, 23.338039}, {113.825696, 23.338042}, {113.825696, 23.338044}, {113.825695, 23.338046}, {113.825694, 23.338049}, {113.825693, 23.338051}, {113.825692, 23.338053}, {113.825691, 23.338056}, {113.82569, 23.338058}, {113.82569, 23.338061}, + {113.825689, 23.338063}, {113.825689, 23.338065}, {113.825688, 23.338068}, {113.825687, 23.33807}, {113.825686, 23.338072}, {113.825685, 23.338075}, {113.825684, 23.338078}, {113.825683, 23.33808}, {113.825682, 23.338083}, {113.825682, 23.338085}, {113.825681, 23.338087}, {113.82568, 23.33809}, {113.825679, 23.338092}, {113.825678, 23.338094}, {113.825677, 23.338097}, {113.825676, 23.338099}, {113.825675, 23.338101}, {113.825675, 23.338104}, {113.825674, 23.338106}, {113.825673, 23.338108}, {113.825672, 23.338111}, {113.825671, 23.338113}, {113.82567, 23.338115}, {113.825669, 23.338118}, {113.825669, 23.33812}, {113.825668, 23.338123}, {113.825668, 23.338125}, + {113.825667, 23.338127}, {113.825666, 23.33813}, {113.825665, 23.338132}, {113.825664, 23.338134}, {113.825664, 23.338138}, {113.825663, 23.33814}, {113.825662, 23.338142}, {113.825661, 23.338145}, {113.82566, 23.338147}, {113.825659, 23.338149}, {113.825659, 23.338152}, {113.825658, 23.338154}, {113.825657, 23.338156}, {113.825656, 23.338159}, {113.825655, 23.338161}, {113.825654, 23.338164}, {113.825654, 23.338166}, {113.825653, 23.338168}, {113.825652, 23.338171}, {113.825651, 23.338173}, {113.82565, 23.338175}, {113.82565, 23.338178}, {113.825649, 23.33818}, {113.825648, 23.338182}, {113.825647, 23.338185}, {113.825646, 23.338187}, {113.825646, 23.33819}, + {113.825646, 23.338192}, {113.825645, 23.338194}, {113.825644, 23.338197}, {113.825643, 23.3382}, {113.825642, 23.338202}, {113.825642, 23.338205}, {113.825641, 23.338207}, {113.82564, 23.33821}, {113.825639, 23.338212}, {113.825639, 23.338214}, {113.825638, 23.338217}, {113.825637, 23.338219}, {113.825636, 23.338221}, {113.825635, 23.338224}, {113.825635, 23.338226}, {113.825634, 23.338229}, {113.825633, 23.338231}, {113.825632, 23.338233}, {113.825632, 23.338236}, {113.825631, 23.338238}, {113.82563, 23.33824}, {113.825629, 23.338243}, {113.825628, 23.338245}, {113.825629, 23.338248}, {113.825628, 23.33825}, {113.825627, 23.338252}, {113.825626, 23.338255}, + {113.825626, 23.338257}, {113.825625, 23.338259}, {113.825624, 23.338262}, {113.825623, 23.338265}, {113.825623, 23.338268}, {113.825622, 23.33827}, {113.825621, 23.338272}, {113.82562, 23.338275}, {113.82562, 23.338277}, {113.825619, 23.33828}, {113.825618, 23.338282}, {113.825618, 23.338284}, {113.825617, 23.338287}, {113.825616, 23.338289}, {113.825615, 23.338292}, {113.825532, 23.338833}, {113.825532, 23.338835}, {113.825532, 23.338838}, {113.825532, 23.33884}, {113.825532, 23.338843}, {113.825532, 23.338845}, {113.825531, 23.338848}, {113.825532, 23.33885}, {113.825532, 23.338853}, {113.825532, 23.338855}, {113.825532, 23.338858}, {113.825532, 23.33886}, + {113.825532, 23.338863}, {113.825531, 23.338865}, {113.825531, 23.338868}, {113.825531, 23.33887}, {113.825531, 23.338873}, {113.825531, 23.338875}, {113.825531, 23.338878}, {113.825532, 23.33888}, {113.825532, 23.338883}, {113.825531, 23.338885}, {113.825531, 23.338888}, {113.825531, 23.33889}, {113.825531, 23.338893}, {113.825531, 23.338895}, {113.825531, 23.338898}, {113.825531, 23.3389}, {113.825531, 23.338903}, {113.825531, 23.338905}, {113.82553, 23.338908}, {113.825531, 23.33891}, {113.825531, 23.338913}, {113.825531, 23.338915}, {113.825531, 23.338918}, {113.825531, 23.33892}, {113.825531, 23.338923}, {113.825531, 23.338925}, {113.825531, 23.338928}, + {113.825531, 23.33893}, {113.825531, 23.338933}, {113.82553, 23.338935}, {113.82553, 23.338938}, {113.825531, 23.33894}, {113.825531, 23.338942}, {113.825531, 23.338945}, {113.825531, 23.338947}, {113.825531, 23.33895}, {113.825531, 23.338952}, {113.825531, 23.338956}, {113.825531, 23.338958}, {113.825531, 23.338961}, {113.825531, 23.338963}, {113.825531, 23.338966}, {113.825532, 23.338968}, {113.825532, 23.338971}, {113.825532, 23.338973}, {113.825531, 23.338976}, {113.825531, 23.338978}, {113.825531, 23.338981}, {113.825531, 23.338983}, {113.825531, 23.338986}, {113.825531, 23.338988}, {113.825531, 23.338991}, {113.825531, 23.338993}, {113.825531, 23.338996}, + {113.825532, 23.338998}, {113.825532, 23.339001}, {113.825532, 23.339003}, {113.825532, 23.339006}, {113.825532, 23.339008}, {113.825532, 23.339011}, {113.825532, 23.339013}, {113.825532, 23.339016}, {113.825532, 23.339018}, {113.825532, 23.339021}, {113.825532, 23.339023}, {113.825533, 23.339026}, {113.825533, 23.339028}, {113.825533, 23.339031}, {113.825533, 23.339033}, {113.825533, 23.339036}, {113.825533, 23.339038}, {113.825533, 23.339041}, {113.825533, 23.339043}, {113.825533, 23.339046}, {113.825533, 23.339048}, {113.825533, 23.339051}, {113.825533, 23.339053}, {113.825534, 23.339056}, {113.825534, 23.339058}, {113.825534, 23.339061}, {113.825534, 23.339063}, + {113.825534, 23.339066}, {113.825534, 23.339068}, {113.825534, 23.339071}, {113.825534, 23.339073}, {113.825534, 23.339076}, {113.825534, 23.339078}, {113.825534, 23.339081}, {113.825535, 23.339083}, {113.825535, 23.339086}, {113.825535, 23.339088}, {113.825535, 23.339091}, {113.825535, 23.339093}, {113.825535, 23.339096}, {113.825535, 23.339098}, {113.825535, 23.339101}, {113.825535, 23.339103}, {113.825535, 23.339106}, {113.825535, 23.339108}, {113.825536, 23.339111}, {113.825536, 23.339113}, {113.825536, 23.339116}, {113.825536, 23.339118}, {113.825536, 23.339121}, {113.825536, 23.339123}, {113.825536, 23.339125}, {113.825536, 23.339128}, {113.825536, 23.33913}, + {113.825536, 23.339133}, {113.825536, 23.339135}, {113.825537, 23.339138}, {113.825537, 23.33914}, {113.825538, 23.339143}, {113.825538, 23.339145}, {113.825538, 23.339148}, {113.825538, 23.33915}, {113.825538, 23.339153}, {113.825538, 23.339156}, {113.825538, 23.339159}, {113.825538, 23.339161}, {113.825538, 23.339164}, {113.825539, 23.339166}, {113.825539, 23.339169}, {113.825539, 23.339171}, {113.825539, 23.339174}, {113.825539, 23.339176}, {113.825539, 23.339179}, {113.825539, 23.339181}, {113.825539, 23.339184}, {113.825539, 23.339186}, {113.825539, 23.339189}, {113.825539, 23.339191}, {113.82554, 23.339194}, {113.82554, 23.339196}, {113.825541, 23.339199}, + {113.825541, 23.339201}, {113.825541, 23.339204}, {113.825541, 23.339206}, {113.825541, 23.339209}, {113.825541, 23.339211}, {113.825541, 23.339214}, {113.825541, 23.339216}, {113.825542, 23.339219}, {113.825542, 23.339221}, {113.825542, 23.339224}, {113.825542, 23.339226}, {113.825542, 23.339229}, {113.825542, 23.339231}, {113.825542, 23.339234}, {113.825542, 23.339236}, {113.825542, 23.339239}, {113.825542, 23.339241}, {113.825543, 23.339244}, {113.825544, 23.339246}, {113.825544, 23.339249}, {113.825544, 23.339251}, {113.825544, 23.339254}, {113.825544, 23.339256}, {113.825544, 23.339259}, {113.825544, 23.339261}, {113.825544, 23.339264}, {113.825544, 23.339266}, + {113.825544, 23.339269}, {113.825544, 23.339271}, {113.825545, 23.339274}, {113.825545, 23.339276}, {113.825545, 23.339279}, {113.825545, 23.339281}, {113.825545, 23.339284}, {113.825675, 23.341405}, {113.825676, 23.341408}, {113.825676, 23.34141}, {113.825676, 23.341413}, {113.825676, 23.341415}, {113.825676, 23.341418}, {113.825676, 23.34142}, {113.825676, 23.341423}, {113.825676, 23.341425}, {113.825676, 23.341428}, {113.825676, 23.34143}, {113.825676, 23.341433}, {113.825676, 23.341435}, {113.825677, 23.341438}, {113.825677, 23.34144}, {113.825677, 23.341443}, {113.825678, 23.341445}, {113.825678, 23.341448}, {113.825678, 23.34145}, {113.825678, 23.341453}, + {113.825678, 23.341455}, {113.825678, 23.341458}, {113.825678, 23.34146}, {113.825679, 23.341463}, {113.825679, 23.341465}, {113.825679, 23.341468}, {113.825679, 23.34147}, {113.825679, 23.341473}, {113.825679, 23.341475}, {113.825679, 23.341478}, {113.825679, 23.34148}, {113.825679, 23.341483}, {113.825679, 23.341485}, {113.82568, 23.341488}, {113.825681, 23.34149}, {113.825681, 23.341493}, {113.825681, 23.341495}, {113.825681, 23.341498}, {113.825681, 23.3415}, {113.825681, 23.341503}, {113.825681, 23.341505}, {113.825681, 23.341508}, {113.825681, 23.34151}, {113.825681, 23.341513}, {113.825681, 23.341515}, {113.825682, 23.341518}, {113.825682, 23.34152}, + {113.825682, 23.341524}, {113.825682, 23.341526}, {113.825682, 23.341529}, {113.825682, 23.341531}, {113.825682, 23.341534}, {113.825683, 23.341536}, {113.825683, 23.341539}, {113.825683, 23.341541}, {113.825684, 23.341544}, {113.825684, 23.341546}, {113.825684, 23.341549}, {113.825684, 23.341551}, {113.825684, 23.341554}, {113.825684, 23.341556}, {113.825684, 23.341558}, {113.825684, 23.341561}, {113.825684, 23.341563}, {113.825684, 23.341566}, {113.825684, 23.341568}, {113.825684, 23.341571}, {113.825685, 23.341573}, {113.825685, 23.341576}, {113.825685, 23.341578}, {113.825685, 23.341581}, {113.825685, 23.341583}, {113.825686, 23.341586}, {113.825686, 23.341588}, + {113.825686, 23.341591}, {113.825686, 23.341593}, {113.825686, 23.341596}, {113.825687, 23.341598}, {113.825687, 23.341601}, {113.825687, 23.341603}, {113.825687, 23.341606}, {113.825687, 23.341608}, {113.825687, 23.341611}, {113.825687, 23.341613}, {113.825687, 23.341616}, {113.825687, 23.341618}, {113.825687, 23.341621}, {113.825687, 23.341623}, {113.825688, 23.341626}, {113.825688, 23.341628}, {113.825688, 23.341631}, {113.825688, 23.341633}, {113.825688, 23.341636}, {113.825688, 23.341638}, {113.825688, 23.341641}, {113.825688, 23.341643}, {113.825688, 23.341646}, {113.825688, 23.341648}, {113.825689, 23.341651}, {113.82569, 23.341653}, {113.82569, 23.341656}, + {113.82569, 23.341658}, {113.82569, 23.341661}, {113.82569, 23.341663}, {113.82569, 23.341666}, {113.82569, 23.341668}, {113.82569, 23.341671}, {113.82569, 23.341673}, {113.82569, 23.341676}, {113.82569, 23.341678}, {113.825691, 23.341681}, {113.825691, 23.341683}, {113.825691, 23.341686}, {113.825691, 23.341688}, {113.825691, 23.341691}, {113.825691, 23.341693}, {113.825691, 23.341696}, {113.825691, 23.341698}, {113.825691, 23.341701}, {113.825691, 23.341703}, {113.825691, 23.341706}, {113.825691, 23.341708}, {113.825692, 23.341711}, {113.825692, 23.341713}, {113.825692, 23.341716}, {113.825692, 23.341718}, {113.825692, 23.341721}, {113.825692, 23.341723}, + {113.825692, 23.341726}, {113.825692, 23.341728}, {113.825692, 23.341731}, {113.825692, 23.341733}, {113.825693, 23.341736}, {113.825693, 23.341738}, {113.825693, 23.34174}, {113.825693, 23.341744}, {113.825693, 23.341746}, {113.825693, 23.341749}, {113.825693, 23.341751}, {113.825693, 23.341754}, {113.825693, 23.341756}, {113.825693, 23.341759}, {113.825693, 23.341761}, {113.825693, 23.341764}, {113.825694, 23.341766}, {113.825694, 23.341769}, {113.825694, 23.341771}, {113.825694, 23.341774}, {113.825694, 23.341776}, {113.825694, 23.341779}, {113.825694, 23.341781}, {113.825694, 23.341784}, {113.825694, 23.341786}, {113.825694, 23.341789}, + {113.825694, 23.341791}, {113.825695, 23.341794}, {113.825695, 23.341796}, {113.825695, 23.341799}, {113.825695, 23.341801}, {113.825695, 23.341804}, {113.825695, 23.341806}, {113.825695, 23.341809}, {113.825695, 23.341811}, {113.825695, 23.341814}, {113.825695, 23.341816}, {113.825695, 23.341819}, {113.825696, 23.341821}, {113.825696, 23.341824}, {113.825696, 23.341826}, {113.825696, 23.341829}, {113.825696, 23.341831}, {113.825696, 23.341834}, {113.825696, 23.341836}, {113.825696, 23.341839}, {113.825696, 23.341841}, {113.825696, 23.341844}, {113.825696, 23.341846}, {113.825697, 23.341849}, {113.825697, 23.341851}, {113.825697, 23.341854}, + {113.825696, 23.341856}, {113.825639, 23.342752}, {113.825909, 23.343038}, {113.826124, 23.34317}, {113.826246, 23.343207}, {113.826544, 23.343462}, {113.826663, 23.343725}, {113.826921, 23.344028}, {113.826946, 23.344045}, {113.827411, 23.344775}, {113.827452, 23.345071}, {113.827433, 23.345444}, {113.827373, 23.345688}, {113.827847, 23.346486}, {113.827867, 23.346492}, {113.828217, 23.346555}, {113.828812, 23.346578}, {113.829478, 23.346804}, {113.829926, 23.347127}, {113.830393, 23.34749}, {113.830756, 23.347827}, {113.83112, 23.348231}, {113.832415, 23.349689}, {113.833006, 23.350048}, {113.834533, 23.350448}, {113.835235, 23.350919}, + {113.835526, 23.35132}, {113.836805, 23.353126}, {113.836931, 23.353474}, {113.837019, 23.35471}, {113.837041, 23.354934}, {113.837129, 23.355633}, {113.837135, 23.355731}, {113.837148, 23.35729}, {113.837124, 23.357391}, {113.837093, 23.357543}, {113.837116, 23.357842}, {113.837231, 23.358547}, {113.837488, 23.359569}, {113.837398, 23.359954}, {113.838019, 23.360873}, {113.838056, 23.360928}, {113.840391, 23.36073}, + }, nil, DefaultIndex) + expect(t, poly.ContainsPoint(P(113.837019, 23.35471))) + expect(t, !poly.ContainsPoint(P(114.837019, 23.35471))) +} + +func TestIssue313(t *testing.T) { + seattle := NewPoly([]Point{ + {-122.459774, 47.672418}, {-122.459709, 47.675379}, {-122.4597, 47.675788}, {-122.459683, 47.675916}, {-122.458971, 47.680926}, {-122.458558, 47.682498}, {-122.458111, 47.683765}, {-122.458, 47.684079}, {-122.457614, 47.685171}, {-122.457488, 47.68543}, {-122.456968, 47.686504}, {-122.456092, 47.688312}, {-122.454756, 47.690489}, {-122.454429, 47.690985}, {-122.453318, 47.692671}, {-122.453026, 47.693266}, {-122.452737, 47.693789}, {-122.451562, 47.695856}, {-122.450629, 47.697496}, {-122.449862, 47.698864}, {-122.449309, 47.700139}, {-122.448927, 47.700862}, {-122.448835, 47.701036}, {-122.448531, 47.70152}, {-122.448055, 47.702279}, {-122.446925, 47.704478}, {-122.446429, 47.705188}, {-122.445657, 47.706293}, {-122.444914, 47.707091}, {-122.444314, 47.707646}, {-122.441601, 47.709459}, {-122.440025, 47.710588}, {-122.439289, 47.711232}, {-122.43746, 47.713048}, {-122.437052, 47.713926}, {-122.436881, 47.714291}, {-122.436612, 47.715198}, {-122.435789, 47.717976}, {-122.435323, 47.719821}, {-122.434396, 47.724353}, {-122.434061, 47.726357}, {-122.434002, 47.726715}, {-122.433464, 47.728908}, {-122.433291, 47.729347}, {-122.433205, 47.729578}, {-122.432673, 47.730958}, {-122.432216, 47.731932}, {-122.431994, 47.732405}, {-122.42992, 47.73528}, {-122.381268, 47.734302}, {-122.375049, 47.734164}, {-122.374886, 47.734164}, {-122.374681, 47.734163}, {-122.374618, 47.734163}, {-122.374476, 47.734165}, {-122.37432, 47.734163}, {-122.374302, 47.734163}, {-122.374241, 47.734163}, {-122.374196, 47.734163}, {-122.373467, 47.734161}, {-122.373354, 47.734161}, {-122.372448, 47.73416}, {-122.37216, 47.734159}, {-122.372085, 47.734159}, {-122.371754, 47.734158}, {-122.371669, 47.734158}, {-122.370363, 47.734156}, {-122.370249, 47.734156}, {-122.36903, 47.734154}, {-122.367154, 47.734144}, {-122.367055, 47.734144}, {-122.3663, 47.73414}, {-122.363917, 47.734136}, {-122.363853, 47.734136}, {-122.361959, 47.734145}, {-122.360931, 47.734139}, {-122.359169, 47.734136}, {-122.355572, 47.734129}, {-122.35426, 47.734128}, {-122.35304, 47.734128}, {-122.351774, 47.734128}, {-122.350507, 47.734128}, {-122.347803, 47.734127}, {-122.3463, 47.734127}, {-122.345211, 47.734127}, {-122.345098, 47.734127}, + {-122.344994, 47.734127}, {-122.344005, 47.734128}, {-122.343425, 47.734128}, {-122.342393, 47.734129}, {-122.341337, 47.73413}, {-122.341034, 47.73413}, {-122.340744, 47.73413}, {-122.339688, 47.734131}, {-122.338631, 47.734132}, {-122.338076, 47.734132}, {-122.336983, 47.734133}, {-122.335926, 47.734134}, {-122.335546, 47.734134}, {-122.335333, 47.734134}, {-122.334277, 47.734135}, {-122.333749, 47.73413}, {-122.33303, 47.734125}, {-122.331579, 47.734114}, {-122.330226, 47.734103}, {-122.328881, 47.734093}, {-122.326382, 47.734074}, {-122.326216, 47.734073}, {-122.325655, 47.734069}, {-122.32532, 47.734062}, {-122.325246, 47.734065}, {-122.324982, 47.734063}, {-122.324734, 47.734061}, {-122.324523, 47.73406}, {-122.324457, 47.734059}, {-122.323589, 47.734052}, {-122.322219, 47.734039}, {-122.320801, 47.734026}, {-122.318117, 47.734001}, {-122.31676, 47.733988}, {-122.315443, 47.733975}, {-122.31275, 47.73395}, {-122.310048, 47.733919}, {-122.308696, 47.733904}, {-122.308045, 47.733896}, {-122.307326, 47.733889}, {-122.306171, 47.733876}, {-122.305994, 47.733874}, {-122.304844, 47.73386}, {-122.304574, 47.733856}, {-122.303599, 47.733846}, {-122.301955, 47.733828}, {-122.300586, 47.733817}, {-122.29923, 47.733806}, {-122.297877, 47.733795}, {-122.29652, 47.733784}, {-122.295306, 47.733774}, {-122.295163, 47.733773}, {-122.294692, 47.733769}, {-122.29381, 47.733762}, {-122.292435, 47.733751}, {-122.291098, 47.73374}, {-122.289753, 47.733732}, {-122.288415, 47.733729}, {-122.28705, 47.733721}, {-122.286711, 47.733724}, {-122.286323, 47.733729}, {-122.285957, 47.733732}, {-122.285869, 47.73372}, {-122.284606, 47.73365}, {-122.284471, 47.733648}, {-122.283695, 47.733605}, {-122.28194, 47.733583}, {-122.276442, 47.733515}, {-122.274368, 47.73349}, {-122.273588, 47.726928}, {-122.273307, 47.725913}, {-122.271923, 47.720434}, {-122.271223, 47.718857}, {-122.269138, 47.714124}, {-122.267094, 47.71093}, {-122.265263, 47.708068}, {-122.262323, 47.704308}, {-122.260725, 47.702264}, {-122.255062, 47.696866}, {-122.25105, 47.693615}, {-122.246956, 47.691202}, {-122.243423, 47.689524}, {-122.242453, 47.689064}, {-122.241812, 47.688797}, {-122.236308, 47.686224}, {-122.233657, 47.684762}, {-122.232807, 47.684085}, {-122.232066, 47.683353}, {-122.231443, 47.682572}, {-122.230944, 47.681752}, {-122.230575, 47.680901}, {-122.23034, 47.680029}, {-122.23027, 47.679561}, {-122.229983, 47.677172}, {-122.229681, 47.668681}, {-122.229725, 47.667777}, {-122.229908, 47.666881}, {-122.230229, 47.666003}, {-122.230684, 47.665152}, {-122.230814, 47.66495}, {-122.231924, 47.663757}, {-122.23226, 47.663395}, {-122.233939, 47.661951}, {-122.235832, 47.660633}, {-122.237078, 47.65993}, {-122.237918, 47.659456}, {-122.240175, 47.658433}, {-122.24199, 47.657767}, {-122.245926, 47.656132}, {-122.249553, 47.654244}, {-122.25103, 47.653346}, {-122.251167, 47.653243}, {-122.253351, 47.651413}, {-122.255292, 47.649384}, {-122.256908, 47.647229}, {-122.258182, 47.644971}, {-122.2591, 47.642636}, {-122.25937, 47.641524}, {-122.259604, 47.640454}, {-122.259622, 47.640374}, {-122.260051, 47.63699}, {-122.260675, 47.632007}, {-122.261091, 47.628694}, {-122.262212, 47.624885}, {-122.262234, 47.624812}, {-122.262238, 47.624776}, {-122.262722, 47.621146}, {-122.263651, 47.613115}, {-122.265147, 47.601321}, {-122.265672, 47.597061}, {-122.266327, 47.591805}, {-122.266544, 47.59005}, {-122.266556, 47.589953}, {-122.266573, 47.589794}, {-122.26661, 47.589473}, {-122.266719, 47.588843}, {-122.266832, 47.587369}, {-122.266821, 47.586371}, {-122.266493, 47.583781}, {-122.266241, 47.582742}, {-122.265413, 47.580486}, {-122.264919, 47.57948}, {-122.264851, 47.579372}, {-122.264156, 47.578503}, {-122.26328, 47.577637}, {-122.262695, 47.577172}, {-122.262276, 47.576838}, {-122.261153, 47.576115}, {-122.259925, 47.575475}, {-122.258604, 47.574925}, {-122.257206, 47.574472}, {-122.255868, 47.574148}, {-122.255706, 47.574107}, {-122.253772, 47.573614}, {-122.251847, 47.57296}, {-122.25116, 47.572682}, {-122.251032, 47.572612}, {-122.249739, 47.571901}, {-122.247649, 47.570537}, {-122.24578, 47.569033}, {-122.244154, 47.567405}, {-122.242788, 47.565671}, {-122.241698, 47.563849}, {-122.240895, 47.561961}, {-122.240388, 47.560026}, {-122.240183, 47.558065}, {-122.240282, 47.556101}, {-122.240575, 47.554038}, {-122.241197, 47.551954}, {-122.242137, 47.549925}, {-122.243386, 47.547974}, {-122.244928, 47.546122}, {-122.245003, 47.546051}, {-122.246748, 47.544389}, {-122.248826, 47.542795}, {-122.251073, 47.541396}, {-122.251215, 47.541298}, {-122.252158, 47.540468}, {-122.253022, 47.539516}, {-122.253734, 47.538509}, {-122.254287, 47.537457}, {-122.254675, 47.536371}, {-122.254892, 47.535264}, {-122.254938, 47.534147}, {-122.254914, 47.53377}, {-122.254699, 47.532185}, {-122.254241, 47.530623}, {-122.253544, 47.529103}, {-122.252617, 47.52764}, {-122.251468, 47.526251}, {-122.251192, 47.525962}, {-122.251058, 47.52586}, {-122.248731, 47.524144}, {-122.246099, 47.522571}, {-122.243239, 47.521192}, {-122.243131, 47.521146}, {-122.240322, 47.519865}, {-122.235699, 47.517769}, {-122.227476, 47.513624}, {-122.219822, 47.509801}, {-122.224613, 47.509758}, {-122.235294, 47.509513}, {-122.236233, 47.509521}, {-122.237053, 47.509524}, {-122.238194, 47.509519}, {-122.23822, 47.50953}, + {-122.238326, 47.509542}, {-122.238425, 47.509544}, {-122.238806, 47.509528}, {-122.238919, 47.509557}, {-122.241152, 47.509591}, {-122.241569, 47.509597}, {-122.243034, 47.509597}, {-122.243218, 47.509604}, {-122.243302, 47.509644}, {-122.24336, 47.509682}, {-122.243726, 47.509687}, {-122.244089, 47.509692}, {-122.244131, 47.509692}, {-122.245001, 47.509703}, {-122.245387, 47.509708}, {-122.245652, 47.509721}, {-122.245787, 47.509728}, {-122.247099, 47.509734}, {-122.247146, 47.509734}, {-122.247118, 47.509658}, {-122.246942, 47.509233}, {-122.246945, 47.50915}, {-122.246897, 47.509076}, {-122.246833, 47.509015}, {-122.246742, 47.508968}, {-122.246972, 47.508211}, {-122.24611, 47.507269}, {-122.246079, 47.507236}, {-122.246079, 47.507227}, {-122.246078, 47.507211}, {-122.246077, 47.507187}, {-122.246075, 47.507147}, {-122.246067, 47.50697}, {-122.246059, 47.506791}, {-122.24605, 47.506612}, {-122.246048, 47.506557}, {-122.246045, 47.506502}, {-122.246039, 47.506374}, {-122.246037, 47.50632}, {-122.246032, 47.506205}, {-122.24603, 47.506169}, {-122.246047, 47.506106}, {-122.246063, 47.506043}, {-122.246069, 47.506021}, {-122.246108, 47.505877}, {-122.24615, 47.505717}, {-122.246203, 47.505517}, {-122.246259, 47.505306}, {-122.246266, 47.505278}, {-122.246278, 47.505232}, {-122.246238, 47.505163}, {-122.246203, 47.505104}, {-122.246188, 47.505079}, {-122.246106, 47.50494}, {-122.245991, 47.504745}, {-122.245896, 47.504583}, {-122.245802, 47.504425}, {-122.245714, 47.504276}, {-122.245609, 47.504097}, {-122.2455, 47.503913}, {-122.245485, 47.503886}, {-122.245496, 47.503814}, {-122.245548, 47.503531}, {-122.245635, 47.503056}, {-122.247206, 47.503073}, {-122.24802, 47.503081}, {-122.248016, 47.503515}, {-122.248015, 47.503653}, {-122.248014, 47.503711}, {-122.248012, 47.50385}, {-122.248009, 47.504048}, {-122.248003, 47.50446}, {-122.248003, 47.504505}, {-122.247994, 47.505148}, {-122.247993, 47.505182}, {-122.247992, 47.505288}, {-122.247988, 47.505591}, {-122.247985, 47.505789}, {-122.247982, 47.505986}, {-122.24798, 47.506128}, {-122.24798, 47.506164}, {-122.247979, 47.506225}, {-122.247979, 47.506253}, {-122.247978, 47.506316}, {-122.247977, 47.506349}, {-122.248001, 47.506349}, {-122.248081, 47.50635}, {-122.248102, 47.506351}, {-122.248327, 47.506354}, {-122.248544, 47.506357}, {-122.248651, 47.506358}, {-122.248687, 47.506359}, {-122.248757, 47.50636}, {-122.248784, 47.506361}, {-122.248787, 47.506278}, {-122.248787, 47.506259}, {-122.24879, 47.506116}, {-122.248811, 47.504792}, {-122.248822, 47.504134}, {-122.248824, 47.504021}, {-122.248827, 47.503838}, {-122.248839, 47.503076}, {-122.248843, 47.502995}, {-122.245644, 47.502953}, {-122.244959, 47.502944}, {-122.243131, 47.502915}, {-122.24238, 47.502904}, {-122.24125, 47.502891}, {-122.240819, 47.502883}, {-122.240828, 47.502227}, {-122.240836, 47.501466}, {-122.240862, 47.499633}, {-122.240861, 47.499568}, {-122.24087, 47.499245}, {-122.241583, 47.49925}, {-122.248161, 47.49935}, {-122.24841, 47.499353}, {-122.24846, 47.499354}, {-122.248674, 47.499357}, {-122.248794, 47.499358}, {-122.248816, 47.499359}, {-122.248835, 47.499359}, {-122.248902, 47.49936}, {-122.249023, 47.499357}, {-122.24951, 47.499344}, {-122.249635, 47.499343}, {-122.249754, 47.499711}, {-122.249954, 47.500388}, {-122.250166, 47.501099}, {-122.250258, 47.5014}, {-122.250349, 47.501702}, {-122.250506, 47.502219}, {-122.250625, 47.502519}, {-122.250734, 47.502716}, {-122.250814, 47.502831}, {-122.251413, 47.502801}, {-122.251642, 47.502825}, {-122.25362, 47.502729}, {-122.259515, 47.502537}, {-122.259529, 47.501306}, {-122.259552, 47.500154}, {-122.259608, 47.497346}, {-122.259638, 47.495987}, {-122.259643, 47.495601}, {-122.264081, 47.495551}, {-122.26427, 47.495571}, {-122.264853, 47.495565}, {-122.265348, 47.495564}, {-122.266417, 47.495563}, {-122.266858, 47.495546}, {-122.268426, 47.495523}, {-122.268575, 47.495524}, {-122.269276, 47.495531}, {-122.269685, 47.495535}, {-122.270261, 47.495541}, {-122.270302, 47.495543}, {-122.270298, 47.496033}, {-122.270294, 47.496354}, {-122.270292, 47.496569}, {-122.270283, 47.497002}, {-122.270282, 47.497059}, {-122.270278, 47.497327}, {-122.270273, 47.49758}, {-122.270227, 47.500282}, {-122.270327, 47.500303}, {-122.270449, 47.500275}, {-122.270591, 47.500284}, {-122.270671, 47.500303}, {-122.270755, 47.500808}, {-122.270704, 47.500827}, {-122.270855, 47.501456}, {-122.270875, 47.501548}, {-122.270887, 47.501613}, {-122.270787, 47.501592}, {-122.270264, 47.501468}, {-122.270202, 47.502629}, {-122.270175, 47.503177}, {-122.270166, 47.504475}, {-122.270157, 47.504896}, {-122.270157, 47.505101}, {-122.270145, 47.50609}, {-122.27014, 47.506581}, {-122.270125, 47.507025}, {-122.270087, 47.508783}, {-122.27007, 47.509537}, {-122.270069, 47.509663}, {-122.271549, 47.509696}, {-122.274076, 47.509753}, {-122.275411, 47.509784}, {-122.275412, 47.509633}, {-122.275418, 47.50893}, {-122.27543, 47.507142}, {-122.275432, 47.506927}, {-122.275774, 47.507007}, {-122.276072, 47.507126}, {-122.276241, 47.507231}, {-122.277078, 47.507773}, {-122.277225, 47.507839}, {-122.277387, 47.507892}, {-122.277542, 47.507936}, {-122.277703, 47.507967}, {-122.277889, 47.507992}, {-122.278229, 47.507991}, {-122.278341, 47.507987}, {-122.27838, 47.508871}, + {-122.278534, 47.509577}, {-122.278544, 47.509813}, {-122.278547, 47.509855}, {-122.278645, 47.509856}, {-122.278721, 47.509858}, {-122.27985, 47.509883}, {-122.280761, 47.509904}, {-122.281278, 47.509916}, {-122.282464, 47.509943}, {-122.282557, 47.509945}, {-122.282967, 47.509953}, {-122.283365, 47.509962}, {-122.283686, 47.509974}, {-122.28387, 47.509975}, {-122.284424, 47.509988}, {-122.284673, 47.509993}, {-122.284798, 47.509996}, {-122.284885, 47.509998}, {-122.285645, 47.510015}, {-122.285757, 47.510018}, {-122.285928, 47.510022}, {-122.286147, 47.510028}, {-122.286235, 47.51003}, {-122.286518, 47.510037}, {-122.287504, 47.510059}, {-122.287646, 47.510063}, {-122.287825, 47.510067}, {-122.289847, 47.510112}, {-122.290052, 47.510117}, {-122.290168, 47.510119}, {-122.290257, 47.510121}, {-122.290795, 47.510133}, {-122.29134, 47.510146}, {-122.291339, 47.510247}, {-122.291338, 47.510399}, {-122.291336, 47.510629}, {-122.291334, 47.510695}, {-122.291334, 47.510766}, {-122.291334, 47.51083}, {-122.291334, 47.510883}, {-122.291325, 47.511627}, {-122.291312, 47.513415}, {-122.291306, 47.513592}, {-122.291305, 47.513673}, {-122.291305, 47.513744}, {-122.291303, 47.513885}, {-122.291287, 47.51489}, {-122.291282, 47.516414}, {-122.29128, 47.51698}, {-122.291281, 47.517682}, {-122.291281, 47.517825}, {-122.291282, 47.518567}, {-122.291283, 47.518838}, {-122.291283, 47.519301}, {-122.291283, 47.519524}, {-122.291284, 47.51969}, {-122.291284, 47.519737}, {-122.291284, 47.519998}, {-122.291285, 47.52049}, {-122.291285, 47.520553}, {-122.291285, 47.520669}, {-122.291286, 47.521258}, {-122.291286, 47.521315}, {-122.291287, 47.522313}, {-122.291287, 47.522362}, {-122.291288, 47.522843}, {-122.291288, 47.522948}, {-122.291289, 47.523339}, {-122.291289, 47.523379}, {-122.29129, 47.524199}, {-122.29129, 47.52428}, {-122.291293, 47.524381}, {-122.291296, 47.524463}, {-122.291372, 47.524462}, {-122.291801, 47.524462}, {-122.293357, 47.524461}, {-122.29365, 47.52446}, {-122.293887, 47.52446}, {-122.294029, 47.52446}, {-122.295486, 47.524459}, {-122.29986, 47.524454}, {-122.299966, 47.524454}, {-122.300481, 47.524454}, {-122.300501, 47.524453}, {-122.300374, 47.524581}, {-122.300302, 47.524663}, {-122.300235, 47.52471}, {-122.300212, 47.524749}, + {-122.300208, 47.52478}, {-122.300139, 47.524907}, {-122.300088, 47.525001}, {-122.299756, 47.526019}, {-122.299746, 47.526257}, {-122.299776, 47.526614}, {-122.299862, 47.526967}, {-122.29989, 47.52709}, {-122.300272, 47.52798}, {-122.300916, 47.529034}, {-122.301367, 47.529248}, {-122.301578, 47.529347}, {-122.303344, 47.530249}, {-122.305076, 47.531051}, {-122.305114, 47.531303}, {-122.305172, 47.531678}, {-122.307104, 47.531709}, {-122.307306, 47.531712}, {-122.30843, 47.531733}, {-122.308924, 47.531741}, {-122.309177, 47.531745}, {-122.313285, 47.531774}, {-122.31681, 47.53177}, {-122.317958, 47.531767}, {-122.31803, 47.531765}, {-122.318264, 47.531766}, {-122.318263, 47.531643}, {-122.318258, 47.530963}, {-122.318256, 47.530464}, {-122.318249, 47.529776}, {-122.318245, 47.529455}, {-122.318244, 47.529438}, {-122.31822, 47.529438}, {-122.318118, 47.529438}, {-122.318016, 47.529437}, {-122.317914, 47.529437}, {-122.317812, 47.529437}, {-122.31771, 47.529436}, {-122.317576, 47.529436}, {-122.317575, 47.529413}, {-122.317568, 47.529169}, {-122.317575, 47.529026}, {-122.31759, 47.528825}, {-122.317599, 47.528549}, {-122.317554, 47.52821}, {-122.317483, 47.528171}, {-122.314898, 47.527369}, {-122.314718, 47.527316}, {-122.312833, 47.526819}, {-122.312645, 47.526777}, {-122.311887, 47.526583}, {-122.311836, 47.526557}, {-122.311724, 47.526485}, {-122.311609, 47.526363}, {-122.311428, 47.526182}, {-122.311084, 47.525758}, {-122.310315, 47.525051}, {-122.310148, 47.524907}, {-122.309972, 47.524754}, {-122.309856, 47.524584}, {-122.309703, 47.524299}, {-122.309637, 47.52412}, {-122.309595, 47.524004}, {-122.309533, 47.523704}, {-122.309521, 47.523466}, {-122.309518, 47.5234}, {-122.309549, 47.523097}, {-122.309619, 47.522756}, {-122.309667, 47.522187}, {-122.309671, 47.522145}, {-122.309758, 47.522145}, {-122.310126, 47.522145}, {-122.310995, 47.522146}, {-122.313361, 47.522133}, {-122.313516, 47.522154}, {-122.313666, 47.522183}, {-122.314266, 47.522183}, {-122.314889, 47.522191}, {-122.314984, 47.522197}, {-122.315112, 47.52221}, {-122.31586, 47.522206}, {-122.316023, 47.522218}, {-122.317656, 47.522222}, {-122.317641, 47.521828}, {-122.317611, 47.521011}, {-122.317593, 47.520549}, {-122.317588, 47.520393}, {-122.317578, 47.520107}, {-122.317571, 47.51995}, {-122.317567, 47.519832}, {-122.317544, 47.519205}, {-122.31753, 47.518832}, {-122.317526, 47.518725}, {-122.318131, 47.518727}, {-122.318413, 47.518724}, {-122.318601, 47.518722}, {-122.320136, 47.518714}, {-122.320343, 47.518714}, {-122.321292, 47.518716}, {-122.321607, 47.518714}, {-122.323154, 47.518686}, {-122.32323, 47.518684}, {-122.323438, 47.518685}, {-122.323858, 47.518686}, {-122.324073, 47.518686}, {-122.324135, 47.518686}, {-122.32451, 47.518687}, {-122.3251, 47.518674}, {-122.325879, 47.518691}, {-122.325881, 47.520232}, {-122.325876, 47.52092}, {-122.327899, 47.520921}, {-122.329592, 47.520913}, {-122.330073, 47.520906}, {-122.330599, 47.520902}, {-122.330779, 47.520901}, {-122.330906, 47.520902}, {-122.33063, 47.520513}, {-122.329917, 47.519564}, {-122.329914, 47.519549}, {-122.329852, 47.519245}, {-122.329836, 47.519168}, {-122.32969, 47.518457}, {-122.329611, 47.518302}, {-122.329556, 47.518195}, {-122.329518, 47.518122}, {-122.329369, 47.517833}, {-122.329357, 47.517809}, {-122.329308, 47.51753}, {-122.329256, 47.517381}, {-122.329238, 47.517329}, {-122.329227, 47.517281}, {-122.329215, 47.517225}, {-122.329207, 47.517184}, {-122.3292, 47.51717}, {-122.329177, 47.517126}, {-122.328849, 47.516755}, {-122.328713, 47.516593}, {-122.328639, 47.516505}, {-122.327326, 47.513997}, {-122.328184, 47.513994}, {-122.328565, 47.513999}, {-122.330079, 47.513997}, {-122.330273, 47.513999}, {-122.330289, 47.514029}, {-122.330586, 47.514539}, {-122.33097, 47.514944}, {-122.331153, 47.515121}, {-122.331182, 47.515149}, {-122.331385, 47.515132}, {-122.331405, 47.515098}, {-122.332616, 47.514882}, {-122.333497, 47.514725}, {-122.333555, 47.514742}, {-122.333512, 47.514531}, {-122.334036, 47.514522}, {-122.334357, 47.514523}, {-122.334353, 47.514499}, {-122.334252, 47.513897}, {-122.335707, 47.513923}, {-122.336507, 47.513914}, {-122.336645, 47.513907}, {-122.33704, 47.513893}, {-122.337124, 47.515023}, {-122.337275, 47.515858}, {-122.33739, 47.516502}, {-122.337408, 47.516602}, {-122.337469, 47.516944}, {-122.33748, 47.517005}, {-122.337492, 47.517073}, {-122.337521, 47.517257}, {-122.337537, 47.517256}, {-122.33793, 47.517258}, {-122.339915, 47.517269}, {-122.340134, 47.517277}, {-122.340448, 47.51724}, {-122.34075, 47.51722}, {-122.341437, 47.517276}, {-122.34161, 47.517281}, {-122.342878, 47.517287}, {-122.343873, 47.517293}, {-122.344151, 47.517294}, {-122.344639, 47.517299}, {-122.345426, 47.517302}, {-122.346699, 47.517309}, {-122.347972, 47.517317}, {-122.348017, 47.517317}, {-122.349246, 47.517324}, {-122.350655, 47.517332}, {-122.351735, 47.517339}, {-122.351778, 47.517339}, {-122.352909, 47.517361}, {-122.353404, 47.51737}, {-122.354037, 47.517374}, {-122.355167, 47.51738}, {-122.356112, 47.517386}, {-122.356513, 47.517382}, {-122.357859, 47.51736}, {-122.358228, 47.517353}, {-122.358557, 47.517347}, {-122.358564, 47.515528}, {-122.359181, 47.515528}, {-122.360495, 47.515528}, {-122.360536, 47.51682}, {-122.360542, 47.51736}, + {-122.361127, 47.517362}, {-122.361851, 47.517361}, {-122.36262, 47.517361}, {-122.363226, 47.517362}, {-122.36457, 47.517362}, {-122.365915, 47.517361}, {-122.36728, 47.517377}, {-122.367695, 47.517381}, {-122.368662, 47.517393}, {-122.3714, 47.517424}, {-122.371316, 47.516653}, {-122.371272, 47.516076}, {-122.371274, 47.516024}, {-122.371263, 47.515745}, {-122.371152, 47.514459}, {-122.371104, 47.513705}, {-122.37108, 47.513335}, {-122.37102, 47.512548}, {-122.371, 47.51229}, {-122.370984, 47.512047}, {-122.370888, 47.510038}, {-122.37086, 47.509458}, {-122.370877, 47.508199}, {-122.370862, 47.508017}, {-122.370714, 47.507259}, {-122.37053, 47.506664}, {-122.370465, 47.506409}, {-122.370464, 47.506398}, {-122.370394, 47.505821}, {-122.370404, 47.505686}, {-122.370449, 47.505503}, {-122.370474, 47.505402}, {-122.37054, 47.505135}, {-122.37055, 47.50452}, {-122.370561, 47.503866}, {-122.370596, 47.503683}, {-122.370694, 47.503511}, {-122.370919, 47.503241}, {-122.371413, 47.502778}, {-122.372051, 47.502182}, {-122.372257, 47.502014}, {-122.373121, 47.501378}, {-122.3738, 47.500834}, {-122.373923, 47.500697}, {-122.373972, 47.500581}, {-122.374059, 47.499917}, {-122.374133, 47.499617}, {-122.374278, 47.499221}, {-122.37485, 47.497659}, {-122.37492, 47.497497}, {-122.375024, 47.49732}, {-122.375068, 47.497248}, {-122.375208, 47.497032}, {-122.375312, 47.496869}, {-122.375652, 47.496434}, {-122.380128, 47.493203}, {-122.38181, 47.491912}, {-122.399237, 47.48522}, {-122.407658, 47.48215}, {-122.409315, 47.485945}, {-122.409341, 47.485999}, {-122.409371, 47.486053}, {-122.415446, 47.497105}, {-122.416946, 47.499846}, {-122.418072, 47.501568}, {-122.423326, 47.508282}, {-122.42629, 47.512069}, {-122.430797, 47.516605}, {-122.436093, 47.521938}, {-122.436484, 47.522337}, {-122.439282, 47.525625}, {-122.441558, 47.529092}, {-122.442968, 47.532037}, {-122.443244, 47.53264}, {-122.443323, 47.532812}, {-122.444449, 47.536414}, {-122.445032, 47.540187}, {-122.445031, 47.541851}, {-122.445031, 47.54253}, {-122.444867, 47.542661}, {-122.444636, 47.543295}, {-122.444778, 47.544416}, {-122.444372, 47.54783}, {-122.443864, 47.551798}, {-122.443888, 47.553717}, {-122.444013, 47.554485}, {-122.444297, 47.555565}, {-122.444622, 47.55646}, {-122.444772, 47.556877}, + {-122.445159, 47.557759}, {-122.445723, 47.558828}, {-122.447001, 47.560745}, {-122.44849, 47.562978}, {-122.450247, 47.565615}, {-122.450834, 47.566373}, {-122.451683, 47.567468}, {-122.452447, 47.568371}, {-122.453062, 47.569097}, {-122.453249, 47.569399}, {-122.453374, 47.569604}, {-122.453994, 47.57091}, {-122.454134, 47.571318}, {-122.45448, 47.572328}, {-122.454766, 47.573685}, {-122.454787, 47.57397}, {-122.454847, 47.574757}, {-122.454781, 47.576186}, {-122.454746, 47.576391}, {-122.454726, 47.576591}, {-122.454627, 47.577209}, {-122.4541, 47.579662}, {-122.453582, 47.581237}, {-122.453158, 47.582208}, {-122.451696, 47.585085}, {-122.451383, 47.585702}, {-122.450513, 47.587416}, {-122.449692, 47.588963}, {-122.448332, 47.591253}, {-122.447883, 47.592297}, {-122.447569, 47.593313}, {-122.447305, 47.594455}, {-122.447157, 47.595574}, {-122.446984, 47.597612}, {-122.446947, 47.5981}, {-122.446859, 47.599162}, {-122.446558, 47.602772}, {-122.446203, 47.607035}, {-122.446063, 47.607998}, {-122.446503, 47.611494}, {-122.446853, 47.61242}, {-122.447298, 47.613069}, {-122.447711, 47.614209}, {-122.447908, 47.614741}, {-122.448321, 47.615696}, {-122.448615, 47.616382}, {-122.448642, 47.616446}, {-122.44867, 47.61651}, {-122.449203, 47.617523}, {-122.449543, 47.618171}, {-122.45005, 47.619022}, {-122.451403, 47.620957}, {-122.453006, 47.622876}, {-122.453763, 47.623782}, {-122.454252, 47.624636}, {-122.454908, 47.626561}, {-122.454985, 47.627192}, {-122.45491, 47.627647}, {-122.454567, 47.629007}, {-122.454372, 47.629669}, {-122.454199, 47.630258}, {-122.454069, 47.630696}, {-122.453871, 47.631371}, {-122.453682, 47.632013}, {-122.453522, 47.632556}, {-122.453067, 47.634524}, {-122.45284, 47.636087}, {-122.452694, 47.638749}, {-122.452675, 47.63911}, {-122.452686, 47.642348}, {-122.452812, 47.643798}, {-122.453181, 47.646248}, {-122.453251, 47.646713}, {-122.453391, 47.647654}, {-122.454105, 47.651119}, {-122.454282, 47.652018}, {-122.454346, 47.652662}, {-122.454405, 47.653253}, {-122.455216, 47.655316}, {-122.456059, 47.657707}, {-122.456757, 47.659683}, {-122.457251, 47.661335}, {-122.458099, 47.663906}, {-122.458569, 47.665605}, {-122.459375, 47.669161}, {-122.45962, 47.671118}, {-122.459737, 47.67212}, {-122.459774, 47.672418}, + }, nil, DefaultIndex) + + bounds := []*Poly{ + NewPoly([]Point{{-114.2399485999843, 42.681876499955926}, {-114.23997050001262, 42.67824680037998}, {-114.23627140565759, 42.678233981038666}, {-114.23504369971833, 42.67822970021946}, {-114.23503067863831, 42.67578671168899}, {-114.23543512533203, 42.67583999946153}, {-114.23594982495554, 42.676124423371455}, {-114.23672596666609, 42.67625624813994}, {-114.23685805764039, 42.676776002285635}, {-114.23663532665391, 42.677055424446124}, {-114.23671287282251, 42.67747314961749}, {-114.23681394047848, 42.677805126285634}, {-114.23706193209075, 42.67813127497741}, {-114.23784992168157, 42.678186635769315}, {-114.23838959806322, 42.67813118119811}, {-114.23875417123647, 42.67791953618522}, {-114.23912485834349, 42.67784524816636}, {-114.23952718950476, 42.67769791345572}, {-114.23999116575574, 42.677386626685646}, {-114.24048944148089, 42.67724527831817}, {-114.241314164082, 42.67699656963459}, {-114.24195248087176, 42.676729402854455}, {-114.24240805338327, 42.67645818447628}, {-114.24311814376759, 42.67608562786248}, {-114.24355505557568, 42.67596026910592}, {-114.24477528843009, 42.67578037408335}, {-114.24521195320149, 42.675790315723084}, {-114.24576310286596, 42.67606342521258}, {-114.2467516503271, 42.676300425443266}, {-114.2474970507903, 42.676777404382534}, {-114.24910805618318, 42.67760249691951}, {-114.24971203666782, 42.67767705336001}, {-114.25014930420876, 42.67756278302376}, {-114.25107740672941, 42.677654319638556}, {-114.25187256038463, 42.677646259180754}, {-114.25216885710465, 42.677753657678224}, {-114.25223329954821, 42.678306700105715}, {-114.25279040055142, 42.678712300300425}, {-114.25345425195313, 42.678693062516}, {-114.25372209992645, 42.67868529937817}, {-114.25422830058905, 42.67839869989619}, {-114.25469089972272, 42.67851959990614}, {-114.25525630026085, 42.67839799985663}, {-114.2563918004244, 42.678740799683624}, {-114.25714179936351, 42.67950249986307}, {-114.25712530000669, 42.6819502000424}, {-114.25711160069862, 42.68397369991884}, {-114.25465699999884, 42.68442620019711}, {-114.25229229999773, 42.684861999674204}, {-114.24974329893801, 42.68533019969429}, {-114.2486267002264, 42.685511500004914}, {-114.24862660051338, 42.68554050010786}, {-114.24483440027073, 42.68552580030558}, {-114.24131170000318, 42.68551210025312}, {-114.24115469964434, 42.685511400293755}, {-114.2399323000535, 42.68459820015348}, {-114.2399485999843, 42.681876499955926}}, nil, DefaultIndex), + NewPoly([]Point{{-114.24970880003785, 42.69640819992281}, {-114.24970389972798, 42.692787600018605}, {-114.25460269953487, 42.69281529997132}, {-114.25950169966605, 42.69284299991165}, {-114.25951680034598, 42.69646220021525}, {-114.2546128001919, 42.69643530009867}, {-114.24970880003785, 42.69640819992281}}, nil, DefaultIndex), + NewPoly([]Point{{-122.50865178071805, 47.61633533468668}, {-122.50746427348945, 47.6148250023897}, {-122.50762759349631, 47.61480254865959}, {-122.50778991457659, 47.61477700108817}, {-122.50795111006781, 47.61474837844346}, {-122.50811105151087, 47.61471670312629}, {-122.51010371333871, 47.61430140449804}, {-122.51016786562644, 47.6142883120613}, {-122.51041138093365, 47.61424362213619}, {-122.51040948099681, 47.61458176413451}, {-122.51040824760994, 47.614801121949036}, {-122.5104030903819, 47.61571858749849}, {-122.51039374700463, 47.61572085227092}, {-122.51038431649077, 47.61572294324917}, {-122.51037480423024, 47.61572485861667}, {-122.51036521830783, 47.61572659716236}, {-122.51026191115182, 47.615750784801655}, {-122.51015949332798, 47.615776646785115}, {-122.51007599582063, 47.61579915156894}, {-122.50999317294806, 47.6158227693494}, {-122.5099449262308, 47.615836900530496}, {-122.50989743499666, 47.61585215743006}, {-122.50985075673788, 47.6158685200638}, {-122.50980494714995, 47.61588596844757}, {-122.50976006103016, 47.61590448380809}, {-122.50971615497232, 47.61592404192218}, {-122.50967327838381, 47.61594461977769}, {-122.50963148606186, 47.61596619133478}, {-122.50959082651548, 47.61598873297563}, {-122.50955135094863, 47.6160122150271}, {-122.50951310517539, 47.61603661144916}, {-122.50947613500989, 47.61606189135753}, {-122.50944048716445, 47.61608802386794}, {-122.50940620296163, 47.616114978095965}, {-122.50939632239182, 47.61612695102005}, {-122.50938583635751, 47.616138685960564}, {-122.50937475743511, 47.61615016959558}, {-122.50936310089598, 47.6161613886031}, {-122.50935087841822, 47.61617232905581}, {-122.50933810527319, 47.61618297763166}, {-122.50932479673226, 47.61619332161441}, {-122.50931096896512, 47.616203350104364}, {-122.50929663724307, 47.61621305038511}, {-122.50928181953245, 47.61622241034595}, {-122.50926653379958, 47.6162314184816}, {-122.50925079801074, 47.61624006570906}, {-122.50923462923394, 47.6162483411286}, {-122.50921804992707, 47.61625623444614}, {-122.50920107715808, 47.61626373657857}, {-122.50918373158828, 47.61627083844286}, {-122.50916603387886, 47.61627753156154}, {-122.50914800648772, 47.616283808668186}, {-122.50912966917784, 47.61628966128524}, {-122.50911104350872, 47.61629508275189}, {-122.50909215373491, 47.616300066407184}, {-122.50907301961938, 47.61630460740689}, {-122.50905366631491, 47.61630869909014}, {-122.50903411627938, 47.61631233782372}, {-122.50901439107238, 47.61631551815777}, {-122.50899451584671, 47.61631823645919}, {-122.5089745139586, 47.61632049030575}, {-122.50895440966254, 47.61632227606429}, {-122.50893422541641, 47.61632359312931}, {-122.50891398637307, 47.61632443726198}, {-122.50889371678699, 47.616324809673436}, {-122.50887343911609, 47.61632470915263}, {-122.50885317941147, 47.61632413630507}, {-122.50883296102938, 47.616323091130724}, {-122.508812807326, 47.6163215748407}, {-122.50879274345411, 47.61631958985712}, {-122.50877279276996, 47.61631713860209}, {-122.50875901261351, 47.61631791309698}, {-122.50874528006777, 47.616319003081905}, {-122.50873160950579, 47.616320408556895}, {-122.5087180179955, 47.61632212588859}, {-122.50870451991005, 47.616324155682584}, {-122.50869113231737, 47.61632649430547}, {-122.50867786959051, 47.616329139335114}, {-122.50866474700086, 47.61633208713811}, {-122.50865178071805, 47.61633533468668}}, nil, DefaultIndex), + } + intersects := false + for _, bounds := range bounds { + if seattle.IntersectsPoly(bounds) { + intersects = true + break + } + } + expect(t, !intersects) + intersects = false + for _, bounds := range bounds { + if bounds.IntersectsPoly(seattle) { + intersects = true + break + } + } + expect(t, !intersects) +} + +func TestIssue241(t *testing.T) { + g := NewPoly([]Point{ + {2.2571754455566406, 48.84472294197522}, + {2.252626419067383, 48.8473212003792}, + {2.2455883026123047, 48.847660093710566}, + {2.2386789321899414, 48.846022088028505}, + {2.2353315353393555, 48.842152792889486}, + {2.235288619995117, 48.83966724854628}, + {2.236189842224121, 48.836870863722986}, + {2.240910530090332, 48.83461104476972}, + {2.2461462020874023, 48.8343568087582}, + {2.2527122497558594, 48.83540199299912}, + {2.2573471069335938, 48.837746516162916}, + {2.259106636047363, 48.84003443899817}, + {2.259106636047363, 48.84302835299516}, + {2.2571754455566406, 48.8447229419752}, + }, [][]Point{[]Point{ + {2.2546112537384033, 48.84285183001222}, + {2.254890203475952, 48.842774159702614}, + {2.255094051361084, 48.84197626682054}, + {2.2551369667053223, 48.84108656596295}, + {2.254589796066284, 48.8398649668164}, + {2.2528302669525146, 48.83974492367404}, + {2.2515535354614253, 48.839942641637556}, + {2.2512423992156982, 48.84047223948045}, + {2.2509634494781494, 48.841891534086436}, + {2.2513389587402344, 48.84261881872204}, + {2.2546112537384033, 48.8428518300122}, + }}, DefaultIndex) + p1 := P(2.253119945526123, 48.841404318083505) + expect(t, !g.IntersectsPoint(p1)) + expect(t, !g.ContainsPoint(p1)) + p2 := P(2.2564244270324703, 48.83788774899389) + expect(t, g.IntersectsPoint(p2)) + expect(t, g.ContainsPoint(p2)) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/line.go b/vendor/github.com/tidwall/geojson/geometry/line.go new file mode 100644 index 00000000..2ea1a36b --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/line.go @@ -0,0 +1,162 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +// Line is a open series of points +type Line struct { + baseSeries +} + +// NewLine creates a new Line +func NewLine(points []Point, index int) *Line { + line := new(Line) + line.baseSeries = makeSeries(points, true, false, index) + return line +} + +// Move ... +func (line *Line) Move(deltaX, deltaY float64) *Line { + if line == nil { + return nil + } + nline := new(Line) + nline.baseSeries = *line.baseSeries.Move(deltaX, deltaY).(*baseSeries) + return nline +} + +// ContainsPoint ... +func (line *Line) ContainsPoint(point Point) bool { + if line == nil { + return false + } + contains := false + line.Search(Rect{point, point}, func(seg Segment, index int) bool { + if seg.Raycast(point).On { + contains = true + return false + } + return true + }) + return contains +} + +// IntersectsPoint ... +func (line *Line) IntersectsPoint(point Point) bool { + if line == nil { + return false + } + return line.ContainsPoint(point) +} + +// ContainsRect ... +func (line *Line) ContainsRect(rect Rect) bool { + if line == nil { + return false + } + // Convert rect into a poly + return line.ContainsPoly(&Poly{Exterior: rect}) +} + +// IntersectsRect ... +func (line *Line) IntersectsRect(rect Rect) bool { + if line == nil { + return false + } + return rect.IntersectsLine(line) +} + +// ContainsLine ... +func (line *Line) ContainsLine(other *Line) bool { + if line == nil || other == nil || line.Empty() || other.Empty() { + return false + } + // locate the first "other" segment that contains the first "line" segment. + lineNumSegments := line.NumSegments() + segIdx := -1 + for j := 0; j < lineNumSegments; j++ { + if line.SegmentAt(j).ContainsSegment(other.SegmentAt(0)) { + segIdx = j + break + } + } + if segIdx == -1 { + return false + } + otherNumSegments := other.NumSegments() + for i := 1; i < otherNumSegments; i++ { + lineSeg := line.SegmentAt(segIdx) + otherSeg := other.SegmentAt(i) + if lineSeg.ContainsSegment(otherSeg) { + continue + } + if otherSeg.A == lineSeg.A { + // reverse it + if segIdx == 0 { + return false + } + segIdx-- + i-- + } else if otherSeg.A == lineSeg.B { + // forward it + if segIdx == lineNumSegments-1 { + return false + } + segIdx++ + i-- + } + } + return true +} + +// IntersectsLine ... +func (line *Line) IntersectsLine(other *Line) bool { + if line == nil || other == nil || line.Empty() || other.Empty() { + return false + } + if !line.Rect().IntersectsRect(other.Rect()) { + return false + } + if line.NumPoints() > other.NumPoints() { + line, other = other, line + } + lineNumSegments := line.NumSegments() + for i := 0; i < lineNumSegments; i++ { + segA := line.SegmentAt(i) + var intersects bool + other.Search(segA.Rect(), func(segB Segment, _ int) bool { + if segA.IntersectsSegment(segB) { + intersects = true + return false + } + return true + }) + if intersects { + return true + } + } + return false +} + +// ContainsPoly ... +func (line *Line) ContainsPoly(poly *Poly) bool { + if line == nil || poly == nil || line.Empty() || poly.Empty() { + return false + } + rect := poly.Rect() + if rect.Min.X != rect.Max.X && rect.Min.Y != rect.Max.Y { + return false + } + // polygon can fit in a straight (vertial or horizontal) line + points := [2]Point{rect.Min, rect.Max} + var other Line + other.baseSeries.points = points[:] + other.baseSeries.rect = rect + return line.ContainsLine(&other) +} + +// IntersectsPoly ... +func (line *Line) IntersectsPoly(poly *Poly) bool { + return poly.IntersectsLine(line) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/line_test.go b/vendor/github.com/tidwall/geojson/geometry/line_test.go new file mode 100644 index 00000000..a20d365f --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/line_test.go @@ -0,0 +1,176 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import "testing" + +func TestLineNewLine(t *testing.T) { + line := NewLine(u1, DefaultIndex) + expect(t, !line.Empty()) +} + +func TestLineGeometryDefaults(t *testing.T) { + g := Geometry(&Line{}) + expect(t, g.Empty()) + expect(t, g.Rect() == R(0, 0, 0, 0)) + expect(t, !g.ContainsLine(nil)) + expect(t, !g.ContainsLine(&Line{})) + expect(t, !g.ContainsPoint(Point{})) + expect(t, !g.ContainsPoly(nil)) + expect(t, !g.ContainsPoly(&Poly{})) + expect(t, !g.ContainsRect(Rect{})) + expect(t, !g.IntersectsLine(nil)) + expect(t, !g.IntersectsLine(&Line{})) + expect(t, !g.IntersectsPoint(Point{})) + expect(t, !g.IntersectsPoly(nil)) + expect(t, !g.IntersectsPoly(&Poly{})) + expect(t, !g.IntersectsRect(Rect{})) +} + +func TestLineMove(t *testing.T) { + ln1 := L(P(0, 1), P(2, 3), P(4, 5)) + ln2 := ln1.Move(7, 8) + expect(t, ln1.NumPoints() == ln2.NumPoints()) + for i := 0; i < ln2.NumPoints(); i++ { + expect(t, ln2.PointAt(i) == ln1.PointAt(i).Move(7, 8)) + } + + var line *Line + expect(t, line.Move(0, 0) == nil) + expect(t, (&Line{}).Move(0, 0) != nil) +} + +func TestLineContainsPoint(t *testing.T) { + line := NewLine(u1, DefaultIndex) + expect(t, line.ContainsPoint(P(0, 0))) + expect(t, line.ContainsPoint(P(10, 10))) + expect(t, line.ContainsPoint(P(0, 5))) + expect(t, !line.ContainsPoint(P(5, 5))) + line = NewLine(v1, DefaultIndex) + expect(t, line.ContainsPoint(P(0, 10))) + expect(t, !line.ContainsPoint(P(0, 0))) + expect(t, line.ContainsPoint(P(5, 0))) + expect(t, line.ContainsPoint(P(2.5, 5))) + line = nil + expect(t, !line.ContainsPoint(Point{})) + expect(t, !(&Line{}).ContainsPoint(Point{})) +} + +func TestLineIntersectsPoint(t *testing.T) { + line := NewLine(v1, DefaultIndex) + expect(t, line.IntersectsPoint(P(0, 10))) + expect(t, !line.IntersectsPoint(P(0, 0))) + expect(t, line.IntersectsPoint(P(5, 0))) + expect(t, line.IntersectsPoint(P(2.5, 5))) + line = nil + expect(t, !line.IntersectsPoint(Point{})) + expect(t, !(&Line{}).IntersectsPoint(Point{})) +} + +func TestLineContainsRect(t *testing.T) { + line := NewLine(v1, DefaultIndex) + expect(t, !line.ContainsRect(R(0, 0, 10, 10))) + expect(t, line.ContainsRect(R(0, 10, 0, 10))) + line = NewLine(u1, DefaultIndex) + expect(t, line.ContainsRect(R(0, 0, 0, 10))) + line = nil + expect(t, !line.ContainsRect(Rect{})) + expect(t, !(&Line{}).ContainsRect(Rect{})) +} + +func TestLineIntersectsRect(t *testing.T) { + line := NewLine(v1, DefaultIndex) + expect(t, line.IntersectsRect(R(0, 0, 10, 10))) + expect(t, line.IntersectsRect(R(0, 0, 2.5, 5))) + expect(t, !line.IntersectsRect(R(0, 0, 2.4, 5))) + line = nil + expect(t, !line.IntersectsRect(Rect{})) + expect(t, !(&Line{}).IntersectsRect(Rect{})) +} + +func TestLineContainsLine(t *testing.T) { + expect(t, !P(15, 15).ContainsLine(L(P(15, 0), P(15, 15), P(30, 15)))) + expect(t, !P(15, 15).ContainsLine(L())) + expect(t, !P(15, 15).ContainsLine(L(P(15, 15)))) + expect(t, P(15, 15).ContainsLine(L(P(15, 15), P(15, 15)))) + expect(t, R(0, 0, 30, 30).ContainsLine(L(P(15, 0), P(15, 15), P(30, 15)))) + expect(t, !R(0, 0, 30, 30).ContainsLine(L())) + expect(t, !R(0, 0, 20, 20).ContainsLine(L(P(15, 0), P(15, 15), P(30, 15)))) + ln1 := L(P(5, 0), P(5, 5), P(10, 5), P(10, 10), P(15, 10), P(15, 15)) + lns := []*Line{ + L(P(7, 5), P(10, 5), P(10, 10), P(12, 10)), + L(P(7, 5), P(8, 5), P(10, 5), P(10, 10), P(12, 10)), + L(P(7, 5), P(8, 5), P(6, 5), P(10, 5), P(10, 8), P(10, 5), P(5, 5), + P(10, 5), P(10, 10), P(12, 10)), + } + for _, ln2 := range lns { + expect(t, ln1.ContainsLine(ln2)) + } + expect(t, !ln1.ContainsLine(L(P(5, -1), P(5, 5), P(10, 5)))) + expect(t, !ln1.ContainsLine(L(P(5, 0), P(5, 5), P(5, 0), P(10, 0)))) + expect(t, !ln1.ContainsLine(L(P(5, 0), P(5, 5), P(10, 5), P(10, 10), + P(15, 10), P(15, 15), P(20, 20)))) + expect(t, !ln1.ContainsLine(L())) + expect(t, !L().ContainsLine(L(P(5, 0)))) + expect(t, !L(P(5, 0), P(10, 0)).ContainsLine(L(P(5, 0)))) + expect(t, R(0, 0, 30, 30).ContainsLine(L(P(15, 0), P(15, 15), P(30, 15)))) + expect(t, !R(0, 0, 30, 30).ContainsLine(L())) +} + +func TestLineClockwise(t *testing.T) { + expect(t, L(P(0, 0), P(0, 10), P(10, 10), P(10, 0), P(0, 0)).Clockwise()) + expect(t, !L(P(0, 0), P(10, 0), P(10, 10), P(0, 10), P(0, 0)).Clockwise()) + expect(t, L(P(0, 0), P(0, 10), P(10, 10)).Clockwise()) + expect(t, !L(P(0, 0), P(10, 0), P(10, 10)).Clockwise()) +} + +func TestLineIntersectsLine(t *testing.T) { + lns := [][]Point{u1, u2, u3, u4, v1, v2, v3, v4} + for i := 0; i < len(lns); i++ { + for j := 0; j < len(lns); j++ { + expect(t, NewLine(lns[i], DefaultIndex).IntersectsLine( + NewLine(lns[j], DefaultIndex), + )) + } + } + line := NewLine(u1, DefaultIndex) + expect(t, !line.IntersectsLine(NewLine(nil, DefaultIndex))) + expect(t, !NewLine(nil, DefaultIndex).IntersectsLine(NewLine(nil, DefaultIndex))) + expect(t, !NewLine(nil, DefaultIndex).IntersectsLine(line)) + expect(t, line.IntersectsLine(line.Move(5, 0))) + expect(t, line.IntersectsLine(line.Move(10, 0))) + expect(t, !line.IntersectsLine(line.Move(11, 0))) + expect(t, !L(v1...).IntersectsLine(L(v1...).Move(0, 1))) + expect(t, !L(v1...).IntersectsLine(L(v1...).Move(0, -1))) +} + +func TestLineContainsPoly(t *testing.T) { + line := NewLine(u1, DefaultIndex) + poly := NewPoly(octagon, nil, DefaultIndex) + expect(t, !line.ContainsPoly(poly)) + expect(t, line.ContainsPoly(NewPoly( + []Point{P(0, 10), P(0, 0), P(0, 10)}, + nil, DefaultIndex, + ))) + expect(t, line.ContainsPoly(NewPoly( + []Point{P(0, 0), P(10, 0), P(0, 0)}, + nil, DefaultIndex, + ))) + expect(t, !L().ContainsPoly(NewPoly( + []Point{P(0, 0), P(10, 0), P(0, 0)}, + nil, DefaultIndex, + ))) + expect(t, !line.ContainsPoly(NewPoly(nil, nil, DefaultIndex))) +} + +func TestLineIntersectsPoly(t *testing.T) { + line := NewLine(u1, DefaultIndex) + poly := NewPoly(octagon, nil, DefaultIndex) + expect(t, line.IntersectsPoly(poly)) + expect(t, line.IntersectsPoly(poly.Move(5, 0))) + expect(t, line.IntersectsPoly(poly.Move(10, 0))) + expect(t, !line.IntersectsPoly(poly.Move(11, 0))) + expect(t, !line.IntersectsPoly(poly.Move(15, 0))) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/pip_bench_test.go b/vendor/github.com/tidwall/geojson/geometry/pip_bench_test.go new file mode 100644 index 00000000..88b18843 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/pip_bench_test.go @@ -0,0 +1,74 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "fmt" + "os" + "testing" + + "github.com/tidwall/lotsa" +) + +func testBig( + t *testing.T, label string, points []Point, pointIn, pointOut Point, +) { + N, T := 100000, 4 + + simple := newRing(points, DefaultIndex) + simple.(*baseSeries).tree = nil + tree := newRing(points, DefaultIndex) + tree.(*baseSeries).buildTree() + pointOn := points[len(points)/2] + + // ioutil.WriteFile(label+".svg", []byte(tools.SVG(tree.(*baseSeries).tree)), 0666) + + expect(t, ringContainsPoint(simple, pointIn, true).hit) + expect(t, ringContainsPoint(tree, pointIn, true).hit) + + expect(t, ringContainsPoint(simple, pointOn, true).hit) + expect(t, ringContainsPoint(tree, pointOn, true).hit) + + expect(t, !ringContainsPoint(simple, pointOn, false).hit) + expect(t, !ringContainsPoint(tree, pointOn, false).hit) + + expect(t, !ringContainsPoint(simple, pointOut, true).hit) + expect(t, !ringContainsPoint(tree, pointOut, true).hit) + if os.Getenv("PIPBENCH") == "1" { + lotsa.Output = os.Stderr + fmt.Printf(label + "/simp/in ") + lotsa.Ops(N, T, func(_, _ int) { + ringContainsPoint(simple, pointIn, true) + }) + fmt.Printf(label + "/tree/in ") + lotsa.Ops(N, T, func(_, _ int) { + ringContainsPoint(tree, pointIn, true) + }) + fmt.Printf(label + "/simp/on ") + lotsa.Ops(N, T, func(_, _ int) { + ringContainsPoint(simple, pointOn, true) + }) + fmt.Printf(label + "/tree/on ") + lotsa.Ops(N, T, func(_, _ int) { + ringContainsPoint(tree, pointOn, true) + }) + fmt.Printf(label + "/simp/out ") + lotsa.Ops(N, T, func(_, _ int) { + ringContainsPoint(simple, pointOut, true) + }) + fmt.Printf(label + "/tree/out ") + lotsa.Ops(N, T, func(_, _ int) { + ringContainsPoint(tree, pointOut, true) + }) + } +} + +func TestBigArizona(t *testing.T) { + testBig(t, "az", az, P(-112, 33), P(-114.477539062, 33.99802726)) +} + +func TestBigTexas(t *testing.T) { + testBig(t, "tx", tx, P(-98.52539, 29.363027), P(-101.953125, 29.324720161)) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/point.go b/vendor/github.com/tidwall/geojson/geometry/point.go new file mode 100644 index 00000000..a0a35d79 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/point.go @@ -0,0 +1,77 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +// Point ... +type Point struct { + X, Y float64 +} + +// Move ... +func (point Point) Move(deltaX, deltaY float64) Point { + return Point{X: point.X + deltaX, Y: point.Y + deltaY} +} + +// Empty ... +func (point Point) Empty() bool { + return false +} + +// Rect ... +func (point Point) Rect() Rect { + return Rect{point, point} +} + +// ContainsPoint ... +func (point Point) ContainsPoint(other Point) bool { + return point == other +} + +// IntersectsPoint ... +func (point Point) IntersectsPoint(other Point) bool { + return point == other +} + +// ContainsRect ... +func (point Point) ContainsRect(rect Rect) bool { + return point.Rect() == rect +} + +// IntersectsRect ... +func (point Point) IntersectsRect(rect Rect) bool { + return rect.ContainsPoint(point) +} + +// ContainsLine ... +func (point Point) ContainsLine(line *Line) bool { + if line == nil { + return false + } + return !line.Empty() && line.Rect() == point.Rect() +} + +// IntersectsLine ... +func (point Point) IntersectsLine(line *Line) bool { + if line == nil { + return false + } + return line.IntersectsPoint(point) +} + +// ContainsPoly ... +func (point Point) ContainsPoly(poly *Poly) bool { + if poly == nil { + return false + } + return !poly.Empty() && poly.Rect() == point.Rect() +} + +// IntersectsPoly ... +func (point Point) IntersectsPoly(poly *Poly) bool { + if poly == nil { + return false + } + return poly.IntersectsPoint(point) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/point_test.go b/vendor/github.com/tidwall/geojson/geometry/point_test.go new file mode 100644 index 00000000..29f19eb6 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/point_test.go @@ -0,0 +1,94 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import "testing" + +func TestPointEmpty(t *testing.T) { + expect(t, !P(0, 0).Empty()) +} + +func TestPointGeometryDefaults(t *testing.T) { + g := Geometry(Point{}) + expect(t, !g.Empty()) + expect(t, g.Rect() == R(0, 0, 0, 0)) + expect(t, !g.ContainsLine(nil)) + expect(t, !g.ContainsLine(&Line{})) + expect(t, g.ContainsPoint(Point{})) + expect(t, !g.ContainsPoly(nil)) + expect(t, !g.ContainsPoly(&Poly{})) + expect(t, g.ContainsRect(Rect{})) + expect(t, !g.IntersectsLine(nil)) + expect(t, !g.IntersectsLine(&Line{})) + expect(t, g.IntersectsPoint(Point{})) + expect(t, !g.IntersectsPoly(nil)) + expect(t, !g.IntersectsPoly(&Poly{})) + expect(t, g.IntersectsRect(Rect{})) +} + +func TestPointRect(t *testing.T) { + expect(t, P(5, 5).Rect() == R(5, 5, 5, 5)) +} + +func TestPointMove(t *testing.T) { + expect(t, P(5, 6).Move(10, 10) == P(15, 16)) +} + +func TestPointContainsPoint(t *testing.T) { + expect(t, P(5, 5).ContainsPoint(P(5, 5))) + expect(t, !P(5, 5).ContainsPoint(P(6, 5))) +} + +func TestPointIntersectsPoint(t *testing.T) { + expect(t, P(5, 5).IntersectsPoint(P(5, 5))) + expect(t, !P(5, 5).IntersectsPoint(P(6, 5))) +} + +func TestPointContainsRect(t *testing.T) { + expect(t, P(5, 5).ContainsRect(R(5, 5, 5, 5))) + expect(t, !P(5, 5).ContainsRect(R(0, 0, 10, 10))) +} + +func TestPointIntersectsRect(t *testing.T) { + expect(t, P(5, 5).IntersectsRect(R(5, 5, 5, 5))) + expect(t, P(5, 5).IntersectsRect(R(0, 0, 10, 10))) + expect(t, P(0, 0).IntersectsRect(R(0, 0, 10, 10))) + expect(t, !P(-1, 0).IntersectsRect(R(0, 0, 10, 10))) +} + +func TestPointContainsLine(t *testing.T) { + expect(t, !P(5, 5).ContainsLine(L())) + expect(t, !P(5, 5).ContainsLine(L(P(5, 5)))) + expect(t, P(5, 5).ContainsLine(L(P(5, 5), P(5, 5)))) + expect(t, !P(5, 5).ContainsLine(L(P(5, 5), P(10, 10)))) +} + +func TestPointIntersectsLine(t *testing.T) { + expect(t, !P(5, 5).IntersectsLine(L())) + expect(t, !P(5, 5).IntersectsLine(L(P(5, 5)))) + expect(t, P(5, 5).IntersectsLine(L(P(5, 5), P(5, 5)))) + expect(t, P(5, 5).IntersectsLine(L(P(0, 0), P(10, 10)))) + expect(t, !P(6, 5).IntersectsLine(L(P(0, 0), P(10, 10)))) +} + +func TestPointContainsPoly(t *testing.T) { + expect(t, !P(5, 5).ContainsPoly(NewPoly(nil, nil, DefaultIndex))) + expect(t, !P(5, 5).ContainsPoly(NewPoly([]Point{P(0, 0), P(10, 0)}, nil, DefaultIndex))) + expect(t, !P(5, 5).ContainsPoly(&Poly{Exterior: R(0, 0, 10, 10)})) + expect(t, P(5, 5).ContainsPoly(&Poly{Exterior: R(5, 5, 5, 5)})) +} + +func TestPointIntersectsPoly(t *testing.T) { + octa := NewPoly(octagon, nil, DefaultIndex) + concave1 := NewPoly(concave1, nil, DefaultIndex) + expect(t, !P(5, 5).IntersectsPoly(NewPoly(nil, nil, DefaultIndex))) + expect(t, !P(5, 5).IntersectsPoly(NewPoly([]Point{P(0, 0), P(10, 0)}, nil, DefaultIndex))) + expect(t, P(5, 5).IntersectsPoly(octa)) + expect(t, P(0, 5).IntersectsPoly(octa)) + expect(t, !P(1, 1).IntersectsPoly(octa)) + expect(t, !P(4, 4).IntersectsPoly(concave1)) + expect(t, P(5, 5).IntersectsPoly(concave1)) + expect(t, P(6, 6).IntersectsPoly(concave1)) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/poly.go b/vendor/github.com/tidwall/geojson/geometry/poly.go new file mode 100644 index 00000000..01c71128 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/poly.go @@ -0,0 +1,195 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +// Poly ... +type Poly struct { + Exterior Ring + Holes []Ring +} + +// NewPoly ... +func NewPoly(exterior []Point, holes [][]Point, index int) *Poly { + poly := new(Poly) + poly.Exterior = newRing(exterior, index) + if len(holes) > 0 { + poly.Holes = make([]Ring, len(holes)) + for i := range holes { + poly.Holes[i] = newRing(holes[i], index) + } + } + return poly +} + +// Clockwise ... +func (poly *Poly) Clockwise() bool { + if poly == nil || poly.Exterior == nil { + return false + } + return poly.Exterior.Clockwise() +} + +// Empty ... +func (poly *Poly) Empty() bool { + if poly == nil || poly.Exterior == nil { + return true + } + return poly.Exterior.Empty() +} + +// Rect ... +func (poly *Poly) Rect() Rect { + if poly == nil || poly.Exterior == nil { + return Rect{} + } + return poly.Exterior.Rect() +} + +// Move the polygon by delta. Returns a new polygon +func (poly *Poly) Move(deltaX, deltaY float64) *Poly { + if poly == nil { + return nil + } + if poly.Exterior == nil { + return new(Poly) + } + npoly := new(Poly) + if series, ok := poly.Exterior.(*baseSeries); ok { + npoly.Exterior = Ring(series.Move(deltaX, deltaY)) + } else { + nseries := makeSeries( + seriesCopyPoints(poly.Exterior), false, true, DefaultIndex) + npoly.Exterior = Ring(nseries.Move(deltaX, deltaY)) + } + if len(poly.Holes) > 0 { + npoly.Holes = make([]Ring, len(poly.Holes)) + for i, hole := range poly.Holes { + if series, ok := hole.(*baseSeries); ok { + npoly.Holes[i] = Ring(series.Move(deltaX, deltaY)) + } else { + nseries := makeSeries( + seriesCopyPoints(hole), false, true, DefaultIndex) + npoly.Holes[i] = Ring(nseries.Move(deltaX, deltaY)) + } + } + } + return npoly +} + +// ContainsPoint ... +func (poly *Poly) ContainsPoint(point Point) bool { + if poly == nil || poly.Exterior == nil { + return false + } + if !ringContainsPoint(poly.Exterior, point, true).hit { + return false + } + contains := true + for _, hole := range poly.Holes { + if ringContainsPoint(hole, point, false).hit { + contains = false + break + } + } + return contains +} + +// IntersectsPoint ... +func (poly *Poly) IntersectsPoint(point Point) bool { + if poly == nil { + return false + } + return poly.ContainsPoint(point) +} + +// ContainsRect ... +func (poly *Poly) ContainsRect(rect Rect) bool { + if poly == nil { + return false + } + // convert rect into a polygon + return poly.ContainsPoly(&Poly{Exterior: rect}) +} + +// IntersectsRect ... +func (poly *Poly) IntersectsRect(rect Rect) bool { + if poly == nil { + return false + } + // convert rect into a polygon + return poly.IntersectsPoly(&Poly{Exterior: rect}) +} + +// ContainsLine ... +func (poly *Poly) ContainsLine(line *Line) bool { + if poly == nil || poly.Exterior == nil || line == nil { + return false + } + if !ringContainsLine(poly.Exterior, line, true) { + return false + } + for _, polyHole := range poly.Holes { + if ringIntersectsLine(polyHole, line, false) { + return false + } + } + return true +} + +// IntersectsLine ... +func (poly *Poly) IntersectsLine(line *Line) bool { + if poly == nil || poly.Exterior == nil || line == nil { + return false + } + return ringIntersectsLine(poly.Exterior, line, true) +} + +// ContainsPoly ... +func (poly *Poly) ContainsPoly(other *Poly) bool { + if poly == nil || poly.Exterior == nil || + other == nil || other.Exterior == nil { + return false + } + // 1) other exterior must be fully contained inside of the poly exterior. + if !ringContainsRing(poly.Exterior, other.Exterior, true) { + return false + } + // 2) ring cannot intersect poly holes + contains := true + for _, polyHole := range poly.Holes { + if ringIntersectsRing(polyHole, other.Exterior, false) { + contains = false + // 3) unless the poly hole is contain inside of a other hole + for _, otherHole := range other.Holes { + if ringContainsRing(otherHole, polyHole, true) { + contains = true + // println(4) + break + } + } + if !contains { + break + } + } + } + return contains +} + +// IntersectsPoly ... +func (poly *Poly) IntersectsPoly(other *Poly) bool { + if poly == nil || poly.Exterior == nil || + other == nil || other.Exterior == nil { + return false + } + if !ringIntersectsRing(other.Exterior, poly.Exterior, true) { + return false + } + for _, hole := range poly.Holes { + if ringContainsRing(hole, other.Exterior, false) { + return false + } + } + return true +} diff --git a/vendor/github.com/tidwall/geojson/geometry/poly_test.go b/vendor/github.com/tidwall/geojson/geometry/poly_test.go new file mode 100644 index 00000000..096b621b --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/poly_test.go @@ -0,0 +1,190 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "testing" +) + +func newPolyIndexed(exterior []Point, holes [][]Point) *Poly { + poly := NewPoly(exterior, holes, DefaultIndex) + poly.Exterior.(*baseSeries).buildTree() + for _, hole := range poly.Holes { + hole.(*baseSeries).buildTree() + } + return poly +} + +func newPolySimple(exterior []Point, holes [][]Point) *Poly { + poly := NewPoly(exterior, holes, DefaultIndex) + poly.Exterior.(*baseSeries).tree = nil + for _, hole := range poly.Holes { + hole.(*baseSeries).tree = nil + } + return poly +} + +func dualPolyTest( + t *testing.T, exterior []Point, holes [][]Point, + do func(t *testing.T, poly *Poly), +) { + t.Run("noindex", func(t *testing.T) { + do(t, newPolySimple(exterior, holes)) + }) + t.Run("index", func(t *testing.T) { + do(t, newPolyIndexed(exterior, holes)) + }) +} + +func TestPolyNewPoly(t *testing.T) { + dualPolyTest(t, octagon, nil, func(t *testing.T, poly *Poly) { + expect(t, !poly.Empty()) + }) + small := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, octagon, [][]Point{small}, func(t *testing.T, poly *Poly) { + expect(t, !poly.Empty()) + }) +} + +func TestPolyGeometryDefaults(t *testing.T) { + g := Geometry(&Poly{}) + expect(t, g.Empty()) + expect(t, g.Rect() == R(0, 0, 0, 0)) + expect(t, !g.ContainsLine(nil)) + expect(t, !g.ContainsLine(&Line{})) + expect(t, !g.ContainsPoint(Point{})) + expect(t, !g.ContainsPoly(nil)) + expect(t, !g.ContainsPoly(&Poly{})) + expect(t, !g.ContainsRect(Rect{})) + expect(t, !g.IntersectsLine(nil)) + expect(t, !g.IntersectsLine(&Line{})) + expect(t, !g.IntersectsPoint(Point{})) + expect(t, !g.IntersectsPoly(nil)) + expect(t, !g.IntersectsPoly(&Poly{})) + expect(t, !g.IntersectsRect(Rect{})) +} + +func TestPolyRect(t *testing.T) { + dualPolyTest(t, octagon, nil, func(t *testing.T, poly *Poly) { + expect(t, poly.Rect() == R(0, 0, 10, 10)) + }) + small := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, octagon, [][]Point{small}, func(t *testing.T, poly *Poly) { + expect(t, poly.Rect() == R(0, 0, 10, 10)) + }) +} + +func TestPolyMove(t *testing.T) { + small := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, octagon, [][]Point{small}, func(t *testing.T, poly *Poly) { + poly2 := poly.Move(5, 8) + expect(t, poly2.Rect() == R(5, 8, 15, 18)) + }) + poly := &Poly{Exterior: R(0, 0, 10, 10), Holes: []Ring{R(2, 2, 8, 8)}} + poly2 := poly.Move(5, 8) + expect(t, poly2.Rect() == R(5, 8, 15, 18)) + expect(t, len(poly2.Holes) == 1) + expect(t, poly2.Holes[0].Rect() == R(7, 10, 13, 16)) + + poly = nil + expect(t, poly.Move(0, 0) == nil) + expect(t, (&Poly{}).Move(0, 0) != nil) +} + +func TestPolyContainsPoint(t *testing.T) { + small := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, octagon, [][]Point{small}, func(t *testing.T, poly *Poly) { + expect(t, !poly.ContainsPoint(P(0, 0))) + expect(t, poly.ContainsPoint(P(0, 5))) + expect(t, poly.ContainsPoint(P(3, 5))) + expect(t, !poly.ContainsPoint(P(5, 5))) + }) +} + +func TestPolyIntersectsPoint(t *testing.T) { + small := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, octagon, [][]Point{small}, func(t *testing.T, poly *Poly) { + expect(t, !poly.IntersectsPoint(P(0, 0))) + expect(t, poly.IntersectsPoint(P(0, 5))) + expect(t, poly.IntersectsPoint(P(3, 5))) + expect(t, !poly.IntersectsPoint(P(5, 5))) + }) + var poly *Poly + expect(t, !poly.IntersectsPoint(Point{})) + expect(t, !(&Poly{}).IntersectsPoint(Point{})) +} +func TestPolyContainsRect(t *testing.T) { + ring := []Point{{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}} + hole := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, ring, [][]Point{hole}, func(t *testing.T, poly *Poly) { + expect(t, poly.ContainsRect(R(0, 0, 4, 4))) + expect(t, !poly.ContainsRect(R(0, 0, 5, 5))) + expect(t, !poly.ContainsRect(R(2, 2, 6, 6))) + expect(t, !poly.ContainsRect(R(4.1, 4.1, 5.9, 5.9))) + expect(t, !poly.ContainsRect(R(4.1, 4.1, 5.9, 5.9))) + }) + + var poly *Poly + expect(t, !poly.ContainsRect(Rect{})) + expect(t, !poly.IntersectsRect(Rect{})) +} + +func TestPolyIntersectsRect(t *testing.T) { + small := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, octagon, [][]Point{small}, func(t *testing.T, poly *Poly) { + expect(t, poly.IntersectsRect(R(0, 4, 4, 6))) + expect(t, poly.IntersectsRect(R(-1, 4, 4, 6))) + expect(t, poly.IntersectsRect(R(4, 4, 6, 6))) + expect(t, !poly.IntersectsRect(R(4.1, 4.1, 5.9, 5.9))) + expect(t, !poly.IntersectsRect(R(0, 0, 1.4, 1.4))) + expect(t, poly.IntersectsRect(R(0, 0, 1.5, 1.5))) + expect(t, !poly.IntersectsRect(R(0, 0, 10, 10).Move(11, 0))) + }) +} + +func TestPolyContainsLine(t *testing.T) { + small := []Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}} + dualPolyTest(t, octagon, [][]Point{small}, func(t *testing.T, poly *Poly) { + expect(t, poly.ContainsLine(L(P(3, 3), P(3, 7), P(7, 7), P(7, 3)))) + expect(t, !poly.ContainsLine(L(P(-1, 3), P(3, 7), P(7, 7), P(7, 3)))) + expect(t, poly.ContainsLine(L(P(4, 3), P(3, 7), P(7, 7), P(7, 3)))) + expect(t, !poly.ContainsLine(L(P(5, 3), P(3, 7), P(7, 7), P(7, 3)))) + }) +} + +func TestPolyIntersectsLine(t *testing.T) { + holes := [][]Point{[]Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}}} + dualPolyTest(t, octagon, holes, func(t *testing.T, poly *Poly) { + expect(t, poly.IntersectsLine(L(P(3, 3), P(4, 4)))) + expect(t, poly.IntersectsLine(L(P(-1, 3), P(3, 7), P(7, 7), P(7, 3)))) + expect(t, poly.IntersectsLine(L(P(4, 3), P(3, 7), P(7, 7), P(7, 3)))) + expect(t, poly.IntersectsLine(L(P(5, 3), P(3, 7), P(7, 7), P(7, 3)))) + expect(t, !poly.IntersectsLine( + L(P(5, 3), P(3, 7), P(7, 7), P(7, 3)).Move(11, 0), + )) + }) +} +func TestPolyContainsPoly(t *testing.T) { + holes1 := [][]Point{[]Point{{4, 4}, {6, 4}, {6, 6}, {4, 6}, {4, 4}}} + holes2 := [][]Point{[]Point{{5, 4}, {7, 4}, {7, 6}, {5, 6}, {5, 4}}} + poly1 := NewPoly(octagon, holes1, DefaultIndex) + poly2 := NewPoly(octagon, holes2, DefaultIndex) + + expect(t, !poly1.ContainsPoly(NewPoly(holes2[0], nil, DefaultIndex))) + expect(t, !poly1.ContainsPoly(poly2)) + + dualPolyTest(t, octagon, holes1, func(t *testing.T, poly *Poly) { + expect(t, poly.ContainsPoly(poly1)) + expect(t, !poly.ContainsPoly(poly1.Move(1, 0))) + expect(t, poly.ContainsPoly(NewPoly(holes1[0], nil, DefaultIndex))) + expect(t, !poly.ContainsPoly(NewPoly(holes2[0], nil, DefaultIndex))) + }) +} + +func TestPolyClockwise(t *testing.T) { + expect(t, !NewPoly(bowtie, nil, DefaultIndex).Clockwise()) + var poly *Poly + expect(t, !poly.Clockwise()) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/rect.go b/vendor/github.com/tidwall/geojson/geometry/rect.go new file mode 100644 index 00000000..8c280d4b --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/rect.go @@ -0,0 +1,184 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +// Rect ... +type Rect struct { + Min, Max Point +} + +// Move ... +func (rect Rect) Move(deltaX, deltaY float64) Rect { + return Rect{ + Min: Point{X: rect.Min.X + deltaX, Y: rect.Min.Y + deltaY}, + Max: Point{X: rect.Max.X + deltaX, Y: rect.Max.Y + deltaY}, + } +} + +// Clockwise ... +func (rect Rect) Clockwise() bool { + return false +} + +// Center ... +func (rect Rect) Center() Point { + return Point{(rect.Max.X + rect.Min.X) / 2, (rect.Max.Y + rect.Min.Y) / 2} +} + +// Area ... +func (rect Rect) Area() float64 { + return (rect.Max.X - rect.Min.X) * (rect.Max.Y - rect.Min.Y) +} + +// NumPoints ... +func (rect Rect) NumPoints() int { + return 5 +} + +// NumSegments ... +func (rect Rect) NumSegments() int { + return 4 +} + +// PointAt ... +func (rect Rect) PointAt(index int) Point { + switch index { + default: + return []Point{}[0] + case 0: + return Point{rect.Min.X, rect.Min.Y} + case 1: + return Point{rect.Max.X, rect.Min.Y} + case 2: + return Point{rect.Max.X, rect.Max.Y} + case 3: + return Point{rect.Min.X, rect.Max.Y} + case 4: + return Point{rect.Min.X, rect.Min.Y} + } +} + +// SegmentAt ... +func (rect Rect) SegmentAt(index int) Segment { + switch index { + default: + return []Segment{}[0] + case 0: + return Segment{ + Point{rect.Min.X, rect.Min.Y}, + Point{rect.Max.X, rect.Min.Y}, + } + case 1: + return Segment{ + Point{rect.Max.X, rect.Min.Y}, + Point{rect.Max.X, rect.Max.Y}, + } + case 2: + return Segment{ + Point{rect.Max.X, rect.Max.Y}, + Point{rect.Min.X, rect.Max.Y}, + } + case 3: + return Segment{ + Point{rect.Min.X, rect.Max.Y}, + Point{rect.Min.X, rect.Min.Y}, + } + } +} + +// Search ... +func (rect Rect) Search(target Rect, iter func(seg Segment, idx int) bool) { + var idx int + rectNumSegments := rect.NumSegments() + for i := 0; i < rectNumSegments; i++ { + seg := rect.SegmentAt(i) + if seg.Rect().IntersectsRect(target) { + if !iter(seg, idx) { + break + } + } + idx++ + } +} + +// Empty ... +func (rect Rect) Empty() bool { + return false +} + +// Rect ... +func (rect Rect) Rect() Rect { + return rect +} + +// Convex ... +func (rect Rect) Convex() bool { + return true +} + +// ContainsPoint ... +func (rect Rect) ContainsPoint(point Point) bool { + return point.X >= rect.Min.X && point.X <= rect.Max.X && + point.Y >= rect.Min.Y && point.Y <= rect.Max.Y +} + +// IntersectsPoint ... +func (rect Rect) IntersectsPoint(point Point) bool { + return rect.ContainsPoint(point) +} + +// ContainsRect ... +func (rect Rect) ContainsRect(other Rect) bool { + if other.Min.X < rect.Min.X || other.Max.X > rect.Max.X { + return false + } + if other.Min.Y < rect.Min.Y || other.Max.Y > rect.Max.Y { + return false + } + return true +} + +// IntersectsRect ... +func (rect Rect) IntersectsRect(other Rect) bool { + if rect.Min.Y > other.Max.Y || rect.Max.Y < other.Min.Y { + return false + } + if rect.Min.X > other.Max.X || rect.Max.X < other.Min.X { + return false + } + return true +} + +// ContainsLine ... +func (rect Rect) ContainsLine(line *Line) bool { + if line == nil { + return false + } + return !line.Empty() && rect.ContainsRect(line.Rect()) +} + +// IntersectsLine ... +func (rect Rect) IntersectsLine(line *Line) bool { + if line == nil { + return false + } + return ringIntersectsLine(rect, line, true) +} + +// ContainsPoly ... +func (rect Rect) ContainsPoly(poly *Poly) bool { + if poly == nil { + return false + } + return !poly.Empty() && rect.ContainsRect(poly.Rect()) +} + +// IntersectsPoly ... +func (rect Rect) IntersectsPoly(poly *Poly) bool { + if poly == nil { + return false + } + return poly.IntersectsRect(rect) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/rect_test.go b/vendor/github.com/tidwall/geojson/geometry/rect_test.go new file mode 100644 index 00000000..1f451ced --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/rect_test.go @@ -0,0 +1,169 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "testing" +) + +func TestRectCenter(t *testing.T) { + expect(t, R(0, 0, 10, 10).Center() == P(5, 5)) + expect(t, R(0, 0, 0, 0).Center() == P(0, 0)) +} + +func TestRectArea(t *testing.T) { + expect(t, R(0, 0, 10, 10).Area() == 100) +} + +func TestRectMove(t *testing.T) { + expect(t, R(1, 2, 3, 4).Move(5, 6) == R(6, 8, 8, 10)) +} + +func TestRectNumPoints(t *testing.T) { + expect(t, R(0, 0, 10, 10).NumPoints() == 5) +} + +func TestRectNumSegments(t *testing.T) { + expect(t, R(0, 0, 10, 10).NumSegments() == 4) +} + +func TestRectPointAt(t *testing.T) { + expect(t, R(0, 0, 10, 10).PointAt(0) == P(0, 0)) + expect(t, R(0, 0, 10, 10).PointAt(1) == P(10, 0)) + expect(t, R(0, 0, 10, 10).PointAt(2) == P(10, 10)) + expect(t, R(0, 0, 10, 10).PointAt(3) == P(0, 10)) + expect(t, R(0, 0, 10, 10).PointAt(4) == P(0, 0)) + defer func() { expect(t, recover() != nil) }() + R(0, 0, 10, 10).PointAt(5) +} + +func TestRectSegmentAt(t *testing.T) { + expect(t, R(0, 0, 10, 10).SegmentAt(0) == S(0, 0, 10, 0)) + expect(t, R(0, 0, 10, 10).SegmentAt(1) == S(10, 0, 10, 10)) + expect(t, R(0, 0, 10, 10).SegmentAt(2) == S(10, 10, 0, 10)) + expect(t, R(0, 0, 10, 10).SegmentAt(3) == S(0, 10, 0, 0)) + defer func() { expect(t, recover() != nil) }() + R(0, 0, 10, 10).SegmentAt(4) +} + +func TestRectSearch(t *testing.T) { + rect := R(0, 0, 10, 10) + var count int + rect.Search(R(0, 0, 10, 10), func(seg Segment, idx int) bool { + expect(t, rect.PointAt(idx) == seg.A) + count++ + return true + }) + expect(t, count == 4) + count = 0 + rect.Search(R(0, 4, 10, 5), func(seg Segment, idx int) bool { + expect(t, rect.PointAt(idx) == seg.A) + count++ + return true + }) + expect(t, count == 2) + count = 0 + rect.Search(R(0, 4, 10, 5), func(seg Segment, idx int) bool { + expect(t, rect.PointAt(idx) == seg.A) + count++ + return false + }) + expect(t, count == 1) +} + +func TestRectEmpty(t *testing.T) { + expect(t, !R(0, 0, 10, 10).Empty()) +} + +func TestRectRect(t *testing.T) { + expect(t, R(0, 0, 10, 10).Rect() == R(0, 0, 10, 10)) +} + +func TestRectConvex(t *testing.T) { + expect(t, R(0, 0, 10, 10).Convex()) +} + +func TestRectContainsPoint(t *testing.T) { + for x := 0.0; x <= 10; x++ { + for y := 0.0; y <= 10; y++ { + expect(t, R(0, 0, 10, 10).ContainsPoint(P(x, y))) + } + } + expect(t, !R(0, 0, 10, 10).ContainsPoint(P(-15, -15))) + expect(t, !R(0, 0, 10, 10).ContainsPoint(P(-15, 5))) + expect(t, !R(0, 0, 10, 10).ContainsPoint(P(-15, 15))) + expect(t, !R(0, 0, 10, 10).ContainsPoint(P(0, -15))) +} + +func TestRectIntersectsPoint(t *testing.T) { + expect(t, R(0, 0, 10, 10).IntersectsPoint(P(5, 5))) + expect(t, !R(0, 0, 10, 10).IntersectsPoint(P(15, 15))) +} + +func TestRectContainsRect(t *testing.T) { + expect(t, R(0, 0, 10, 10).ContainsRect(R(0, 0, 10, 10))) + expect(t, R(0, 0, 10, 10).ContainsRect(R(2, 2, 10, 10))) + expect(t, R(0, 0, 10, 10).ContainsRect(R(2, 2, 8, 8))) + expect(t, R(0, 0, 10, 10).ContainsRect(R(0, 0, 8, 8))) + expect(t, R(0, 0, 10, 10).ContainsRect(R(0, 2, 8, 8))) + expect(t, R(0, 0, 10, 10).ContainsRect(R(2, 0, 8, 8))) + expect(t, R(0, 0, 10, 10).ContainsRect(R(2, 2, 10, 8))) + expect(t, R(0, 0, 10, 10).ContainsRect(R(2, 2, 8, 10))) + expect(t, !R(0, 0, 10, 10).ContainsRect(R(-1, 0, 10, 10))) + expect(t, !R(0, 0, 10, 10).ContainsRect(R(0, -1, 10, 10))) + expect(t, !R(0, 0, 10, 10).ContainsRect(R(0, 0, 11, 10))) + expect(t, !R(0, 0, 10, 10).ContainsRect(R(0, 0, 10, 11))) +} + +func TestRectIntersectsRect(t *testing.T) { + expect(t, R(0, 0, 10, 10).IntersectsRect(R(0, 0, 10, 10))) + expect(t, R(0, 0, 10, 10).IntersectsRect(R(2, 2, 8, 8))) + expect(t, R(0, 0, 10, 10).IntersectsRect(R(-1, 0, 10, 10))) + expect(t, R(0, 0, 10, 10).IntersectsRect(R(0, -1, 10, 10))) + expect(t, R(0, 0, 10, 10).IntersectsRect(R(0, 0, 11, 10))) + expect(t, R(0, 0, 10, 10).IntersectsRect(R(0, 0, 10, 11))) + expect(t, !R(0, 0, 10, 10).IntersectsRect(R(11, 0, 21, 10))) + expect(t, !R(0, 0, 10, 10).IntersectsRect(R(0, 11, 10, 21))) + expect(t, !R(0, 0, 10, 10).IntersectsRect(R(11, 0, 21, 10))) + expect(t, !R(0, 0, 10, 10).IntersectsRect(R(11, 11, 21, 21))) + expect(t, !R(0, 0, 10, 10).IntersectsRect(R(-11, 11, 1, 21))) + expect(t, !R(0, 0, 10, 10).IntersectsRect(R(-11, -11, -1, -1))) +} + +func TestRectContainsLine(t *testing.T) { + expect(t, R(0, 0, 10, 10).ContainsLine(L(P(0, 0), P(10, 10)))) + expect(t, !R(0, 0, 10, 10).ContainsLine(L(P(0, 0), P(11, 11)))) + expect(t, !R(0, 0, 10, 10).ContainsLine(L())) +} + +func TestRectIntersectsLine(t *testing.T) { + expect(t, R(0, 0, 10, 10).IntersectsLine(L(P(0, 0), P(10, 10)))) + expect(t, R(0, 0, 10, 10).IntersectsLine(L(P(0, 0), P(11, 11)))) + expect(t, !R(0, 0, 10, 10).IntersectsLine(L())) + expect(t, !R(0, 0, 10, 10).IntersectsLine(L(P(11, 11), P(12, 12)))) +} + +func TestRectContainsPoly(t *testing.T) { + oct := NewPoly(octagon, nil, DefaultIndex) + expect(t, R(0, 0, 10, 10).ContainsPoly(oct)) + expect(t, !R(0, 0, 10, 10).ContainsPoly(oct.Move(1, 0))) + expect(t, !R(0, 0, 10, 10).ContainsPoly(oct.Move(1, 1))) + expect(t, !R(0, 0, 10, 10).ContainsPoly(NewPoly(nil, nil, DefaultIndex))) +} + +func TestRectIntersectsPoly(t *testing.T) { + oct := NewPoly(octagon, nil, DefaultIndex) + expect(t, R(0, 0, 10, 10).IntersectsPoly(oct)) + expect(t, R(0, 0, 10, 10).IntersectsPoly(oct.Move(1, 0))) + expect(t, R(0, 0, 10, 10).IntersectsPoly(oct.Move(0, 1))) + expect(t, !R(0, 0, 10, 10).IntersectsPoly(oct.Move(10, 10))) + expect(t, !R(0, 0, 10, 10).IntersectsPoly(oct.Move(11, 10))) + expect(t, !R(0, 0, 10, 10).IntersectsPoly(oct.Move(-11, 0))) + expect(t, !R(0, 0, 10, 10).IntersectsPoly(NewPoly(nil, nil, DefaultIndex))) +} + +func TestRectClockwise(t *testing.T) { + expect(t, !R(10, 11, 12, 13).Clockwise()) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/ring.go b/vendor/github.com/tidwall/geojson/geometry/ring.go new file mode 100644 index 00000000..cd032ad1 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/ring.go @@ -0,0 +1,353 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "math" +) + +const complexRingMinPoints = 16 + +// Ring ... +type Ring = Series + +func newRing(points []Point, index int) Ring { + series := makeSeries(points, true, true, index) + return &series +} + +type ringResult struct { + hit bool // contains/intersects + idx int // edge index +} + +func ringContainsPoint(ring Ring, point Point, allowOnEdge bool) ringResult { + // println("A") + var idx = -1 + // find all intersecting segments on the y-axis + var in bool + ring.Search( + Rect{Point{math.Inf(-1), point.Y}, Point{math.Inf(+1), point.Y}}, + func(seg Segment, index int) bool { + // fmt.Printf("%v %v\n", point, seg) + // perform a raycast operation on the segments + res := seg.Raycast(point) + if res.On { + // println(1) + in = allowOnEdge + idx = index + return false + } + if res.In { + // println(2) + in = !in + } + return true + }, + ) + return ringResult{hit: in, idx: idx} +} + +func ringIntersectsPoint(ring Ring, point Point, allowOnEdge bool) ringResult { + return ringContainsPoint(ring, point, allowOnEdge) +} + +// func segmentsIntersects(seg, other Segment, allowOnEdge bool) bool { +// if seg.IntersectsSegment(other) { + +// } +// return false +// } + +func ringContainsSegment(ring Ring, seg Segment, allowOnEdge bool) bool { + // fmt.Printf("%v %v\n", seg.A, seg.B) + // Test that segment points are contained in the ring. + resA := ringContainsPoint(ring, seg.A, allowOnEdge) + if !resA.hit { + // seg A is not inside ring + return false + } + if seg.B == seg.A { + return true + } + resB := ringContainsPoint(ring, seg.B, allowOnEdge) + if !resB.hit { + // seg B is not inside ring + return false + } + if ring.Convex() { + // ring is convex so the segment must be contained + return true + } + + // The ring is concave so it's possible that the segment crosses over the + // edge of the ring. + if allowOnEdge { + // do some logic around seg points that are on the edge of the ring. + if resA.idx != -1 { + // seg A is on a ring segment + if resB.idx != -1 { + // seg B is on a ring segment + if resB.idx == resA.idx { + // case (3) + // seg A and B share the same ring segment, so it must be + // on the inside. + return true + } + // case (1) + // seg A and seg B are on different segments. + // determine if the space that the seg passes over is inside or + // outside of the ring. To do so we create a ring from the two + // ring segments and check if that ring winding order matches + // the winding order of the ring. + // -- create a ring + + rSegA := ring.SegmentAt(resA.idx) + rSegB := ring.SegmentAt(resB.idx) + if rSegA.A == seg.A || rSegA.B == seg.A || + rSegB.A == seg.A || rSegB.B == seg.A || + rSegA.A == seg.B || rSegA.B == seg.B || + rSegB.A == seg.B || rSegB.B == seg.B { + return true + } + + // fix the order of the + if resB.idx < resA.idx { + rSegA, rSegB = rSegB, rSegA + } + + pts := [5]Point{rSegA.A, rSegA.B, rSegB.A, rSegB.B, rSegA.A} + // -- calc winding order + var cwc float64 + for i := 0; i < len(pts)-1; i++ { + a, b := pts[i], pts[i+1] + cwc += (b.X - a.X) * (b.Y + a.Y) + } + clockwise := cwc > 0 + if clockwise != ring.Clockwise() { + // -- on the outside + return false + } + // the passover space is on the inside of the ring. + // check if seg intersects any ring segments where A and B are + // not on. + var intersects bool + ring.Search(seg.Rect(), func(seg2 Segment, index int) bool { + if seg.IntersectsSegment(seg2) { + if !seg2.Raycast(seg.A).On && !seg2.Raycast(seg.B).On { + intersects = true + return false + } + } + return true + }) + return !intersects + } + // case (4) + // seg A is on a ring segment, but seg B is not. + // check if seg intersects any ring segments where A is not on. + var intersects bool + ring.Search(seg.Rect(), func(seg2 Segment, index int) bool { + if seg.IntersectsSegment(seg2) { + if !seg2.Raycast(seg.A).On { + intersects = true + return false + } + } + return true + }) + return !intersects + } else if resB.idx != -1 { + // case (2) + // seg B is on a ring segment, but seg A is not. + // check if seg intersects any ring segments where B is not on. + var intersects bool + ring.Search(seg.Rect(), func(seg2 Segment, index int) bool { + if seg.IntersectsSegment(seg2) { + if !seg2.Raycast(seg.B).On { + intersects = true + return false + } + } + return true + }) + return !intersects + } + // case (5) (15) + var intersects bool + ring.Search(seg.Rect(), func(seg2 Segment, index int) bool { + if seg.IntersectsSegment(seg2) { + if !seg.Raycast(seg2.A).On && !seg.Raycast(seg2.B).On { + intersects = true + return false + } + } + return true + }) + return !intersects + } + + // allowOnEdge is false. (not allow on edge) + var intersects bool + ring.Search(seg.Rect(), func(seg2 Segment, index int) bool { + if seg.IntersectsSegment(seg2) { + // if seg.Raycast(seg2.A).On || seg.Raycast(seg2.B).On { + intersects = true + // return false + // } + return false + } + return true + }) + return !intersects +} + +// ringIntersectsSegment detect if the segment intersects the ring +func ringIntersectsSegment(ring Ring, seg Segment, allowOnEdge bool) bool { + // Quick check that either point is inside of the ring + if ringContainsPoint(ring, seg.A, allowOnEdge).hit { + return true + } + if ringContainsPoint(ring, seg.B, allowOnEdge).hit { + return true + } + // Neither point A or B is inside the the ring. It's possible that both + // are on the outside and are passing over segments. If the segment passes + // over at least two ring segments then it's intersecting. + var count int + var segAOn bool + var segBOn bool + ring.Search(seg.Rect(), func(seg2 Segment, index int) bool { + if seg.IntersectsSegment(seg2) { + if !allowOnEdge { + // for segments that are not allowed on the edge, extra care + // must be taken. + if !(seg.CollinearPoint(seg2.A) && seg.CollinearPoint(seg2.B)) { + if !segAOn { + if seg.A == seg2.A || seg.A == seg2.B { + segAOn = true + return true + } + } + if !segBOn { + if seg.B == seg2.A || seg.B == seg2.B { + segBOn = true + return true + } + } + count++ + } + } else { + count++ + } + } + return count < 2 + }) + return count >= 2 +} + +func ringContainsRing(ring, other Ring, allowOnEdge bool) bool { + if ring.Empty() || other.Empty() { + return false + } + if other.NumPoints() >= complexRingMinPoints { + // inner ring has a lot of points, and is convex, so let just check if + // the rect ring is fully contained before we do the complicated stuff. + if ringContainsRing(ring, other.Rect(), allowOnEdge) { + return true + } + } + // test if the inner rect does not contain the outer rect + if !ring.Rect().ContainsRect(other.Rect()) { + // not contained so it's not possible for the outer ring to contain + // the inner ring + return false + } + if ring.Convex() { + // outer ring is convex so test that all inner points are inside of + // the outer ring + otherNumPoints := other.NumPoints() + for i := 0; i < otherNumPoints; i++ { + if !ringContainsPoint(ring, other.PointAt(i), allowOnEdge).hit { + // point is on the outside the outer ring + return false + } + } + } else { + // outer ring is concave so let's make sure that all inner segments are + // fully contained inside of the outer ring. + otherNumSegments := other.NumSegments() + for i := 0; i < otherNumSegments; i++ { + if !ringContainsSegment(ring, other.SegmentAt(i), allowOnEdge) { + // fmt.Printf("%v %v\n", ring, other.SegmentAt(i)) + return false + } + } + } + return true +} + +func ringIntersectsRing(ring, other Ring, allowOnEdge bool) bool { + if ring.Empty() || other.Empty() { + return false + } + // check outer and innter rects intersection first + if !ring.Rect().IntersectsRect(other.Rect()) { + return false + } + if other.Rect().Area() > ring.Rect().Area() { + // swap the rings so that the inner ring is smaller than the outer ring + ring, other = other, ring + } + if ring.Convex() && allowOnEdge { + // outer ring is convex so test that any inner points are inside of + // the outer ring + otherNumPoints := other.NumPoints() + for i := 0; i < otherNumPoints; i++ { + if ringContainsPoint(ring, other.PointAt(i), allowOnEdge).hit { + return true + } + } + } else { + // outer ring is concave so let's make sure that all inner segments are + // fully contained inside of the outer ring. + otherNumSegments := other.NumSegments() + for i := 0; i < otherNumSegments; i++ { + if ringIntersectsSegment(ring, other.SegmentAt(i), allowOnEdge) { + return true + } + } + } + return false +} + +func ringContainsLine(ring Ring, line *Line, allowOnEdge bool) bool { + // shares the same logic + return ringContainsRing(ring, Ring(&line.baseSeries), allowOnEdge) +} + +func ringIntersectsLine(ring Ring, line *Line, allowOnEdge bool) bool { + if ring.Empty() || line.Empty() { + return false + } + // check outer and innter rects intersection first + if !ring.Rect().IntersectsRect(line.Rect()) { + return false + } + // check if any points are inside ring + lineNumPoints := line.NumPoints() + for i := 0; i < lineNumPoints; i++ { + if ringContainsPoint(ring, line.PointAt(i), allowOnEdge).hit { + return true + } + } + lineNumSegments := line.NumSegments() + for i := 0; i < lineNumSegments; i++ { + if ringIntersectsSegment(ring, line.SegmentAt(i), allowOnEdge) { + return true + } + } + return false +} diff --git a/vendor/github.com/tidwall/geojson/geometry/ring_test.go b/vendor/github.com/tidwall/geojson/geometry/ring_test.go new file mode 100644 index 00000000..7501b1b9 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/ring_test.go @@ -0,0 +1,1387 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "fmt" + "testing" +) + +func newRingXSimple(points []Point) Ring { + ring := newRing(points, DefaultIndex) + if ring.(*baseSeries).tree != nil { + ring.(*baseSeries).tree = nil + } + return ring +} +func newRingXIndexed(points []Point) Ring { + ring := newRing(points, DefaultIndex) + if ring.(*baseSeries).tree == nil { + ring.(*baseSeries).buildTree() + } + return ring +} + +func testDualRingX(t *testing.T, points []Point, do func(t *testing.T, ring Ring)) { + t.Run("index", func(t *testing.T) { + do(t, newRingXSimple(points)) + }) + t.Run("noindex", func(t *testing.T) { + do(t, newRingXIndexed(points)) + }) +} + +func TestRectGeometryDefaults(t *testing.T) { + g := Geometry(Rect{}) + expect(t, !g.Empty()) + expect(t, g.Rect() == R(0, 0, 0, 0)) + expect(t, !g.ContainsLine(nil)) + expect(t, !g.ContainsLine(&Line{})) + expect(t, g.ContainsPoint(Point{})) + expect(t, !g.ContainsPoly(nil)) + expect(t, !g.ContainsPoly(&Poly{})) + expect(t, g.ContainsRect(Rect{})) + expect(t, !g.IntersectsLine(nil)) + expect(t, !g.IntersectsLine(&Line{})) + expect(t, g.IntersectsPoint(Point{})) + expect(t, !g.IntersectsPoly(nil)) + expect(t, !g.IntersectsPoly(&Poly{})) + expect(t, g.IntersectsRect(Rect{})) +} + +func TestRingXContainsPoint(t *testing.T) { + testDualRingX(t, octagon, func(t *testing.T, ring Ring) { + expect(t, !ringContainsPoint(ring, P(0, 0), true).hit) + expect(t, ringContainsPoint(ring, P(0, 5), true).hit) + expect(t, !ringContainsPoint(ring, P(0, 5), false).hit) + expect(t, ringContainsPoint(ring, P(4, 4), true).hit) + expect(t, !ringContainsPoint(ring, P(1.4, 1.4), true).hit) + expect(t, ringContainsPoint(ring, P(1.5, 1.5), true).hit) + expect(t, !ringContainsPoint(ring, P(1.5, 1.5), false).hit) + }) + shape := []Point{ + P(0, 0), P(4, 0), P(4, 3), + P(3, 4), P(1, 4), + P(0, 3), P(0, 0), + } + ring := newRing(shape, DefaultIndex) + expect(t, !ringContainsPoint(ring, P(0, 3.5), true).hit) + expect(t, !ringContainsPoint(ring, P(0.4, 3.5), true).hit) + expect(t, ringContainsPoint(ring, P(0.5, 3.5), true).hit) + expect(t, ringContainsPoint(ring, P(0.6, 3.5), true).hit) + expect(t, ringContainsPoint(ring, P(1, 3.5), true).hit) + expect(t, ringContainsPoint(ring, P(3, 3.5), true).hit) + expect(t, ringContainsPoint(ring, P(3.4, 3.5), true).hit) + expect(t, ringContainsPoint(ring, P(3.5, 3.5), true).hit) + expect(t, !ringContainsPoint(ring, P(3.9, 3.5), true).hit) + expect(t, !ringContainsPoint(ring, P(4, 3.5), true).hit) + expect(t, !ringContainsPoint(ring, P(4.1, 3.5), true).hit) + +} + +func TestRingXIntersectsPoint(t *testing.T) { + testDualRingX(t, octagon, func(t *testing.T, ring Ring) { + expect(t, !ringIntersectsPoint(ring, P(0, 0), true).hit) + expect(t, ringIntersectsPoint(ring, P(0, 5), true).hit) + expect(t, !ringIntersectsPoint(ring, P(0, 5), false).hit) + expect(t, ringIntersectsPoint(ring, P(4, 4), true).hit) + expect(t, !ringIntersectsPoint(ring, P(1.4, 1.4), true).hit) + expect(t, ringIntersectsPoint(ring, P(1.5, 1.5), true).hit) + expect(t, !ringIntersectsPoint(ring, P(1.5, 1.5), false).hit) + }) +} + +func TestRingXIntersectsSegment(t *testing.T) { + t.Run("Cases", func(t *testing.T) { + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + t.Run("1", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(2, 2, 4, 4), true)) + expect(t, ringIntersectsSegment(ring, S(2, 2, 4, 4), false)) + }) + t.Run("2", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(2, 0, 4, 2), true)) + expect(t, ringIntersectsSegment(ring, S(2, 0, 4, 2), false)) + }) + t.Run("3", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(1, 0, 3, 0), true)) + expect(t, !ringIntersectsSegment(ring, S(1, 0, 3, 0), false)) + }) + t.Run("4", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(2, 4, 2, 0), true)) + expect(t, ringIntersectsSegment(ring, S(2, 4, 2, 0), false)) + }) + t.Run("5", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(2, 0, 2, -3), true)) + expect(t, !ringIntersectsSegment(ring, S(2, 0, 2, -3), false)) + }) + t.Run("6", func(t *testing.T) { + expect(t, !ringIntersectsSegment(ring, S(2, -1, 2, -3), true)) + expect(t, !ringIntersectsSegment(ring, S(2, -1, 2, -3), false)) + }) + t.Run("7", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(-1, 2, 3, 2), true)) + expect(t, ringIntersectsSegment(ring, S(-1, 2, 3, 2), false)) + }) + t.Run("8", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(-1, 3, 2, 1), true)) + expect(t, ringIntersectsSegment(ring, S(-1, 3, 2, 1), false)) + }) + t.Run("9", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(-1, 2, 5, 1), true)) + expect(t, ringIntersectsSegment(ring, S(-1, 2, 5, 1), false)) + }) + t.Run("10", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(2, 5, 1, -1), true)) + expect(t, ringIntersectsSegment(ring, S(2, 5, 1, -1), false)) + }) + t.Run("11", func(t *testing.T) { + expect(t, ringIntersectsSegment(ring, S(0, 4, 4, 0), true)) + expect(t, ringIntersectsSegment(ring, S(0, 4, 4, 0), false)) + }) + t.Run("12", func(t *testing.T) { + ring := newRing([]Point{ + {0, 0}, {2, 0}, {4, 0}, {4, 4}, {0, 4}, {0, 0}, + }, DefaultIndex) + expect(t, ringIntersectsSegment(ring, S(1, 0, 3, 0), true)) + expect(t, !ringIntersectsSegment(ring, S(1, 0, 3, 0), false)) + }) + t.Run("13", func(t *testing.T) { + ring := newRing([]Point{ + {0, 0}, {4, 0}, {4, 4}, {2, 4}, {0, 4}, {0, 0}, + }, DefaultIndex) + expect(t, ringIntersectsSegment(ring, S(0, 4, 4, 4), true)) + expect(t, !ringIntersectsSegment(ring, S(0, 4, 4, 4), false)) + }) + t.Run("14", func(t *testing.T) { + ring := newRing([]Point{ + {0, 0}, {4, 0}, {4, 4}, {2, 4}, {0, 4}, {0, 0}, + }, DefaultIndex) + expect(t, ringIntersectsSegment(ring, S(-1, -2, 0, 0), true)) + expect(t, !ringIntersectsSegment(ring, S(-1, -2, 0, 0), false)) + }) + t.Run("15", func(t *testing.T) { + ring := newRing([]Point{ + {0, 0}, {4, 0}, {4, 4}, {2, 4}, {0, 4}, {0, 0}, + }, DefaultIndex) + expect(t, ringIntersectsSegment(ring, S(0, 4, 5, 4), true)) + expect(t, !ringIntersectsSegment(ring, S(0, 4, 5, 4), false)) + }) + t.Run("16", func(t *testing.T) { + ring := newRing([]Point{ + {0, 0}, {4, 0}, {4, 4}, {2, 4}, {0, 4}, {0, 0}, + }, DefaultIndex) + expect(t, ringIntersectsSegment(ring, S(1, 4, 5, 4), true)) + expect(t, !ringIntersectsSegment(ring, S(1, 4, 5, 4), false)) + }) + t.Run("17", func(t *testing.T) { + ring := newRing([]Point{ + {0, 0}, {4, 0}, {4, 4}, {2, 4}, {0, 4}, {0, 0}, + }, DefaultIndex) + expect(t, ringIntersectsSegment(ring, S(1, 4, 4, 4), true)) + expect(t, !ringIntersectsSegment(ring, S(1, 4, 4, 4), false)) + }) + }) + + // convex shape + t.Run("Octagon", func(t *testing.T) { + shape := octagon + t.Run("Outside", func(t *testing.T) { + // test coming off of the edges + segs := []Segment{ + S(0, 5, -5, 5).Move(-1, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(-1, -1), // bottom-left corner + S(5, 0, 5, -5).Move(0, -1), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(1, -1), // bottom-right corner + S(10, 5, 15, 5).Move(1, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(1, 1), // top-right corner + S(5, 10, 5, 15).Move(0, 5), // top + S(1.5, 8.5, -3.5, 13.5).Move(-1, 1), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("OutsideEdgeTouch", func(t *testing.T) { + // test coming off of the edges + segs := []Segment{ + S(0, 5, -5, 5), // left + S(1.5, 1.5, -3.5, -3.5), // bottom-left corner + S(5, 0, 5, -5), // bottom + S(8.5, 1.5, 13.5, -3.5), // bottom-right corner + S(10, 5, 15, 5), // right + S(8.5, 8.5, 13.5, 13.5), // top-right corner + S(5, 10, 5, 15), // top + S(1.5, 8.5, -3.5, 13.5), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("EdgeCross", func(t *testing.T) { + segs := []Segment{ + S(0, 5, -5, 5).Move(2.5, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(2.5, 2.5), // bottom-left corner + S(5, 0, 5, -5).Move(0, 2.5), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(-2.5, 2.5), // bottom-right corner + S(10, 5, 15, 5).Move(-2.5, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(-2.5, -2.5), // top-right corner + S(5, 10, 5, 15).Move(0, -2.5), // top + S(1.5, 8.5, -3.5, 13.5).Move(2.5, -2.5), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeTouch", func(t *testing.T) { + segs := []Segment{ + S(0, 5, -5, 5).Move(5, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(5, 5), // bottom-left corner + S(5, 0, 5, -5).Move(0, 5), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(-5, 5), // bottom-right corner + S(10, 5, 15, 5).Move(-5, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(-5, -5), // top-right corner + S(5, 10, 5, 15).Move(0, -5), // top + S(1.5, 8.5, -3.5, 13.5).Move(5, -5), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("Inside", func(t *testing.T) { + segs := []Segment{ + S(0, 5, -5, 5).Move(6, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(6, 6), // bottom-left corner + S(5, 0, 5, -5).Move(0, 6), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(-6, 6), // bottom-right corner + S(10, 5, 15, 5).Move(-6, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(-6, -6), // top-right corner + S(5, 10, 5, 15).Move(0, -6), // top + S(1.5, 8.5, -3.5, 13.5).Move(6, -6), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(0, 5, 10, 5), // left to right + S(1.5, 1.5, 8.5, 8.5), // bottom-left to top-right + S(5, 0, 5, 10), // bottom to top + S(8.5, 1.5, 1.5, 8.5), // bottom-right to top-left + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("PassoverEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(-1, 5, 11, 5), // left to right + S(0.5, 0.5, 9.5, 9.5), // bottom-left to top-right + S(5, -1, 5, 11), // bottom to top + S(9.5, 0.5, 0.5, 9.5), // bottom-right to top-left + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + }) + + // concave shape + t.Run("Concave", func(t *testing.T) { + shape := concave1 + t.Run("Outside", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -5, 7.5).Move(-1, 0), // left + S(2.5, 5, 2.5, 0).Move(0, -1), // bottom-left-1 + S(5, 2.5, 0, 2.5).Move(-1, 0), // bottom-left-2 + S(7.5, 0, 7.5, -5).Move(0, -1), // bottom + S(10, 5, 15, 5).Move(1, 0), // right + S(5, 10, 5, 15).Move(0, 1), // top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("OutsideEdgeTouch", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -5, 7.5), // left + S(2.5, 5, 2.5, 0), // bottom-left-1 + S(5, 2.5, 0, 2.5), // bottom-left-2 + S(7.5, 0, 7.5, -5), // bottom + S(10, 5, 15, 5), // right + S(5, 10, 5, 15), // top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, !ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("EdgeCross", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -5, 7.5).Move(2.5, 0), // left + S(2.5, 5, 2.5, 0).Move(0, 2.5), // bottom-left-1 + S(5, 2.5, 0, 2.5).Move(2.5, 0), // bottom-left-2 + S(7.5, 0, 7.5, -5).Move(0, 2.5), // bottom + S(10, 5, 15, 5).Move(-2.5, 0), // right + S(5, 10, 5, 15).Move(0, -2.5), // top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeTouch", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -3, 7.5).Move(3, 0), // 0:left + S(2.5, 5, 2.5, 2).Move(0, 3), // 1:bottom-left-1 + S(5, 2.5, 2, 2.5).Move(3, 0), // 2:bottom-left-2 + S(7.5, 0, 7.5, -3).Move(0, 3), // 3:bottom + S(10, 5, 13, 5).Move(-3, 0), // 4:right + S(5, 10, 5, 13).Move(0, -3), // 5:top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("Inside", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -3, 7.5).Move(4, 0), // 0:left + S(2.5, 5, 2.5, 2).Move(0, 4), // 1:bottom-left-1 + S(5, 2.5, 2, 2.5).Move(4, 0), // 2:bottom-left-2 + S(7.5, 0, 7.5, -3).Move(0, 4), // 3:bottom + S(10, 5, 13, 5).Move(-4, 0), // 4:right + S(5, 10, 5, 13).Move(0, -4), // 5:top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, 10, 7.5), // 0:left + S(2.5, 5, 2.5, 10), // 1:bottom-left-1 + S(5, 2.5, 10, 2.5), // 2:bottom-left-2 + S(7.5, 0, 7.5, 10), // 3:bottom + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("PassoverEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(-1, 7.5, 11, 7.5), // 0:left + S(2.5, 4, 2.5, 11), // 1:bottom-left-1 + S(4, 2.5, 11, 2.5), // 2:bottom-left-2 + S(7.5, -1, 7.5, 11), // 3:bottom + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringIntersectsSegment(ring, seg, true)) + expect(t, ringIntersectsSegment(ring, seg, false)) + }) + }) + } + }) + }) +} + +func TestRingXContainsSegment(t *testing.T) { + expect(t, ringContainsSegment(R(0, 0, 10, 10), S(5, 5, 5, 5), true)) + expect(t, ringContainsSegment(R(0, 0, 10, 10), S(5, 5, 5, 5), false)) + expect(t, ringContainsSegment(R(0, 0, 10, 10), S(0, 0, 0, 0), true)) + expect(t, !ringContainsSegment(R(0, 0, 10, 10), S(0, 0, 0, 0), false)) + expect(t, ringContainsSegment(R(0, 0, 10, 10), S(10, 10, 10, 10), true)) + expect(t, !ringContainsSegment(R(0, 0, 10, 10), S(10, 10, 10, 10), false)) + + // convex shape + t.Run("Octagon", func(t *testing.T) { + shape := octagon + t.Run("Outside", func(t *testing.T) { + // test coming off of the edges + segs := []Segment{ + S(0, 5, -5, 5).Move(-1, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(-1, -1), // bottom-left corner + S(5, 0, 5, -5).Move(0, -1), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(1, -1), // bottom-right corner + S(10, 5, 15, 5).Move(1, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(1, 1), // top-right corner + S(5, 10, 5, 15).Move(0, 5), // top + S(1.5, 8.5, -3.5, 13.5).Move(-1, 1), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("OutsideEdgeTouch", func(t *testing.T) { + // test coming off of the edges + segs := []Segment{ + S(0, 5, -5, 5), // left + S(1.5, 1.5, -3.5, -3.5), // bottom-left corner + S(5, 0, 5, -5), // bottom + S(8.5, 1.5, 13.5, -3.5), // bottom-right corner + S(10, 5, 15, 5), // right + S(8.5, 8.5, 13.5, 13.5), // top-right corner + S(5, 10, 5, 15), // top + S(1.5, 8.5, -3.5, 13.5), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("EdgeCross", func(t *testing.T) { + segs := []Segment{ + S(0, 5, -5, 5).Move(2.5, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(2.5, 2.5), // bottom-left corner + S(5, 0, 5, -5).Move(0, 2.5), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(-2.5, 2.5), // bottom-right corner + S(10, 5, 15, 5).Move(-2.5, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(-2.5, -2.5), // top-right corner + S(5, 10, 5, 15).Move(0, -2.5), // top + S(1.5, 8.5, -3.5, 13.5).Move(2.5, -2.5), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeTouch", func(t *testing.T) { + segs := []Segment{ + S(0, 5, -5, 5).Move(5, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(5, 5), // bottom-left corner + S(5, 0, 5, -5).Move(0, 5), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(-5, 5), // bottom-right corner + S(10, 5, 15, 5).Move(-5, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(-5, -5), // top-right corner + S(5, 10, 5, 15).Move(0, -5), // top + S(1.5, 8.5, -3.5, 13.5).Move(5, -5), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("Inside", func(t *testing.T) { + segs := []Segment{ + S(0, 5, -5, 5).Move(6, 0), // left + S(1.5, 1.5, -3.5, -3.5).Move(6, 6), // bottom-left corner + S(5, 0, 5, -5).Move(0, 6), // bottom + S(8.5, 1.5, 13.5, -3.5).Move(-6, 6), // bottom-right corner + S(10, 5, 15, 5).Move(-6, 0), // right + S(8.5, 8.5, 13.5, 13.5).Move(-6, -6), // top-right corner + S(5, 10, 5, 15).Move(0, -6), // top + S(1.5, 8.5, -3.5, 13.5).Move(6, -6), // top-left corner + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(0, 5, 10, 5), // left to right + S(1.5, 1.5, 8.5, 8.5), // bottom-left to top-right + S(5, 0, 5, 10), // bottom to top + S(8.5, 1.5, 1.5, 8.5), // bottom-right to top-left + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("PassoverEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(-1, 5, 11, 5), // left to right + S(0.5, 0.5, 9.5, 9.5), // bottom-left to top-right + S(5, -1, 5, 11), // bottom to top + S(9.5, 0.5, 0.5, 9.5), // bottom-right to top-left + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + }) + + // concave shape + t.Run("Concave", func(t *testing.T) { + shape := concave1 + t.Run("Outside", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -5, 7.5).Move(-1, 0), // left + S(2.5, 5, 2.5, 0).Move(0, -1), // bottom-left-1 + S(5, 2.5, 0, 2.5).Move(-1, 0), // bottom-left-2 + S(7.5, 0, 7.5, -5).Move(0, -1), // bottom + S(10, 5, 15, 5).Move(1, 0), // right + S(5, 10, 5, 15).Move(0, 1), // top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("OutsideEdgeTouch", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -5, 7.5), // left + S(2.5, 5, 2.5, 0), // bottom-left-1 + S(5, 2.5, 0, 2.5), // bottom-left-2 + S(7.5, 0, 7.5, -5), // bottom + S(10, 5, 15, 5), // right + S(5, 10, 5, 15), // top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("EdgeCross", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -5, 7.5).Move(2.5, 0), // left + S(2.5, 5, 2.5, 0).Move(0, 2.5), // bottom-left-1 + S(5, 2.5, 0, 2.5).Move(2.5, 0), // bottom-left-2 + S(7.5, 0, 7.5, -5).Move(0, 2.5), // bottom + S(10, 5, 15, 5).Move(-2.5, 0), // right + S(5, 10, 5, 15).Move(0, -2.5), // top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeTouch", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -3, 7.5).Move(3, 0), // 0:left + S(2.5, 5, 2.5, 2).Move(0, 3), // 1:bottom-left-1 + S(5, 2.5, 2, 2.5).Move(3, 0), // 2:bottom-left-2 + S(7.5, 0, 7.5, -3).Move(0, 3), // 3:bottom + S(10, 5, 13, 5).Move(-3, 0), // 4:right + S(5, 10, 5, 13).Move(0, -3), // 5:top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("Inside", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, -3, 7.5).Move(4, 0), // 0:left + S(2.5, 5, 2.5, 2).Move(0, 4), // 1:bottom-left-1 + S(5, 2.5, 2, 2.5).Move(4, 0), // 2:bottom-left-2 + S(7.5, 0, 7.5, -3).Move(0, 4), // 3:bottom + S(10, 5, 13, 5).Move(-4, 0), // 4:right + S(5, 10, 5, 13).Move(0, -4), // 5:top + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("InsideEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(0, 7.5, 10, 7.5), // 0:left + S(2.5, 5, 2.5, 10), // 1:bottom-left-1 + S(5, 2.5, 10, 2.5), // 2:bottom-left-2 + S(7.5, 0, 7.5, 10), // 3:bottom + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + t.Run("PassoverEdgeToEdge", func(t *testing.T) { + segs := []Segment{ + S(-1, 7.5, 11, 7.5), // 0:left + S(2.5, 4, 2.5, 11), // 1:bottom-left-1 + S(4, 2.5, 11, 2.5), // 2:bottom-left-2 + S(7.5, -1, 7.5, 11), // 3:bottom + } + for i, seg := range segs { + t.Run(fmt.Sprintf("Segment-%d", i), func(t *testing.T) { + testDualRingX(t, shape, func(t *testing.T, ring Ring) { + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + seg.A, seg.B = seg.B, seg.A + expect(t, !ringContainsSegment(ring, seg, true)) + expect(t, !ringContainsSegment(ring, seg, false)) + }) + }) + } + }) + }) + + t.Run("Cases", func(t *testing.T) { + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(3, 4), + P(2, 3), P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + t.Run("1", func(t *testing.T) { + expect(t, !ringContainsSegment(ring, S(1.5, 3.5, 2.5, 3.5), true)) + expect(t, !ringContainsSegment(ring, S(1.5, 3.5, 2.5, 3.5), false)) + }) + t.Run("2", func(t *testing.T) { + expect(t, !ringContainsSegment(ring, S(1.0, 3.5, 2.5, 3.5), true)) + expect(t, !ringContainsSegment(ring, S(1.0, 3.5, 2.5, 3.5), false)) + }) + t.Run("3", func(t *testing.T) { + expect(t, ringContainsSegment(ring, S(1.25, 3.75, 1.75, 3.25), true)) + expect(t, !ringContainsSegment(ring, S(1.25, 3.75, 1.75, 3.25), false)) + }) + t.Run("4", func(t *testing.T) { + expect(t, !ringContainsSegment(ring, S(1.5, 3.5, 3, 3.5), true)) + expect(t, !ringContainsSegment(ring, S(1.5, 3.5, 3, 3.5), false)) + }) + t.Run("5", func(t *testing.T) { + expect(t, !ringContainsSegment(ring, S(1.0, 3.5, 3, 3.5), true)) + expect(t, !ringContainsSegment(ring, S(1.0, 3.5, 3, 3.5), false)) + }) + t.Run("6", func(t *testing.T) { + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(3, 4), + P(2, 5), P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + expect(t, ringContainsSegment(ring, S(1.5, 4.5, 2.5, 4.5), true)) + expect(t, !ringContainsSegment(ring, S(1.5, 4.5, 2.5, 4.5), false)) + }) + t.Run("7", func(t *testing.T) { + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(3, 4), + P(2.5, 3), P(2, 4), P(1.5, 3), + P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + expect(t, !ringContainsSegment(ring, S(1.25, 3.5, 2.75, 3.5), true)) + expect(t, !ringContainsSegment(ring, S(1.25, 3.5, 2.75, 3.5), false)) + }) + t.Run("8", func(t *testing.T) { + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(3, 4), + P(2.5, 5), P(2, 4), P(1.5, 5), + P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + expect(t, !ringContainsSegment(ring, S(1.25, 4.5, 2.75, 4.5), true)) + expect(t, !ringContainsSegment(ring, S(1.25, 4.5, 2.75, 4.5), false)) + }) + t.Run("9", func(t *testing.T) { + expect(t, ringContainsSegment(ring, S(1, 2, 3, 2), true)) + expect(t, ringContainsSegment(ring, S(1, 2, 3, 2), false)) + }) + t.Run("10", func(t *testing.T) { + expect(t, ringContainsSegment(ring, S(1, 3, 3, 2), true)) + expect(t, ringContainsSegment(ring, S(1, 3, 3, 2), false)) + }) + t.Run("11", func(t *testing.T) { + expect(t, ringContainsSegment(ring, S(1, 2, 3, 3), true)) + expect(t, ringContainsSegment(ring, S(1, 2, 3, 3), false)) + }) + t.Run("12", func(t *testing.T) { + expect(t, ringContainsSegment(ring, S(1.5, 3.5, 1.5, 2), true)) + expect(t, !ringContainsSegment(ring, S(1.5, 3.5, 1.5, 2), false)) + }) + t.Run("13", func(t *testing.T) { + ring := newRing([]Point{ + P(0, 0), P(2, 0), P(4, 0), P(4, 4), P(3, 4), + P(2, 3), P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + expect(t, ringContainsSegment(ring, S(1, 0, 3, 0), true)) + expect(t, !ringContainsSegment(ring, S(1, 0, 3, 0), false)) + }) + t.Run("14", func(t *testing.T) { + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(2, 2), P(0, 4), P(0, 0), + }, DefaultIndex) + expect(t, ringContainsSegment(ring, S(1, 3, 3, 1), true)) + expect(t, ringContainsSegment(ring, S(3, 1, 1, 3), true)) + expect(t, !ringContainsSegment(ring, S(1, 3, 3, 1), false)) + expect(t, !ringContainsSegment(ring, S(3, 1, 1, 3), false)) + }) + t.Run("15", func(t *testing.T) { + expect(t, ringContainsSegment(ring, S(1, 3, 3, 3), true)) + expect(t, !ringContainsSegment(ring, S(1, 3, 3, 3), false)) + }) + t.Run("16", func(t *testing.T) { + expect(t, ringContainsSegment(ring, S(1, 3, 2, 3), true)) + expect(t, !ringContainsSegment(ring, S(1, 3, 2, 3), false)) + }) + }) +} + +func TestRingXContainsRing(t *testing.T) { + t.Run("Empty", func(t *testing.T) { + expect(t, !ringContainsRing(newRing(nil, DefaultIndex), R(0, 0, 1, 1), true)) + expect(t, !ringContainsRing(R(0, 0, 1, 1), newRing(nil, DefaultIndex), true)) + }) + + t.Run("Exact", func(t *testing.T) { + expect(t, ringContainsRing(R(0, 0, 1, 1), R(0, 0, 1, 1), true)) + expect(t, !ringContainsRing(R(0, 0, 1, 1), R(0, 0, 1, 1), false)) + expect(t, ringContainsRing(newRing(concave1, DefaultIndex), newRing(concave1, DefaultIndex), true)) + expect(t, !ringContainsRing(newRing(concave1, DefaultIndex), newRing(concave1, DefaultIndex), false)) + expect(t, ringContainsRing(newRing(octagon, DefaultIndex), newRing(octagon, DefaultIndex), true)) + expect(t, !ringContainsRing(newRing(octagon, DefaultIndex), newRing(octagon, DefaultIndex), false)) + }) + t.Run("Cases", func(t *testing.T) { + // concave + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(3, 4), + P(2, 3), P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + t.Run("1", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(1, 1, 3, 2), true)) + expect(t, ringContainsRing(ring, R(1, 1, 3, 2), false)) + }) + t.Run("2", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(0, 0, 2, 1), true)) + expect(t, !ringContainsRing(ring, R(0, 0, 2, 1), false)) + }) + t.Run("3", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(-1.5, 1, 1.5, 2), true)) + expect(t, !ringContainsRing(ring, R(-1.5, 1, 1.5, 2), false)) + }) + t.Run("4", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(1, 2.5, 3, 3.5), true)) + expect(t, !ringContainsRing(ring, R(1, 2.5, 3, 3.5), false)) + }) + t.Run("5", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(1, 2, 3, 3), true)) + expect(t, !ringContainsRing(ring, R(1, 2, 3, 3), false)) + }) + // convex + ring = newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), + P(3, 4), P(2, 5), P(1, 4), + P(0, 4), P(0, 0), + }, DefaultIndex) + t.Run("6", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(1, 2, 3, 3), true)) + expect(t, ringContainsRing(ring, R(1, 2, 3, 3), false)) + }) + t.Run("7", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(1, 3, 3, 4), true)) + expect(t, !ringContainsRing(ring, R(1, 3, 3, 4), false)) + }) + t.Run("8", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(1, 3.5, 3, 4.5), true)) + expect(t, !ringContainsRing(ring, R(1, 3.5, 3, 4.5), false)) + }) + t.Run("9", func(t *testing.T) { + ring = newRing([]Point{ + P(0, 0), P(2, 0), P(4, 0), P(4, 4), P(3, 4), + P(2, 5), P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + expect(t, ringContainsRing(ring, R(1, 0, 3, 1), true)) + expect(t, !ringContainsRing(ring, R(1, 0, 3, 1), false)) + }) + t.Run("10", func(t *testing.T) { + ring = newRing([]Point{ + P(0, 0), P(4, 0), P(4, 3), P(2, 4), + P(0, 3), P(0, 0), + }, DefaultIndex) + expect(t, ringContainsRing(ring, R(1, 1, 3, 2), true)) + expect(t, ringContainsRing(ring, R(1, 1, 3, 2), false)) + }) + ring = newRing([]Point{ + P(0, 0), P(4, 0), P(4, 3), P(3, 4), P(1, 4), P(0, 3), P(0, 0), + }, DefaultIndex) + t.Run("11", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(1, 1, 3, 2), true)) + expect(t, ringContainsRing(ring, R(1, 1, 3, 2), false)) + }) + t.Run("12", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(0, 1, 2, 2), true)) + expect(t, !ringContainsRing(ring, R(0, 1, 2, 2), false)) + }) + t.Run("13", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(-1, 1, 1, 2), true)) + expect(t, !ringContainsRing(ring, R(-1, 1, 1, 2), false)) + }) + t.Run("14", func(t *testing.T) { + expect(t, ringContainsRing(ring, R(0.5, 2.5, 2.5, 3.5), true)) + expect(t, !ringContainsRing(ring, R(0.5, 2.5, 2.5, 3.5), false)) + }) + t.Run("15", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(0.25, 2.75, 2.25, 3.75), true)) + expect(t, !ringContainsRing(ring, R(0.25, 2.75, 2.25, 3.75), false)) + }) + t.Run("16", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(-2, 1, -1, 2), true)) + expect(t, !ringContainsRing(ring, R(-2, 1, -1, 2), false)) + }) + t.Run("17", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(-0.5, 3.5, 0.5, 4.5), true)) + expect(t, !ringContainsRing(ring, R(-0.5, 3.5, 0.5, 4.5), false)) + }) + t.Run("18", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(-0.75, 3.75, 0.25, 4.75), true)) + expect(t, !ringContainsRing(ring, R(-0.74, 3.75, 0.25, 4.75), false)) + }) + t.Run("19", func(t *testing.T) { + expect(t, !ringContainsRing(ring, R(-1, -1, 5, 5), true)) + expect(t, !ringContainsRing(ring, R(-1, -1, 5, 5), false)) + }) + + }) + t.Run("Identical", func(t *testing.T) { + shapes := []Ring{ + R(0, 0, 10, 10), + newRing(octagon, DefaultIndex), + newRing(concave1, DefaultIndex), newRing(concave2, DefaultIndex), + newRing(concave3, DefaultIndex), newRing(concave4, DefaultIndex), + newRing(ri, DefaultIndex), + } + for i, shape := range shapes { + t.Run(fmt.Sprintf("Shape%d", i), func(t *testing.T) { + expect(t, ringContainsRing(shape, shape, true)) + }) + } + }) + + t.Run("Big", func(t *testing.T) { + // use rhode island + ring := newRing(ri, DefaultIndex) + expect(t, ringContainsRing(ring.Rect(), ring, true)) + expect(t, ringContainsRing(ring, ring, true)) + + }) +} + +func TestRingXIntersectsRing(t *testing.T) { + intersectsBothWays := func(ringA, ringB Ring, allowOnEdge bool) bool { + t1 := ringIntersectsRing(ringA, ringB, allowOnEdge) + t2 := ringIntersectsRing(ringB, ringA, allowOnEdge) + if t1 != t2 { + panic("mismatch") + } + return t1 + } + t.Run("Empty", func(t *testing.T) { + expect(t, !intersectsBothWays(newRing(nil, DefaultIndex), R(0, 0, 1, 1), true)) + expect(t, !intersectsBothWays(R(0, 0, 1, 1), newRing(nil, DefaultIndex), true)) + }) + t.Run("Cases", func(t *testing.T) { + // concave + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(3, 4), + P(2, 3), P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + t.Run("1", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(1, 1, 3, 2), true)) + expect(t, intersectsBothWays(ring, R(1, 1, 3, 2), false)) + }) + t.Run("2", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(0, 0, 2, 1), true)) + expect(t, intersectsBothWays(ring, R(0, 0, 2, 1), false)) + }) + t.Run("3", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(-1.5, 1, 1.5, 2), true)) + expect(t, intersectsBothWays(ring, R(-1.5, 1, 1.5, 2), false)) + }) + t.Run("4", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(1, 2.5, 3, 3.5), true)) + expect(t, intersectsBothWays(ring, R(1, 2.5, 3, 3.5), false)) + }) + t.Run("5", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(1, 2, 3, 3), true)) + expect(t, intersectsBothWays(ring, R(1, 2, 3, 3), false)) + }) + // convex + ring = newRing([]Point{ + P(0, 0), P(4, 0), P(4, 4), + P(3, 4), P(2, 5), P(1, 4), + P(0, 4), P(0, 0), + }, DefaultIndex) + t.Run("6", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(1, 2, 3, 3), true)) + expect(t, intersectsBothWays(ring, R(1, 2, 3, 3), false)) + }) + t.Run("7", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(1, 3, 3, 4), true)) + expect(t, intersectsBothWays(ring, R(1, 3, 3, 4), false)) + }) + t.Run("8", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(1, 3.5, 3, 4.5), true)) + expect(t, intersectsBothWays(ring, R(1, 3.5, 3, 4.5), false)) + }) + t.Run("9", func(t *testing.T) { + ring = newRing([]Point{ + P(0, 0), P(2, 0), P(4, 0), P(4, 4), P(3, 4), + P(2, 5), P(1, 4), P(0, 4), P(0, 0), + }, DefaultIndex) + expect(t, intersectsBothWays(ring, R(1, 0, 3, 1), true)) + expect(t, intersectsBothWays(ring, R(1, 0, 3, 1), false)) + }) + t.Run("10", func(t *testing.T) { + ring = newRing([]Point{ + P(0, 0), P(4, 0), P(4, 3), P(2, 4), + P(0, 3), P(0, 0), + }, DefaultIndex) + expect(t, intersectsBothWays(ring, R(1, 1, 3, 2), true)) + expect(t, intersectsBothWays(ring, R(1, 1, 3, 2), false)) + }) + ring = newRing([]Point{ + P(0, 0), P(4, 0), P(4, 3), P(3, 4), P(1, 4), P(0, 3), P(0, 0), + }, DefaultIndex) + t.Run("11", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(1, 1, 3, 2), true)) + expect(t, intersectsBothWays(ring, R(1, 1, 3, 2), false)) + }) + t.Run("12", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(0, 1, 2, 2), true)) + expect(t, intersectsBothWays(ring, R(0, 1, 2, 2), false)) + }) + t.Run("13", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(-1, 1, 1, 2), true)) + expect(t, intersectsBothWays(ring, R(-1, 1, 1, 2), false)) + }) + t.Run("14", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(0.5, 2.5, 2.5, 3.5), true)) + expect(t, intersectsBothWays(ring, R(0.5, 2.5, 2.5, 3.5), false)) + }) + t.Run("15", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(0.25, 2.75, 2.25, 3.75), true)) + expect(t, intersectsBothWays(ring, R(0.25, 2.75, 2.25, 3.75), false)) + }) + t.Run("16", func(t *testing.T) { + expect(t, !intersectsBothWays(ring, R(-2, 1, -1, 2), true)) + expect(t, !intersectsBothWays(ring, R(-2, 1, -1, 2), false)) + }) + t.Run("17", func(t *testing.T) { + expect(t, intersectsBothWays(ring, R(-0.5, 3.5, 0.5, 4.5), true)) + expect(t, !intersectsBothWays(ring, R(-0.5, 3.5, 0.5, 4.5), false)) + }) + t.Run("18", func(t *testing.T) { + expect(t, !intersectsBothWays(ring, R(-0.75, 3.75, 0.25, 4.75), true)) + expect(t, !intersectsBothWays(ring, R(-0.74, 3.75, 0.25, 4.75), false)) + }) + t.Run("19", func(t *testing.T) { + expect(t, ringIntersectsRing(ring, R(-1, -1, 5, 5), true)) + expect(t, ringIntersectsRing(ring, R(-1, -1, 5, 5), false)) + }) + }) +} + +func TestRingXContainsLine(t *testing.T) { + t.Run("Cases", func(t *testing.T) { + // convex + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 3), + P(3, 4), P(1, 4), + P(0, 3), P(0, 0), + }, DefaultIndex) + makeLine := func(start Point) *Line { + return NewLine([]Point{ + start, start.Move(0, 1), start.Move(1, 1), + start.Move(1, 2), start.Move(2, 2), + }, DefaultIndex) + } + t.Run("1", func(t *testing.T) { + expect(t, ringContainsLine(ring, makeLine(P(1, 1)), true)) + expect(t, ringContainsLine(ring, makeLine(P(1, 1)), false)) + }) + t.Run("2", func(t *testing.T) { + expect(t, ringContainsLine(ring, makeLine(P(0, 1)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(0, 1)), false)) + }) + t.Run("3", func(t *testing.T) { + expect(t, !ringContainsLine(ring, makeLine(P(-0.5, 1)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(-0.5, 1)), false)) + }) + t.Run("4", func(t *testing.T) { + expect(t, ringContainsLine(ring, makeLine(P(0, 2)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(0, 2)), false)) + }) + t.Run("5", func(t *testing.T) { + expect(t, !ringContainsLine(ring, makeLine(P(0, 2.5)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(0, 2.5)), false)) + }) + t.Run("6", func(t *testing.T) { + expect(t, !ringContainsLine(ring, makeLine(P(2, 2)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(2, 2)), false)) + }) + t.Run("7", func(t *testing.T) { + expect(t, !ringContainsLine(ring, makeLine(P(2, 1.5)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(2, 1.5)), false)) + }) + t.Run("8", func(t *testing.T) { + expect(t, ringContainsLine(ring, makeLine(P(2, 1)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(2, 1)), false)) + }) + t.Run("9", func(t *testing.T) { + expect(t, ringContainsLine(ring, makeLine(P(2, 0)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(2, 0)), false)) + }) + t.Run("10", func(t *testing.T) { + expect(t, ringContainsLine(ring, makeLine(P(1.5, 0)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(1.5, 0)), false)) + }) + t.Run("11", func(t *testing.T) { + expect(t, !ringContainsLine(ring, makeLine(P(1, -1)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(1, -1)), false)) + }) + t.Run("12", func(t *testing.T) { + expect(t, !ringContainsLine(ring, makeLine(P(1.5, -0.5)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(1.5, -0.5)), false)) + }) + t.Run("13", func(t *testing.T) { + expect(t, ringContainsLine(ring, makeLine(P(0, 0)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(0, 0)), false)) + }) + t.Run("14", func(t *testing.T) { + expect(t, !ringContainsLine(ring, makeLine(P(3, 1)), true)) + expect(t, !ringContainsLine(ring, makeLine(P(3, 1)), false)) + }) + t.Run("15", func(t *testing.T) { + line := L(P(-1, -1), P(-1, 2), P(2, 2), P(2, 5), P(5, 5)) + expect(t, !ringContainsLine(ring, line, true)) + expect(t, !ringContainsLine(ring, line, false)) + }) + t.Run("16", func(t *testing.T) { + line := L(P(0, -1), P(0, 1), P(3, 3), P(5, 3)) + expect(t, !ringContainsLine(ring, line, true)) + expect(t, !ringContainsLine(ring, line, false)) + }) + t.Run("17", func(t *testing.T) { + line := L(P(-1, 2), P(2, 1), P(5, 2)) + expect(t, !ringContainsLine(ring, line, true)) + expect(t, !ringContainsLine(ring, line, false)) + }) + t.Run("18", func(t *testing.T) { + line := L(P(1, -1), P(3, 2), P(2, 5)) + expect(t, !ringContainsLine(ring, line, true)) + expect(t, !ringContainsLine(ring, line, false)) + }) + t.Run("19", func(t *testing.T) { + line := L(P(3.5, 4.5), P(3.5, 3.5), P(5, 3.5), P(5, 2.5)) + expect(t, !ringContainsLine(ring, line, true)) + expect(t, !ringContainsLine(ring, line, false)) + }) + t.Run("20", func(t *testing.T) { + line := L(P(4, 5), P(4, 4), P(5, 4), P(5, 3)) + expect(t, !ringContainsLine(ring, line, true)) + expect(t, !ringContainsLine(ring, line, false)) + }) + t.Run("21", func(t *testing.T) { + line := L(P(1, -1), P(1, -2), P(2, -2), P(2, -3)) + expect(t, !ringContainsLine(ring, line, true)) + expect(t, !ringContainsLine(ring, line, false)) + }) + }) +} + +func TestRingXIntersectsLine(t *testing.T) { + t.Run("Various", func(t *testing.T) { + expect(t, !ringIntersectsLine(R(0, 0, 0, 0), L(), true)) + expect(t, !ringIntersectsLine(R(0, 0, 10, 10), + L(P(-1, -1), P(11, -1), P(11, 11), P(-1, 11)), + true)) + expect(t, ringIntersectsLine(R(0, 0, 10, 10), + L(P(-1, -1), P(11, -1), P(11, 11), P(-1, 11), P(5, -1)), + true)) + }) + + t.Run("Cases", func(t *testing.T) { + // convex + ring := newRing([]Point{ + P(0, 0), P(4, 0), P(4, 3), + P(3, 4), P(1, 4), + P(0, 3), P(0, 0), + }, DefaultIndex) + makeLine := func(start Point) *Line { + return NewLine([]Point{ + start, start.Move(0, 1), start.Move(1, 1), + start.Move(1, 2), start.Move(2, 2), + }, DefaultIndex) + } + t.Run("1", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(1, 1)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(1, 1)), false)) + }) + t.Run("2", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(0, 1)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(0, 1)), false)) + }) + t.Run("3", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(-0.5, 1)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(-0.5, 1)), false)) + }) + t.Run("4", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(0, 2)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(0, 2)), false)) + }) + t.Run("5", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(0, 2.5)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(0, 2.5)), false)) + }) + t.Run("6", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(2, 2)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(2, 2)), false)) + }) + t.Run("7", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(2, 1.5)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(2, 1.5)), false)) + }) + t.Run("8", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(2, 1)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(2, 1)), false)) + }) + t.Run("9", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(2, 0)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(2, 0)), false)) + }) + t.Run("10", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(1.5, 0)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(1.5, 0)), false)) + }) + t.Run("11", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(1, -1)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(1, -1)), false)) + }) + t.Run("12", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(1.5, -0.5)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(1.5, -0.5)), false)) + }) + t.Run("13", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(0, 0)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(0, 0)), false)) + }) + t.Run("14", func(t *testing.T) { + expect(t, ringIntersectsLine(ring, makeLine(P(3, 1)), true)) + expect(t, ringIntersectsLine(ring, makeLine(P(3, 1)), false)) + }) + t.Run("15", func(t *testing.T) { + line := L(P(-1, -1), P(-1, 2), P(2, 2), P(2, 5), P(5, 5)) + expect(t, ringIntersectsLine(ring, line, true)) + expect(t, ringIntersectsLine(ring, line, false)) + }) + t.Run("16", func(t *testing.T) { + line := L(P(0, -1), P(0, 1), P(3, 3), P(5, 3)) + expect(t, ringIntersectsLine(ring, line, true)) + expect(t, ringIntersectsLine(ring, line, false)) + }) + t.Run("17", func(t *testing.T) { + line := L(P(-1, 2), P(2, 1), P(5, 2)) + expect(t, ringIntersectsLine(ring, line, true)) + expect(t, ringIntersectsLine(ring, line, false)) + }) + t.Run("18", func(t *testing.T) { + line := L(P(1, -1), P(3, 2), P(2, 5)) + expect(t, ringIntersectsLine(ring, line, true)) + expect(t, ringIntersectsLine(ring, line, false)) + }) + t.Run("19", func(t *testing.T) { + line := L(P(3.5, 4.5), P(3.5, 3.5), P(5, 3.5), P(5, 2.5)) + expect(t, ringIntersectsLine(ring, line, true)) + expect(t, !ringIntersectsLine(ring, line, false)) + }) + t.Run("20", func(t *testing.T) { + line := L(P(4, 5), P(4, 4), P(5, 4), P(5, 3)) + expect(t, !ringIntersectsLine(ring, line, true)) + expect(t, !ringIntersectsLine(ring, line, false)) + }) + t.Run("21", func(t *testing.T) { + line := L(P(1, -1), P(1, -2), P(2, -2), P(2, -3)) + expect(t, !ringIntersectsLine(ring, line, true)) + expect(t, !ringIntersectsLine(ring, line, false)) + }) + }) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/segment.go b/vendor/github.com/tidwall/geojson/geometry/segment.go new file mode 100644 index 00000000..1153e7a3 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/segment.go @@ -0,0 +1,235 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "math" +) + +// Segment is a two point line +type Segment struct { + A, B Point +} + +// Move a segment by delta +func (seg Segment) Move(deltaX, deltaY float64) Segment { + return Segment{ + A: Point{X: seg.A.X + deltaX, Y: seg.A.Y + deltaY}, + B: Point{X: seg.B.X + deltaX, Y: seg.B.Y + deltaY}, + } +} + +// Rect is the outer boundaries of the segment. +func (seg Segment) Rect() Rect { + var rect Rect + rect.Min = seg.A + rect.Max = seg.B + if rect.Min.X > rect.Max.X { + rect.Min.X, rect.Max.X = rect.Max.X, rect.Min.X + } + if rect.Min.Y > rect.Max.Y { + rect.Min.Y, rect.Max.Y = rect.Max.Y, rect.Min.Y + } + return rect +} + +// CollinearPoint ... +func (seg Segment) CollinearPoint(point Point) bool { + cmpx, cmpy := point.X-seg.A.X, point.Y-seg.A.Y + rx, ry := seg.B.X-seg.A.X, seg.B.Y-seg.A.Y + cmpxr := cmpx*ry - cmpy*rx + return cmpxr == 0 +} + +// ContainsPoint ... +func (seg Segment) ContainsPoint(point Point) bool { + return seg.Raycast(point).On +} + +// // Angle ... +// func (seg Segment) Angle() float64 { +// return math.Atan2(seg.B.Y-seg.A.Y, seg.B.X-seg.A.X) +// } + +// RaycastResult holds the results of the Raycast operation +type RaycastResult struct { + In bool // point on the left + On bool // point is directly on top of +} + +// Raycast performs the raycast operation +func (seg Segment) Raycast(point Point) RaycastResult { + + p, a, b := point, seg.A, seg.B + // make sure that the point is inside the segment bounds + if a.Y < b.Y && (p.Y < a.Y || p.Y > b.Y) { + return RaycastResult{false, false} + } else if a.Y > b.Y && (p.Y < b.Y || p.Y > a.Y) { + return RaycastResult{false, false} + } + + // test if point is in on the segment + if a.Y == b.Y { + if a.X == b.X { + if p == a { + return RaycastResult{false, true} + } + return RaycastResult{false, false} + } + if p.Y == b.Y { + // horizontal segment + // check if the point in on the line + if a.X < b.X { + if p.X >= a.X && p.X <= b.X { + return RaycastResult{false, true} + } + } else { + if p.X >= b.X && p.X <= a.X { + return RaycastResult{false, true} + } + } + } + } + if a.X == b.X && p.X == b.X { + // vertical segment + // check if the point in on the line + if a.Y < b.Y { + if p.Y >= a.Y && p.Y <= b.Y { + return RaycastResult{false, true} + } + } else { + if p.Y >= b.Y && p.Y <= a.Y { + return RaycastResult{false, true} + } + } + } + if (p.X-a.X)/(b.X-a.X) == (p.Y-a.Y)/(b.Y-a.Y) { + return RaycastResult{false, true} + } + + // do the actual raycast here. + for p.Y == a.Y || p.Y == b.Y { + p.Y = math.Nextafter(p.Y, math.Inf(1)) + } + if a.Y < b.Y { + if p.Y < a.Y || p.Y > b.Y { + return RaycastResult{false, false} + } + } else { + if p.Y < b.Y || p.Y > a.Y { + return RaycastResult{false, false} + } + } + if a.X > b.X { + if p.X >= a.X { + return RaycastResult{false, false} + } + if p.X <= b.X { + return RaycastResult{true, false} + } + } else { + if p.X >= b.X { + return RaycastResult{false, false} + } + if p.X <= a.X { + return RaycastResult{true, false} + } + } + if a.Y < b.Y { + if (p.Y-a.Y)/(p.X-a.X) >= (b.Y-a.Y)/(b.X-a.X) { + return RaycastResult{true, false} + } + } else { + if (p.Y-b.Y)/(p.X-b.X) >= (a.Y-b.Y)/(a.X-b.X) { + return RaycastResult{true, false} + } + } + return RaycastResult{false, false} +} + +// IntersectsSegment detects if segment intersects with other segement +func (seg Segment) IntersectsSegment(other Segment) bool { + a, b, c, d := seg.A, seg.B, other.A, other.B + // do the bounding boxes intersect? + if a.Y > b.Y { + if c.Y > d.Y { + if b.Y > c.Y || a.Y < d.Y { + return false + } + } else { + if b.Y > d.Y || a.Y < c.Y { + return false + } + } + } else { + if c.Y > d.Y { + if a.Y > c.Y || b.Y < d.Y { + return false + } + } else { + if a.Y > d.Y || b.Y < c.Y { + return false + } + } + } + if a.X > b.X { + if c.X > d.X { + if b.X > c.X || a.X < d.X { + return false + } + } else { + if b.X > d.X || a.X < c.X { + return false + } + } + } else { + if c.X > d.X { + if a.X > c.X || b.X < d.X { + return false + } + } else { + if a.X > d.X || b.X < c.X { + return false + } + } + } + if seg.A == other.A || seg.A == other.B || + seg.B == other.A || seg.B == other.B { + return true + } + + // the following code is from http://ideone.com/PnPJgb + cmpx, cmpy := c.X-a.X, c.Y-a.Y + rx, ry := b.X-a.X, b.Y-a.Y + cmpxr := cmpx*ry - cmpy*rx + if cmpxr == 0 { + // Lines are collinear, and so intersect if they have any overlap + if !(((c.X-a.X <= 0) != (c.X-b.X <= 0)) || + ((c.Y-a.Y <= 0) != (c.Y-b.Y <= 0))) { + return seg.Raycast(other.A).On || seg.Raycast(other.B).On + //return false + } + return true + } + sx, sy := d.X-c.X, d.Y-c.Y + cmpxs := cmpx*sy - cmpy*sx + rxs := rx*sy - ry*sx + if rxs == 0 { + return false // segments are parallel. + } + rxsr := 1 / rxs + t := cmpxs * rxsr + u := cmpxr * rxsr + if !((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) { + return false + } + return true + +} + +// ContainsSegment returns true if segment contains other segment +func (seg Segment) ContainsSegment(other Segment) bool { + return seg.Raycast(other.A).On && seg.Raycast(other.B).On +} diff --git a/vendor/github.com/tidwall/geojson/geometry/segment_test.go b/vendor/github.com/tidwall/geojson/geometry/segment_test.go new file mode 100644 index 00000000..23f1fe21 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/segment_test.go @@ -0,0 +1,510 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "fmt" + "strings" + "testing" +) + +func TestSegmentRaycast(t *testing.T) { + // This is full coverage raycast test. It uses a 7x7 grid where the each + // point is checked for a total of 49 tests per segment. There are 16 + // segments at 0º,30º,45º,90º,180º in both directions for a total of 784 + // tests. + segs := []interface{}{ + S(1, 1, 5, 5), "A", + S(5, 5, 1, 1), "B", + [7]string{ + "-------", + "-----*-", + "++++*--", + "+++*---", + "++*----", + "+*-----", + "-------", + }, + S(1, 5, 5, 1), "C", + S(5, 1, 1, 5), "D", + [7]string{ + "-------", + "-*-----", + "++*----", + "+++*---", + "++++*--", + "+++++*-", + "-------", + }, + S(1, 3, 5, 3), "E", + S(5, 3, 1, 3), "F", + [7]string{ + "-------", + "-------", + "-------", + "-*****-", + "-------", + "-------", + "-------", + }, + S(3, 5, 3, 1), "G", + S(3, 1, 3, 5), "H", + [7]string{ + "-------", + "---*---", + "+++*---", + "+++*---", + "+++*---", + "+++*---", + "-------", + }, + S(1, 2, 5, 4), "I", + S(5, 4, 1, 2), "J", + [7]string{ + "-------", + "-------", + "-----*-", + "+++*---", + "+*-----", + "-------", + "-------", + }, + S(1, 4, 5, 2), "K", + S(5, 2, 1, 4), "L", + [7]string{ + "-------", + "-------", + "-*-----", + "+++*---", + "+++++*-", + "-------", + "-------", + }, + S(2, 1, 4, 5), "M", + S(4, 5, 2, 1), "N", + [7]string{ + "-------", + "----*--", + "++++---", + "+++*---", + "+++----", + "++*----", + "-------", + }, + S(2, 5, 4, 1), "O", + S(4, 1, 2, 5), "P", + [7]string{ + "-------", + "--*----", + "+++----", + "+++*---", + "++++---", + "++++*--", + "-------", + }, + S(3, 3, 3, 3), "Q", + S(3, 3, 3, 3), "R", + [7]string{ + "-------", + "-------", + "-------", + "---*---", + "-------", + "-------", + "-------", + }, + } + + var ms string + for i := 0; i < len(segs); i += 5 { + segs := []interface{}{ + segs[i], segs[i+1], segs[i+4], + segs[i+2], segs[i+3], segs[i+4], + } + for i := 0; i < len(segs); i += 3 { + seg := segs[i].(Segment) + label := segs[i+1].(string) + grid := segs[i+2].([7]string) + // + var ngrid [7]string + for y, sy := 0, 6; y < 7; y, sy = y+1, sy-1 { + var nline string + for x := 0; x < 7; x++ { + // ch := grid[sy][x] + pt := Point{float64(x), float64(y)} + res := seg.Raycast(pt) + if res.In { + nline += "+" + } else if res.On { + nline += "*" + } else { + nline += "-" + } + } + ngrid[sy] = nline + } + if grid != ngrid { + ms += fmt.Sprintf("MISMATCH (%s) SEGMENT: %v\n", label, seg) + ms += fmt.Sprintf("EXPECTED\n%s\n", strings.Join(grid[:], "\n")) + ms += fmt.Sprintf("GOT\n%s\n", strings.Join(ngrid[:], "\n")) + } + } + } + if ms != "" { + t.Fatalf("\n%s", ms) + } +} + +func TestSegmentContainsPoint(t *testing.T) { + expect(t, S(0, 0, 1, 1).ContainsPoint(P(0, 0))) + expect(t, S(0, 0, 1, 1).ContainsPoint(P(0.5, 0.5))) + expect(t, S(0, 0, 1, 1).ContainsPoint(P(1, 1))) + expect(t, !S(0, 0, 1, 1).ContainsPoint(P(1.1, 1.1))) + expect(t, !S(0, 0, 1, 1).ContainsPoint(P(0.5, 0.6))) + expect(t, !S(0, 0, 1, 1).ContainsPoint(P(-0.1, -0.1))) +} + +func TestSegmentCollinearPoint(t *testing.T) { + expect(t, S(0, 0, 1, 1).CollinearPoint(P(-1, -1))) + expect(t, S(0, 0, 1, 1).CollinearPoint(P(0.5, 0.5))) + expect(t, S(0, 0, 1, 1).CollinearPoint(P(2, 2))) + expect(t, S(1, 1, 0, 0).CollinearPoint(P(-1, -1))) + expect(t, S(1, 1, 0, 0).CollinearPoint(P(0.5, 0.5))) + expect(t, S(1, 1, 0, 0).CollinearPoint(P(2, 2))) + expect(t, S(1, 0, 0, 1).CollinearPoint(P(2, -1))) + expect(t, S(1, 0, 0, 1).CollinearPoint(P(0.5, 0.5))) + expect(t, S(1, 0, 0, 1).CollinearPoint(P(-1, 2))) + expect(t, S(0, 1, 1, 0).CollinearPoint(P(2, -1))) + expect(t, S(0, 1, 1, 0).CollinearPoint(P(0.5, 0.5))) + expect(t, S(0, 1, 1, 0).CollinearPoint(P(-1, 2))) +} + +func TestSegmentContainsSegment(t *testing.T) { + expect(t, S(0, 0, 10, 10).ContainsSegment(S(0, 0, 10, 10))) + expect(t, S(0, 0, 10, 10).ContainsSegment(S(2, 2, 10, 10))) + expect(t, S(0, 0, 10, 10).ContainsSegment(S(2, 2, 8, 8))) + expect(t, !S(0, 0, 10, 10).ContainsSegment(S(-1, -1, 8, 8))) +} + +func TestSegmentIntersectsSegment(t *testing.T) { + vals := []interface{}{ + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `----A----`, 1, + `----A----`, `----A----`, 1, `----A----`, `----*----`, 1, + `----*----`, `----*----`, 1, `----*----`, `----B----`, 1, + `----B----`, `----B----`, 1, `----B----`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `----A----`, 0, + `---------`, `----A----`, 1, `---------`, `----*----`, 0, + `---------`, `----*----`, 1, `---------`, `----B----`, 0, + `----A----`, `----B----`, 1, `----A----`, `---------`, 0, + `----*----`, `---------`, 1, `----*----`, `---------`, 0, + `----B----`, `---------`, 1, `----B----`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `----A----`, `---------`, 1, `----A----`, `---------`, 1, + `----*----`, `----A----`, 1, `----*----`, `---------`, 1, + `----B----`, `----*----`, 1, `----B----`, `----A----`, 1, + `---------`, `----B----`, 1, `---------`, `----*----`, 1, + `---------`, `---------`, 1, `---------`, `----B----`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `----A----`, `---------`, 1, `----A----`, `---------`, 0, + `----*----`, `---------`, 1, `----*----`, `---------`, 0, + `----B----`, `----A----`, 1, `----B----`, `---------`, 0, + `---------`, `----*----`, 1, `---------`, `----A----`, 0, + `---------`, `----B----`, 1, `---------`, `----*----`, 0, + `---------`, `---------`, 1, `---------`, `----B----`, 0, + + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `----A----`, `-----A---`, 0, `----A----`, `---A-----`, 0, + `----*----`, `-----*---`, 0, `----*----`, `---*-----`, 0, + `----B----`, `-----B---`, 0, `----B----`, `---B-----`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---A*B---`, `---A*B---`, 1, `---A*B---`, `----A*B--`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---A*B---`, `-----A*B-`, 1, `---A*B---`, `------A*B`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---A*B---`, `--A*B----`, 1, `---A*B---`, `-A*B-----`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---A*B---`, `--A*B----`, 1, `---A*B---`, `A*B------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---A*B---`, 0, `---------`, `---------`, 0, + `---A*B---`, `---------`, 0, `---A*B---`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---A*B---`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `------A--`, 1, + `-----A---`, `-----A---`, 1, `-----A---`, `-----*---`, 1, + `----*----`, `----*----`, 1, `----*----`, `----B----`, 1, + `---B-----`, `---B-----`, 1, `---B-----`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `--------A`, 0, + `---------`, `-------A-`, 1, `---------`, `-------*-`, 0, + `---------`, `------*--`, 1, `---------`, `------B--`, 0, + `-----A---`, `-----B---`, 1, `-----A---`, `---------`, 0, + `----*----`, `---------`, 1, `----*----`, `---------`, 0, + `---B-----`, `---------`, 1, `---B-----`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `-----A---`, `---------`, 1, `-----A---`, `---------`, 1, + `----*----`, `----A----`, 1, `----*----`, `---------`, 1, + `---B-----`, `---*-----`, 1, `---B-----`, `---A-----`, 1, + `---------`, `--B------`, 1, `---------`, `--*------`, 1, + `---------`, `---------`, 1, `---------`, `-B-------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `-----A---`, `---------`, 0, `-----A---`, `------A--`, 0, + `----*----`, `---------`, 0, `----*----`, `-----*---`, 0, + `---B-----`, `---------`, 0, `---B-----`, `----B----`, 0, + `---------`, `--A------`, 0, `---------`, `---------`, 0, + `---------`, `-*-------`, 0, `---------`, `---------`, 0, + `---------`, `B--------`, 0, `---------`, `---------`, 0, + + `---------`, `---------`, 0, `---------`, `---------`, 1, + `---------`, `---------`, 0, `---------`, `---------`, 1, + `---------`, `---------`, 0, `---------`, `---------`, 1, + `-----A---`, `----A----`, 0, `---A-----`, `---A-----`, 1, + `----*----`, `---*-----`, 0, `----*----`, `----*----`, 1, + `---B-----`, `--B------`, 0, `-----B---`, `-----B---`, 1, + `---------`, `---------`, 0, `---------`, `---------`, 1, + `---------`, `---------`, 0, `---------`, `---------`, 1, + `---------`, `---------`, 0, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `-A-------`, 1, + `---------`, `--A------`, 1, `---------`, `--*------`, 1, + `---A-----`, `---*-----`, 1, `---A-----`, `---B-----`, 1, + `----*----`, `----B----`, 1, `----*----`, `---------`, 1, + `-----B---`, `---------`, 1, `-----B---`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `A--------`, 0, `---------`, `---------`, 1, + `---------`, `-*-------`, 0, `---------`, `---------`, 1, + `---------`, `--B------`, 0, `---------`, `---------`, 1, + `---A-----`, `---------`, 0, `---A-----`, `---------`, 1, + `----*----`, `---------`, 0, `----*----`, `----A----`, 1, + `-----B---`, `---------`, 0, `-----B---`, `-----*---`, 1, + `---------`, `---------`, 0, `---------`, `------B--`, 1, + `---------`, `---------`, 0, `---------`, `---------`, 1, + `---------`, `---------`, 0, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---A-----`, `---------`, 1, `---A-----`, `---------`, 0, + `----*----`, `---------`, 1, `----*----`, `---------`, 0, + `-----B---`, `-----A---`, 1, `-----B---`, `---------`, 0, + `---------`, `------*--`, 1, `---------`, `------A--`, 0, + `---------`, `-------B-`, 1, `---------`, `-------*-`, 0, + `---------`, `---------`, 1, `---------`, `--------B`, 0, + + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---A-----`, `----A----`, 0, `---A-----`, `--A------`, 0, + `----*----`, `-----*---`, 0, `----*----`, `---*-----`, 0, + `-----B---`, `------B--`, 0, `-----B---`, `----B----`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---A-----`, `-----A---`, 1, `---A-----`, `------A--`, 1, + `----*----`, `----*----`, 1, `----*----`, `-----*---`, 1, + `-----B---`, `---B-----`, 1, `-----B---`, `----B----`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---A-----`, `-------A-`, 1, `---A-----`, `--------A`, 0, + `----*----`, `------*--`, 1, `----*----`, `-------*-`, 0, + `-----B---`, `-----B---`, 1, `-----B---`, `------B--`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---A-----`, `-----A---`, 1, `---A-----`, `----A----`, 1, + `----*----`, `----*----`, 1, `----*----`, `---*-----`, 1, + `-----B---`, `---B-----`, 1, `-----B---`, `--B------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---A-----`, `---A-----`, 1, `---A-----`, `--A------`, 0, + `----*----`, `--*------`, 1, `----*----`, `-*-------`, 0, + `-----B---`, `-B-------`, 1, `-----B---`, `B--------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + `---------`, `---------`, 1, `---------`, `---------`, 0, + + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `-----A---`, 1, + `---------`, `-----A---`, 1, `---------`, `----*----`, 1, + `---A-----`, `----*----`, 1, `---A-----`, `---B-----`, 1, + `----*----`, `---B-----`, 1, `----*----`, `---------`, 1, + `-----B---`, `---------`, 1, `-----B---`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + `---------`, `---------`, 1, `---------`, `---------`, 1, + + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `-------A-`, 0, `---------`, `---------`, 0, + `---A-----`, `------*--`, 0, `---A-----`, `---------`, 0, + `----*----`, `-----B---`, 0, `----*----`, `---A-----`, 0, + `-----B---`, `---------`, 0, `-----B---`, `--*------`, 0, + `---------`, `---------`, 0, `---------`, `-B-------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + `---------`, `---------`, 0, `---------`, `---------`, 0, + } + + flip := func(seg Segment) Segment { + return Segment{A: seg.B, B: seg.A} + } + + var ms string + var ncol = 3 + for i, k := 0, 0; i < len(vals); k++ { + var grids [2][9]string + for j := 0; j < 9; j++ { + grids[0][j] = vals[i+ncol*2*j].(string) + } + for j := 0; j < 9; j++ { + grids[1][j] = vals[i+1+ncol*2*j].(string) + } + //label := "?" //vals[i+2].(string) + expect := vals[i+2].(int) != 0 + + var segs [2]Segment + for j := 0; j < 2; j++ { + for y := 0; y < 9; y++ { + for x := 0; x < 9; x++ { + ch := grids[j][8-y][x] + if ch == 'A' { + segs[j].A.X = float64(x) + segs[j].A.Y = float64(y) + } else if ch == 'B' { + segs[j].B.X = float64(x) + segs[j].B.Y = float64(y) + } + } + } + } + + tests := [][2]Segment{ + [2]Segment{segs[0], segs[1]}, + [2]Segment{flip(segs[0]), segs[1]}, + [2]Segment{segs[0], flip(segs[1])}, + [2]Segment{flip(segs[0]), flip(segs[1])}, + } + for j := 0; j < len(tests); j++ { + if tests[j][0].IntersectsSegment(tests[j][1]) != expect { + ms += fmt.Sprintf("MISMATCH SEGMENTS: %v (TEST %d)\n", segs, j) + ms += fmt.Sprintf("EXPECTED: %t, GOT: %t\n", expect, !expect) + ms += fmt.Sprintf("GRID 1\n%s\n", strings.Join(grids[0][:], "\n")) + ms += fmt.Sprintf("GRID 2\n%s\n", strings.Join(grids[1][:], "\n")) + } + } + // move to next block + if k%2 == 1 { + i += ncol*2*9 - ncol + } else { + i += ncol + } + } + + if ms != "" { + t.Fatalf("\n%s", ms) + } +} + +func TestSegmentMove(t *testing.T) { + expect(t, S(10, 11, 12, 13).Move(10, 20) == S(20, 31, 22, 33)) +} + +func TestSegmentRect(t *testing.T) { + expect(t, S(12, 13, 11, 12).Rect() == R(11, 12, 12, 13)) +} diff --git a/vendor/github.com/tidwall/geojson/geometry/series.go b/vendor/github.com/tidwall/geojson/geometry/series.go new file mode 100644 index 00000000..24e99ea5 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/series.go @@ -0,0 +1,279 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import "github.com/tidwall/boxtree/d2" + +// DefaultIndex are the minumum number of points required before it makes +// sense to index the segments. +// 64 seems to be the sweet spot +const DefaultIndex = 64 + +// Series is just a series of points with utilities for efficiently accessing +// segments from rectangle queries, making stuff like point-in-polygon lookups +// very quick. +type Series interface { + Rect() Rect + Empty() bool + Convex() bool + Clockwise() bool + NumPoints() int + NumSegments() int + PointAt(index int) Point + SegmentAt(index int) Segment + Search(rect Rect, iter func(seg Segment, index int) bool) +} + +func seriesCopyPoints(series Series) []Point { + points := make([]Point, series.NumPoints()) + for i := 0; i < len(points); i++ { + points[i] = series.PointAt(i) + } + return points +} + +// baseSeries is a concrete type containing all that is needed to make a Series. +type baseSeries struct { + closed bool // points create a closed shape + clockwise bool // points move clockwise + convex bool // points create a convex shape + rect Rect // minumum bounding rectangle + points []Point // original points + tree *d2.BoxTree // segment tree +} + +// makeSeries returns a processed baseSeries. +func makeSeries(points []Point, copyPoints, closed bool, index int) baseSeries { + var series baseSeries + series.closed = closed + if copyPoints { + series.points = make([]Point, len(points)) + copy(series.points, points) + } else { + series.points = points + } + if index != 0 && len(points) >= int(index) { + series.tree = new(d2.BoxTree) + } + series.convex, series.rect, series.clockwise = + processPoints(points, closed, series.tree) + return series +} + +// Clockwise ... +func (series *baseSeries) Clockwise() bool { + return series.clockwise +} + +func (series *baseSeries) Move(deltaX, deltaY float64) Series { + points := make([]Point, len(series.points)) + for i := 0; i < len(series.points); i++ { + points[i].X = series.points[i].X + deltaX + points[i].Y = series.points[i].Y + deltaY + } + nseries := makeSeries(points, false, series.closed, 0) + if series.tree != nil { + nseries.buildTree() + } + return &nseries +} + +// Empty returns true if the series does not take up space. +func (series *baseSeries) Empty() bool { + if series == nil { + return true + } + return (series.closed && len(series.points) < 3) || len(series.points) < 2 +} + +// Rect returns the series rectangle +func (series *baseSeries) Rect() Rect { + return series.rect +} + +// Convex returns true if the points create a convex loop or linestring +func (series *baseSeries) Convex() bool { + return series.convex +} + +// Closed return true if the shape is closed +func (series *baseSeries) Closed() bool { + return series.closed +} + +// NumPoints returns the number of points in the series +func (series *baseSeries) NumPoints() int { + return len(series.points) +} + +// PointAt returns the point at index +func (series *baseSeries) PointAt(index int) Point { + return series.points[index] +} + +// Search finds a searches for segments that intersect the provided rectangle +func (series *baseSeries) Search(rect Rect, iter func(seg Segment, idx int) bool) { + if series.tree == nil { + n := series.NumSegments() + for i := 0; i < n; i++ { + seg := series.SegmentAt(i) + if seg.Rect().IntersectsRect(rect) { + if !iter(seg, i) { + return + } + } + } + } else { + series.tree.Search( + []float64{rect.Min.X, rect.Min.Y}, + []float64{rect.Max.X, rect.Max.Y}, + func(_, _ []float64, value interface{}) bool { + index := value.(int) + var seg Segment + seg.A = series.points[index] + if series.closed && index == len(series.points)-1 { + seg.B = series.points[0] + } else { + seg.B = series.points[index+1] + } + if !iter(seg, index) { + return false + } + return true + }, + ) + } +} + +// NumSegments ... +func (series *baseSeries) NumSegments() int { + if series.closed { + if len(series.points) < 3 { + return 0 + } + if series.points[len(series.points)-1] == series.points[0] { + return len(series.points) - 1 + } + return len(series.points) + } + if len(series.points) < 2 { + return 0 + } + return len(series.points) - 1 +} + +// SegmentAt ... +func (series *baseSeries) SegmentAt(index int) Segment { + var seg Segment + seg.A = series.points[index] + if index == len(series.points)-1 { + seg.B = series.points[0] + } else { + seg.B = series.points[index+1] + } + return seg +} + +func (series *baseSeries) buildTree() { + if series.tree == nil { + series.tree = new(d2.BoxTree) + processPoints(series.points, series.closed, series.tree) + } +} + +// processPoints tests if the ring is convex, calculates the outer +// rectangle, and inserts segments into a boxtree in one pass. +func processPoints(points []Point, closed bool, tree *d2.BoxTree) ( + convex bool, rect Rect, clockwise bool, +) { + if (closed && len(points) < 3) || len(points) < 2 { + return + } + var concave bool + var dir int + var a, b, c Point + var segCount int + var cwc float64 + if closed { + segCount = len(points) + } else { + segCount = len(points) - 1 + } + + for i := 0; i < len(points); i++ { + // process the segments for tree insertion + if tree != nil && i < segCount { + var seg Segment + seg.A = points[i] + if closed && i == len(points)-1 { + if seg.A == points[0] { + break + } + seg.B = points[0] + } else { + seg.B = points[i+1] + } + rect := seg.Rect() + tree.Insert( + []float64{rect.Min.X, rect.Min.Y}, + []float64{rect.Max.X, rect.Max.Y}, i) + } + + // process the rectangle inflation + if i == 0 { + rect = Rect{points[i], points[i]} + } else { + if points[i].X < rect.Min.X { + rect.Min.X = points[i].X + } else if points[i].X > rect.Max.X { + rect.Max.X = points[i].X + } + if points[i].Y < rect.Min.Y { + rect.Min.Y = points[i].Y + } else if points[i].Y > rect.Max.Y { + rect.Max.Y = points[i].Y + } + } + + // gather some point positions for concave and clockwise detection + a = points[i] + if i == len(points)-1 { + b = points[0] + c = points[1] + } else if i == len(points)-2 { + b = points[i+1] + c = points[0] + } else { + b = points[i+1] + c = points[i+2] + } + + // process the clockwise detection + cwc += (b.X - a.X) * (b.Y + a.Y) + + // process the convex calculation + if concave { + continue + } + + zCrossProduct := (b.X-a.X)*(c.Y-b.Y) - (b.Y-a.Y)*(c.X-b.X) + if dir == 0 { + if zCrossProduct < 0 { + dir = -1 + } else if zCrossProduct > 0 { + dir = 1 + } + } else if zCrossProduct < 0 { + if dir == 1 { + concave = true + } + } else if zCrossProduct > 0 { + if dir == -1 { + concave = true + } + } + } + return !concave, rect, cwc > 0 +} diff --git a/vendor/github.com/tidwall/geojson/geometry/series_test.go b/vendor/github.com/tidwall/geojson/geometry/series_test.go new file mode 100644 index 00000000..fe9989d2 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/series_test.go @@ -0,0 +1,382 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +import ( + "reflect" + "testing" +) + +func seriesForEachSegment(ring Ring, iter func(seg Segment) bool) { + n := ring.NumSegments() + for i := 0; i < n; i++ { + if !iter(ring.SegmentAt(i)) { + return + } + } +} + +func seriesForEachPoint(ring Ring, iter func(point Point) bool) { + n := ring.NumPoints() + for i := 0; i < n; i++ { + if !iter(ring.PointAt(i)) { + return + } + } +} + +func TestSeriesEmpty(t *testing.T) { + var series *baseSeries + expect(t, series.Empty()) + series2 := makeSeries(nil, false, false, 0) + expect(t, series2.Empty()) +} + +func TestSeriesClockwise(t *testing.T) { + var series baseSeries + series = makeSeries([]Point{ + P(0, 0), P(10, 0), P(10, 10), P(0, 10), P(0, 0), + }, true, true, DefaultIndex) + expect(t, !series.Clockwise()) + series = makeSeries([]Point{ + P(0, 0), P(10, 0), P(10, 10), P(0, 10), + }, true, true, DefaultIndex) + expect(t, !series.Clockwise()) + series = makeSeries([]Point{ + P(0, 0), P(10, 0), P(10, 10), + }, true, true, DefaultIndex) + expect(t, !series.Clockwise()) + series = makeSeries([]Point{ + P(0, 0), P(0, 10), P(10, 10), P(10, 0), P(0, 0), + }, true, true, DefaultIndex) + expect(t, series.Clockwise()) + series = makeSeries([]Point{ + P(0, 0), P(0, 10), P(10, 10), P(10, 0), + }, true, true, DefaultIndex) + expect(t, series.Clockwise()) + series = makeSeries([]Point{ + P(0, 0), P(0, 10), P(10, 10), + }, true, true, DefaultIndex) + expect(t, series.Clockwise()) +} + +func TestSeriesConvex(t *testing.T) { + t.Run("1", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(0, 4), P(0, 0), + }, true, true, DefaultIndex) + expect(t, series.Convex()) + }) + t.Run("2", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(3, 4), P(1, 4), P(0, 4), P(0, 0), + }, true, true, DefaultIndex) + expect(t, series.Convex()) + }) + t.Run("3", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(2, 5), P(0, 4), P(0, 0), + }, true, true, DefaultIndex) + expect(t, series.Convex()) + }) + t.Run("4", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), + P(3, 4), P(2, 5), P(1, 4), + P(0, 4), P(0, 0), + }, true, true, DefaultIndex) + expect(t, !series.Convex()) + }) + t.Run("5", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), + P(3, 4), P(2, 3), P(1, 4), + P(0, 4), P(0, 0), + }, true, true, DefaultIndex) + expect(t, !series.Convex()) + }) + t.Run("5", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(0, 4), + P(-1, 2), P(0, 0), + }, true, true, DefaultIndex) + expect(t, series.Convex()) + }) + t.Run("6", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(0, 4), P(0, 3), + P(-1, 2), P(0, 0), + }, true, true, DefaultIndex) + expect(t, !series.Convex()) + }) + t.Run("6", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), P(0, 4), + P(-1, 2), P(0, 1), P(0, 0), + }, true, true, DefaultIndex) + expect(t, !series.Convex()) + }) + t.Run("7", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(4, 0), P(4, 4), + P(3, 3), P(2, 5), P(1, 3), P(0, 4), + P(0, 0), + }, true, true, DefaultIndex) + expect(t, !series.Convex()) + }) + t.Run("8", func(t *testing.T) { + series := makeSeries([]Point{ + P(0, 0), P(0, 4), P(1, 3), P(4, 4), P(2, 5), P(3, 3), P(4, 0), + P(0, 0), + }, true, true, DefaultIndex) + expect(t, !series.Convex()) + }) +} + +func testScanSeries( + t *testing.T, series *baseSeries, index bool, + expectSegmentCount int, expectedEmpty bool, +) []Segment { + t.Helper() + if index { + series.buildTree() + } else { + series.tree = nil + } + var segs1 []Segment + seriesForEachSegment(series, func(seg Segment) bool { + segs1 = append(segs1, seg) + return true + }) + var segs2Count int + segs2 := make([]Segment, len(segs1)) + series.Search(series.Rect(), func(seg Segment, idx int) bool { + segs2[idx] = seg + segs2Count++ + return true + }) + expect(t, segs2Count == len(segs2)) + expect(t, len(segs1) == len(segs2)) + if len(segs1) != 0 { + expect(t, reflect.DeepEqual(segs1, segs2)) + } + expect(t, expectSegmentCount == len(segs1)) + expect(t, series.Empty() == expectedEmpty) + return segs1 +} + +func TestSeriesBasic(t *testing.T) { + series := makeSeries(octagon, true, true, DefaultIndex) + expect(t, reflect.DeepEqual(seriesCopyPoints(&series), octagon)) + expect(t, series.Convex()) + expect(t, series.Rect() == R(0, 0, 10, 10)) + expect(t, series.Closed()) + series = makeSeries(octagon, false, true, DefaultIndex) + expect(t, reflect.DeepEqual(seriesCopyPoints(&series), octagon)) + + series = makeSeries(ri, true, true, DefaultIndex) + testScanSeries(t, &series, true, len(ri)-1, false) + testScanSeries(t, &series, false, len(ri)-1, false) + + // small lines + series = makeSeries([]Point{}, true, false, DefaultIndex) + testScanSeries(t, &series, true, 0, true) + testScanSeries(t, &series, false, 0, true) + + series = makeSeries([]Point{P(5, 5)}, true, false, DefaultIndex) + testScanSeries(t, &series, true, 0, true) + testScanSeries(t, &series, false, 0, true) + + series = makeSeries([]Point{P(5, 5), P(10, 10)}, true, false, DefaultIndex) + testScanSeries(t, &series, true, 1, false) + testScanSeries(t, &series, false, 1, false) + + // small rings + series = makeSeries([]Point{}, true, true, DefaultIndex) + testScanSeries(t, &series, true, 0, true) + testScanSeries(t, &series, false, 0, true) + + series = makeSeries([]Point{P(5, 5)}, true, true, DefaultIndex) + testScanSeries(t, &series, true, 0, true) + testScanSeries(t, &series, false, 0, true) + + series = makeSeries([]Point{P(5, 5), P(10, 10)}, true, true, DefaultIndex) + testScanSeries(t, &series, true, 0, true) + testScanSeries(t, &series, false, 0, true) + + series = makeSeries([]Point{P(5, 5), P(10, 10), P(10, 5)}, true, true, DefaultIndex) + testScanSeries(t, &series, true, 3, false) + testScanSeries(t, &series, false, 3, false) + +} +func TestSeriesSearch(t *testing.T) { + series := makeSeries(octagon, true, true, DefaultIndex) + var segs []Segment + series.Search(R(0, 0, 0, 0), func(seg Segment, _ int) bool { + segs = append(segs, seg) + return true + }) + segsExpect := []Segment{ + S(0, 3, 3, 0), + } + expect(t, checkSegsDups(segsExpect, segs)) + segs = nil + series.Search(R(0, 0, 0, 10), func(seg Segment, _ int) bool { + segs = append(segs, seg) + return true + }) + segsExpect = []Segment{ + S(3, 10, 0, 7), + S(0, 7, 0, 3), + S(0, 3, 3, 0), + } + expect(t, checkSegsDups(segsExpect, segs)) + segs = nil + series.Search(R(0, 0, 5, 10), func(seg Segment, _ int) bool { + segs = append(segs, seg) + return true + }) + segsExpect = []Segment{ + S(3, 0, 7, 0), + S(7, 10, 3, 10), + S(3, 10, 0, 7), + S(0, 7, 0, 3), + S(0, 3, 3, 0), + } + expect(t, checkSegsDups(segsExpect, segs)) + var seg2sA []Segment + series.Search(R(0, 0, 10, 10), func(seg Segment, idx int) bool { + seg2sA = append(seg2sA, seg) + return true + }) + + var seg2sB []Segment + seriesForEachSegment(&series, func(seg Segment) bool { + seg2sB = append(seg2sB, seg) + return true + }) + + expect(t, checkSegsDups(seg2sA, seg2sB)) + + var first Segment + var once bool + series.Search(R(0, 0, 10, 10), func(seg Segment, idx int) bool { + expect(t, !once) + first = seg + once = true + return false + }) + expect(t, first == seg2sA[0]) + +} +func TestSeriesBig(t *testing.T) { + t.Run("Closed", func(t *testing.T) { + // clip off the last point to force an auto closure + series := makeSeries(ri[:len(ri)-1], true, true, DefaultIndex) + var seg2sA []Segment + series.Search(series.Rect(), func(seg Segment, idx int) bool { + seg2sA = append(seg2sA, seg) + return true + }) + var seg2sB []Segment + seriesForEachSegment(&series, func(seg Segment) bool { + seg2sB = append(seg2sB, seg) + return true + }) + + expect(t, checkSegsDups(seg2sA, seg2sB)) + + // use all points + series2 := makeSeries(ri, true, true, DefaultIndex) + var seg2sC []Segment + seriesForEachSegment(&series2, func(seg Segment) bool { + seg2sC = append(seg2sC, seg) + return true + }) + expect(t, checkSegsDups(seg2sA, seg2sC)) + + var first Segment + var once bool + series.Search(series.Rect(), func(seg Segment, idx int) bool { + expect(t, !once) + first = seg + once = true + return false + }) + expect(t, first == seg2sA[0]) + }) + t.Run("Opened", func(t *testing.T) { + series := makeSeries(az, true, false, DefaultIndex) + var seg2sA []Segment + series.Search(series.Rect(), func(seg Segment, idx int) bool { + seg2sA = append(seg2sA, seg) + return true + }) + var seg2sB []Segment + seriesForEachSegment(&series, func(seg Segment) bool { + seg2sB = append(seg2sB, seg) + return true + }) + expect(t, checkSegsDups(seg2sA, seg2sB)) + }) +} +func TestSeriesReverse(t *testing.T) { + shapes := [][]Point{octagon, concave1, concave2, concave3, concave4} + for _, shape := range shapes { + var rev []Point + for i := len(shape) - 1; i >= 0; i-- { + rev = append(rev, shape[i]) + } + series := makeSeries(rev, true, true, DefaultIndex) + var seg2sA []Segment + series.Search(series.Rect(), func(seg Segment, idx int) bool { + seg2sA = append(seg2sA, seg) + return true + }) + var seg2sB []Segment + seriesForEachSegment(&series, func(seg Segment) bool { + seg2sB = append(seg2sB, seg) + return true + }) + } +} + +func checkSegsDups(a, b []Segment) bool { + if len(a) != len(b) { + return false + } + for _, a := range a { + var found bool + for _, b := range b { + if a == b { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +func TestSeriesMove(t *testing.T) { + shapes := [][]Point{ri, octagon} + for _, shape := range shapes { + series := makeSeries(shape, true, true, DefaultIndex) + series.tree = nil + series2 := series.Move(60, 70) + expect(t, series2.NumPoints() == len(shape)) + for i := 0; i < len(shape); i++ { + expect(t, series2.PointAt(i) == shape[i].Move(60, 70)) + } + series.buildTree() + series2 = series.Move(60, 70) + expect(t, series2.NumPoints() == len(shape)) + for i := 0; i < len(shape); i++ { + expect(t, series2.PointAt(i) == shape[i].Move(60, 70)) + } + } +} diff --git a/vendor/github.com/tidwall/geojson/geometry/states_test.go b/vendor/github.com/tidwall/geojson/geometry/states_test.go new file mode 100644 index 00000000..b4517d39 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometry/states_test.go @@ -0,0 +1,937 @@ +// Copyright 2018 Joshua J Baker. All rights reserved. +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package geometry + +// https://github.com/unitedstates/districts + +// func stringifyPoints(name string, points []Point) string { +// out := "var " + name + " = []Point{\n" +// for i := 0; i < len(points); i++ { +// if i == 0 { +// out += "\t" +// } + +// out += fmt.Sprintf("{%.6f, %.6f}", points[i].X, points[i].Y) +// if i == len(az)-1 { +// out += ",\n" +// } else { +// if i%16 < 15 { +// out += ", " +// } else { +// out += ",\n\t" +// } +// } +// } +// out += "}\n" +// return out +// } + +// func TestBBB(t *testing.T) { +// f, _ := os.Create("out.txt") +// f.WriteString(stringifyPoints("az", az)) +// f.WriteString(stringifyPoints("tx", tx)) +// f.WriteString(stringifyPoints("ri", ri)) + +// } + +var az = []Point{ + {-114.635458, 34.876902}, {-114.636768, 34.885705}, {-114.636725, 34.889107}, {-114.635425, 34.895192}, {-114.631850, 34.903942}, {-114.630877, 34.907263}, {-114.630552, 34.911852}, {-114.631537, 34.916153}, {-114.633237, 34.921230}, {-114.633253, 34.924608}, {-114.632196, 34.930628}, {-114.629753, 34.938684}, {-114.629811, 34.944810}, {-114.631681, 34.951310}, {-114.634274, 34.956662}, {-114.634953, 34.958918}, + {-114.635237, 34.965149}, {-114.634607, 34.969060}, {-114.629907, 34.980791}, {-114.629129, 34.986132}, {-114.629443, 34.991825}, {-114.630244, 34.994640}, {-114.631807, 34.998632}, {-114.632665, 34.999806}, {-114.635570, 35.005933}, {-114.637071, 35.010371}, {-114.637769, 35.014948}, {-114.638190, 35.022069}, {-114.637524, 35.027053}, {-114.633715, 35.035602}, {-114.629027, 35.042531}, {-114.625799, 35.045834}, + {-114.615902, 35.052720}, {-114.610701, 35.055458}, {-114.606694, 35.058941}, {-114.604715, 35.061744}, {-114.603619, 35.064226}, {-114.602908, 35.068588}, {-114.603175, 35.070445}, {-114.604736, 35.074830}, {-114.607701, 35.078533}, {-114.613132, 35.083097}, {-114.618420, 35.086539}, {-114.622517, 35.088703}, {-114.632053, 35.092559}, {-114.639370, 35.094733}, {-114.642831, 35.096503}, {-114.646579, 35.100820}, + {-114.646764, 35.101868}, {-114.645152, 35.104995}, {-114.644354, 35.105903}, {-114.641116, 35.108401}, {-114.637432, 35.112489}, {-114.632282, 35.117088}, {-114.628427, 35.118943}, {-114.623761, 35.120602}, {-114.628993, 35.119411}, {-114.624954, 35.120742}, {-114.618697, 35.121749}, {-114.604007, 35.121252}, {-114.602740, 35.121666}, {-114.597794, 35.121735}, {-114.589787, 35.123522}, {-114.584877, 35.125194}, + {-114.579882, 35.127506}, {-114.578263, 35.128810}, {-114.577146, 35.130982}, {-114.574411, 35.134950}, {-114.572597, 35.139557}, {-114.573706, 35.142698}, {-114.573879, 35.145351}, {-114.569529, 35.162317}, {-114.568760, 35.172195}, {-114.569214, 35.172890}, {-114.568989, 35.175085}, {-114.569258, 35.183424}, {-114.569653, 35.186267}, {-114.571404, 35.191026}, {-114.572084, 35.200794}, {-114.574037, 35.203790}, + {-114.574233, 35.205481}, {-114.574958, 35.206714}, {-114.578581, 35.208113}, {-114.579535, 35.208911}, {-114.579897, 35.210970}, {-114.580312, 35.220095}, {-114.583523, 35.230348}, {-114.582480, 35.233173}, {-114.582842, 35.238703}, {-114.584993, 35.242717}, {-114.586053, 35.248891}, {-114.585714, 35.253145}, {-114.585768, 35.257743}, {-114.586604, 35.262386}, {-114.587497, 35.265473}, {-114.590513, 35.272334}, + {-114.593247, 35.284361}, {-114.595705, 35.289939}, {-114.596682, 35.294557}, {-114.597268, 35.299565}, {-114.597210, 35.303223}, {-114.595163, 35.321883}, {-114.595553, 35.326547}, {-114.599771, 35.341110}, {-114.604607, 35.355239}, {-114.606173, 35.359651}, {-114.611206, 35.370119}, {-114.617698, 35.380131}, {-114.618257, 35.382646}, {-114.618984, 35.389391}, {-114.620887, 35.396867}, {-114.621783, 35.399450}, + {-114.625702, 35.407976}, {-114.626765, 35.409644}, {-114.629061, 35.411175}, {-114.652080, 35.430134}, {-114.653817, 35.432853}, {-114.654295, 35.436854}, {-114.658105, 35.441835}, {-114.661747, 35.444735}, {-114.662896, 35.446449}, {-114.663934, 35.449466}, {-114.664215, 35.451707}, {-114.663880, 35.454657}, {-114.664217, 35.455845}, {-114.665142, 35.457331}, {-114.666151, 35.458198}, {-114.667217, 35.460370}, + {-114.666769, 35.462085}, {-114.665790, 35.463915}, {-114.665651, 35.466911}, {-114.665988, 35.467985}, {-114.667389, 35.469904}, {-114.672350, 35.473740}, {-114.673164, 35.474814}, {-114.673585, 35.475843}, {-114.673473, 35.476849}, {-114.672074, 35.479709}, {-114.671794, 35.480806}, {-114.671907, 35.482087}, {-114.673534, 35.485675}, {-114.676815, 35.489787}, {-114.676704, 35.491845}, {-114.676257, 35.493103}, + {-114.677743, 35.495182}, {-114.678642, 35.497628}, {-114.678587, 35.499846}, {-114.678892, 35.501276}, {-114.677480, 35.510948}, {-114.677143, 35.512945}, {-114.675685, 35.515630}, {-114.672767, 35.518428}, {-114.669540, 35.520790}, {-114.668586, 35.521225}, {-114.666565, 35.520993}, {-114.664601, 35.521519}, {-114.663983, 35.522161}, {-114.661682, 35.526682}, {-114.659886, 35.527919}, {-114.657753, 35.530741}, + {-114.657163, 35.532301}, {-114.656770, 35.534964}, {-114.657809, 35.536963}, {-114.660335, 35.540433}, {-114.661457, 35.544062}, {-114.661570, 35.545692}, {-114.661120, 35.549021}, {-114.661963, 35.552604}, {-114.661963, 35.555887}, {-114.663451, 35.559884}, {-114.663535, 35.560963}, {-114.662805, 35.564268}, {-114.663900, 35.566290}, {-114.664433, 35.568426}, {-114.666231, 35.571642}, {-114.668393, 35.574331}, + {-114.670022, 35.575596}, {-114.671567, 35.576217}, {-114.674881, 35.578379}, {-114.675751, 35.579459}, {-114.675667, 35.580033}, {-114.670191, 35.583471}, {-114.664209, 35.585944}, {-114.660558, 35.586583}, {-114.659238, 35.587477}, {-114.654518, 35.596609}, {-114.653900, 35.598491}, {-114.653731, 35.600373}, {-114.654489, 35.605173}, {-114.653618, 35.607192}, {-114.653534, 35.609672}, {-114.653927, 35.611739}, + {-114.655219, 35.614059}, {-114.657241, 35.617046}, {-114.659461, 35.619552}, {-114.660641, 35.620334}, {-114.663647, 35.620773}, {-114.665389, 35.621556}, {-114.666682, 35.623073}, {-114.668087, 35.627115}, {-114.669015, 35.628861}, {-114.672134, 35.633365}, {-114.675001, 35.638304}, {-114.677615, 35.641774}, {-114.679415, 35.643429}, {-114.686133, 35.647522}, {-114.689001, 35.650280}, {-114.689507, 35.651429}, + {-114.689226, 35.652898}, {-114.690494, 35.662657}, {-114.690214, 35.665159}, {-114.686055, 35.670642}, {-114.682317, 35.677825}, {-114.680827, 35.682255}, {-114.680631, 35.684046}, {-114.680997, 35.685929}, {-114.682657, 35.688571}, {-114.691263, 35.693125}, {-114.696214, 35.696550}, {-114.701416, 35.701084}, {-114.703608, 35.703922}, {-114.704501, 35.705993}, {-114.704959, 35.706366}, {-114.704842, 35.706744}, + {-114.705597, 35.708274}, {-114.705347, 35.708344}, {-114.705447, 35.711757}, {-114.699405, 35.726929}, {-114.697859, 35.731657}, {-114.696540, 35.738934}, {-114.696400, 35.742653}, {-114.696655, 35.746143}, {-114.697585, 35.748417}, {-114.697726, 35.750966}, {-114.696854, 35.752756}, {-114.696546, 35.754638}, {-114.694267, 35.756633}, {-114.694717, 35.757897}, {-114.697420, 35.760677}, {-114.700266, 35.766879}, + {-114.701027, 35.769680}, {-114.701170, 35.774112}, {-114.699036, 35.788046}, {-114.699318, 35.790480}, {-114.703178, 35.794685}, {-114.705827, 35.798889}, {-114.711490, 35.804380}, {-114.712026, 35.805529}, {-114.710534, 35.807525}, {-114.709324, 35.810050}, {-114.706340, 35.812022}, {-114.703665, 35.814614}, {-114.700654, 35.822004}, {-114.697276, 35.826776}, {-114.695530, 35.829897}, {-114.695277, 35.831091}, + {-114.695249, 35.832285}, {-114.695757, 35.833387}, {-114.701478, 35.839316}, {-114.702293, 35.840792}, {-114.702339, 35.842151}, {-114.703527, 35.841845}, {-114.704173, 35.842669}, {-114.704203, 35.844274}, {-114.706288, 35.846218}, {-114.706532, 35.849027}, {-114.705856, 35.850508}, {-114.703599, 35.852595}, {-114.701904, 35.853223}, {-114.696581, 35.853727}, {-114.694370, 35.854463}, {-114.693446, 35.855125}, + {-114.691456, 35.858661}, {-114.687798, 35.860728}, {-114.682050, 35.862950}, {-114.678186, 35.863311}, {-114.672289, 35.865011}, {-114.669680, 35.865036}, {-114.667471, 35.867061}, {-114.662623, 35.869213}, {-114.661636, 35.870545}, {-114.661636, 35.871233}, {-114.663214, 35.873692}, {-114.668145, 35.875201}, {-114.672009, 35.878018}, {-114.678972, 35.885510}, {-114.693602, 35.895311}, {-114.694540, 35.896587}, + {-114.696064, 35.896464}, {-114.694928, 35.897594}, {-114.696132, 35.898662}, {-114.697558, 35.899360}, {-114.700258, 35.901757}, {-114.700769, 35.903064}, {-114.703538, 35.906707}, {-114.705119, 35.907637}, {-114.705991, 35.908598}, {-114.705714, 35.909316}, {-114.706767, 35.908950}, {-114.708112, 35.909933}, {-114.709187, 35.916827}, {-114.707784, 35.916993}, {-114.707398, 35.918057}, {-114.707880, 35.919207}, + {-114.707329, 35.926177}, {-114.707603, 35.927950}, {-114.712965, 35.932159}, {-114.712756, 35.932639}, {-114.713413, 35.931900}, {-114.713312, 35.933844}, {-114.729762, 35.959895}, {-114.728496, 35.960395}, {-114.728666, 35.961757}, {-114.730090, 35.962691}, {-114.732456, 35.965891}, {-114.736195, 35.969421}, {-114.740536, 35.975545}, {-114.743494, 35.983553}, {-114.743638, 35.985785}, {-114.743117, 35.987387}, + {-114.740043, 35.990534}, {-114.739318, 35.991804}, {-114.740544, 35.994853}, {-114.740815, 35.997464}, {-114.741536, 35.999690}, {-114.741679, 36.002283}, {-114.743163, 36.006722}, {-114.743005, 36.008450}, {-114.740866, 36.012928}, {-114.738555, 36.015223}, {-114.728874, 36.021387}, {-114.723324, 36.026588}, {-114.722214, 36.027964}, {-114.722096, 36.028952}, {-114.722742, 36.030286}, {-114.723673, 36.031230}, + {-114.727602, 36.033099}, {-114.730563, 36.036207}, {-114.733417, 36.037913}, {-114.735739, 36.038033}, {-114.740018, 36.037467}, {-114.741262, 36.038044}, {-114.742105, 36.039792}, {-114.742661, 36.042573}, {-114.742479, 36.045697}, {-114.741677, 36.047877}, {-114.735701, 36.053393}, {-114.735080, 36.054435}, {-114.735285, 36.056648}, {-114.736023, 36.059063}, {-114.740060, 36.062437}, {-114.742200, 36.067833}, + {-114.742138, 36.068676}, {-114.743542, 36.071037}, {-114.748891, 36.074981}, {-114.750570, 36.080330}, {-114.754032, 36.083093}, {-114.754681, 36.085052}, {-114.754508, 36.086171}, {-114.752836, 36.089393}, {-114.750095, 36.092275}, {-114.748913, 36.095183}, {-114.737497, 36.103102}, {-114.734857, 36.104426}, {-114.718257, 36.107164}, {-114.709269, 36.107396}, {-114.706091, 36.108239}, {-114.703737, 36.108348}, + {-114.696981, 36.110297}, {-114.693655, 36.112482}, {-114.691631, 36.112535}, {-114.688074, 36.111457}, {-114.684426, 36.109472}, {-114.681847, 36.109192}, {-114.679775, 36.109874}, {-114.678375, 36.110815}, {-114.675106, 36.114111}, {-114.671867, 36.115964}, {-114.664343, 36.116300}, {-114.662144, 36.117742}, {-114.660448, 36.119999}, {-114.658131, 36.124127}, {-114.655512, 36.126187}, {-114.645728, 36.131995}, + {-114.641976, 36.133730}, {-114.640125, 36.135126}, {-114.636862, 36.135552}, {-114.635809, 36.136170}, {-114.630474, 36.142218}, {-114.628462, 36.141822}, {-114.627079, 36.140761}, {-114.623837, 36.137144}, {-114.620605, 36.131759}, {-114.618429, 36.130328}, {-114.615455, 36.129653}, {-114.613240, 36.130266}, {-114.609288, 36.132229}, {-114.596474, 36.141537}, {-114.593035, 36.142674}, {-114.589828, 36.143192}, + {-114.583716, 36.145560}, {-114.580707, 36.145987}, {-114.578828, 36.147175}, {-114.577060, 36.148845}, {-114.571090, 36.151099}, {-114.561173, 36.150921}, {-114.556162, 36.152470}, {-114.548742, 36.150697}, {-114.543232, 36.151871}, {-114.539233, 36.151764}, {-114.534478, 36.150230}, {-114.532924, 36.149282}, {-114.532308, 36.148040}, {-114.531091, 36.147644}, {-114.526210, 36.148177}, {-114.514280, 36.150795}, + {-114.511218, 36.150576}, {-114.508104, 36.149713}, {-114.501049, 36.144516}, {-114.500236, 36.143226}, {-114.499992, 36.141594}, {-114.500339, 36.140700}, {-114.501080, 36.140060}, {-114.505150, 36.138078}, {-114.507175, 36.136340}, {-114.509210, 36.133247}, {-114.508467, 36.129913}, {-114.507201, 36.128484}, {-114.504715, 36.127188}, {-114.501798, 36.126556}, {-114.498849, 36.126612}, {-114.487635, 36.128656}, + {-114.483827, 36.129720}, {-114.478248, 36.132683}, {-114.468674, 36.138889}, {-114.465579, 36.139496}, {-114.462600, 36.139644}, {-114.458945, 36.139214}, {-114.456487, 36.138032}, {-114.455110, 36.136372}, {-114.453798, 36.133586}, {-114.451331, 36.129831}, {-114.447135, 36.126022}, {-114.445042, 36.125346}, {-114.443736, 36.125593}, {-114.435507, 36.130057}, {-114.423114, 36.137350}, {-114.418193, 36.142771}, + {-114.415253, 36.145123}, {-114.412491, 36.146511}, {-114.409140, 36.147000}, {-114.405624, 36.146983}, {-114.398373, 36.145799}, {-114.381479, 36.141349}, {-114.379976, 36.141388}, {-114.375278, 36.143592}, {-114.373745, 36.143722}, {-114.370181, 36.142624}, {-114.368551, 36.140892}, {-114.367381, 36.138520}, {-114.365529, 36.136306}, {-114.364499, 36.134072}, {-114.358968, 36.127795}, {-114.348592, 36.121147}, + {-114.345100, 36.118556}, {-114.342601, 36.115878}, {-114.340950, 36.113457}, {-114.338815, 36.111309}, {-114.337264, 36.110428}, {-114.334632, 36.106784}, {-114.333587, 36.106342}, {-114.328801, 36.105902}, {-114.325814, 36.103933}, {-114.325539, 36.102989}, {-114.323458, 36.101186}, {-114.320866, 36.096463}, {-114.316983, 36.093409}, {-114.313086, 36.088816}, {-114.306939, 36.082487}, {-114.304171, 36.075580}, + {-114.304384, 36.074019}, {-114.305853, 36.071478}, {-114.307485, 36.069672}, {-114.312420, 36.066117}, {-114.313600, 36.064148}, {-114.314328, 36.062016}, {-114.314427, 36.060523}, {-114.313591, 36.059048}, {-114.311904, 36.057661}, {-114.308624, 36.056976}, {-114.300971, 36.057460}, {-114.298593, 36.057263}, {-114.295941, 36.056168}, {-114.293435, 36.054500}, {-114.290867, 36.050511}, {-114.287992, 36.049070}, + {-114.284006, 36.048242}, {-114.279637, 36.046103}, {-114.278166, 36.045819}, {-114.273911, 36.046529}, {-114.272299, 36.046289}, {-114.270862, 36.045523}, {-114.269548, 36.043769}, {-114.268896, 36.040940}, {-114.269220, 36.036807}, {-114.268586, 36.035034}, {-114.264380, 36.027911}, {-114.262388, 36.026107}, {-114.259518, 36.024206}, {-114.251633, 36.019886}, {-114.248419, 36.018556}, {-114.246111, 36.017164}, + {-114.243865, 36.015266}, {-114.240439, 36.015245}, {-114.238154, 36.014473}, {-114.236892, 36.013247}, {-114.233443, 36.012835}, {-114.231854, 36.013147}, {-114.228015, 36.014731}, {-114.226459, 36.014606}, {-114.224798, 36.013699}, {-114.218759, 36.014511}, {-114.216609, 36.014336}, {-114.214679, 36.014806}, {-114.213549, 36.014615}, {-114.211932, 36.014834}, {-114.206052, 36.016634}, {-114.204156, 36.016575}, + {-114.201227, 36.017751}, {-114.200066, 36.017743}, {-114.191221, 36.020019}, {-114.185860, 36.022266}, {-114.179438, 36.024313}, {-114.176304, 36.026129}, {-114.174683, 36.026670}, {-114.164402, 36.026852}, {-114.161237, 36.026279}, {-114.157344, 36.024966}, {-114.153400, 36.023170}, {-114.151390, 36.023133}, {-114.150225, 36.023515}, {-114.145907, 36.027229}, {-114.145637, 36.028559}, {-114.145672, 36.032970}, + {-114.144666, 36.034272}, {-114.143153, 36.035295}, {-114.138260, 36.037190}, {-114.137112, 36.038491}, {-114.135721, 36.041238}, {-114.134841, 36.043873}, {-114.134824, 36.045343}, {-114.135927, 36.050358}, {-114.136206, 36.053232}, {-114.135200, 36.056946}, {-114.133389, 36.061665}, {-114.129768, 36.068484}, {-114.125891, 36.072935}, {-114.124019, 36.075563}, {-114.121186, 36.082755}, {-114.119648, 36.085822}, + {-114.112297, 36.094050}, {-114.111998, 36.094910}, {-114.111900, 36.095845}, {-114.115208, 36.099878}, {-114.117070, 36.101177}, {-114.119329, 36.101930}, {-114.121033, 36.103885}, {-114.121779, 36.105699}, {-114.121670, 36.108294}, {-114.120865, 36.110850}, {-114.118497, 36.113900}, {-114.116061, 36.115471}, {-114.108381, 36.119154}, {-114.107419, 36.119401}, {-114.100433, 36.119359}, {-114.097707, 36.120213}, + {-114.096994, 36.120823}, {-114.092753, 36.132356}, {-114.092366, 36.135331}, {-114.091701, 36.137303}, {-114.089279, 36.140326}, {-114.087899, 36.142923}, {-114.081234, 36.150208}, {-114.079450, 36.154625}, {-114.078832, 36.157434}, {-114.075641, 36.162523}, {-114.071652, 36.170921}, {-114.066798, 36.179087}, {-114.058662, 36.187835}, {-114.052743, 36.190919}, {-114.049484, 36.192134}, {-114.043944, 36.193350}, + {-114.043849, 36.245114}, {-114.045518, 36.274390}, {-114.045559, 36.288837}, {-114.045033, 36.303050}, {-114.044345, 36.310234}, {-114.044051, 36.317628}, {-114.044776, 36.331969}, {-114.044702, 36.346298}, {-114.043034, 36.385870}, {-114.042843, 36.448175}, {-114.043133, 36.469716}, {-114.044816, 36.491343}, {-114.045647, 36.521095}, {-114.046320, 36.564615}, {-114.049935, 36.709521}, {-114.049973, 36.738672}, + {-114.050327, 36.752899}, {-114.049879, 36.781909}, {-114.050502, 36.895232}, {-114.049995, 36.957769}, {-114.050600, 37.000396}, {-114.000800, 37.000448}, {-113.962660, 36.999973}, {-113.052912, 36.999983}, {-112.875756, 37.000533}, {-112.538546, 37.000652}, {-112.529846, 37.000899}, {-112.361020, 37.001114}, {-112.360370, 37.000912}, {-112.359329, 37.001117}, {-112.125741, 37.001237}, {-112.000735, 37.000959}, + {-111.625720, 37.001401}, {-111.616249, 37.001647}, {-111.406146, 37.001481}, {-111.405895, 37.001702}, {-111.313211, 37.000894}, {-111.312169, 37.001193}, {-111.305843, 37.000776}, {-111.278221, 37.000467}, {-111.254853, 37.001076}, {-111.133718, 37.000779}, {-111.081493, 37.002261}, {-111.052354, 37.002460}, {-111.001820, 37.002293}, {-110.625691, 37.003725}, {-110.625605, 37.003416}, {-110.599512, 37.003448}, + {-110.509004, 37.003985}, {-110.500690, 37.004260}, {-110.490908, 37.003566}, {-110.478446, 36.999996}, {-110.477290, 36.999997}, {-110.470190, 36.997997}, {-110.023043, 36.998601}, {-110.000876, 36.998502}, {-110.000677, 36.997968}, {-109.969958, 36.997949}, {-109.938511, 36.998491}, {-109.750669, 36.998160}, {-109.743284, 36.998453}, {-109.625658, 36.998308}, {-109.495338, 36.999105}, {-109.362565, 36.999304}, + {-109.125691, 36.999389}, {-109.045223, 36.999084}, {-109.045554, 36.645013}, {-109.045390, 36.503241}, {-109.045946, 36.375002}, {-109.045637, 36.374625}, {-109.045744, 36.257214}, {-109.046024, 36.247197}, {-109.045877, 36.188719}, {-109.046183, 36.181751}, {-109.045726, 36.116908}, {-109.045767, 36.033679}, {-109.046124, 35.990618}, {-109.046009, 35.875012}, {-109.046423, 35.624911}, {-109.046181, 35.614569}, + {-109.046795, 35.379918}, {-109.046084, 35.249986}, {-109.046256, 35.125041}, {-109.045842, 34.966076}, {-109.046136, 34.875006}, {-109.046072, 34.828566}, {-109.045626, 34.814226}, {-109.046104, 34.799981}, {-109.045363, 34.785406}, {-109.046087, 34.770963}, {-109.046175, 34.520102}, {-109.046561, 34.379479}, {-109.046337, 34.283639}, {-109.046664, 34.250046}, {-109.046960, 34.068968}, {-109.047006, 34.000050}, + {-109.046426, 33.875052}, {-109.046869, 33.844183}, {-109.047145, 33.740010}, {-109.046662, 33.625055}, {-109.046825, 33.469389}, {-109.047309, 33.462131}, {-109.046928, 33.442800}, {-109.047304, 33.439442}, {-109.047298, 33.409774}, {-109.046564, 33.375059}, {-109.047045, 33.369280}, {-109.046827, 33.365271}, {-109.047104, 33.270460}, {-109.047470, 33.250168}, {-109.047122, 33.240800}, {-109.047324, 33.184080}, + {-109.047208, 33.107377}, {-109.046905, 33.091931}, {-109.047513, 33.059137}, {-109.047382, 33.000311}, {-109.047110, 32.992250}, {-109.047117, 32.777569}, {-109.047518, 32.749997}, {-109.047796, 32.682630}, {-109.047912, 32.500261}, {-109.047629, 32.413987}, {-109.048323, 32.070887}, {-109.048731, 32.028174}, {-109.048465, 32.000089}, {-109.048738, 31.876905}, {-109.049048, 31.870689}, {-109.049298, 31.796742}, + {-109.048990, 31.721922}, {-109.049311, 31.544932}, {-109.050173, 31.480004}, {-109.049934, 31.437907}, {-109.050044, 31.332502}, {-109.125600, 31.332685}, {-109.271744, 31.333942}, {-109.494490, 31.334125}, {-109.500621, 31.333911}, {-109.875628, 31.334050}, {-110.000613, 31.333145}, {-110.140512, 31.333965}, {-110.375635, 31.332896}, {-110.460172, 31.332827}, {-110.681430, 31.333090}, {-110.750638, 31.333636}, + {-110.795467, 31.333630}, {-110.942320, 31.332833}, {-111.000643, 31.332177}, {-111.074825, 31.332239}, {-111.125646, 31.348978}, {-111.129451, 31.349979}, {-111.357436, 31.423346}, {-111.500659, 31.468862}, {-111.560194, 31.488138}, {-111.659998, 31.519448}, {-111.738873, 31.544718}, {-111.875674, 31.587657}, {-111.979304, 31.620648}, {-112.200717, 31.690033}, {-112.365328, 31.741078}, {-112.375759, 31.743987}, + {-112.399254, 31.751638}, {-112.433246, 31.762162}, {-112.737399, 31.855527}, {-112.800213, 31.875070}, {-112.834233, 31.885137}, {-112.871505, 31.896838}, {-113.125961, 31.972780}, {-113.211630, 32.000061}, {-113.211365, 32.000061}, {-113.217307, 32.002106}, {-113.250731, 32.012405}, {-113.493196, 32.088943}, {-113.750756, 32.169005}, {-113.781680, 32.179034}, {-114.250775, 32.323909}, {-114.625785, 32.437890}, + {-114.790245, 32.487505}, {-114.813613, 32.494276}, {-114.813991, 32.497231}, {-114.812316, 32.500054}, {-114.813402, 32.501764}, {-114.813753, 32.504260}, {-114.815185, 32.506023}, {-114.816510, 32.506963}, {-114.816591, 32.507696}, {-114.815591, 32.508612}, {-114.814321, 32.509023}, {-114.812942, 32.509116}, {-114.810159, 32.508383}, {-114.807726, 32.508726}, {-114.804076, 32.510375}, {-114.802833, 32.511749}, + {-114.802211, 32.513191}, {-114.802238, 32.515206}, {-114.803670, 32.516374}, {-114.807753, 32.516925}, {-114.809672, 32.517567}, {-114.810374, 32.518391}, {-114.809969, 32.520291}, {-114.810482, 32.521758}, {-114.810969, 32.522444}, {-114.812888, 32.523590}, {-114.813348, 32.524186}, {-114.812645, 32.525399}, {-114.811293, 32.526429}, {-114.810563, 32.527666}, {-114.808617, 32.529017}, {-114.806400, 32.531191}, + {-114.804858, 32.533689}, {-114.802559, 32.535521}, {-114.802181, 32.536414}, {-114.802018, 32.539460}, {-114.802370, 32.540078}, {-114.804776, 32.541659}, {-114.805966, 32.545346}, {-114.805830, 32.546354}, {-114.803883, 32.548001}, {-114.795635, 32.550956}, {-114.793769, 32.552329}, {-114.792065, 32.555009}, {-114.791551, 32.557023}, {-114.791523, 32.558602}, {-114.792955, 32.562085}, {-114.792088, 32.568497}, + {-114.792358, 32.569091}, {-114.793224, 32.569459}, {-114.794684, 32.568703}, {-114.795253, 32.566620}, {-114.797660, 32.564444}, {-114.801311, 32.562865}, {-114.803664, 32.560689}, {-114.806830, 32.558880}, {-114.808885, 32.558467}, {-114.810318, 32.558628}, {-114.812914, 32.560049}, {-114.813995, 32.562201}, {-114.814212, 32.563690}, {-114.813968, 32.566209}, {-114.812995, 32.568706}, {-114.811480, 32.569781}, + {-114.804421, 32.572941}, {-114.803474, 32.573628}, {-114.801877, 32.576009}, {-114.801471, 32.578255}, {-114.801930, 32.579194}, {-114.803879, 32.580889}, {-114.803987, 32.582652}, {-114.802823, 32.585079}, {-114.800441, 32.588079}, {-114.799737, 32.592177}, {-114.799683, 32.593621}, {-114.801251, 32.596232}, {-114.801548, 32.598591}, {-114.802361, 32.599370}, {-114.805932, 32.600721}, {-114.806905, 32.601430}, + {-114.808041, 32.603172}, {-114.807879, 32.605416}, {-114.809042, 32.608806}, {-114.808906, 32.612951}, {-114.809555, 32.616203}, {-114.808662, 32.619157}, {-114.807390, 32.621332}, {-114.806821, 32.621721}, {-114.799302, 32.625115}, {-114.797564, 32.624578}, {-114.794102, 32.622475}, {-114.792640, 32.621948}, {-114.791179, 32.621833}, {-114.787715, 32.623573}, {-114.782573, 32.624304}, {-114.781896, 32.624702}, + {-114.781766, 32.625613}, {-114.782518, 32.628625}, {-114.782235, 32.630215}, {-114.779215, 32.633578}, {-114.774570, 32.635930}, {-114.771978, 32.637954}, {-114.768199, 32.639874}, {-114.764382, 32.642666}, {-114.763310, 32.644616}, {-114.763512, 32.645995}, {-114.764917, 32.648079}, {-114.764950, 32.649391}, {-114.758310, 32.655178}, {-114.751079, 32.659789}, {-114.749480, 32.661780}, {-114.748000, 32.664184}, + {-114.748183, 32.665098}, {-114.747817, 32.667777}, {-114.746383, 32.669853}, {-114.745344, 32.672190}, {-114.744900, 32.677231}, {-114.744349, 32.678935}, {-114.740541, 32.684196}, {-114.739405, 32.686385}, {-114.730453, 32.698844}, {-114.729810, 32.700282}, {-114.729740, 32.703121}, {-114.730086, 32.704298}, {-114.728408, 32.706648}, {-114.726974, 32.707875}, {-114.725340, 32.710369}, {-114.722410, 32.713597}, + {-114.719938, 32.718290}, {-114.717695, 32.721547}, {-114.715788, 32.727758}, {-114.714522, 32.730390}, {-114.712629, 32.732678}, {-114.710615, 32.733936}, {-114.709074, 32.735456}, {-114.706114, 32.740986}, {-114.702940, 32.744793}, {-114.701582, 32.745632}, {-114.699247, 32.745098}, {-114.695387, 32.742244}, {-114.691801, 32.740147}, {-114.689282, 32.737927}, {-114.688230, 32.737530}, {-114.682614, 32.737348}, + {-114.672025, 32.734951}, {-114.665921, 32.734028}, {-114.654247, 32.733570}, {-114.645353, 32.732139}, {-114.635006, 32.731372}, {-114.629299, 32.729908}, {-114.617479, 32.728243}, {-114.615670, 32.728454}, {-114.615870, 32.729717}, {-114.615501, 32.730044}, {-114.615504, 32.731449}, {-114.614786, 32.732846}, {-114.614787, 32.734076}, {-114.615112, 32.734515}, {-114.581784, 32.734946}, {-114.581736, 32.742320}, + {-114.564508, 32.742274}, {-114.564447, 32.749554}, {-114.539224, 32.749812}, {-114.539092, 32.756949}, {-114.526856, 32.757094}, {-114.528443, 32.767276}, {-114.529264, 32.769484}, {-114.531831, 32.774264}, {-114.532432, 32.776922}, {-114.532426, 32.778644}, {-114.531746, 32.782503}, {-114.531669, 32.791185}, {-114.529633, 32.795477}, {-114.522031, 32.801675}, {-114.520385, 32.803576}, {-114.520363, 32.804385}, + {-114.519758, 32.805676}, {-114.515389, 32.811439}, {-114.510327, 32.816488}, {-114.494116, 32.823287}, {-114.475892, 32.838693}, {-114.468971, 32.845155}, {-114.465711, 32.873681}, {-114.465172, 32.885295}, {-114.463307, 32.899116}, {-114.462929, 32.907944}, {-114.463650, 32.911682}, {-114.464448, 32.913128}, {-114.473713, 32.920594}, {-114.476640, 32.923628}, {-114.477952, 32.925706}, {-114.479005, 32.928291}, + {-114.480783, 32.933678}, {-114.480925, 32.936276}, {-114.480740, 32.937027}, {-114.478456, 32.940555}, {-114.474042, 32.945150}, {-114.470768, 32.949424}, {-114.468536, 32.953922}, {-114.467624, 32.956663}, {-114.467274, 32.960172}, {-114.467367, 32.965384}, {-114.468379, 32.970745}, {-114.468995, 32.972239}, {-114.470511, 32.973858}, {-114.472606, 32.974654}, {-114.475171, 32.975154}, {-114.477308, 32.975023}, + {-114.479477, 32.974189}, {-114.480831, 32.973362}, {-114.481315, 32.972064}, {-114.484806, 32.971339}, {-114.488625, 32.969946}, {-114.490129, 32.969884}, {-114.492184, 32.971021}, {-114.492938, 32.971781}, {-114.494212, 32.974262}, {-114.495712, 32.980075}, {-114.496798, 32.986534}, {-114.497052, 32.990206}, {-114.499410, 33.000040}, {-114.499797, 33.003905}, {-114.502870, 33.011154}, {-114.506129, 33.017009}, + {-114.507956, 33.019708}, {-114.511343, 33.023455}, {-114.514900, 33.026524}, {-114.520130, 33.029984}, {-114.523578, 33.030960}, {-114.538459, 33.033422}, {-114.553189, 33.033974}, {-114.560850, 33.035285}, {-114.564800, 33.035077}, {-114.571653, 33.036624}, {-114.575161, 33.036541}, {-114.578287, 33.035375}, {-114.581404, 33.032545}, {-114.584765, 33.028230}, {-114.586982, 33.026944}, {-114.589778, 33.026228}, + {-114.598093, 33.025384}, {-114.601014, 33.025410}, {-114.611584, 33.026221}, {-114.618788, 33.027202}, {-114.625787, 33.029435}, {-114.628294, 33.031050}, {-114.629732, 33.032546}, {-114.634190, 33.039024}, {-114.639552, 33.045290}, {-114.641621, 33.046894}, {-114.644820, 33.048644}, {-114.645979, 33.048902}, {-114.647049, 33.048416}, {-114.649001, 33.046762}, {-114.650999, 33.044131}, {-114.655038, 33.037106}, + {-114.657827, 33.033824}, {-114.659832, 33.032664}, {-114.662317, 33.032670}, {-114.665060, 33.033906}, {-114.670803, 33.037983}, {-114.673659, 33.041896}, {-114.674830, 33.045507}, {-114.675103, 33.047530}, {-114.674295, 33.057169}, {-114.679114, 33.061966}, {-114.686991, 33.070968}, {-114.689120, 33.076121}, {-114.689307, 33.079179}, {-114.688597, 33.082869}, {-114.689020, 33.084035}, {-114.692548, 33.085786}, + {-114.694628, 33.086226}, {-114.701165, 33.086368}, {-114.704730, 33.087051}, {-114.706488, 33.088160}, {-114.707819, 33.091102}, {-114.708133, 33.094022}, {-114.707896, 33.097431}, {-114.706175, 33.105334}, {-114.703682, 33.113768}, {-114.696914, 33.131119}, {-114.694858, 33.133460}, {-114.690246, 33.137724}, {-114.687405, 33.141983}, {-114.684907, 33.147823}, {-114.682759, 33.154808}, {-114.679945, 33.159059}, + {-114.679350, 33.162433}, {-114.680890, 33.169074}, {-114.680237, 33.169637}, {-114.679115, 33.174608}, {-114.675830, 33.181520}, {-114.675359, 33.185488}, {-114.675189, 33.188178}, {-114.678163, 33.199488}, {-114.678749, 33.203448}, {-114.676072, 33.210835}, {-114.673715, 33.219245}, {-114.673626, 33.223121}, {-114.674479, 33.225504}, {-114.678097, 33.230300}, {-114.682731, 33.234918}, {-114.689421, 33.245250}, + {-114.689541, 33.246428}, {-114.688205, 33.247965}, {-114.683253, 33.250034}, {-114.677660, 33.254426}, {-114.674491, 33.255597}, {-114.672924, 33.257042}, {-114.672088, 33.258499}, {-114.672401, 33.260469}, {-114.677032, 33.270169}, {-114.680507, 33.273576}, {-114.684363, 33.276023}, {-114.688599, 33.277861}, {-114.694449, 33.279785}, {-114.702873, 33.281916}, {-114.711197, 33.283341}, {-114.717875, 33.285156}, + {-114.721670, 33.286982}, {-114.723259, 33.288079}, {-114.731223, 33.302433}, {-114.731222, 33.304039}, {-114.729904, 33.305745}, {-114.726484, 33.308273}, {-114.724665, 33.310097}, {-114.723623, 33.312109}, {-114.718610, 33.315761}, {-114.710627, 33.320500}, {-114.707870, 33.323316}, {-114.705186, 33.327709}, {-114.700938, 33.337014}, {-114.699350, 33.345692}, {-114.699124, 33.349258}, {-114.698035, 33.352442}, + {-114.698170, 33.356575}, {-114.699056, 33.361148}, {-114.701959, 33.367134}, {-114.704201, 33.371238}, {-114.706722, 33.375030}, {-114.707348, 33.376627}, {-114.707485, 33.378375}, {-114.707009, 33.380633}, {-114.707309, 33.382540}, {-114.708407, 33.384142}, {-114.713602, 33.388256}, {-114.724250, 33.400420}, {-114.725292, 33.402341}, {-114.725535, 33.404055}, {-114.725282, 33.405048}, {-114.723829, 33.406531}, + {-114.722201, 33.407384}, {-114.720065, 33.407891}, {-114.710878, 33.407254}, {-114.701788, 33.408377}, {-114.697708, 33.410942}, {-114.696805, 33.412087}, {-114.696507, 33.414063}, {-114.695658, 33.415128}, {-114.687950, 33.417934}, {-114.673691, 33.419157}, {-114.658254, 33.413021}, {-114.656735, 33.412813}, {-114.652828, 33.412923}, {-114.649540, 33.413633}, {-114.643302, 33.416746}, {-114.635183, 33.422725}, + {-114.633262, 33.425024}, {-114.630903, 33.426754}, {-114.629640, 33.428137}, {-114.627479, 33.432307}, {-114.622283, 33.447558}, {-114.622519, 33.450879}, {-114.623395, 33.454490}, {-114.622918, 33.456561}, {-114.618354, 33.462708}, {-114.614331, 33.467315}, {-114.613782, 33.469049}, {-114.612472, 33.470768}, {-114.607843, 33.474834}, {-114.603396, 33.480631}, {-114.601694, 33.481396}, {-114.599712, 33.484316}, + {-114.597283, 33.490653}, {-114.593721, 33.495932}, {-114.592369, 33.498675}, {-114.589246, 33.501813}, {-114.580468, 33.506465}, {-114.573757, 33.507543}, {-114.569533, 33.509219}, {-114.560963, 33.516739}, {-114.560552, 33.518272}, {-114.560835, 33.524334}, {-114.560098, 33.526663}, {-114.559507, 33.530724}, {-114.558898, 33.531819}, {-114.542011, 33.542481}, {-114.531802, 33.547862}, {-114.530401, 33.550099}, + {-114.525997, 33.551457}, {-114.524599, 33.552231}, {-114.524215, 33.553068}, {-114.528220, 33.559318}, {-114.531613, 33.561702}, {-114.532333, 33.562879}, {-114.533192, 33.565823}, {-114.535965, 33.569154}, {-114.536784, 33.570959}, {-114.537801, 33.575555}, {-114.538983, 33.576792}, {-114.540300, 33.580615}, {-114.540652, 33.582872}, {-114.540111, 33.588354}, {-114.540664, 33.589789}, {-114.540617, 33.591412}, + {-114.537493, 33.594895}, {-114.536777, 33.596394}, {-114.531051, 33.604482}, {-114.529186, 33.606650}, {-114.526782, 33.608831}, {-114.523994, 33.609990}, {-114.522071, 33.611277}, {-114.521845, 33.612544}, {-114.522367, 33.614172}, {-114.527378, 33.617828}, {-114.528578, 33.619994}, {-114.529080, 33.621711}, {-114.531215, 33.623913}, {-114.531034, 33.628213}, {-114.530311, 33.629037}, {-114.526370, 33.630259}, + {-114.523802, 33.634700}, {-114.525394, 33.640669}, {-114.529549, 33.643861}, {-114.533215, 33.648443}, {-114.533194, 33.651660}, {-114.532164, 33.653194}, {-114.530583, 33.654461}, {-114.525163, 33.655939}, {-114.518337, 33.655927}, {-114.514559, 33.658014}, {-114.514057, 33.660179}, {-114.515336, 33.662033}, {-114.517112, 33.662877}, {-114.520671, 33.662681}, {-114.526439, 33.663880}, {-114.530267, 33.666821}, + {-114.532123, 33.669702}, {-114.531523, 33.675108}, {-114.530348, 33.679245}, {-114.527782, 33.682684}, {-114.523959, 33.685879}, {-114.519113, 33.688473}, {-114.512409, 33.691282}, {-114.507996, 33.692018}, {-114.504993, 33.693022}, {-114.502899, 33.694255}, {-114.496489, 33.696901}, {-114.495719, 33.698454}, {-114.495537, 33.701506}, {-114.494407, 33.705395}, {-114.494197, 33.707922}, {-114.494901, 33.714430}, + {-114.496565, 33.719155}, {-114.498133, 33.720634}, {-114.500788, 33.722204}, {-114.502661, 33.724584}, {-114.504176, 33.728055}, {-114.506799, 33.730518}, {-114.510265, 33.732146}, {-114.512348, 33.734214}, {-114.510777, 33.737574}, {-114.508206, 33.741587}, {-114.506000, 33.746344}, {-114.504483, 33.750998}, {-114.504340, 33.756381}, {-114.504863, 33.760465}, {-114.507089, 33.767930}, {-114.516734, 33.788345}, + {-114.518942, 33.797302}, {-114.521555, 33.801982}, {-114.524682, 33.808961}, {-114.527188, 33.812639}, {-114.528050, 33.814963}, {-114.527886, 33.815617}, {-114.527161, 33.816191}, {-114.522714, 33.818979}, {-114.520733, 33.822031}, {-114.519970, 33.825381}, {-114.520465, 33.827778}, {-114.523409, 33.835323}, {-114.525539, 33.838614}, {-114.529597, 33.848063}, {-114.530607, 33.855440}, {-114.529883, 33.857563}, + {-114.527069, 33.859429}, {-114.525666, 33.860003}, {-114.524292, 33.860133}, {-114.522870, 33.859965}, {-114.518998, 33.858563}, {-114.516811, 33.858120}, {-114.514673, 33.858638}, {-114.511346, 33.861570}, {-114.506635, 33.863484}, {-114.505638, 33.864276}, {-114.503887, 33.865754}, {-114.503104, 33.867166}, {-114.503017, 33.867998}, {-114.503860, 33.871234}, {-114.503395, 33.875018}, {-114.504340, 33.876882}, + {-114.510138, 33.880777}, {-114.518660, 33.888263}, {-114.522768, 33.892583}, {-114.524813, 33.895684}, {-114.525872, 33.901008}, {-114.525690, 33.901428}, {-114.524289, 33.901587}, {-114.516344, 33.897918}, {-114.513715, 33.897959}, {-114.510944, 33.899099}, {-114.508708, 33.900640}, {-114.507988, 33.901813}, {-114.507920, 33.903807}, {-114.508558, 33.906098}, {-114.511511, 33.911092}, {-114.514503, 33.914214}, + {-114.518434, 33.917518}, {-114.523393, 33.921221}, {-114.525361, 33.922272}, {-114.528385, 33.923674}, {-114.531107, 33.924633}, {-114.534146, 33.925187}, {-114.534951, 33.925700}, {-114.535853, 33.928103}, {-114.535478, 33.934651}, {-114.530566, 33.943629}, {-114.528680, 33.947817}, {-114.526353, 33.950917}, {-114.522002, 33.955623}, {-114.515860, 33.958106}, {-114.514970, 33.958149}, {-114.511231, 33.957040}, + {-114.509568, 33.957264}, {-114.499883, 33.961789}, {-114.496042, 33.965890}, {-114.490398, 33.970620}, {-114.488459, 33.972832}, {-114.484784, 33.975519}, {-114.483097, 33.977745}, {-114.482333, 33.980181}, {-114.481455, 33.981261}, {-114.475907, 33.984424}, {-114.471138, 33.988040}, {-114.467932, 33.992877}, {-114.466187, 33.993465}, {-114.461133, 33.993541}, {-114.460120, 33.993888}, {-114.459258, 33.994711}, + {-114.458028, 33.997158}, {-114.458026, 33.997820}, {-114.459184, 34.000016}, {-114.460689, 34.001128}, {-114.466280, 34.003885}, {-114.467310, 34.005190}, {-114.467404, 34.007450}, {-114.465867, 34.010987}, {-114.464525, 34.011982}, {-114.463336, 34.012259}, {-114.454807, 34.010968}, {-114.450206, 34.012574}, {-114.446815, 34.014210}, {-114.443821, 34.016176}, {-114.440540, 34.019329}, {-114.438266, 34.022609}, + {-114.436171, 34.028083}, {-114.434949, 34.037784}, {-114.435816, 34.043730}, {-114.438602, 34.050205}, {-114.439406, 34.053810}, {-114.439340, 34.057893}, {-114.437683, 34.071937}, {-114.435907, 34.077491}, {-114.434181, 34.087379}, {-114.433380, 34.088413}, {-114.428026, 34.092787}, {-114.426168, 34.097042}, {-114.422899, 34.099661}, {-114.420499, 34.103466}, {-114.415908, 34.107636}, {-114.411680, 34.110031}, + {-114.405916, 34.111468}, {-114.401336, 34.111638}, {-114.390565, 34.110084}, {-114.379223, 34.115990}, {-114.369292, 34.117519}, {-114.366517, 34.118577}, {-114.360402, 34.123577}, {-114.359389, 34.125016}, {-114.358358, 34.127617}, {-114.356372, 34.130428}, {-114.353030, 34.133120}, {-114.350478, 34.134107}, {-114.348051, 34.134457}, {-114.336112, 34.134034}, {-114.324576, 34.136759}, {-114.320777, 34.138635}, + {-114.312206, 34.144776}, {-114.307802, 34.150574}, {-114.298168, 34.160321}, {-114.292806, 34.166725}, {-114.287294, 34.170529}, {-114.275267, 34.172149}, {-114.268460, 34.170177}, {-114.257034, 34.172837}, {-114.253444, 34.174129}, {-114.244421, 34.179403}, {-114.240712, 34.183232}, {-114.229715, 34.186928}, {-114.227034, 34.188866}, {-114.225814, 34.191238}, {-114.224941, 34.193896}, {-114.225075, 34.196814}, + {-114.225790, 34.199236}, {-114.225861, 34.201774}, {-114.225194, 34.203642}, {-114.223384, 34.205136}, {-114.215454, 34.208956}, {-114.211761, 34.211539}, {-114.208253, 34.215505}, {-114.190876, 34.230858}, {-114.178050, 34.239969}, {-114.176403, 34.241512}, {-114.175948, 34.242695}, {-114.175906, 34.245587}, {-114.174597, 34.247303}, {-114.166536, 34.249647}, {-114.166124, 34.250015}, {-114.164476, 34.251667}, + {-114.163867, 34.253349}, {-114.163959, 34.255377}, {-114.165335, 34.258486}, {-114.165249, 34.259125}, {-114.164648, 34.259699}, {-114.156853, 34.258415}, {-114.153346, 34.258289}, {-114.147159, 34.259564}, {-114.144779, 34.259623}, {-114.135450, 34.257886}, {-114.133264, 34.258462}, {-114.131489, 34.260387}, {-114.131211, 34.262730}, {-114.134768, 34.268965}, {-114.136671, 34.274377}, {-114.137045, 34.277018}, + {-114.136050, 34.280833}, {-114.136677, 34.283936}, {-114.138365, 34.288564}, {-114.139534, 34.295844}, {-114.139187, 34.298074}, {-114.138167, 34.300936}, {-114.138282, 34.303230}, {-114.140930, 34.305919}, {-114.157206, 34.317862}, {-114.157939, 34.320277}, {-114.164249, 34.330816}, {-114.168807, 34.339513}, {-114.172845, 34.344979}, {-114.176909, 34.349306}, {-114.181145, 34.352186}, {-114.185556, 34.354386}, + {-114.191094, 34.356125}, {-114.196480, 34.359187}, {-114.199482, 34.361373}, {-114.213774, 34.362460}, {-114.226107, 34.365916}, {-114.229686, 34.368908}, {-114.233065, 34.375013}, {-114.234275, 34.376662}, {-114.245261, 34.385659}, {-114.248649, 34.388113}, {-114.252739, 34.390100}, {-114.258220, 34.395046}, {-114.262909, 34.400373}, {-114.264317, 34.401329}, {-114.267521, 34.402486}, {-114.272184, 34.402961}, + {-114.280108, 34.403147}, {-114.282261, 34.403641}, {-114.286802, 34.405340}, {-114.288663, 34.406623}, {-114.290219, 34.408291}, {-114.291751, 34.411104}, {-114.291903, 34.416231}, {-114.292226, 34.417606}, {-114.294836, 34.421389}, {-114.301016, 34.426807}, {-114.308659, 34.430485}, {-114.312251, 34.432726}, {-114.319054, 34.435831}, {-114.326130, 34.437251}, {-114.326880, 34.438048}, {-114.330669, 34.445295}, + {-114.332991, 34.448082}, {-114.335372, 34.450038}, {-114.339627, 34.451435}, {-114.342615, 34.451442}, {-114.348974, 34.450166}, {-114.356025, 34.449744}, {-114.363404, 34.447773}, {-114.373719, 34.446938}, {-114.375789, 34.447798}, {-114.378852, 34.450376}, {-114.382985, 34.453971}, {-114.386699, 34.457911}, {-114.387407, 34.460492}, {-114.387187, 34.462021}, {-114.383525, 34.470405}, {-114.381701, 34.476040}, + {-114.381555, 34.477883}, {-114.383038, 34.488903}, {-114.382358, 34.495758}, {-114.381402, 34.499245}, {-114.378124, 34.507288}, {-114.378223, 34.516521}, {-114.380838, 34.529724}, {-114.389603, 34.542982}, {-114.398847, 34.559149}, {-114.405228, 34.569637}, {-114.422382, 34.580711}, {-114.435671, 34.593841}, {-114.436810, 34.596074}, {-114.436363, 34.596797}, {-114.427502, 34.599227}, {-114.425338, 34.600842}, + {-114.424326, 34.602338}, {-114.424202, 34.610453}, {-114.428648, 34.614641}, {-114.438739, 34.621455}, {-114.439495, 34.625858}, {-114.441398, 34.630171}, {-114.441525, 34.631529}, {-114.440685, 34.634739}, {-114.440294, 34.638240}, {-114.440519, 34.640066}, {-114.441465, 34.642530}, {-114.444276, 34.646542}, {-114.445664, 34.647542}, {-114.449549, 34.651423}, {-114.457985, 34.657113}, {-114.458210, 34.657994}, + {-114.457702, 34.659328}, {-114.457185, 34.659992}, {-114.451785, 34.663891}, {-114.450614, 34.665793}, {-114.450506, 34.666836}, {-114.451532, 34.668605}, {-114.454305, 34.671234}, {-114.454910, 34.673092}, {-114.454881, 34.675639}, {-114.455536, 34.677335}, {-114.458163, 34.681161}, {-114.462178, 34.685800}, {-114.463633, 34.687940}, {-114.465246, 34.691202}, {-114.468090, 34.701786}, {-114.468130, 34.704445}, + {-114.468620, 34.707573}, {-114.470477, 34.711368}, {-114.471620, 34.712966}, {-114.473682, 34.713964}, {-114.477297, 34.714514}, {-114.482779, 34.714511}, {-114.487508, 34.716626}, {-114.489287, 34.720155}, {-114.490971, 34.724848}, {-114.492017, 34.725702}, {-114.495858, 34.727956}, {-114.499007, 34.729096}, {-114.500795, 34.730418}, {-114.510292, 34.733582}, {-114.514178, 34.735288}, {-114.516619, 34.736745}, + {-114.521048, 34.741173}, {-114.522619, 34.743730}, {-114.525611, 34.747005}, {-114.529079, 34.750006}, {-114.529615, 34.750822}, {-114.540306, 34.757109}, {-114.546884, 34.761802}, {-114.552682, 34.766871}, {-114.558653, 34.773852}, {-114.563979, 34.782597}, {-114.565184, 34.785976}, {-114.569383, 34.791568}, {-114.571010, 34.794294}, {-114.574474, 34.804214}, {-114.574694, 34.807471}, {-114.576452, 34.815300}, + {-114.578681, 34.820977}, {-114.581126, 34.826115}, {-114.586842, 34.835672}, {-114.592339, 34.841153}, {-114.600653, 34.847361}, {-114.604255, 34.849573}, {-114.619878, 34.856873}, {-114.623939, 34.859738}, {-114.628276, 34.863596}, {-114.630682, 34.866352}, {-114.633051, 34.869971}, {-114.634382, 34.872890}, {-114.635176, 34.875003}, {-114.635458, 34.876902}, +} +var tx = []Point{ + {-103.064657, 32.959097}, {-103.064639, 33.000106}, {-103.063980, 33.038693}, {-103.059558, 33.241728}, {-103.059649, 33.252511}, {-103.059173, 33.260362}, {-103.057487, 33.329476}, {-103.056494, 33.397068}, {-103.055384, 33.435256}, {-103.052858, 33.553381}, {-103.053063, 33.553403}, {-103.052285, 33.585908}, {-103.051777, 33.627599}, {-103.051166, 33.649202}, {-103.051535, 33.650487}, {-103.050234, 33.684178}, + {-103.049608, 33.737766}, {-103.049096, 33.746270}, {-103.047886, 33.796166}, {-103.046471, 33.875145}, {-103.045644, 33.901537}, {-103.044173, 33.974557}, {-103.043478, 34.020439}, {-103.043780, 34.035816}, {-103.043516, 34.079382}, {-103.043682, 34.110056}, {-103.043283, 34.124264}, {-103.043862, 34.133232}, {-103.043903, 34.162064}, {-103.043595, 34.181260}, {-103.043833, 34.199838}, {-103.043566, 34.204873}, + {-103.043835, 34.204900}, {-103.043837, 34.211545}, {-103.043568, 34.214370}, {-103.043482, 34.238098}, {-103.043748, 34.245595}, {-103.043557, 34.274781}, {-103.043977, 34.311410}, {-103.043762, 34.312750}, {-103.043072, 34.619782}, {-103.043293, 34.653098}, {-103.042827, 34.671188}, {-103.043123, 34.672589}, {-103.042668, 34.740129}, {-103.042974, 34.757315}, {-103.042661, 34.784966}, {-103.042982, 34.792150}, + {-103.042521, 35.014239}, {-103.042741, 35.022395}, {-103.042640, 35.109974}, {-103.043264, 35.125060}, {-103.042369, 35.138464}, {-103.042339, 35.181922}, {-103.042773, 35.241293}, {-103.042363, 35.250017}, {-103.042289, 35.383144}, {-103.041146, 35.791583}, {-103.041920, 35.796425}, {-103.041717, 35.814066}, {-103.042186, 35.825217}, {-103.041305, 35.837694}, {-103.040824, 36.000037}, {-103.041263, 36.250031}, + {-103.041745, 36.318267}, {-103.041618, 36.461804}, {-103.041923, 36.500350}, {-102.875477, 36.500238}, {-102.821224, 36.500632}, {-102.750472, 36.500400}, {-102.729727, 36.500672}, {-102.625467, 36.500364}, {-102.250453, 36.500369}, {-102.244990, 36.500704}, {-102.220580, 36.500333}, {-102.125450, 36.500324}, {-102.122066, 36.500684}, {-102.012467, 36.500581}, {-101.993161, 36.500278}, {-101.986513, 36.500526}, + {-101.899344, 36.500424}, {-101.878154, 36.500026}, {-101.838447, 36.500026}, {-101.834063, 36.499556}, {-101.826503, 36.499533}, {-101.743820, 36.499799}, {-101.699028, 36.499711}, {-101.698685, 36.499508}, {-101.411358, 36.499332}, {-101.375437, 36.499343}, {-101.373576, 36.499583}, {-101.125434, 36.499519}, {-101.098994, 36.499118}, {-101.052740, 36.499567}, {-100.977271, 36.499593}, {-100.977037, 36.499926}, + {-100.513719, 36.499286}, {-100.334464, 36.499420}, {-100.324210, 36.499970}, {-100.308121, 36.499618}, {-100.276714, 36.499881}, {-100.181221, 36.499633}, {-100.177147, 36.499635}, {-100.177134, 36.499862}, {-100.172187, 36.499590}, {-100.172128, 36.499882}, {-100.163270, 36.499609}, {-100.163170, 36.499890}, {-100.158627, 36.499626}, {-100.158601, 36.499898}, {-100.155633, 36.499566}, {-100.154740, 36.499880}, + {-100.152288, 36.499632}, {-100.147417, 36.499872}, {-100.147383, 36.499613}, {-100.145390, 36.499850}, {-100.133838, 36.499640}, {-100.127230, 36.499860}, {-100.127194, 36.499638}, {-100.115007, 36.499867}, {-100.114895, 36.499568}, {-100.112930, 36.499900}, {-100.109325, 36.499893}, {-100.109325, 36.499639}, {-100.054930, 36.499881}, {-100.000406, 36.499702}, {-100.000391, 35.519130}, {-99.999632, 35.500440}, + {-99.999628, 35.482947}, {-99.999660, 35.422364}, {-100.000393, 35.422369}, {-100.000386, 35.247405}, {-100.000121, 35.238798}, {-100.000420, 35.204641}, {-100.000479, 34.708601}, {-100.000372, 34.659853}, {-100.000095, 34.652869}, {-100.000420, 34.652598}, {-100.000277, 34.560450}, {-99.999620, 34.560170}, {-99.998159, 34.560662}, {-99.996070, 34.560729}, {-99.992406, 34.559367}, {-99.990694, 34.560243}, + {-99.985851, 34.560078}, {-99.982549, 34.560773}, {-99.980639, 34.560686}, {-99.977672, 34.561460}, {-99.974832, 34.561311}, {-99.973517, 34.561957}, {-99.971606, 34.562169}, {-99.970045, 34.563491}, {-99.969685, 34.564517}, {-99.965610, 34.565844}, {-99.962618, 34.568136}, {-99.962021, 34.569143}, {-99.958899, 34.571271}, {-99.957542, 34.572709}, {-99.957555, 34.574169}, {-99.956717, 34.576524}, + {-99.955562, 34.577616}, {-99.953817, 34.578527}, {-99.944679, 34.579135}, {-99.934276, 34.576761}, {-99.929334, 34.576714}, {-99.923211, 34.574552}, {-99.921801, 34.570253}, {-99.909988, 34.562135}, {-99.906534, 34.560387}, {-99.903489, 34.558362}, {-99.900596, 34.557202}, {-99.898943, 34.555804}, {-99.896007, 34.555530}, {-99.893760, 34.554219}, {-99.888741, 34.550452}, {-99.884842, 34.546953}, + {-99.880898, 34.542219}, {-99.878551, 34.540648}, {-99.877660, 34.540577}, {-99.876957, 34.540098}, {-99.876597, 34.539709}, {-99.876677, 34.538975}, {-99.876183, 34.537901}, {-99.875377, 34.537073}, {-99.875377, 34.537360}, {-99.874403, 34.537095}, {-99.873255, 34.535348}, {-99.872255, 34.531722}, {-99.870401, 34.530221}, {-99.868953, 34.527615}, {-99.867654, 34.527191}, {-99.864891, 34.522993}, + {-99.862235, 34.520506}, {-99.861078, 34.519986}, {-99.858408, 34.516969}, {-99.856472, 34.515744}, {-99.855224, 34.513655}, {-99.853053, 34.511585}, {-99.846697, 34.508522}, {-99.843974, 34.506502}, {-99.834768, 34.501740}, {-99.832904, 34.500068}, {-99.830720, 34.499267}, {-99.828369, 34.498878}, {-99.825325, 34.497596}, {-99.822835, 34.498100}, {-99.821839, 34.497848}, {-99.819431, 34.495192}, + {-99.816803, 34.490199}, {-99.816720, 34.489718}, {-99.817771, 34.488733}, {-99.818186, 34.487840}, {-99.818130, 34.486007}, {-99.818739, 34.484976}, {-99.815751, 34.480693}, {-99.814313, 34.476204}, {-99.809860, 34.471830}, {-99.806929, 34.469791}, {-99.802864, 34.465668}, {-99.801619, 34.464019}, {-99.801149, 34.462324}, {-99.798605, 34.460125}, {-99.797167, 34.457674}, {-99.795315, 34.456116}, + {-99.793684, 34.453894}, {-99.789841, 34.451718}, {-99.788790, 34.451489}, {-99.787104, 34.451763}, {-99.786081, 34.451099}, {-99.785445, 34.450045}, {-99.784037, 34.445509}, {-99.782986, 34.444364}, {-99.775743, 34.444225}, {-99.774278, 34.443285}, {-99.772702, 34.441269}, {-99.765599, 34.437488}, {-99.764826, 34.436434}, {-99.764882, 34.435266}, {-99.765297, 34.434373}, {-99.766514, 34.433411}, + {-99.767648, 34.431854}, {-99.767234, 34.430502}, {-99.764001, 34.428302}, {-99.759607, 34.426674}, {-99.756210, 34.422710}, {-99.754248, 34.421289}, {-99.752480, 34.420533}, {-99.750021, 34.420348}, {-99.748789, 34.419753}, {-99.748014, 34.419112}, {-99.747378, 34.417784}, {-99.746686, 34.417395}, {-99.743120, 34.417282}, {-99.742318, 34.416709}, {-99.740907, 34.414763}, {-99.734356, 34.412429}, + {-99.733168, 34.412521}, {-99.730763, 34.411698}, {-99.730348, 34.411240}, {-99.727031, 34.410554}, {-99.726064, 34.409958}, {-99.725096, 34.408355}, {-99.724349, 34.407852}, {-99.720259, 34.406295}, {-99.717854, 34.403708}, {-99.716416, 34.402815}, {-99.715089, 34.400754}, {-99.714232, 34.397822}, {-99.714231, 34.397089}, {-99.714977, 34.395325}, {-99.714838, 34.394524}, {-99.712682, 34.390928}, + {-99.710029, 34.389669}, {-99.709532, 34.388523}, {-99.707901, 34.387539}, {-99.706658, 34.385981}, {-99.705249, 34.385775}, {-99.704005, 34.385982}, {-99.701187, 34.385203}, {-99.700414, 34.384699}, {-99.696462, 34.381036}, {-99.695799, 34.379272}, {-99.694970, 34.378333}, {-99.690604, 34.378219}, {-99.686709, 34.378654}, {-99.682482, 34.379822}, {-99.678283, 34.379799}, {-99.673145, 34.378516}, + {-99.671377, 34.377714}, {-99.669388, 34.375652}, {-99.668339, 34.375487}, {-99.665992, 34.374185}, {-99.662705, 34.373680}, {-99.659362, 34.374390}, {-99.658451, 34.374723}, {-99.657150, 34.375833}, {-99.654194, 34.376519}, {-99.649662, 34.379885}, {-99.648833, 34.381603}, {-99.648142, 34.382312}, {-99.646511, 34.382541}, {-99.644578, 34.381670}, {-99.642755, 34.381555}, {-99.639661, 34.379905}, + {-99.633859, 34.378757}, {-99.631843, 34.377703}, {-99.631181, 34.377061}, {-99.630905, 34.376007}, {-99.629690, 34.375482}, {-99.628530, 34.375401}, {-99.628545, 34.375076}, {-99.626847, 34.374356}, {-99.624197, 34.373577}, {-99.621407, 34.373784}, {-99.619556, 34.374627}, {-99.616863, 34.375076}, {-99.616793, 34.375391}, {-99.611572, 34.375643}, {-99.610136, 34.376560}, {-99.607263, 34.376928}, + {-99.600026, 34.374688}, {-99.596323, 34.377137}, {-99.596296, 34.378741}, {-99.595137, 34.380322}, {-99.592044, 34.383163}, {-99.587596, 34.385867}, {-99.586215, 34.387402}, {-99.585442, 34.388914}, {-99.584531, 34.391205}, {-99.584973, 34.393175}, {-99.584891, 34.396336}, {-99.585306, 34.398122}, {-99.584534, 34.401673}, {-99.583484, 34.403735}, {-99.583595, 34.405040}, {-99.584341, 34.406346}, + {-99.584480, 34.407673}, {-99.583071, 34.409598}, {-99.582684, 34.410995}, {-99.581193, 34.413927}, {-99.581138, 34.414752}, {-99.580170, 34.415943}, {-99.580060, 34.416653}, {-99.576909, 34.417799}, {-99.574367, 34.418281}, {-99.569696, 34.418418}, {-99.567015, 34.418304}, {-99.565134, 34.418006}, {-99.564056, 34.417388}, {-99.562204, 34.417319}, {-99.555986, 34.414640}, {-99.554604, 34.414571}, + {-99.549242, 34.412715}, {-99.541531, 34.413241}, {-99.539707, 34.412782}, {-99.536640, 34.412736}, {-99.533517, 34.411796}, {-99.529786, 34.411452}, {-99.523650, 34.412206}, {-99.520886, 34.413144}, {-99.518895, 34.414289}, {-99.517624, 34.414494}, {-99.514280, 34.414035}, {-99.511102, 34.412705}, {-99.506903, 34.409520}, {-99.503144, 34.409289}, {-99.500367, 34.409677}, {-99.498999, 34.409173}, + {-99.497091, 34.407731}, {-99.496399, 34.406495}, {-99.494104, 34.404755}, {-99.493606, 34.403495}, {-99.491975, 34.402030}, {-99.490426, 34.399694}, {-99.487219, 34.397955}, {-99.481125, 34.396683}, {-99.479827, 34.396803}, {-99.477782, 34.396288}, {-99.476243, 34.396360}, {-99.473789, 34.395350}, {-99.472268, 34.395602}, {-99.470306, 34.396748}, {-99.468427, 34.396634}, {-99.466383, 34.395900}, + {-99.464036, 34.393634}, {-99.462462, 34.391137}, {-99.462297, 34.388639}, {-99.461854, 34.387241}, {-99.462075, 34.383462}, {-99.461053, 34.380666}, {-99.459395, 34.380117}, {-99.457626, 34.380117}, {-99.456328, 34.380392}, {-99.455085, 34.381264}, {-99.454891, 34.381905}, {-99.455472, 34.386097}, {-99.454588, 34.388640}, {-99.453787, 34.388663}, {-99.451465, 34.386922}, {-99.450222, 34.385021}, + {-99.449117, 34.384333}, {-99.445336, 34.379768}, {-99.444255, 34.378134}, {-99.443669, 34.376457}, {-99.442394, 34.375453}, {-99.441937, 34.374412}, {-99.441057, 34.374042}, {-99.440671, 34.372774}, {-99.438269, 34.371164}, {-99.437095, 34.371144}, {-99.436474, 34.370539}, {-99.434921, 34.370634}, {-99.433458, 34.370223}, {-99.431043, 34.371117}, {-99.431118, 34.374003}, {-99.429793, 34.375003}, + {-99.429324, 34.375834}, {-99.430145, 34.377589}, {-99.430010, 34.381019}, {-99.429154, 34.382849}, {-99.427497, 34.385136}, {-99.424897, 34.385823}, {-99.423679, 34.385663}, {-99.421908, 34.384201}, {-99.421410, 34.383355}, {-99.420714, 34.380386}, {-99.417385, 34.378838}, {-99.414159, 34.377773}, {-99.413045, 34.377012}, {-99.412103, 34.375041}, {-99.410195, 34.373402}, {-99.409005, 34.372758}, + {-99.407307, 34.372593}, {-99.405286, 34.372858}, {-99.400334, 34.374623}, {-99.398728, 34.375832}, {-99.397355, 34.377868}, {-99.396151, 34.382546}, {-99.396162, 34.383502}, {-99.396789, 34.384777}, {-99.396714, 34.392667}, {-99.395887, 34.394197}, {-99.395676, 34.397690}, {-99.394046, 34.399976}, {-99.393714, 34.401439}, {-99.391750, 34.405533}, {-99.387929, 34.409469}, {-99.387181, 34.410842}, + {-99.387653, 34.411984}, {-99.389484, 34.413432}, {-99.391918, 34.414674}, {-99.394183, 34.415177}, {-99.395148, 34.415855}, {-99.396751, 34.417210}, {-99.397164, 34.418607}, {-99.396802, 34.420968}, {-99.397298, 34.422411}, {-99.397267, 34.423924}, {-99.396297, 34.425298}, {-99.393306, 34.427613}, {-99.393802, 34.429010}, {-99.393049, 34.431921}, {-99.395122, 34.433044}, {-99.396115, 34.434970}, + {-99.395187, 34.442030}, {-99.393495, 34.443679}, {-99.389227, 34.446863}, {-99.386757, 34.449521}, {-99.381204, 34.456879}, {-99.377132, 34.458550}, {-99.375550, 34.458789}, {-99.368932, 34.458461}, {-99.366054, 34.456743}, {-99.358970, 34.455807}, {-99.355705, 34.453082}, {-99.354848, 34.451800}, {-99.354850, 34.450539}, {-99.355016, 34.449600}, {-99.356624, 34.447467}, {-99.356956, 34.446481}, + {-99.357290, 34.444854}, {-99.357236, 34.442860}, {-99.356903, 34.442082}, {-99.350594, 34.436992}, {-99.345462, 34.434031}, {-99.340130, 34.430102}, {-99.334090, 34.427525}, {-99.333665, 34.426293}, {-99.330693, 34.424420}, {-99.328698, 34.422383}, {-99.326728, 34.419111}, {-99.326338, 34.417667}, {-99.325064, 34.416340}, {-99.324230, 34.414464}, {-99.322459, 34.413685}, {-99.320073, 34.409267}, + {-99.319602, 34.408879}, {-99.318357, 34.408306}, {-99.316365, 34.408215}, {-99.313959, 34.409085}, {-99.309257, 34.409704}, {-99.305774, 34.411055}, {-99.304613, 34.412062}, {-99.300466, 34.413344}, {-99.299084, 34.414236}, {-99.294632, 34.415381}, {-99.289903, 34.414739}, {-99.287910, 34.414030}, {-99.285586, 34.412358}, {-99.284396, 34.411030}, {-99.282843, 34.407480}, {-99.283170, 34.402946}, + {-99.281510, 34.401457}, {-99.280045, 34.400655}, {-99.276865, 34.400058}, {-99.275260, 34.400104}, {-99.272303, 34.400812}, {-99.269566, 34.401910}, {-99.268296, 34.403558}, {-99.267745, 34.404955}, {-99.266308, 34.405481}, {-99.264124, 34.405159}, {-99.261275, 34.403508}, {-99.260859, 34.402203}, {-99.261825, 34.398677}, {-99.261822, 34.395036}, {-99.261241, 34.394073}, {-99.258807, 34.392078}, + {-99.258917, 34.391255}, {-99.261126, 34.389561}, {-99.264442, 34.388099}, {-99.271655, 34.387690}, {-99.272042, 34.387919}, {-99.273894, 34.387577}, {-99.275275, 34.386616}, {-99.274858, 34.384922}, {-99.274415, 34.384349}, {-99.272922, 34.383547}, {-99.272202, 34.382356}, {-99.269796, 34.380843}, {-99.264958, 34.378756}, {-99.260441, 34.378078}, {-99.259767, 34.377600}, {-99.259203, 34.377004}, + {-99.259334, 34.373621}, {-99.258606, 34.372650}, {-99.256498, 34.372229}, {-99.254631, 34.372419}, {-99.250668, 34.375518}, {-99.248884, 34.375995}, {-99.247584, 34.375551}, {-99.246007, 34.374235}, {-99.242855, 34.372676}, {-99.243024, 34.371142}, {-99.243543, 34.370711}, {-99.246328, 34.369828}, {-99.247942, 34.368487}, {-99.248120, 34.367433}, {-99.247009, 34.364780}, {-99.246393, 34.364397}, + {-99.244454, 34.363701}, {-99.241476, 34.364536}, {-99.237562, 34.366701}, {-99.236494, 34.365805}, {-99.236188, 34.364390}, {-99.237129, 34.362723}, {-99.237141, 34.361095}, {-99.236521, 34.358569}, {-99.234843, 34.356441}, {-99.234135, 34.353464}, {-99.232646, 34.352540}, {-99.228772, 34.350986}, {-99.227977, 34.349464}, {-99.228636, 34.348564}, {-99.230310, 34.347506}, {-99.232113, 34.345576}, + {-99.234046, 34.342173}, {-99.234298, 34.340451}, {-99.232632, 34.338872}, {-99.230519, 34.337777}, {-99.227117, 34.337767}, {-99.221843, 34.340007}, {-99.219647, 34.341365}, {-99.217203, 34.341508}, {-99.208832, 34.339172}, {-99.206905, 34.338277}, {-99.209460, 34.337984}, {-99.210725, 34.337337}, {-99.210820, 34.336826}, {-99.210582, 34.336292}, {-99.210992, 34.332101}, {-99.210660, 34.329206}, + {-99.209574, 34.324925}, {-99.205936, 34.320582}, {-99.205628, 34.319201}, {-99.206584, 34.316960}, {-99.211432, 34.313967}, {-99.213302, 34.310673}, {-99.213161, 34.308476}, {-99.212521, 34.306278}, {-99.210999, 34.303392}, {-99.209695, 34.298904}, {-99.210075, 34.295587}, {-99.211448, 34.292247}, {-99.210920, 34.289661}, {-99.209535, 34.286364}, {-99.208927, 34.285220}, {-99.207352, 34.283525}, + {-99.203473, 34.281944}, {-99.200016, 34.281167}, {-99.196058, 34.281472}, {-99.195553, 34.281282}, {-99.195403, 34.280849}, {-99.195744, 34.278286}, {-99.194651, 34.274552}, {-99.194459, 34.271881}, {-99.196679, 34.269252}, {-99.196461, 34.267806}, {-99.196682, 34.266198}, {-99.198017, 34.262723}, {-99.199254, 34.260791}, {-99.199053, 34.260540}, {-99.196700, 34.260973}, {-99.193838, 34.260307}, + {-99.191849, 34.259001}, {-99.191422, 34.257830}, {-99.192018, 34.255632}, {-99.193943, 34.253374}, {-99.196199, 34.249385}, {-99.196912, 34.244382}, {-99.195061, 34.239218}, {-99.190936, 34.232442}, {-99.189986, 34.229756}, {-99.189920, 34.227277}, {-99.192019, 34.222282}, {-99.192811, 34.218888}, {-99.192355, 34.216740}, {-99.191156, 34.215296}, {-99.189776, 34.214357}, {-99.186817, 34.213555}, + {-99.173824, 34.211794}, {-99.164135, 34.208976}, {-99.160881, 34.209207}, {-99.159194, 34.208931}, {-99.156823, 34.207214}, {-99.153653, 34.207328}, {-99.151340, 34.207603}, {-99.148721, 34.208770}, {-99.147239, 34.209914}, {-99.145075, 34.212385}, {-99.143910, 34.214838}, {-99.142471, 34.215408}, {-99.141545, 34.216690}, {-99.139489, 34.217353}, {-99.138970, 34.218660}, {-99.138076, 34.219233}, + {-99.133397, 34.219665}, {-99.130443, 34.219478}, {-99.128351, 34.218835}, {-99.127394, 34.218055}, {-99.126799, 34.217115}, {-99.126495, 34.215396}, {-99.127429, 34.213838}, {-99.130012, 34.212263}, {-99.131559, 34.209417}, {-99.131986, 34.207436}, {-99.131168, 34.205742}, {-99.129889, 34.204460}, {-99.126640, 34.203064}, {-99.124969, 34.202972}, {-99.121004, 34.201850}, {-99.119254, 34.201809}, + {-99.117780, 34.202447}, {-99.116965, 34.203729}, {-99.115104, 34.204895}, {-99.111807, 34.205980}, {-99.108417, 34.205271}, {-99.108383, 34.204788}, {-99.109622, 34.205084}, {-99.110628, 34.204968}, {-99.111003, 34.204600}, {-99.110459, 34.204257}, {-99.108766, 34.204235}, {-99.108546, 34.204143}, {-99.108894, 34.203524}, {-99.108201, 34.203547}, {-99.106301, 34.204698}, {-99.103395, 34.205046}, + {-99.102186, 34.205483}, {-99.100249, 34.208867}, {-99.099354, 34.209852}, {-99.097876, 34.211019}, {-99.096211, 34.211705}, {-99.094168, 34.211658}, {-99.092851, 34.210742}, {-99.093299, 34.210124}, {-99.092064, 34.209368}, {-99.088923, 34.208475}, {-99.086600, 34.208702}, {-99.083804, 34.209730}, {-99.080577, 34.211483}, {-99.079392, 34.211550}, {-99.075837, 34.211249}, {-99.070159, 34.209074}, + {-99.066329, 34.208430}, {-99.063105, 34.206851}, {-99.060212, 34.204794}, {-99.059497, 34.203926}, {-99.058688, 34.201296}, {-99.057976, 34.200610}, {-99.048685, 34.198248}, {-99.044463, 34.198106}, {-99.043358, 34.198242}, {-99.041972, 34.199359}, {-99.040837, 34.200864}, {-99.038880, 34.204669}, {-99.037336, 34.206446}, {-99.036150, 34.206901}, {-99.025760, 34.204807}, {-99.024768, 34.204418}, + {-99.024465, 34.203984}, {-99.020661, 34.204684}, {-99.017271, 34.206314}, {-99.015041, 34.203896}, {-99.013673, 34.203190}, {-99.012957, 34.203194}, {-99.009823, 34.205289}, {-99.008944, 34.205167}, {-99.005674, 34.206594}, {-99.002242, 34.209374}, {-99.001745, 34.210353}, {-99.004198, 34.211954}, {-99.004695, 34.214145}, {-99.003867, 34.214895}, {-99.001549, 34.215599}, {-99.000632, 34.216715}, + {-99.000592, 34.217672}, {-98.997327, 34.218650}, {-98.995838, 34.219652}, {-98.991703, 34.221427}, {-98.988172, 34.221310}, {-98.985662, 34.220417}, {-98.980917, 34.217059}, {-98.980695, 34.215735}, {-98.978569, 34.210123}, {-98.976610, 34.207885}, {-98.976470, 34.206198}, {-98.974015, 34.203482}, {-98.972609, 34.202591}, {-98.968886, 34.201218}, {-98.966626, 34.201101}, {-98.965743, 34.201465}, + {-98.962931, 34.203833}, {-98.962352, 34.204562}, {-98.961967, 34.206272}, {-98.962189, 34.211177}, {-98.961941, 34.211838}, {-98.959984, 34.213272}, {-98.958357, 34.213704}, {-98.951284, 34.212189}, {-98.950350, 34.211578}, {-98.949673, 34.209995}, {-98.948685, 34.209229}, {-98.947141, 34.208862}, {-98.942453, 34.205060}, {-98.940220, 34.203686}, {-98.936664, 34.200020}, {-98.935202, 34.197890}, + {-98.934679, 34.197523}, {-98.933300, 34.197317}, {-98.928145, 34.192689}, {-98.927456, 34.191155}, {-98.925030, 34.188635}, {-98.923129, 34.185978}, {-98.920704, 34.183435}, {-98.918333, 34.181831}, {-98.915357, 34.180960}, {-98.909349, 34.177499}, {-98.905876, 34.177178}, {-98.896616, 34.174495}, {-98.895129, 34.173509}, {-98.892263, 34.172294}, {-98.891657, 34.171355}, {-98.890473, 34.170392}, + {-98.887112, 34.168260}, {-98.884467, 34.167618}, {-98.880415, 34.167272}, {-98.876060, 34.167705}, {-98.874849, 34.167522}, {-98.872922, 34.166584}, {-98.871543, 34.165027}, {-98.871211, 34.163012}, {-98.872229, 34.160446}, {-98.874955, 34.157031}, {-98.874872, 34.155657}, {-98.873271, 34.153596}, {-98.869246, 34.150207}, {-98.868116, 34.149635}, {-98.865747, 34.149132}, {-98.862550, 34.149111}, + {-98.860125, 34.149913}, {-98.858419, 34.152732}, {-98.858420, 34.155389}, {-98.857900, 34.159627}, {-98.857322, 34.161094}, {-98.855585, 34.161621}, {-98.853849, 34.161553}, {-98.850762, 34.159882}, {-98.846518, 34.159654}, {-98.844727, 34.159082}, {-98.843542, 34.158143}, {-98.842191, 34.158051}, {-98.840676, 34.157296}, {-98.837066, 34.156495}, {-98.836294, 34.156678}, {-98.834063, 34.158259}, + {-98.833346, 34.159107}, {-98.832135, 34.161787}, {-98.831115, 34.162154}, {-98.825079, 34.160391}, {-98.815269, 34.158101}, {-98.812954, 34.158444}, {-98.806810, 34.155901}, {-98.804413, 34.153931}, {-98.799756, 34.147883}, {-98.797966, 34.144608}, {-98.796203, 34.142775}, {-98.792015, 34.143736}, {-98.790720, 34.144584}, {-98.789094, 34.146485}, {-98.788405, 34.146668}, {-98.783006, 34.142337}, + {-98.778076, 34.139495}, {-98.771795, 34.138302}, {-98.765570, 34.136376}, {-98.761797, 34.133785}, {-98.760558, 34.132388}, {-98.759486, 34.128882}, {-98.759653, 34.126912}, {-98.757450, 34.124713}, {-98.757037, 34.124633}, {-98.749291, 34.124238}, {-98.746263, 34.125540}, {-98.741966, 34.125530}, {-98.740589, 34.126294}, {-98.739461, 34.127394}, {-98.737232, 34.130992}, {-98.737177, 34.132550}, + {-98.736820, 34.133374}, {-98.735471, 34.135208}, {-98.734287, 34.135758}, {-98.731036, 34.136011}, {-98.728502, 34.135279}, {-98.726188, 34.135486}, {-98.723626, 34.135189}, {-98.718639, 34.136473}, {-98.717537, 34.136450}, {-98.716104, 34.135947}, {-98.714947, 34.134847}, {-98.714230, 34.133404}, {-98.714120, 34.132144}, {-98.712467, 34.131160}, {-98.710731, 34.131206}, {-98.709960, 34.131549}, + {-98.706132, 34.134986}, {-98.703047, 34.135903}, {-98.700182, 34.135995}, {-98.696518, 34.133521}, {-98.693405, 34.133750}, {-98.690898, 34.133018}, {-98.690072, 34.133155}, {-98.685828, 34.136041}, {-98.685360, 34.137187}, {-98.684120, 34.138790}, {-98.678693, 34.142135}, {-98.677150, 34.142501}, {-98.676626, 34.142913}, {-98.673733, 34.145868}, {-98.672934, 34.147472}, {-98.670922, 34.149373}, + {-98.665824, 34.151617}, {-98.663179, 34.153312}, {-98.659899, 34.156128}, {-98.655655, 34.158258}, {-98.652347, 34.161029}, {-98.650583, 34.163113}, {-98.649534, 34.163433}, {-98.648073, 34.164441}, {-98.643223, 34.164531}, {-98.639642, 34.163430}, {-98.635730, 34.161618}, {-98.629117, 34.159806}, {-98.621666, 34.157195}, {-98.616733, 34.156418}, {-98.613096, 34.156282}, {-98.611829, 34.156558}, + {-98.608853, 34.157521}, {-98.606732, 34.159034}, {-98.603978, 34.160249}, {-98.602131, 34.160593}, {-98.599789, 34.160571}, {-98.597502, 34.159128}, {-98.592954, 34.157297}, {-98.583914, 34.151298}, {-98.579147, 34.149695}, {-98.578128, 34.149031}, {-98.577136, 34.148962}, {-98.572451, 34.145091}, {-98.571459, 34.143488}, {-98.569724, 34.141586}, {-98.567740, 34.139960}, {-98.567298, 34.138242}, + {-98.566609, 34.137440}, {-98.560191, 34.133202}, {-98.558621, 34.132973}, {-98.553717, 34.133660}, {-98.552836, 34.133568}, {-98.550880, 34.132583}, {-98.550384, 34.131736}, {-98.550357, 34.129239}, {-98.551046, 34.127818}, {-98.551845, 34.127131}, {-98.554324, 34.126605}, {-98.557602, 34.128437}, {-98.558593, 34.128254}, {-98.559558, 34.127704}, {-98.560136, 34.126926}, {-98.560976, 34.125092}, + {-98.560977, 34.123922}, {-98.558027, 34.122856}, {-98.556820, 34.121079}, {-98.555747, 34.120134}, {-98.553870, 34.119542}, {-98.550917, 34.119334}, {-98.536257, 34.107347}, {-98.533678, 34.103269}, {-98.530611, 34.099843}, {-98.528200, 34.094961}, {-98.524614, 34.092595}, {-98.521928, 34.089803}, {-98.516875, 34.085984}, {-98.513812, 34.082011}, {-98.504149, 34.072287}, {-98.501375, 34.071921}, + {-98.498120, 34.070307}, {-98.494613, 34.070090}, {-98.491727, 34.067771}, {-98.489346, 34.066579}, {-98.488268, 34.065473}, {-98.487824, 34.063768}, {-98.486328, 34.062598}, {-98.483551, 34.062277}, {-98.480713, 34.062499}, {-98.473525, 34.064734}, {-98.471465, 34.065665}, {-98.468184, 34.068991}, {-98.466002, 34.073081}, {-98.465133, 34.073729}, {-98.461959, 34.073836}, {-98.454665, 34.072577}, + {-98.451690, 34.072615}, {-98.449034, 34.073462}, {-98.446379, 34.075430}, {-98.445784, 34.076827}, {-98.445921, 34.078413}, {-98.445585, 34.079298}, {-98.443724, 34.082152}, {-98.442808, 34.083144}, {-98.440092, 34.084311}, {-98.432127, 34.085622}, {-98.428480, 34.085523}, {-98.425230, 34.084799}, {-98.423533, 34.082843}, {-98.420408, 34.082314}, {-98.417813, 34.083029}, {-98.414426, 34.085074}, + {-98.411052, 34.088592}, {-98.410517, 34.090807}, {-98.409723, 34.092509}, {-98.409596, 34.094070}, {-98.411511, 34.098615}, {-98.411649, 34.099554}, {-98.411374, 34.100324}, {-98.409924, 34.101247}, {-98.407727, 34.101369}, {-98.406735, 34.101140}, {-98.403821, 34.099279}, {-98.402158, 34.098653}, {-98.400510, 34.099042}, {-98.399777, 34.099973}, {-98.398389, 34.104566}, {-98.398297, 34.111333}, + {-98.398648, 34.115728}, {-98.398160, 34.121396}, {-98.400494, 34.121778}, {-98.400967, 34.122236}, {-98.400159, 34.124364}, {-98.399355, 34.125093}, {-98.398524, 34.127356}, {-98.398441, 34.128456}, {-98.394032, 34.131913}, {-98.389403, 34.133217}, {-98.387750, 34.133033}, {-98.385767, 34.132162}, {-98.383785, 34.129916}, {-98.382959, 34.129549}, {-98.382298, 34.129572}, {-98.380093, 34.130831}, + {-98.379762, 34.131174}, {-98.379790, 34.131839}, {-98.380421, 34.134290}, {-98.383726, 34.136307}, {-98.384965, 34.136651}, {-98.386232, 34.137728}, {-98.386865, 34.138873}, {-98.387084, 34.140912}, {-98.384850, 34.143844}, {-98.384381, 34.146317}, {-98.383222, 34.147806}, {-98.381238, 34.149454}, {-98.378152, 34.149957}, {-98.376911, 34.150552}, {-98.374461, 34.150758}, {-98.370936, 34.151860}, + {-98.370082, 34.152616}, {-98.367907, 34.155824}, {-98.366778, 34.156649}, {-98.365621, 34.156993}, {-98.364023, 34.157109}, {-98.348894, 34.154502}, {-98.343163, 34.154115}, {-98.341454, 34.153153}, {-98.336384, 34.152444}, {-98.333601, 34.151688}, {-98.331479, 34.151391}, {-98.328421, 34.151643}, {-98.325445, 34.151025}, {-98.322580, 34.149720}, {-98.318750, 34.146421}, {-98.315939, 34.144864}, + {-98.314589, 34.143695}, {-98.310952, 34.141932}, {-98.308638, 34.139755}, {-98.301201, 34.135472}, {-98.300209, 34.134579}, {-98.296849, 34.134189}, {-98.293901, 34.133020}, {-98.280321, 34.130750}, {-98.276878, 34.131046}, {-98.272994, 34.132213}, {-98.271561, 34.132282}, {-98.268972, 34.131616}, {-98.266217, 34.132051}, {-98.264729, 34.131913}, {-98.261700, 34.131568}, {-98.257872, 34.129665}, + {-98.256467, 34.129481}, {-98.253272, 34.129732}, {-98.247954, 34.130717}, {-98.241013, 34.133103}, {-98.239141, 34.134547}, {-98.235477, 34.134800}, {-98.232474, 34.134641}, {-98.231042, 34.134023}, {-98.225558, 34.129283}, {-98.225282, 34.127245}, {-98.223600, 34.125093}, {-98.219494, 34.123469}, {-98.216463, 34.121821}, {-98.209219, 34.120997}, {-98.208008, 34.120539}, {-98.205170, 34.118341}, + {-98.201728, 34.117058}, {-98.187213, 34.115341}, {-98.170827, 34.114171}, {-98.168790, 34.114262}, {-98.166228, 34.116255}, {-98.157412, 34.120467}, {-98.154354, 34.122734}, {-98.147440, 34.130680}, {-98.147274, 34.131939}, {-98.145951, 34.133932}, {-98.142754, 34.136359}, {-98.141596, 34.137847}, {-98.140079, 34.141008}, {-98.138316, 34.142519}, {-98.136991, 34.144786}, {-98.135145, 34.145494}, + {-98.134207, 34.147510}, {-98.130816, 34.150532}, {-98.128198, 34.151057}, {-98.125993, 34.153164}, {-98.123377, 34.154540}, {-98.122468, 34.154678}, {-98.120925, 34.154358}, {-98.118280, 34.154749}, {-98.114506, 34.154727}, {-98.109462, 34.154111}, {-98.107065, 34.152531}, {-98.105217, 34.150173}, {-98.103178, 34.148318}, {-98.101937, 34.146830}, {-98.100559, 34.143486}, {-98.100558, 34.142135}, + {-98.103172, 34.135194}, {-98.103309, 34.134140}, {-98.102895, 34.132605}, {-98.102344, 34.131918}, {-98.100498, 34.131369}, {-98.094439, 34.132379}, {-98.092373, 34.132150}, {-98.090224, 34.130181}, {-98.089755, 34.128211}, {-98.090634, 34.125093}, {-98.090660, 34.121980}, {-98.092421, 34.116917}, {-98.095118, 34.111190}, {-98.099328, 34.104295}, {-98.104309, 34.098200}, {-98.109924, 34.092930}, + {-98.112649, 34.090867}, {-98.114575, 34.088668}, {-98.118482, 34.085619}, {-98.119417, 34.084474}, {-98.119527, 34.083672}, {-98.121039, 34.081266}, {-98.121039, 34.080327}, {-98.120598, 34.079250}, {-98.120208, 34.072127}, {-98.118030, 34.067065}, {-98.114587, 34.062280}, {-98.110870, 34.059876}, {-98.101893, 34.050969}, {-98.100627, 34.049984}, {-98.095260, 34.047626}, {-98.093526, 34.047192}, + {-98.087472, 34.044100}, {-98.083839, 34.041719}, {-98.082463, 34.039428}, {-98.082655, 34.038191}, {-98.083810, 34.036656}, {-98.089037, 34.033838}, {-98.091788, 34.033654}, {-98.093219, 34.034547}, {-98.096550, 34.038051}, {-98.098613, 34.038852}, {-98.101695, 34.039057}, {-98.103538, 34.038530}, {-98.105025, 34.037545}, {-98.106179, 34.034887}, {-98.106261, 34.033696}, {-98.104884, 34.031841}, + {-98.103617, 34.029207}, {-98.099268, 34.025429}, {-98.098442, 34.023918}, {-98.095443, 34.021697}, {-98.089555, 34.019270}, {-98.087684, 34.016888}, {-98.085538, 34.015950}, {-98.084025, 34.014369}, {-98.082401, 34.009834}, {-98.083611, 34.009490}, {-98.084189, 34.008528}, {-98.084601, 34.008368}, {-98.087104, 34.008344}, {-98.088149, 34.007932}, {-98.088809, 34.007176}, {-98.088203, 34.005481}, + {-98.085260, 34.003259}, {-98.082839, 34.002412}, {-98.078851, 34.001542}, {-98.073267, 34.001016}, {-98.071425, 34.000096}, {-98.061907, 33.997971}, {-98.059350, 33.996780}, {-98.055197, 33.995841}, {-98.048118, 33.994474}, {-98.044692, 33.994282}, {-98.041117, 33.993456}, {-98.033252, 33.993890}, {-98.027672, 33.993357}, {-98.025086, 33.994091}, {-98.022551, 33.993612}, {-98.019485, 33.993804}, + {-98.017345, 33.994303}, {-98.015101, 33.994136}, {-97.999624, 33.997110}, {-97.998938, 33.997024}, {-97.990861, 33.998873}, {-97.987388, 33.999823}, {-97.985366, 34.000959}, {-97.983521, 34.001559}, {-97.980607, 34.003599}, {-97.979508, 34.004722}, {-97.976840, 34.006051}, {-97.974173, 34.006716}, {-97.972605, 34.006144}, {-97.971670, 34.005434}, {-97.969358, 34.002136}, {-97.967818, 34.000946}, + {-97.967708, 34.000576}, {-97.968230, 34.000785}, {-97.968340, 34.000530}, {-97.963028, 33.994235}, {-97.958325, 33.990846}, {-97.955850, 33.990136}, {-97.952688, 33.990114}, {-97.947572, 33.991053}, {-97.946473, 33.990732}, {-97.945730, 33.989839}, {-97.945950, 33.988396}, {-97.948644, 33.982601}, {-97.949827, 33.979188}, {-97.952740, 33.969567}, {-97.954499, 33.966451}, {-97.956917, 33.958502}, + {-97.958455, 33.956120}, {-97.958895, 33.954516}, {-97.960351, 33.951928}, {-97.965737, 33.947392}, {-97.966616, 33.947415}, {-97.968897, 33.945880}, {-97.972662, 33.944527}, {-97.974173, 33.942832}, {-97.974062, 33.940289}, {-97.972494, 33.937907}, {-97.971175, 33.937129}, {-97.965953, 33.936191}, {-97.963425, 33.936237}, {-97.955511, 33.938186}, {-97.954467, 33.937774}, {-97.953395, 33.936445}, + {-97.952679, 33.929482}, {-97.953695, 33.924373}, {-97.954217, 33.923549}, {-97.957155, 33.914454}, {-97.958226, 33.912897}, {-97.961632, 33.909414}, {-97.965395, 33.906917}, {-97.968747, 33.906023}, {-97.970532, 33.906297}, {-97.973143, 33.908014}, {-97.974764, 33.909709}, {-97.975837, 33.911633}, {-97.976963, 33.912549}, {-97.978804, 33.912548}, {-97.979985, 33.911402}, {-97.983552, 33.904002}, + {-97.984540, 33.900703}, {-97.984373, 33.898024}, {-97.981900, 33.895322}, {-97.977860, 33.889825}, {-97.974263, 33.886706}, {-97.974177, 33.886347}, {-97.967752, 33.882214}, {-97.960716, 33.879733}, {-97.957064, 33.878767}, {-97.951201, 33.878396}, {-97.942868, 33.879062}, {-97.942483, 33.878776}, {-97.941578, 33.879181}, {-97.938948, 33.879466}, {-97.934169, 33.878109}, {-97.932290, 33.876495}, + {-97.928584, 33.874187}, {-97.918260, 33.869948}, {-97.914443, 33.868642}, {-97.912961, 33.868481}, {-97.909858, 33.867221}, {-97.909282, 33.866625}, {-97.907058, 33.865571}, {-97.905467, 33.863531}, {-97.901294, 33.861240}, {-97.898467, 33.858925}, {-97.896738, 33.857985}, {-97.894239, 33.857068}, {-97.888721, 33.856723}, {-97.888419, 33.856265}, {-97.889408, 33.856288}, {-97.883204, 33.854362}, + {-97.879829, 33.852436}, {-97.878183, 33.850534}, {-97.875311, 33.849721}, {-97.871502, 33.849093}, {-97.864175, 33.849745}, {-97.852241, 33.853091}, {-97.847802, 33.854735}, {-97.843777, 33.856813}, {-97.841581, 33.856272}, {-97.834320, 33.857634}, {-97.833225, 33.858334}, {-97.831854, 33.860087}, {-97.831894, 33.860774}, {-97.831132, 33.861741}, {-97.825877, 33.864474}, {-97.822994, 33.866467}, + {-97.820743, 33.868895}, {-97.819672, 33.870544}, {-97.818931, 33.874003}, {-97.819428, 33.877900}, {-97.819236, 33.879091}, {-97.817643, 33.880992}, {-97.815501, 33.882000}, {-97.813853, 33.881931}, {-97.812508, 33.881153}, {-97.811464, 33.880053}, {-97.810119, 33.877923}, {-97.808938, 33.876709}, {-97.807400, 33.876159}, {-97.806659, 33.876205}, {-97.805423, 33.877167}, {-97.803473, 33.880190}, + {-97.801578, 33.885138}, {-97.801631, 33.889833}, {-97.801136, 33.890933}, {-97.801630, 33.895880}, {-97.798910, 33.898537}, {-97.797015, 33.899499}, {-97.794790, 33.899956}, {-97.792400, 33.899085}, {-97.788145, 33.893770}, {-97.788145, 33.892076}, {-97.785317, 33.890701}, {-97.784656, 33.890631}, {-97.781280, 33.894460}, {-97.780839, 33.894447}, {-97.781003, 33.894936}, {-97.780041, 33.897205}, + {-97.779683, 33.899243}, {-97.779682, 33.901374}, {-97.780340, 33.904833}, {-97.783717, 33.910560}, {-97.785474, 33.912164}, {-97.786133, 33.913424}, {-97.785913, 33.914981}, {-97.783522, 33.918302}, {-97.780499, 33.919675}, {-97.778301, 33.919629}, {-97.775527, 33.917841}, {-97.774072, 33.916444}, {-97.772672, 33.914382}, {-97.767781, 33.913510}, {-97.765446, 33.913532}, {-97.763770, 33.914241}, + {-97.761736, 33.915615}, {-97.760224, 33.917194}, {-97.759399, 33.918820}, {-97.759834, 33.925210}, {-97.761425, 33.929219}, {-97.762661, 33.930846}, {-97.763043, 33.934145}, {-97.762768, 33.934396}, {-97.754990, 33.936752}, {-97.752950, 33.937102}, {-97.751143, 33.936979}, {-97.749172, 33.936229}, {-97.741327, 33.937376}, {-97.738467, 33.937305}, {-97.735507, 33.936248}, {-97.732261, 33.936519}, + {-97.724698, 33.941456}, {-97.724280, 33.942237}, {-97.721172, 33.944506}, {-97.720647, 33.944618}, {-97.720604, 33.943955}, {-97.716637, 33.947179}, {-97.712194, 33.951641}, {-97.709539, 33.954881}, {-97.704159, 33.963336}, {-97.701769, 33.969932}, {-97.699460, 33.974193}, {-97.699185, 33.975705}, {-97.698581, 33.975842}, {-97.697564, 33.978316}, {-97.697206, 33.978385}, {-97.696134, 33.979942}, + {-97.695694, 33.980080}, {-97.693110, 33.983699}, {-97.690058, 33.984981}, {-97.688738, 33.986058}, {-97.688711, 33.986516}, {-97.688023, 33.986607}, {-97.687694, 33.987180}, {-97.686016, 33.987890}, {-97.684229, 33.988417}, {-97.683762, 33.988073}, {-97.681425, 33.988439}, {-97.673669, 33.990912}, {-97.672514, 33.990935}, {-97.671277, 33.991553}, {-97.667703, 33.991094}, {-97.668033, 33.990407}, + {-97.661489, 33.990818}, {-97.657585, 33.989535}, {-97.656210, 33.989488}, {-97.655633, 33.989190}, {-97.655660, 33.988870}, {-97.657255, 33.988824}, {-97.649640, 33.986532}, {-97.648209, 33.986394}, {-97.644059, 33.984468}, {-97.633778, 33.981257}, {-97.630728, 33.979309}, {-97.629601, 33.979240}, {-97.628941, 33.978735}, {-97.628694, 33.978094}, {-97.624573, 33.975850}, {-97.621850, 33.972988}, + {-97.617560, 33.970791}, {-97.616075, 33.969716}, {-97.614151, 33.969488}, {-97.609091, 33.968093}, {-97.606974, 33.966193}, {-97.606726, 33.965574}, {-97.604526, 33.964040}, {-97.604333, 33.962758}, {-97.600979, 33.960789}, {-97.600896, 33.958888}, {-97.600401, 33.957926}, {-97.599466, 33.956323}, {-97.597980, 33.955247}, {-97.596358, 33.954537}, {-97.590971, 33.954241}, {-97.589597, 33.953554}, + {-97.588827, 33.951882}, {-97.591409, 33.948057}, {-97.592151, 33.945949}, {-97.594377, 33.944094}, {-97.595420, 33.941436}, {-97.595419, 33.938917}, {-97.595034, 33.937360}, {-97.593302, 33.933924}, {-97.591405, 33.931955}, {-97.591212, 33.929643}, {-97.591514, 33.928200}, {-97.595084, 33.922954}, {-97.596155, 33.922106}, {-97.596979, 33.920228}, {-97.596950, 33.916311}, {-97.596289, 33.913769}, + {-97.593816, 33.910265}, {-97.593239, 33.910059}, {-97.592799, 33.910357}, {-97.591288, 33.909922}, {-97.589914, 33.908044}, {-97.589282, 33.905754}, {-97.589254, 33.903922}, {-97.587441, 33.902479}, {-97.581078, 33.899679}, {-97.578082, 33.899216}, {-97.572349, 33.899263}, {-97.569404, 33.901115}, {-97.568715, 33.901379}, {-97.568340, 33.901077}, {-97.566956, 33.901583}, {-97.562788, 33.900925}, + {-97.560550, 33.899343}, {-97.560686, 33.898610}, {-97.559917, 33.897603}, {-97.558270, 33.897099}, {-97.555002, 33.897282}, {-97.551541, 33.897947}, {-97.549399, 33.899068}, {-97.543246, 33.901289}, {-97.537999, 33.904334}, {-97.534207, 33.905730}, {-97.525277, 33.911751}, {-97.523904, 33.912232}, {-97.524096, 33.911705}, {-97.519838, 33.913421}, {-97.517173, 33.914016}, {-97.510990, 33.916327}, + {-97.506050, 33.917495}, {-97.500271, 33.919635}, {-97.494846, 33.919193}, {-97.487716, 33.917508}, {-97.486508, 33.916991}, {-97.486395, 33.916399}, {-97.470542, 33.908984}, {-97.469113, 33.908618}, {-97.463564, 33.904955}, {-97.461420, 33.905230}, {-97.460267, 33.904360}, {-97.460431, 33.903581}, {-97.459772, 33.902596}, {-97.458069, 33.901635}, {-97.457492, 33.900513}, {-97.456722, 33.899826}, + {-97.456283, 33.898566}, {-97.454689, 33.896825}, {-97.451887, 33.892428}, {-97.451058, 33.891676}, {-97.451026, 33.890690}, {-97.451300, 33.890459}, {-97.450807, 33.886608}, {-97.451499, 33.885214}, {-97.451776, 33.881595}, {-97.451199, 33.878183}, {-97.451469, 33.870930}, {-97.452182, 33.868960}, {-97.453281, 33.868044}, {-97.453968, 33.866693}, {-97.456025, 33.859432}, {-97.455723, 33.858104}, + {-97.455915, 33.857646}, {-97.456931, 33.857073}, {-97.457617, 33.855126}, {-97.457974, 33.855080}, {-97.459566, 33.853316}, {-97.461486, 33.849560}, {-97.463202, 33.843090}, {-97.463242, 33.842004}, {-97.462768, 33.841864}, {-97.462307, 33.840261}, {-97.459068, 33.834581}, {-97.456048, 33.832246}, {-97.454594, 33.830002}, {-97.453057, 33.828536}, {-97.445235, 33.824643}, {-97.444879, 33.824048}, + {-97.444193, 33.823773}, {-97.437364, 33.821810}, {-97.433264, 33.821259}, {-97.430297, 33.820418}, {-97.428786, 33.819415}, {-97.426799, 33.818641}, {-97.418191, 33.818707}, {-97.414155, 33.817977}, {-97.413692, 33.819304}, {-97.410387, 33.818845}, {-97.406435, 33.818821}, {-97.403263, 33.819523}, {-97.400083, 33.819483}, {-97.395129, 33.819963}, {-97.391617, 33.819343}, {-97.390575, 33.818839}, + {-97.386205, 33.819513}, {-97.380288, 33.818526}, {-97.375775, 33.818832}, {-97.374690, 33.818552}, {-97.368731, 33.821443}, {-97.365497, 33.823744}, {-97.358513, 33.830018}, {-97.357334, 33.831369}, {-97.355991, 33.835010}, {-97.354318, 33.837965}, {-97.352946, 33.838836}, {-97.349710, 33.843372}, {-97.348338, 33.843876}, {-97.347213, 33.845113}, {-97.346747, 33.846945}, {-97.344388, 33.851504}, + {-97.344141, 33.853061}, {-97.342797, 33.854825}, {-97.342166, 33.856199}, {-97.342138, 33.857184}, {-97.339641, 33.860734}, {-97.338379, 33.866300}, {-97.338106, 33.870560}, {-97.337244, 33.875101}, {-97.335915, 33.879168}, {-97.334845, 33.881802}, {-97.333610, 33.883566}, {-97.331880, 33.884483}, {-97.328640, 33.884827}, {-97.326608, 33.884414}, {-97.323779, 33.881712}, {-97.323367, 33.880934}, + {-97.323340, 33.879170}, {-97.324218, 33.876628}, {-97.328057, 33.870263}, {-97.334178, 33.862888}, {-97.334644, 33.861445}, {-97.334616, 33.857231}, {-97.334177, 33.856246}, {-97.333436, 33.855559}, {-97.332063, 33.855056}, {-97.328729, 33.855434}, {-97.326098, 33.857083}, {-97.320346, 33.862299}, {-97.318091, 33.863530}, {-97.316389, 33.865775}, {-97.315511, 33.866164}, {-97.314257, 33.865089}, + {-97.314254, 33.866985}, {-97.310727, 33.872476}, {-97.309946, 33.874449}, {-97.309426, 33.877769}, {-97.309442, 33.880147}, {-97.309846, 33.883003}, {-97.311192, 33.887089}, {-97.310324, 33.888490}, {-97.309172, 33.888698}, {-97.307633, 33.888011}, {-97.302224, 33.883888}, {-97.297200, 33.878597}, {-97.287808, 33.870054}, {-97.281032, 33.865482}, {-97.275487, 33.863266}, {-97.275903, 33.860878}, + {-97.275241, 33.859675}, {-97.275607, 33.858168}, {-97.270644, 33.858073}, {-97.266874, 33.859957}, {-97.265839, 33.861731}, {-97.265087, 33.862413}, {-97.263446, 33.862888}, {-97.256628, 33.863292}, {-97.255639, 33.863702}, {-97.254235, 33.865323}, {-97.253163, 33.867911}, {-97.250293, 33.872708}, {-97.249209, 33.875101}, {-97.248789, 33.882818}, {-97.246537, 33.887499}, {-97.247124, 33.889383}, + {-97.246870, 33.897357}, {-97.246302, 33.900472}, {-97.245049, 33.903216}, {-97.241536, 33.906911}, {-97.239467, 33.907976}, {-97.231587, 33.910223}, {-97.228462, 33.913254}, {-97.226936, 33.913170}, {-97.226243, 33.913343}, {-97.225815, 33.913833}, {-97.210921, 33.916064}, {-97.206141, 33.914280}, {-97.203174, 33.912105}, {-97.200372, 33.908761}, {-97.198450, 33.907525}, {-97.190813, 33.903815}, + {-97.189165, 33.902601}, {-97.186584, 33.901548}, {-97.182575, 33.897609}, {-97.180845, 33.895204}, {-97.179307, 33.891127}, {-97.178155, 33.883707}, {-97.176426, 33.879081}, {-97.176343, 33.878394}, {-97.176591, 33.877432}, {-97.177030, 33.877203}, {-97.176975, 33.876699}, {-97.169947, 33.859656}, {-97.168685, 33.856037}, {-97.166629, 33.847311}, {-97.167262, 33.841563}, {-97.166824, 33.840395}, + {-97.171627, 33.835335}, {-97.175826, 33.833801}, {-97.176869, 33.833001}, {-97.179943, 33.831673}, {-97.181370, 33.831375}, {-97.186254, 33.830894}, {-97.193690, 33.831307}, {-97.195832, 33.830805}, {-97.197509, 33.829834}, {-97.199801, 33.827405}, {-97.203514, 33.821825}, {-97.204995, 33.818870}, {-97.204748, 33.816947}, {-97.204829, 33.810122}, {-97.204911, 33.809710}, {-97.205323, 33.809756}, + {-97.205570, 33.811450}, {-97.205789, 33.810053}, {-97.205569, 33.809091}, {-97.205705, 33.802908}, {-97.205431, 33.801488}, {-97.204827, 33.799908}, {-97.203236, 33.797343}, {-97.200959, 33.795442}, {-97.200492, 33.793198}, {-97.196843, 33.788940}, {-97.195554, 33.787840}, {-97.195691, 33.786810}, {-97.194786, 33.785344}, {-97.192838, 33.783146}, {-97.191220, 33.782184}, {-97.190397, 33.781153}, + {-97.189328, 33.776939}, {-97.189383, 33.776138}, {-97.189712, 33.775978}, {-97.190809, 33.776436}, {-97.188513, 33.772410}, {-97.187724, 33.769831}, {-97.188108, 33.768007}, {-97.190691, 33.765143}, {-97.191321, 33.763537}, {-97.192817, 33.761169}, {-97.192851, 33.760362}, {-97.191454, 33.757966}, {-97.185325, 33.755503}, {-97.184968, 33.756030}, {-97.181843, 33.755870}, {-97.182720, 33.755480}, + {-97.181843, 33.755366}, {-97.180143, 33.754495}, {-97.178209, 33.750104}, {-97.176302, 33.747760}, {-97.174630, 33.745035}, {-97.174000, 33.743180}, {-97.173617, 33.739607}, {-97.173287, 33.738988}, {-97.172190, 33.737545}, {-97.165256, 33.730954}, {-97.163145, 33.729329}, {-97.157373, 33.725535}, {-97.154367, 33.724094}, {-97.142227, 33.719918}, {-97.133915, 33.718128}, {-97.125135, 33.717090}, + {-97.120685, 33.717211}, {-97.111057, 33.719374}, {-97.107191, 33.721162}, {-97.103344, 33.723356}, {-97.096748, 33.728106}, {-97.090488, 33.735912}, {-97.087365, 33.741139}, {-97.086201, 33.744162}, {-97.084944, 33.752328}, {-97.084802, 33.761600}, {-97.085191, 33.764738}, {-97.086568, 33.770695}, {-97.090822, 33.780780}, {-97.094274, 33.787962}, {-97.095098, 33.792172}, {-97.095238, 33.798404}, + {-97.093765, 33.802488}, {-97.092646, 33.804198}, {-97.087335, 33.808464}, {-97.082476, 33.811102}, {-97.079105, 33.812317}, {-97.072067, 33.814049}, {-97.057957, 33.816193}, {-97.050852, 33.816604}, {-97.048146, 33.817456}, {-97.047539, 33.822297}, {-97.048315, 33.827870}, {-97.049316, 33.829794}, {-97.051949, 33.832993}, {-97.054675, 33.835049}, {-97.056967, 33.836203}, {-97.062282, 33.837990}, + {-97.071041, 33.839638}, {-97.083450, 33.840456}, {-97.087204, 33.841368}, {-97.090115, 33.843961}, {-97.090538, 33.845215}, {-97.090334, 33.847523}, {-97.088498, 33.851849}, {-97.086772, 33.853849}, {-97.082708, 33.856070}, {-97.073473, 33.859314}, {-97.069536, 33.860365}, {-97.067040, 33.860514}, {-97.063786, 33.859965}, {-97.062047, 33.859354}, {-97.057373, 33.857214}, {-97.056546, 33.856476}, + {-97.054692, 33.853922}, {-97.053560, 33.850998}, {-97.051143, 33.846810}, {-97.046126, 33.839560}, {-97.041832, 33.837756}, {-97.039428, 33.837893}, {-97.031333, 33.841238}, {-97.021438, 33.846379}, {-97.014392, 33.853889}, {-97.010951, 33.857976}, {-97.009856, 33.859762}, {-97.003514, 33.865397}, {-96.999998, 33.867894}, {-96.997088, 33.871017}, {-96.993933, 33.875656}, {-96.991900, 33.879850}, + {-96.989576, 33.882846}, {-96.989070, 33.884424}, {-96.988043, 33.886013}, {-96.986034, 33.888256}, {-96.984683, 33.888651}, {-96.983835, 33.890949}, {-96.984725, 33.903059}, {-96.985326, 33.906607}, {-96.987746, 33.914057}, {-96.987875, 33.917046}, {-96.993997, 33.928979}, {-96.995023, 33.932035}, {-96.996251, 33.942664}, {-96.995791, 33.946076}, {-96.994674, 33.948681}, {-96.990835, 33.952701}, + {-96.987892, 33.954671}, {-96.981337, 33.956378}, {-96.979927, 33.956347}, {-96.979290, 33.955969}, {-96.979347, 33.955130}, {-96.980676, 33.951814}, {-96.981031, 33.949160}, {-96.979818, 33.941588}, {-96.978754, 33.939541}, {-96.976955, 33.937453}, {-96.974869, 33.936060}, {-96.972870, 33.935698}, {-96.970703, 33.937286}, {-96.966378, 33.939539}, {-96.960017, 33.941265}, {-96.951782, 33.944744}, + {-96.949041, 33.946858}, {-96.939136, 33.951927}, {-96.934840, 33.954453}, {-96.924268, 33.959159}, {-96.920733, 33.959523}, {-96.917652, 33.958580}, {-96.916300, 33.957798}, {-96.911336, 33.953960}, {-96.907387, 33.950025}, {-96.905253, 33.947219}, {-96.900996, 33.938784}, {-96.899442, 33.933728}, {-96.898274, 33.925800}, {-96.897868, 33.920817}, {-96.896469, 33.913318}, {-96.897194, 33.902954}, + {-96.895728, 33.896414}, {-96.893673, 33.893116}, {-96.892786, 33.889938}, {-96.890499, 33.885648}, {-96.889497, 33.881616}, {-96.887558, 33.878487}, {-96.886015, 33.875102}, {-96.885687, 33.873543}, {-96.883010, 33.868019}, {-96.875281, 33.860505}, {-96.866438, 33.853149}, {-96.860247, 33.849689}, {-96.856090, 33.847490}, {-96.850593, 33.847211}, {-96.845896, 33.848975}, {-96.843709, 33.850575}, + {-96.841592, 33.852894}, {-96.841481, 33.857239}, {-96.840819, 33.863645}, {-96.839775, 33.868398}, {-96.837413, 33.871349}, {-96.832879, 33.874025}, {-96.832157, 33.874835}, {-96.828163, 33.874603}, {-96.812778, 33.872646}, {-96.794276, 33.868886}, {-96.783485, 33.863534}, {-96.780569, 33.860098}, {-96.779588, 33.857939}, {-96.779376, 33.856116}, {-96.778783, 33.854734}, {-96.777202, 33.848162}, + {-96.777104, 33.843792}, {-96.776766, 33.841976}, {-96.774531, 33.838109}, {-96.770675, 33.829621}, {-96.769377, 33.827478}, {-96.766234, 33.825459}, {-96.761587, 33.824407}, {-96.754040, 33.824658}, {-96.746038, 33.825699}, {-96.731618, 33.828475}, {-96.725102, 33.830313}, {-96.718922, 33.830588}, {-96.712422, 33.831633}, {-96.708134, 33.833060}, {-96.704457, 33.835021}, {-96.699574, 33.839049}, + {-96.690708, 33.849959}, {-96.688191, 33.854613}, {-96.684727, 33.862905}, {-96.683048, 33.869222}, {-96.682209, 33.873876}, {-96.682103, 33.876645}, {-96.683464, 33.884217}, {-96.681494, 33.894675}, {-96.680947, 33.896204}, {-96.678572, 33.900857}, {-96.675306, 33.909114}, {-96.673449, 33.912278}, {-96.670618, 33.914914}, {-96.667187, 33.916940}, {-96.664410, 33.917267}, {-96.659896, 33.916666}, + {-96.658207, 33.915841}, {-96.654984, 33.913789}, {-96.647735, 33.907973}, {-96.644050, 33.905962}, {-96.630117, 33.895422}, {-96.628294, 33.894477}, {-96.626404, 33.894392}, {-96.624093, 33.895141}, {-96.614844, 33.894546}, {-96.592170, 33.895513}, {-96.587934, 33.894784}, {-96.585452, 33.891281}, {-96.585360, 33.888948}, {-96.587494, 33.884251}, {-96.590112, 33.880665}, {-96.597348, 33.875101}, + {-96.601686, 33.872823}, {-96.611970, 33.869016}, {-96.612963, 33.867651}, {-96.625399, 33.856542}, {-96.629022, 33.852408}, {-96.629751, 33.850863}, {-96.630020, 33.848539}, {-96.629847, 33.846561}, {-96.629274, 33.845500}, {-96.623147, 33.841496}, {-96.618022, 33.839586}, {-96.612331, 33.838156}, {-96.601256, 33.834329}, {-96.592926, 33.830915}, {-96.587067, 33.828008}, {-96.572937, 33.819098}, + {-96.566666, 33.818511}, {-96.563599, 33.819165}, {-96.560420, 33.818862}, {-96.556213, 33.819142}, {-96.556997, 33.817983}, {-96.558004, 33.817642}, {-96.558109, 33.817131}, {-96.557122, 33.817565}, {-96.555654, 33.819389}, {-96.554923, 33.819279}, {-96.555028, 33.818867}, {-96.553982, 33.819175}, {-96.551065, 33.819252}, {-96.551809, 33.818834}, {-96.551704, 33.818477}, {-96.548754, 33.819944}, + {-96.545417, 33.820124}, {-96.543139, 33.820816}, {-96.541677, 33.820805}, {-96.533368, 33.822770}, {-96.531966, 33.823989}, {-96.531729, 33.823462}, {-96.532131, 33.823572}, {-96.532513, 33.822858}, {-96.528623, 33.821664}, {-96.524865, 33.819526}, {-96.524069, 33.817503}, {-96.520609, 33.812623}, {-96.517848, 33.806858}, {-96.516560, 33.802248}, {-96.515689, 33.795165}, {-96.515745, 33.791418}, + {-96.516581, 33.788279}, {-96.515910, 33.787792}, {-96.512709, 33.782801}, {-96.510855, 33.780608}, {-96.508487, 33.778321}, {-96.500984, 33.772801}, {-96.500352, 33.773257}, {-96.500233, 33.772586}, {-96.493375, 33.773040}, {-96.486341, 33.772965}, {-96.476694, 33.774930}, {-96.473092, 33.775445}, {-96.471154, 33.775205}, {-96.469365, 33.775792}, {-96.459154, 33.775232}, {-96.456254, 33.776035}, + {-96.450510, 33.780588}, {-96.448045, 33.781031}, {-96.442042, 33.780488}, {-96.436714, 33.779329}, {-96.431987, 33.779105}, {-96.423532, 33.776432}, {-96.421462, 33.774939}, {-96.418978, 33.771637}, {-96.416146, 33.766099}, {-96.414576, 33.761199}, {-96.413632, 33.759268}, {-96.413408, 33.757714}, {-96.408220, 33.750542}, {-96.406589, 33.749042}, {-96.405452, 33.748497}, {-96.399976, 33.743551}, + {-96.395046, 33.738401}, {-96.388986, 33.733585}, {-96.382828, 33.729269}, {-96.379452, 33.725764}, {-96.378438, 33.723975}, {-96.375485, 33.720876}, {-96.370847, 33.717996}, {-96.369589, 33.716809}, {-96.368447, 33.714926}, {-96.366944, 33.711223}, {-96.366015, 33.705493}, {-96.363252, 33.701050}, {-96.363135, 33.694215}, {-96.362197, 33.691818}, {-96.359374, 33.689432}, {-96.355634, 33.687494}, + {-96.354089, 33.686983}, {-96.351933, 33.687028}, {-96.350566, 33.686572}, {-96.346859, 33.686216}, {-96.342568, 33.686755}, {-96.338756, 33.688316}, {-96.334604, 33.692366}, {-96.332917, 33.693158}, {-96.328076, 33.694158}, {-96.322152, 33.694705}, {-96.321098, 33.695102}, {-96.318378, 33.697118}, {-96.314886, 33.702696}, {-96.312230, 33.706304}, {-96.309964, 33.710491}, {-96.308682, 33.713988}, + {-96.308183, 33.716851}, {-96.308529, 33.717620}, {-96.308168, 33.718670}, {-96.307298, 33.719540}, {-96.306364, 33.723062}, {-96.306310, 33.729347}, {-96.307874, 33.735022}, {-96.307650, 33.737451}, {-96.306920, 33.738956}, {-96.306163, 33.744032}, {-96.303657, 33.748328}, {-96.300147, 33.755681}, {-96.299637, 33.756150}, {-96.299610, 33.756924}, {-96.298110, 33.759125}, {-96.297794, 33.761213}, + {-96.298202, 33.761015}, {-96.298255, 33.759400}, {-96.298827, 33.758548}, {-96.298761, 33.760905}, {-96.297031, 33.763234}, {-96.296412, 33.766833}, {-96.295194, 33.768338}, {-96.293708, 33.769409}, {-96.292161, 33.769810}, {-96.290359, 33.770831}, {-96.287083, 33.771243}, {-96.278301, 33.770598}, {-96.268928, 33.767958}, {-96.263593, 33.767467}, {-96.262133, 33.766922}, {-96.256893, 33.763728}, + {-96.255092, 33.761634}, {-96.248243, 33.759106}, {-96.238724, 33.753693}, {-96.236926, 33.753259}, {-96.230352, 33.748524}, {-96.229023, 33.748021}, {-96.224599, 33.748327}, {-96.220522, 33.747390}, {-96.215851, 33.749179}, {-96.208994, 33.750096}, {-96.203266, 33.751339}, {-96.202142, 33.752015}, {-96.201319, 33.751795}, {-96.197985, 33.752554}, {-96.190166, 33.755570}, {-96.188015, 33.756020}, + {-96.186627, 33.755021}, {-96.187732, 33.756097}, {-96.178603, 33.760426}, {-96.172933, 33.765221}, {-96.169090, 33.770417}, {-96.168333, 33.772538}, {-96.168112, 33.774475}, {-96.167538, 33.774485}, {-96.166595, 33.777680}, {-96.165199, 33.780130}, {-96.165114, 33.781638}, {-96.164100, 33.784261}, {-96.162775, 33.786421}, {-96.163597, 33.786936}, {-96.163713, 33.787728}, {-96.162757, 33.788769}, + {-96.162483, 33.789904}, {-96.162902, 33.791974}, {-96.162745, 33.792613}, {-96.160756, 33.795910}, {-96.161954, 33.799713}, {-96.161121, 33.802692}, {-96.161385, 33.803154}, {-96.161114, 33.805767}, {-96.159822, 33.811550}, {-96.159273, 33.812328}, {-96.156355, 33.813349}, {-96.155538, 33.814040}, {-96.158772, 33.813941}, {-96.163766, 33.813051}, {-96.164979, 33.810940}, {-96.166896, 33.809602}, + {-96.168041, 33.808289}, {-96.169121, 33.806048}, {-96.169950, 33.805131}, {-96.170721, 33.803192}, {-96.170280, 33.803708}, {-96.170023, 33.803252}, {-96.169457, 33.804109}, {-96.169592, 33.800944}, {-96.172857, 33.801102}, {-96.175726, 33.802321}, {-96.177339, 33.805114}, {-96.179122, 33.810224}, {-96.176910, 33.813935}, {-96.176307, 33.814375}, {-96.175349, 33.814736}, {-96.173630, 33.814480}, + {-96.171353, 33.814725}, {-96.170497, 33.815132}, {-96.170432, 33.815841}, {-96.166503, 33.817867}, {-96.161455, 33.817640}, {-96.159295, 33.818599}, {-96.150765, 33.816987}, {-96.148792, 33.819197}, {-96.151630, 33.831946}, {-96.150147, 33.835855}, {-96.149227, 33.837091}, {-96.146535, 33.838303}, {-96.138905, 33.839159}, {-96.135189, 33.838602}, {-96.131058, 33.837438}, {-96.130349, 33.837824}, + {-96.129807, 33.838872}, {-96.127818, 33.839896}, {-96.122949, 33.839964}, {-96.118167, 33.837883}, {-96.114437, 33.835056}, {-96.109994, 33.832396}, {-96.104078, 33.830730}, {-96.099360, 33.830470}, {-96.097449, 33.832729}, {-96.097638, 33.837936}, {-96.099153, 33.842409}, {-96.100785, 33.844229}, {-96.101349, 33.845721}, {-96.101473, 33.846710}, {-96.100746, 33.847683}, {-96.100095, 33.847971}, + {-96.099112, 33.847586}, {-96.098296, 33.847848}, {-96.091683, 33.846873}, {-96.084626, 33.846656}, {-96.077442, 33.844973}, {-96.072658, 33.842937}, {-96.068290, 33.842690}, {-96.063934, 33.841528}, {-96.055358, 33.838262}, {-96.053560, 33.838203}, {-96.048834, 33.836468}, {-96.043271, 33.838996}, {-96.038978, 33.840280}, {-96.034988, 33.842646}, {-96.032545, 33.845266}, {-96.030865, 33.848363}, + {-96.030488, 33.851060}, {-96.030809, 33.854230}, {-96.030501, 33.855261}, {-96.029438, 33.856348}, {-96.026968, 33.857474}, {-96.022408, 33.857042}, {-96.020083, 33.855046}, {-96.019542, 33.853497}, {-96.020326, 33.849579}, {-96.022508, 33.846130}, {-96.023095, 33.844128}, {-96.023241, 33.842479}, {-96.022736, 33.841092}, {-96.020624, 33.840627}, {-96.017055, 33.841454}, {-96.010907, 33.844296}, + {-96.005704, 33.845435}, {-96.004050, 33.845237}, {-96.004237, 33.845544}, {-96.003595, 33.846028}, {-96.004998, 33.845987}, {-96.004473, 33.846836}, {-96.002747, 33.848532}, {-96.000257, 33.849403}, {-95.998351, 33.851050}, {-95.996833, 33.853898}, {-95.995590, 33.857907}, {-95.994869, 33.861824}, {-95.999694, 33.862552}, {-96.002875, 33.866670}, {-96.003982, 33.868405}, {-96.004573, 33.870712}, + {-96.002925, 33.873590}, {-96.001873, 33.873744}, {-96.000255, 33.872540}, {-95.998617, 33.872230}, {-95.996377, 33.871231}, {-95.992111, 33.868849}, {-95.989191, 33.868590}, {-95.987836, 33.867949}, {-95.984927, 33.865805}, {-95.984059, 33.860118}, {-95.986040, 33.856931}, {-95.984856, 33.852492}, {-95.974629, 33.855464}, {-95.962305, 33.857099}, {-95.957729, 33.858166}, {-95.951962, 33.858832}, + {-95.947327, 33.858915}, {-95.948400, 33.858745}, {-95.948340, 33.858547}, {-95.950046, 33.857783}, {-95.951264, 33.857755}, {-95.952383, 33.857310}, {-95.951382, 33.857332}, {-95.948676, 33.858041}, {-95.944140, 33.859998}, {-95.941796, 33.861651}, {-95.941250, 33.862613}, {-95.941224, 33.864151}, {-95.939143, 33.866453}, {-95.936404, 33.871424}, {-95.935634, 33.875000}, {-95.933474, 33.878614}, + {-95.933500, 33.880031}, {-95.932868, 33.881372}, {-95.932677, 33.883173}, {-95.932209, 33.883882}, {-95.931241, 33.884640}, {-95.928250, 33.885354}, {-95.925089, 33.884936}, {-95.918583, 33.880838}, {-95.913907, 33.878854}, {-95.909904, 33.876639}, {-95.901865, 33.871366}, {-95.895215, 33.867580}, {-95.890409, 33.865563}, {-95.887952, 33.865408}, {-95.886833, 33.864935}, {-95.882930, 33.862033}, + {-95.885418, 33.863353}, {-95.885188, 33.863012}, {-95.881153, 33.860555}, {-95.879435, 33.858961}, {-95.877302, 33.857873}, {-95.872360, 33.854369}, {-95.867189, 33.851290}, {-95.861046, 33.849799}, {-95.860381, 33.849805}, {-95.860025, 33.850250}, {-95.858630, 33.850690}, {-95.856832, 33.850141}, {-95.854540, 33.848225}, {-95.848921, 33.841784}, {-95.843935, 33.838198}, {-95.837114, 33.835646}, + {-95.830406, 33.834785}, {-95.823699, 33.837406}, {-95.820974, 33.839516}, {-95.819486, 33.841296}, {-95.818855, 33.843021}, {-95.819158, 33.844982}, {-95.818881, 33.846663}, {-95.819448, 33.849020}, {-95.821121, 33.853068}, {-95.821161, 33.854233}, {-95.820654, 33.855447}, {-95.820911, 33.856172}, {-95.819930, 33.857546}, {-95.817718, 33.858331}, {-95.816796, 33.859331}, {-95.815571, 33.859853}, + {-95.810073, 33.860271}, {-95.806432, 33.860832}, {-95.805016, 33.861381}, {-95.802184, 33.861562}, {-95.797614, 33.860590}, {-95.791135, 33.857875}, {-95.788679, 33.856408}, {-95.784914, 33.853353}, {-95.783939, 33.853111}, {-95.782366, 33.851079}, {-95.781142, 33.850870}, {-95.779884, 33.851331}, {-95.775902, 33.847311}, {-95.771968, 33.845604}, {-95.770239, 33.845202}, {-95.763586, 33.846824}, + {-95.757959, 33.849566}, {-95.754520, 33.852218}, {-95.752109, 33.854953}, {-95.751555, 33.856035}, {-95.751342, 33.860287}, {-95.752071, 33.862029}, {-95.754131, 33.865249}, {-95.757803, 33.868975}, {-95.760056, 33.873817}, {-95.761520, 33.873118}, {-95.762063, 33.875107}, {-95.762467, 33.881144}, {-95.761729, 33.884317}, {-95.759389, 33.888768}, {-95.757941, 33.890752}, {-95.756612, 33.892028}, + {-95.752153, 33.894577}, {-95.748856, 33.896081}, {-95.745131, 33.896690}, {-95.736316, 33.896866}, {-95.733183, 33.896447}, {-95.731732, 33.895513}, {-95.729309, 33.894623}, {-95.728159, 33.894633}, {-95.726663, 33.893506}, {-95.719980, 33.890944}, {-95.717999, 33.889667}, {-95.716950, 33.888288}, {-95.715198, 33.887001}, {-95.712183, 33.885519}, {-95.700635, 33.885845}, {-95.699752, 33.886216}, + {-95.694888, 33.886787}, {-95.690483, 33.887936}, {-95.688876, 33.888486}, {-95.687053, 33.889890}, {-95.684579, 33.890646}, {-95.683949, 33.891571}, {-95.679280, 33.894364}, {-95.677922, 33.896976}, {-95.672101, 33.902280}, {-95.670787, 33.904406}, {-95.669241, 33.905848}, {-95.668632, 33.906995}, {-95.665478, 33.909004}, {-95.660460, 33.910162}, {-95.658350, 33.910258}, {-95.657535, 33.909650}, + {-95.656321, 33.909377}, {-95.655232, 33.909914}, {-95.651128, 33.908631}, {-95.648120, 33.908128}, {-95.638772, 33.904938}, {-95.635337, 33.905263}, {-95.634367, 33.904727}, {-95.632774, 33.904815}, {-95.631781, 33.905534}, {-95.629256, 33.905895}, {-95.627318, 33.907806}, {-95.626024, 33.912501}, {-95.624798, 33.915664}, {-95.621640, 33.920513}, {-95.619053, 33.923015}, {-95.618360, 33.924208}, + {-95.618357, 33.924849}, {-95.616367, 33.927626}, {-95.614746, 33.928561}, {-95.613661, 33.928414}, {-95.611000, 33.929044}, {-95.609074, 33.929200}, {-95.605132, 33.928757}, {-95.603138, 33.929200}, {-95.599108, 33.933071}, {-95.598416, 33.934380}, {-95.597954, 33.935722}, {-95.597959, 33.939361}, {-95.597522, 33.942342}, {-95.594085, 33.943057}, {-95.589840, 33.942045}, {-95.587870, 33.940811}, + {-95.586598, 33.939586}, {-95.584542, 33.936682}, {-95.581854, 33.934152}, {-95.577324, 33.932927}, {-95.573998, 33.933506}, {-95.570264, 33.933717}, {-95.567393, 33.933616}, {-95.563784, 33.932945}, {-95.557711, 33.930351}, {-95.554210, 33.926958}, {-95.552799, 33.924311}, {-95.551312, 33.916220}, {-95.549114, 33.911534}, {-95.548234, 33.906616}, {-95.547832, 33.901856}, {-95.548479, 33.897573}, + {-95.550313, 33.893942}, {-95.551210, 33.891104}, {-95.551216, 33.889713}, {-95.548961, 33.885053}, {-95.547638, 33.883509}, {-95.544141, 33.880603}, {-95.538576, 33.880671}, {-95.535721, 33.881144}, {-95.534306, 33.881052}, {-95.532622, 33.881521}, {-95.529811, 33.883515}, {-95.528007, 33.883884}, {-95.526523, 33.884684}, {-95.523788, 33.887322}, {-95.518529, 33.889877}, {-95.517553, 33.891303}, + {-95.517428, 33.892117}, {-95.516433, 33.893035}, {-95.515979, 33.893986}, {-95.512759, 33.895792}, {-95.511439, 33.897296}, {-95.509526, 33.897586}, {-95.507952, 33.897433}, {-95.506619, 33.896716}, {-95.505788, 33.895564}, {-95.505092, 33.893406}, {-95.504838, 33.889714}, {-95.505656, 33.885831}, {-95.505365, 33.882430}, {-95.506210, 33.877809}, {-95.505776, 33.876391}, {-95.504999, 33.875922}, + {-95.501634, 33.875015}, {-95.499913, 33.875230}, {-95.492040, 33.874942}, {-95.483531, 33.876825}, {-95.473296, 33.880197}, {-95.470470, 33.881599}, {-95.468830, 33.883116}, {-95.468554, 33.884632}, {-95.464938, 33.886891}, {-95.463594, 33.887249}, {-95.461440, 33.887106}, {-95.459730, 33.886611}, {-95.460091, 33.884199}, {-95.462790, 33.879051}, {-95.462847, 33.876856}, {-95.463785, 33.876038}, + {-95.464339, 33.874389}, {-95.464225, 33.873381}, {-95.463336, 33.872396}, {-95.453655, 33.870862}, {-95.449880, 33.869527}, {-95.445466, 33.867236}, {-95.442746, 33.866506}, {-95.438300, 33.866342}, {-95.434546, 33.867551}, {-95.432372, 33.868765}, {-95.427684, 33.870459}, {-95.424606, 33.869905}, {-95.421651, 33.868683}, {-95.418710, 33.868483}, {-95.415820, 33.866916}, {-95.410715, 33.865406}, + {-95.405209, 33.864575}, {-95.400493, 33.864756}, {-95.398042, 33.865211}, {-95.389695, 33.867978}, {-95.383826, 33.868646}, {-95.381415, 33.868453}, {-95.378800, 33.868721}, {-95.375743, 33.868401}, {-95.372733, 33.867322}, {-95.369301, 33.866574}, {-95.367832, 33.866472}, {-95.365626, 33.867064}, {-95.362571, 33.866902}, {-95.358374, 33.868431}, {-95.357383, 33.869235}, {-95.352359, 33.869597}, + {-95.351422, 33.870300}, {-95.350322, 33.870605}, {-95.347728, 33.870503}, {-95.346587, 33.871557}, {-95.342513, 33.872972}, {-95.341114, 33.873323}, {-95.339879, 33.873017}, {-95.339064, 33.874071}, {-95.337706, 33.874659}, {-95.335696, 33.874252}, {-95.332071, 33.879301}, {-95.330458, 33.882532}, {-95.329936, 33.886334}, {-95.325535, 33.885734}, {-95.314428, 33.881632}, {-95.313697, 33.881100}, + {-95.312564, 33.879001}, {-95.310318, 33.877033}, {-95.306397, 33.874906}, {-95.303478, 33.874307}, {-95.300975, 33.874186}, {-95.294513, 33.872785}, {-95.292747, 33.873230}, {-95.288926, 33.873070}, {-95.286798, 33.873487}, {-95.280725, 33.875491}, {-95.279025, 33.876677}, {-95.278174, 33.878386}, {-95.278640, 33.883579}, {-95.278244, 33.883947}, {-95.277940, 33.887293}, {-95.278163, 33.890343}, + {-95.280691, 33.896075}, {-95.281784, 33.897537}, {-95.283952, 33.899126}, {-95.284558, 33.899917}, {-95.285591, 33.903357}, {-95.284629, 33.905483}, {-95.281537, 33.908637}, {-95.275960, 33.911279}, {-95.273198, 33.911624}, {-95.269949, 33.911491}, {-95.268697, 33.911090}, {-95.266385, 33.908468}, {-95.264997, 33.904588}, {-95.265086, 33.898307}, {-95.265825, 33.895527}, {-95.266142, 33.895510}, + {-95.266656, 33.893900}, {-95.265780, 33.893697}, {-95.265141, 33.892707}, {-95.264425, 33.889778}, {-95.262120, 33.887826}, {-95.261026, 33.887848}, {-95.257724, 33.890122}, {-95.253187, 33.894878}, {-95.252671, 33.897296}, {-95.250217, 33.901614}, {-95.248501, 33.906974}, {-95.247597, 33.915613}, {-95.248187, 33.919926}, {-95.249661, 33.925684}, {-95.250426, 33.932711}, {-95.250893, 33.934255}, + {-95.250840, 33.936358}, {-95.250001, 33.938861}, {-95.245996, 33.943721}, {-95.240539, 33.946784}, {-95.240605, 33.947229}, {-95.238746, 33.947785}, {-95.236557, 33.947907}, {-95.234191, 33.948771}, {-95.232879, 33.949736}, {-95.230884, 33.955141}, {-95.230258, 33.955988}, {-95.230423, 33.957785}, {-95.230142, 33.959724}, {-95.230865, 33.959512}, {-95.230444, 33.960043}, {-95.227574, 33.960798}, + {-95.227076, 33.961577}, {-95.226041, 33.962237}, {-95.220488, 33.961997}, {-95.216947, 33.962674}, {-95.214137, 33.962142}, {-95.205761, 33.958616}, {-95.202615, 33.956907}, {-95.199541, 33.954105}, {-95.198104, 33.953352}, {-95.192551, 33.951803}, {-95.189478, 33.951281}, {-95.184968, 33.949529}, {-95.180280, 33.948836}, {-95.171503, 33.946720}, {-95.167792, 33.944686}, {-95.156961, 33.936645}, + {-95.149069, 33.935297}, {-95.139484, 33.934733}, {-95.134861, 33.935990}, {-95.130157, 33.936799}, {-95.128171, 33.936259}, {-95.124700, 33.934675}, {-95.121184, 33.931307}, {-95.121327, 33.927181}, {-95.121727, 33.925762}, {-95.127731, 33.920435}, {-95.131642, 33.916027}, {-95.131644, 33.913926}, {-95.131041, 33.912535}, {-95.127364, 33.907826}, {-95.125777, 33.906497}, {-95.123101, 33.905417}, + {-95.122488, 33.905528}, {-95.120893, 33.904539}, {-95.117768, 33.904233}, {-95.113148, 33.904493}, {-95.110388, 33.905407}, {-95.108622, 33.906429}, {-95.106784, 33.908496}, {-95.105176, 33.909464}, {-95.099943, 33.910318}, {-95.095778, 33.911374}, {-95.089801, 33.914069}, {-95.085570, 33.917719}, {-95.084655, 33.920038}, {-95.083198, 33.921417}, {-95.082229, 33.921742}, {-95.080060, 33.920989}, + {-95.077950, 33.918696}, {-95.076782, 33.913584}, {-95.077830, 33.911265}, {-95.079742, 33.908595}, {-95.081606, 33.906860}, {-95.087583, 33.903018}, {-95.090219, 33.902720}, {-95.092064, 33.901945}, {-95.091003, 33.901572}, {-95.091503, 33.900329}, {-95.092946, 33.900148}, {-95.094765, 33.899438}, {-95.096300, 33.899377}, {-95.097408, 33.898861}, {-95.099351, 33.897030}, {-95.100003, 33.896793}, + {-95.100378, 33.895595}, {-95.099856, 33.892776}, {-95.098709, 33.890683}, {-95.097786, 33.889650}, {-95.095833, 33.888430}, {-95.092118, 33.883216}, {-95.089607, 33.880857}, {-95.087400, 33.880321}, {-95.082408, 33.879882}, {-95.080102, 33.885706}, {-95.079834, 33.887479}, {-95.078696, 33.905040}, {-95.077704, 33.906712}, {-95.075908, 33.907656}, {-95.074988, 33.908805}, {-95.073966, 33.915636}, + {-95.069476, 33.917586}, {-95.067955, 33.917551}, {-95.066760, 33.917101}, {-95.064512, 33.915346}, {-95.063459, 33.914040}, {-95.063318, 33.912059}, {-95.064235, 33.910156}, {-95.065432, 33.908953}, {-95.066979, 33.908253}, {-95.070573, 33.908401}, {-95.074629, 33.907267}, {-95.076417, 33.904298}, {-95.075876, 33.901846}, {-95.073597, 33.898810}, {-95.073267, 33.897904}, {-95.072918, 33.897849}, + {-95.071801, 33.895561}, {-95.069952, 33.895578}, {-95.071231, 33.898778}, {-95.073528, 33.901966}, {-95.072413, 33.902699}, {-95.069128, 33.903419}, {-95.062284, 33.903618}, {-95.060105, 33.901873}, {-95.058834, 33.894846}, {-95.059088, 33.887491}, {-95.058460, 33.886246}, {-95.056333, 33.884142}, {-95.054787, 33.881648}, {-95.050572, 33.878299}, {-95.051319, 33.874782}, {-95.050704, 33.868179}, + {-95.049025, 33.864090}, {-95.046568, 33.862565}, {-95.043921, 33.861660}, {-95.037207, 33.860250}, {-95.029715, 33.860091}, {-95.020672, 33.858300}, {-95.015745, 33.857733}, {-95.012214, 33.858034}, {-95.010185, 33.858731}, {-95.003589, 33.862246}, {-95.002187, 33.863713}, {-95.000223, 33.862505}, {-94.998019, 33.860504}, {-94.997124, 33.859067}, {-94.995524, 33.857438}, {-94.993921, 33.854759}, + {-94.993418, 33.853322}, {-94.992671, 33.852455}, {-94.992163, 33.852034}, {-94.988487, 33.851000}, {-94.983295, 33.851357}, {-94.981634, 33.852314}, {-94.977010, 33.857971}, {-94.975896, 33.860188}, {-94.972735, 33.861983}, {-94.971435, 33.862123}, {-94.970205, 33.861817}, {-94.968892, 33.860913}, {-94.968317, 33.856696}, {-94.965799, 33.848696}, {-94.965647, 33.843746}, {-94.964401, 33.837021}, + {-94.957676, 33.835004}, {-94.952298, 33.834949}, {-94.947281, 33.832648}, {-94.944681, 33.831055}, {-94.943888, 33.830226}, {-94.943531, 33.828378}, {-94.944838, 33.825840}, {-94.947663, 33.823552}, {-94.953518, 33.816475}, {-94.953084, 33.815203}, {-94.947874, 33.808356}, {-94.943667, 33.806291}, {-94.942217, 33.805780}, {-94.940436, 33.805637}, {-94.923289, 33.808742}, {-94.924266, 33.812494}, + {-94.923099, 33.815456}, {-94.923815, 33.819168}, {-94.926571, 33.826339}, {-94.926616, 33.827557}, {-94.926072, 33.828533}, {-94.924573, 33.829047}, {-94.922439, 33.829113}, {-94.920925, 33.828728}, {-94.918599, 33.826820}, {-94.918010, 33.826783}, {-94.915908, 33.824760}, {-94.913655, 33.821729}, {-94.912160, 33.819443}, {-94.910882, 33.815915}, {-94.911042, 33.815023}, {-94.912596, 33.812591}, + {-94.914487, 33.808516}, {-94.916748, 33.808969}, {-94.917600, 33.807001}, {-94.917325, 33.805861}, {-94.917533, 33.805210}, {-94.919436, 33.800960}, {-94.921314, 33.798408}, {-94.922353, 33.793535}, {-94.921526, 33.790472}, {-94.918071, 33.786880}, {-94.917598, 33.786717}, {-94.916937, 33.784936}, {-94.912868, 33.779476}, {-94.911427, 33.778383}, {-94.906243, 33.778190}, {-94.904172, 33.777198}, + {-94.904599, 33.775678}, {-94.904198, 33.773249}, {-94.901281, 33.770850}, {-94.898198, 33.769021}, {-94.895303, 33.768404}, {-94.886934, 33.769067}, {-94.885678, 33.765234}, {-94.883847, 33.763243}, {-94.881395, 33.761177}, {-94.879395, 33.760188}, {-94.877357, 33.758574}, {-94.876372, 33.757330}, {-94.874772, 33.756904}, {-94.874143, 33.757342}, {-94.873833, 33.758059}, {-94.874930, 33.767432}, + {-94.874935, 33.768768}, {-94.874564, 33.769618}, {-94.873453, 33.770520}, {-94.871737, 33.770395}, {-94.869606, 33.769862}, {-94.868144, 33.768874}, {-94.866513, 33.766970}, {-94.865929, 33.765637}, {-94.866097, 33.763401}, {-94.866736, 33.761830}, {-94.868374, 33.760126}, {-94.869667, 33.759521}, {-94.873965, 33.756340}, {-94.876124, 33.755501}, {-94.876082, 33.754899}, {-94.877125, 33.753123}, + {-94.877083, 33.752220}, {-94.874669, 33.749164}, {-94.872944, 33.748785}, {-94.869913, 33.746196}, {-94.868820, 33.745881}, {-94.867768, 33.745009}, {-94.866149, 33.744669}, {-94.860687, 33.741945}, {-94.856554, 33.740710}, {-94.849314, 33.739587}, {-94.842574, 33.739445}, {-94.827948, 33.740882}, {-94.828321, 33.734947}, {-94.827256, 33.734131}, {-94.822928, 33.732724}, {-94.821989, 33.732878}, + {-94.820429, 33.733807}, {-94.820089, 33.734826}, {-94.820155, 33.736984}, {-94.823042, 33.747525}, {-94.830844, 33.758079}, {-94.830875, 33.759985}, {-94.829499, 33.764104}, {-94.827275, 33.766885}, {-94.825294, 33.768525}, {-94.823385, 33.769179}, {-94.820707, 33.768964}, {-94.818574, 33.767704}, {-94.817335, 33.765842}, {-94.816387, 33.763963}, {-94.815937, 33.759787}, {-94.816831, 33.754216}, + {-94.815253, 33.751382}, {-94.814602, 33.751838}, {-94.812764, 33.752113}, {-94.812155, 33.750936}, {-94.812193, 33.747726}, {-94.812912, 33.745447}, {-94.814803, 33.743119}, {-94.816983, 33.741591}, {-94.817023, 33.740007}, {-94.812021, 33.735262}, {-94.809145, 33.734510}, {-94.807268, 33.733411}, {-94.803433, 33.732739}, {-94.801874, 33.732949}, {-94.797260, 33.736925}, {-94.790051, 33.739356}, + {-94.781542, 33.743322}, {-94.780068, 33.744469}, {-94.778416, 33.748445}, {-94.777841, 33.753006}, {-94.776368, 33.756399}, {-94.774923, 33.758115}, {-94.772915, 33.759733}, {-94.771345, 33.760648}, {-94.769850, 33.761034}, {-94.763035, 33.759764}, {-94.762043, 33.759022}, {-94.760058, 33.758291}, {-94.759323, 33.756730}, {-94.758125, 33.756537}, {-94.757847, 33.755889}, {-94.757189, 33.753028}, + {-94.757813, 33.750109}, {-94.758948, 33.748532}, {-94.760218, 33.748031}, {-94.760868, 33.747320}, {-94.762226, 33.746609}, {-94.763233, 33.746642}, {-94.765256, 33.746096}, {-94.769023, 33.746202}, {-94.772425, 33.746767}, {-94.775836, 33.746712}, {-94.776815, 33.746437}, {-94.778866, 33.745099}, {-94.781360, 33.742110}, {-94.785974, 33.738515}, {-94.788637, 33.735669}, {-94.782498, 33.730125}, + {-94.779372, 33.728872}, {-94.773126, 33.727187}, {-94.768695, 33.726989}, {-94.760604, 33.727054}, {-94.754396, 33.733390}, {-94.752389, 33.734556}, {-94.751825, 33.734326}, {-94.751371, 33.733727}, {-94.750287, 33.729234}, {-94.747668, 33.725148}, {-94.757738, 33.717817}, {-94.756998, 33.716040}, {-94.755905, 33.715073}, {-94.751366, 33.713540}, {-94.747585, 33.714734}, {-94.745855, 33.716158}, + {-94.744916, 33.718614}, {-94.745089, 33.722908}, {-94.744495, 33.724346}, {-94.742092, 33.726692}, {-94.736739, 33.727187}, {-94.735178, 33.726616}, {-94.733413, 33.725191}, {-94.732630, 33.723917}, {-94.732329, 33.722505}, {-94.732786, 33.720035}, {-94.733593, 33.718685}, {-94.736129, 33.716080}, {-94.738572, 33.714501}, {-94.741237, 33.713358}, {-94.753069, 33.710753}, {-94.753801, 33.708663}, + {-94.753651, 33.706931}, {-94.751942, 33.704862}, {-94.743091, 33.701988}, {-94.740922, 33.700555}, {-94.739413, 33.696990}, {-94.737179, 33.692928}, {-94.735856, 33.691625}, {-94.734366, 33.691144}, {-94.730387, 33.691254}, {-94.726123, 33.690899}, {-94.725243, 33.690098}, {-94.724076, 33.689667}, {-94.719531, 33.688857}, {-94.715943, 33.694913}, {-94.716873, 33.700358}, {-94.718872, 33.703349}, + {-94.719633, 33.703921}, {-94.722821, 33.704811}, {-94.727199, 33.705314}, {-94.728484, 33.705791}, {-94.730691, 33.707355}, {-94.732524, 33.709211}, {-94.733643, 33.711306}, {-94.734918, 33.714765}, {-94.734731, 33.715437}, {-94.733965, 33.715687}, {-94.732330, 33.715650}, {-94.730280, 33.716060}, {-94.725295, 33.714672}, {-94.712719, 33.708962}, {-94.706024, 33.706293}, {-94.702601, 33.704038}, + {-94.701560, 33.702878}, {-94.700884, 33.701417}, {-94.701055, 33.700053}, {-94.701996, 33.698655}, {-94.703496, 33.697538}, {-94.706336, 33.696726}, {-94.709321, 33.697174}, {-94.710841, 33.696743}, {-94.713201, 33.697740}, {-94.714432, 33.697189}, {-94.715123, 33.695961}, {-94.715112, 33.694108}, {-94.718338, 33.688664}, {-94.716188, 33.687902}, {-94.712067, 33.687935}, {-94.709101, 33.688311}, + {-94.704114, 33.687945}, {-94.701750, 33.687621}, {-94.697246, 33.686203}, {-94.691756, 33.685474}, {-94.688220, 33.684440}, {-94.684796, 33.684353}, {-94.677383, 33.688342}, {-94.673933, 33.691844}, {-94.671773, 33.693307}, {-94.668891, 33.694392}, {-94.666885, 33.694583}, {-94.664914, 33.694255}, {-94.659167, 33.692138}, {-94.657529, 33.690194}, {-94.654861, 33.688568}, {-94.651402, 33.685843}, + {-94.648804, 33.686573}, {-94.647093, 33.690346}, {-94.647241, 33.692654}, {-94.648621, 33.697011}, {-94.648392, 33.699490}, {-94.647902, 33.700516}, {-94.647277, 33.701187}, {-94.645882, 33.701778}, {-94.643715, 33.701522}, {-94.641776, 33.700385}, {-94.639450, 33.698259}, {-94.638731, 33.697110}, {-94.638646, 33.695804}, {-94.639775, 33.693938}, {-94.643132, 33.690323}, {-94.645492, 33.685932}, + {-94.650373, 33.684438}, {-94.649821, 33.683407}, {-94.648055, 33.681527}, {-94.645579, 33.675500}, {-94.645337, 33.673644}, {-94.650685, 33.670604}, {-94.651850, 33.670453}, {-94.653996, 33.671147}, {-94.659525, 33.675129}, {-94.661806, 33.675133}, {-94.667540, 33.672800}, {-94.668848, 33.671878}, {-94.670184, 33.670343}, {-94.671048, 33.668388}, {-94.670564, 33.665934}, {-94.669311, 33.664362}, + {-94.666855, 33.662808}, {-94.663955, 33.661994}, {-94.663195, 33.661084}, {-94.661829, 33.660457}, {-94.657219, 33.660592}, {-94.654919, 33.659925}, {-94.651734, 33.660704}, {-94.649234, 33.662604}, {-94.645923, 33.661945}, {-94.644692, 33.662310}, {-94.642451, 33.663887}, {-94.640054, 33.663672}, {-94.638679, 33.663988}, {-94.637563, 33.665281}, {-94.636939, 33.667469}, {-94.634938, 33.667649}, + {-94.630919, 33.673442}, {-94.630554, 33.674459}, {-94.630278, 33.678116}, {-94.629922, 33.679094}, {-94.628711, 33.680532}, {-94.627239, 33.681954}, {-94.624298, 33.682336}, {-94.623214, 33.682068}, {-94.622043, 33.682535}, {-94.618329, 33.681608}, {-94.616418, 33.680103}, {-94.614303, 33.677109}, {-94.612707, 33.673765}, {-94.611891, 33.670040}, {-94.607683, 33.665569}, {-94.605823, 33.664189}, + {-94.603705, 33.663197}, {-94.601163, 33.662761}, {-94.596019, 33.663863}, {-94.593294, 33.665258}, {-94.592050, 33.667039}, {-94.591401, 33.669413}, {-94.591953, 33.672863}, {-94.591415, 33.673686}, {-94.590298, 33.674405}, {-94.590027, 33.675850}, {-94.590335, 33.677690}, {-94.592829, 33.682300}, {-94.593030, 33.684019}, {-94.592750, 33.684714}, {-94.591585, 33.685867}, {-94.589658, 33.686368}, + {-94.587930, 33.686255}, {-94.586242, 33.685131}, {-94.584837, 33.682824}, {-94.580335, 33.678880}, {-94.579620, 33.677623}, {-94.579554, 33.675822}, {-94.581251, 33.673618}, {-94.580259, 33.672892}, {-94.579269, 33.670817}, {-94.579376, 33.669307}, {-94.580784, 33.666392}, {-94.577876, 33.665682}, {-94.575593, 33.666270}, {-94.572568, 33.667700}, {-94.570458, 33.668305}, {-94.563668, 33.669272}, + {-94.561203, 33.668504}, {-94.560501, 33.667463}, {-94.560339, 33.666591}, {-94.561140, 33.664911}, {-94.562538, 33.663297}, {-94.564802, 33.661804}, {-94.568433, 33.660941}, {-94.569993, 33.660877}, {-94.575570, 33.659409}, {-94.576765, 33.658763}, {-94.586986, 33.650180}, {-94.590830, 33.645563}, {-94.590478, 33.645075}, {-94.588632, 33.644183}, {-94.583273, 33.643793}, {-94.580047, 33.644500}, + {-94.576243, 33.646546}, {-94.573988, 33.649517}, {-94.570566, 33.652226}, {-94.565105, 33.654386}, {-94.560198, 33.656967}, {-94.549636, 33.660676}, {-94.548111, 33.660330}, {-94.546732, 33.659566}, {-94.545437, 33.657635}, {-94.543527, 33.656005}, {-94.543022, 33.655182}, {-94.542627, 33.653992}, {-94.542593, 33.651411}, {-94.543033, 33.649322}, {-94.545436, 33.646228}, {-94.546006, 33.645908}, + {-94.550035, 33.645167}, {-94.555995, 33.642811}, {-94.559042, 33.641971}, {-94.563334, 33.640065}, {-94.568386, 33.637203}, {-94.570379, 33.630536}, {-94.569975, 33.628470}, {-94.568965, 33.627453}, {-94.564076, 33.626265}, {-94.560217, 33.626746}, {-94.558012, 33.626691}, {-94.553372, 33.627584}, {-94.551869, 33.628185}, {-94.546437, 33.631504}, {-94.541858, 33.636299}, {-94.539510, 33.637006}, + {-94.537750, 33.638255}, {-94.528126, 33.641248}, {-94.523253, 33.641149}, {-94.521837, 33.640728}, {-94.519942, 33.639275}, {-94.518529, 33.637570}, {-94.521123, 33.634932}, {-94.527579, 33.630818}, {-94.530434, 33.628624}, {-94.532859, 33.627430}, {-94.534814, 33.625521}, {-94.535425, 33.624217}, {-94.535340, 33.622502}, {-94.534689, 33.621193}, {-94.532601, 33.619126}, {-94.527740, 33.615898}, + {-94.523498, 33.615899}, {-94.520989, 33.617023}, {-94.519838, 33.618077}, {-94.519673, 33.619611}, {-94.519098, 33.620230}, {-94.518961, 33.620985}, {-94.518441, 33.621352}, {-94.518495, 33.622222}, {-94.517920, 33.623734}, {-94.518686, 33.624558}, {-94.520245, 33.629805}, {-94.517468, 33.633056}, {-94.515956, 33.631090}, {-94.513908, 33.626877}, {-94.511514, 33.623105}, {-94.509498, 33.621708}, + {-94.508375, 33.621290}, {-94.505655, 33.620668}, {-94.504525, 33.620728}, {-94.501764, 33.621716}, {-94.500699, 33.622974}, {-94.498911, 33.623924}, {-94.493065, 33.624531}, {-94.491503, 33.625115}, {-94.488363, 33.627406}, {-94.487514, 33.628939}, {-94.487737, 33.630634}, {-94.489095, 33.633438}, {-94.491670, 33.636454}, {-94.483042, 33.638588}, {-94.478067, 33.639007}, {-94.475507, 33.638777}, + {-94.472770, 33.637575}, {-94.468362, 33.637542}, {-94.466075, 33.636262}, {-94.464186, 33.637656}, {-94.462592, 33.641789}, {-94.461453, 33.643616}, {-94.459872, 33.644849}, {-94.458630, 33.645266}, {-94.454820, 33.644903}, {-94.448637, 33.642766}, {-94.447408, 33.641334}, {-94.446871, 33.640178}, {-94.447161, 33.639783}, {-94.447030, 33.638055}, {-94.447514, 33.636255}, {-94.448054, 33.635917}, + {-94.448438, 33.634498}, {-94.452960, 33.633312}, {-94.458660, 33.632473}, {-94.462736, 33.630910}, {-94.461813, 33.630464}, {-94.461325, 33.629706}, {-94.460874, 33.628185}, {-94.461244, 33.625811}, {-94.460274, 33.624415}, {-94.458976, 33.623862}, {-94.456433, 33.624696}, {-94.454490, 33.625742}, {-94.452711, 33.622621}, {-94.452325, 33.618817}, {-94.452701, 33.617373}, {-94.454769, 33.615155}, + {-94.462335, 33.610567}, {-94.469451, 33.607316}, {-94.471498, 33.605767}, {-94.472496, 33.604035}, {-94.472443, 33.603469}, {-94.470629, 33.601161}, {-94.468960, 33.599869}, {-94.467364, 33.599155}, {-94.467587, 33.598842}, {-94.467212, 33.599144}, {-94.462302, 33.598379}, {-94.459310, 33.600211}, {-94.457439, 33.600412}, {-94.458232, 33.598270}, {-94.456834, 33.597011}, {-94.455755, 33.594359}, + {-94.453996, 33.592222}, {-94.453197, 33.591780}, {-94.449197, 33.590910}, {-94.445459, 33.590896}, {-94.442214, 33.591276}, {-94.440606, 33.592162}, {-94.438979, 33.593620}, {-94.430041, 33.591124}, {-94.428232, 33.590076}, {-94.427578, 33.589319}, {-94.425982, 33.586425}, {-94.413164, 33.569359}, {-94.410926, 33.568344}, {-94.406709, 33.568014}, {-94.403342, 33.568424}, {-94.400669, 33.569803}, + {-94.397342, 33.571608}, {-94.393534, 33.574686}, {-94.386412, 33.581590}, {-94.382898, 33.583277}, {-94.379674, 33.580631}, {-94.378106, 33.577015}, {-94.377756, 33.574610}, {-94.378464, 33.571688}, {-94.380080, 33.568947}, {-94.382520, 33.567061}, {-94.388033, 33.565518}, {-94.392339, 33.565236}, {-94.394851, 33.566521}, {-94.395941, 33.565227}, {-94.397688, 33.564903}, {-94.400438, 33.562935}, + {-94.401026, 33.561899}, {-94.401616, 33.559378}, {-94.400531, 33.557471}, {-94.397079, 33.553618}, {-94.394614, 33.551971}, {-94.392388, 33.551195}, {-94.391466, 33.550133}, {-94.389530, 33.546739}, {-94.386768, 33.545009}, {-94.383953, 33.544834}, {-94.381664, 33.544035}, {-94.373393, 33.544471}, {-94.371952, 33.544741}, {-94.370690, 33.545671}, {-94.369875, 33.545661}, {-94.365926, 33.544964}, + {-94.361351, 33.544613}, {-94.358970, 33.543230}, {-94.357791, 33.543050}, {-94.355945, 33.543180}, {-94.353593, 33.544005}, {-94.348945, 33.548358}, {-94.347389, 33.551052}, {-94.347293, 33.552152}, {-94.352643, 33.560630}, {-94.352418, 33.562193}, {-94.351441, 33.563246}, {-94.345513, 33.567313}, {-94.344023, 33.567824}, {-94.341709, 33.567954}, {-94.339562, 33.567668}, {-94.337057, 33.566050}, + {-94.335524, 33.564484}, {-94.334351, 33.562527}, {-94.333826, 33.557154}, {-94.333203, 33.555366}, {-94.332653, 33.554309}, {-94.331202, 33.552951}, {-94.321266, 33.549030}, {-94.319492, 33.548864}, {-94.316304, 33.549252}, {-94.312150, 33.550666}, {-94.309582, 33.551673}, {-94.308454, 33.552608}, {-94.306411, 33.555598}, {-94.306215, 33.557675}, {-94.307182, 33.559798}, {-94.303576, 33.568280}, + {-94.301023, 33.573021}, {-94.298377, 33.576219}, {-94.294969, 33.579125}, {-94.291212, 33.581478}, {-94.289215, 33.582131}, {-94.287049, 33.582380}, {-94.283582, 33.581891}, {-94.282648, 33.580978}, {-94.281216, 33.578211}, {-94.280605, 33.574908}, {-94.282172, 33.572989}, {-94.290127, 33.568111}, {-94.290216, 33.565947}, {-94.291341, 33.563682}, {-94.291362, 33.561004}, {-94.290897, 33.559906}, + {-94.289173, 33.559625}, {-94.289545, 33.558272}, {-94.287860, 33.557137}, {-94.287895, 33.556858}, {-94.285940, 33.556229}, {-94.282833, 33.557102}, {-94.278260, 33.557556}, {-94.275746, 33.558533}, {-94.271348, 33.562094}, {-94.270300, 33.563421}, {-94.269881, 33.566702}, {-94.269577, 33.566791}, {-94.265669, 33.573589}, {-94.262755, 33.577353}, {-94.256794, 33.583605}, {-94.252656, 33.586144}, + {-94.245932, 33.589114}, {-94.242777, 33.589709}, {-94.240177, 33.589539}, {-94.236971, 33.587407}, {-94.236365, 33.585994}, {-94.236833, 33.580918}, {-94.237969, 33.577768}, {-94.238866, 33.576748}, {-94.242421, 33.574973}, {-94.244350, 33.573570}, {-94.250193, 33.567460}, {-94.251099, 33.565851}, {-94.251783, 33.563650}, {-94.252158, 33.560496}, {-94.251918, 33.559411}, {-94.250794, 33.557270}, + {-94.250130, 33.556753}, {-94.247509, 33.555620}, {-94.242967, 33.554704}, {-94.237575, 33.552669}, {-94.231970, 33.552086}, {-94.226188, 33.552976}, {-94.222923, 33.554098}, {-94.219210, 33.556096}, {-94.213625, 33.563132}, {-94.208034, 33.566913}, {-94.205666, 33.567219}, {-94.203594, 33.566534}, {-94.202827, 33.564583}, {-94.201585, 33.559508}, {-94.200448, 33.557078}, {-94.199792, 33.556336}, + {-94.197769, 33.555204}, {-94.195432, 33.555094}, {-94.192877, 33.556133}, {-94.191472, 33.557183}, {-94.189988, 33.560075}, {-94.189660, 33.561807}, {-94.189890, 33.563434}, {-94.191814, 33.566496}, {-94.192366, 33.570526}, {-94.194127, 33.573469}, {-94.196178, 33.575334}, {-94.199220, 33.576730}, {-94.201587, 33.577348}, {-94.203925, 33.576844}, {-94.207405, 33.574353}, {-94.209665, 33.573510}, + {-94.211244, 33.573738}, {-94.216208, 33.576569}, {-94.217463, 33.579378}, {-94.217114, 33.580994}, {-94.215283, 33.582624}, {-94.213007, 33.583482}, {-94.210732, 33.582986}, {-94.203843, 33.579773}, {-94.199186, 33.580321}, {-94.196435, 33.581293}, {-94.194564, 33.582798}, {-94.193186, 33.585156}, {-94.184308, 33.594578}, {-94.183912, 33.594685}, {-94.183266, 33.594598}, {-94.180880, 33.592612}, + {-94.176327, 33.591077}, {-94.162266, 33.588906}, {-94.161081, 33.587973}, {-94.161069, 33.586199}, {-94.161902, 33.582844}, {-94.162006, 33.580884}, {-94.161272, 33.579280}, {-94.156775, 33.575762}, {-94.154371, 33.575615}, {-94.152626, 33.575923}, {-94.148732, 33.580197}, {-94.146048, 33.581975}, {-94.144383, 33.582098}, {-94.142160, 33.581390}, {-94.141727, 33.580526}, {-94.141852, 33.579590}, + {-94.143024, 33.577725}, {-94.145669, 33.575600}, {-94.149506, 33.573602}, {-94.151344, 33.571389}, {-94.151830, 33.569488}, {-94.151574, 33.568406}, {-94.149693, 33.566325}, {-94.148645, 33.565698}, {-94.147428, 33.565183}, {-94.145218, 33.564979}, {-94.143391, 33.565501}, {-94.136862, 33.570998}, {-94.136044, 33.571386}, {-94.135144, 33.571033}, {-94.134532, 33.570348}, {-94.133715, 33.565957}, + {-94.132142, 33.560514}, {-94.132377, 33.559369}, {-94.132184, 33.556645}, {-94.131121, 33.552809}, {-94.129756, 33.551450}, {-94.127612, 33.550721}, {-94.125468, 33.551138}, {-94.123160, 33.553268}, {-94.122423, 33.554617}, {-94.120737, 33.560561}, {-94.120355, 33.565500}, {-94.120860, 33.567051}, {-94.112843, 33.566991}, {-94.103173, 33.570349}, {-94.101638, 33.571146}, {-94.100109, 33.572600}, + {-94.097441, 33.573744}, {-94.088937, 33.575334}, {-94.082352, 33.575720}, {-94.072826, 33.572298}, {-94.071974, 33.574409}, {-94.072008, 33.576295}, {-94.074777, 33.580438}, {-94.075256, 33.582510}, {-94.074694, 33.584053}, {-94.073635, 33.584773}, {-94.072212, 33.584422}, {-94.070518, 33.582678}, {-94.070062, 33.581294}, {-94.070664, 33.578156}, {-94.070597, 33.576725}, {-94.068531, 33.571103}, + {-94.066846, 33.568909}, {-94.061314, 33.568790}, {-94.057086, 33.568003}, {-94.056665, 33.567464}, {-94.056824, 33.562771}, {-94.056580, 33.560932}, {-94.059848, 33.559248}, {-94.061178, 33.559158}, {-94.066684, 33.560953}, {-94.067984, 33.560960}, {-94.070960, 33.560140}, {-94.072593, 33.559255}, {-94.073190, 33.558793}, {-94.073893, 33.557430}, {-94.073833, 33.556006}, {-94.072388, 33.554164}, + {-94.071686, 33.553818}, {-94.071318, 33.554132}, {-94.069334, 33.553707}, {-94.065661, 33.550967}, {-94.064276, 33.550835}, {-94.062835, 33.551242}, {-94.061477, 33.550001}, {-94.059164, 33.550002}, {-94.055907, 33.550843}, {-94.051918, 33.552640}, {-94.050186, 33.551076}, {-94.046040, 33.551321}, {-94.043450, 33.552253}, {-94.043430, 33.551479}, {-94.042886, 33.420221}, {-94.043149, 33.409015}, + {-94.043232, 33.354012}, {-94.042995, 33.345547}, {-94.043050, 33.255128}, {-94.042730, 33.241822}, {-94.043129, 33.117322}, {-94.042870, 33.092726}, {-94.043225, 32.808299}, {-94.042993, 32.807430}, {-94.043190, 32.797117}, {-94.042870, 32.787185}, {-94.043226, 32.770226}, {-94.042953, 32.767907}, {-94.043199, 32.755485}, {-94.042938, 32.659726}, {-94.043307, 32.640446}, {-94.042931, 32.619442}, + {-94.043147, 32.610783}, {-94.042819, 32.591175}, {-94.043100, 32.588620}, {-94.042817, 32.574534}, {-94.043103, 32.573542}, {-94.042965, 32.551100}, {-94.043352, 32.522112}, {-94.043258, 32.516199}, {-94.042916, 32.513605}, {-94.043167, 32.510705}, {-94.042926, 32.494047}, {-94.043140, 32.478500}, {-94.042875, 32.471348}, {-94.042980, 32.388203}, {-94.042762, 32.375155}, {-94.043003, 32.373394}, + {-94.042567, 32.185249}, {-94.042812, 32.144788}, {-94.042751, 32.125163}, {-94.042337, 32.119914}, {-94.042720, 32.086182}, {-94.042778, 32.024831}, {-94.042720, 31.999271}, {-94.042105, 31.998898}, {-94.041398, 31.996306}, {-94.042609, 31.994260}, {-94.042566, 31.993502}, {-94.041833, 31.992402}, {-94.039466, 31.992205}, {-94.030865, 31.995638}, {-94.029283, 31.995865}, {-94.027896, 31.995695}, + {-94.027289, 31.995182}, {-94.025916, 31.992820}, {-94.024084, 31.991040}, {-94.022893, 31.990450}, {-94.021931, 31.990366}, {-94.021029, 31.990790}, {-94.019973, 31.992126}, {-94.019043, 31.992561}, {-94.018514, 31.992432}, {-94.018301, 31.991522}, {-94.018664, 31.990843}, {-94.018765, 31.989118}, {-94.017302, 31.986022}, {-94.016688, 31.983857}, {-94.016515, 31.981174}, {-94.015630, 31.979856}, + {-94.012433, 31.978100}, {-94.006795, 31.973430}, {-94.000969, 31.961795}, {-93.998606, 31.958826}, {-93.995713, 31.954140}, {-93.994501, 31.952648}, {-93.989706, 31.948783}, {-93.987878, 31.944315}, {-93.986985, 31.943307}, {-93.982321, 31.939764}, {-93.980795, 31.935225}, {-93.979707, 31.933290}, {-93.978155, 31.927847}, {-93.977461, 31.926419}, {-93.975377, 31.923660}, {-93.971710, 31.920386}, + {-93.970826, 31.919968}, {-93.969032, 31.920151}, {-93.964881, 31.923183}, {-93.962948, 31.924156}, {-93.959768, 31.923226}, {-93.956035, 31.917022}, {-93.955659, 31.912782}, {-93.954796, 31.911564}, {-93.953546, 31.910563}, {-93.949837, 31.910865}, {-93.945979, 31.910328}, {-93.942646, 31.910373}, {-93.937537, 31.909222}, {-93.932798, 31.910514}, {-93.931007, 31.911414}, {-93.929903, 31.912719}, + {-93.928651, 31.912645}, {-93.927611, 31.910684}, {-93.924896, 31.908706}, {-93.923939, 31.907100}, {-93.922109, 31.905324}, {-93.921594, 31.904421}, {-93.921979, 31.903781}, {-93.924449, 31.903038}, {-93.925157, 31.902475}, {-93.927436, 31.898286}, {-93.929523, 31.895773}, {-93.932135, 31.893672}, {-93.932410, 31.893197}, {-93.932205, 31.892681}, {-93.931190, 31.891495}, {-93.927986, 31.893119}, + {-93.926618, 31.894375}, {-93.925526, 31.894615}, {-93.924542, 31.892929}, {-93.926747, 31.891876}, {-93.927172, 31.890893}, {-93.926210, 31.888683}, {-93.924200, 31.886963}, {-93.922085, 31.886009}, {-93.920975, 31.885878}, {-93.919882, 31.886232}, {-93.919693, 31.886642}, {-93.920711, 31.889731}, {-93.920511, 31.890304}, {-93.919681, 31.890963}, {-93.919051, 31.890898}, {-93.917398, 31.889816}, + {-93.916889, 31.889847}, {-93.916097, 31.890604}, {-93.916213, 31.892652}, {-93.914999, 31.892948}, {-93.912534, 31.892469}, {-93.912272, 31.891767}, {-93.913047, 31.890640}, {-93.913001, 31.890245}, {-93.912549, 31.890145}, {-93.910399, 31.892022}, {-93.906539, 31.892509}, {-93.906042, 31.891863}, {-93.907005, 31.890832}, {-93.907091, 31.890284}, {-93.906603, 31.889856}, {-93.906188, 31.889928}, + {-93.904905, 31.891128}, {-93.903080, 31.893922}, {-93.903858, 31.895310}, {-93.903819, 31.895769}, {-93.901782, 31.897016}, {-93.900616, 31.896788}, {-93.898464, 31.894099}, {-93.897188, 31.891019}, {-93.896458, 31.886286}, {-93.896668, 31.885327}, {-93.898733, 31.883984}, {-93.900302, 31.881487}, {-93.901888, 31.880063}, {-93.902069, 31.879104}, {-93.901041, 31.876732}, {-93.901882, 31.875371}, + {-93.901676, 31.874570}, {-93.900624, 31.873868}, {-93.899345, 31.873876}, {-93.896975, 31.875982}, {-93.894917, 31.876297}, {-93.892115, 31.875917}, {-93.891572, 31.874583}, {-93.893256, 31.873379}, {-93.893438, 31.871731}, {-93.890936, 31.869767}, {-93.888739, 31.869458}, {-93.886137, 31.865499}, {-93.885415, 31.865257}, {-93.884804, 31.865433}, {-93.884426, 31.866269}, {-93.885078, 31.868642}, + {-93.884879, 31.869372}, {-93.884230, 31.869653}, {-93.881678, 31.868581}, {-93.881394, 31.866040}, {-93.881894, 31.864835}, {-93.889193, 31.856819}, {-93.889396, 31.855900}, {-93.888859, 31.855073}, {-93.883929, 31.855003}, {-93.882878, 31.854672}, {-93.882112, 31.852319}, {-93.882679, 31.851504}, {-93.884138, 31.850758}, {-93.884637, 31.850163}, {-93.884751, 31.849408}, {-93.884117, 31.847606}, + {-93.883245, 31.847042}, {-93.881696, 31.846734}, {-93.878997, 31.845145}, {-93.878942, 31.844455}, {-93.879178, 31.844126}, {-93.880079, 31.843942}, {-93.881252, 31.844249}, {-93.882079, 31.844089}, {-93.882485, 31.843599}, {-93.881914, 31.842216}, {-93.880592, 31.841522}, {-93.879183, 31.841979}, {-93.877155, 31.841896}, {-93.874964, 31.840868}, {-93.874679, 31.837920}, {-93.872819, 31.836301}, + {-93.876199, 31.834248}, {-93.877358, 31.832528}, {-93.877275, 31.831853}, {-93.874645, 31.829888}, {-93.874761, 31.821661}, {-93.874000, 31.821065}, {-93.872375, 31.820752}, {-93.871285, 31.820189}, {-93.870937, 31.819383}, {-93.870917, 31.816837}, {-93.873010, 31.816272}, {-93.873432, 31.815813}, {-93.873253, 31.814886}, {-93.871947, 31.813920}, {-93.870839, 31.813783}, {-93.868484, 31.815253}, + {-93.867319, 31.815316}, {-93.861384, 31.812297}, {-93.860482, 31.811516}, {-93.859316, 31.809115}, {-93.854573, 31.806651}, {-93.853390, 31.805467}, {-93.853529, 31.802607}, {-93.852655, 31.801108}, {-93.851481, 31.800150}, {-93.847632, 31.799635}, {-93.847060, 31.799861}, {-93.846188, 31.802021}, {-93.842892, 31.800676}, {-93.839343, 31.800330}, {-93.838413, 31.798002}, {-93.835942, 31.795690}, + {-93.835613, 31.794489}, {-93.837556, 31.789792}, {-93.837696, 31.787858}, {-93.836634, 31.786263}, {-93.835874, 31.785731}, {-93.835855, 31.785216}, {-93.836371, 31.784547}, {-93.837778, 31.783708}, {-93.838144, 31.782082}, {-93.837693, 31.780817}, {-93.836229, 31.780487}, {-93.832821, 31.781316}, {-93.831732, 31.780914}, {-93.830849, 31.779859}, {-93.830849, 31.779094}, {-93.833242, 31.778143}, + {-93.833120, 31.777522}, {-93.831515, 31.776910}, {-93.827451, 31.777741}, {-93.825621, 31.776895}, {-93.823443, 31.775098}, {-93.822598, 31.773559}, {-93.823133, 31.772085}, {-93.824568, 31.770449}, {-93.825112, 31.769257}, {-93.825328, 31.767170}, {-93.825956, 31.766509}, {-93.828809, 31.765630}, {-93.829587, 31.764204}, {-93.829587, 31.763568}, {-93.829099, 31.763294}, {-93.827288, 31.764075}, + {-93.826810, 31.763777}, {-93.826171, 31.762327}, {-93.826105, 31.759894}, {-93.826461, 31.759193}, {-93.827343, 31.759370}, {-93.827541, 31.760885}, {-93.828348, 31.761070}, {-93.829821, 31.760619}, {-93.830112, 31.759644}, {-93.830065, 31.757895}, {-93.830909, 31.756606}, {-93.830975, 31.755671}, {-93.831622, 31.754592}, {-93.835234, 31.752843}, {-93.836884, 31.750171}, {-93.835257, 31.747236}, + {-93.831698, 31.745514}, {-93.826905, 31.737596}, {-93.824579, 31.734397}, {-93.822177, 31.732675}, {-93.815962, 31.730110}, {-93.814930, 31.728688}, {-93.814309, 31.721919}, {-93.812477, 31.715246}, {-93.812760, 31.714415}, {-93.816918, 31.708765}, {-93.816694, 31.707917}, {-93.816291, 31.707497}, {-93.811802, 31.707485}, {-93.811052, 31.706112}, {-93.811121, 31.703681}, {-93.809798, 31.703082}, + {-93.807047, 31.702813}, {-93.797407, 31.705615}, {-93.795070, 31.704975}, {-93.794462, 31.703320}, {-93.794548, 31.702076}, {-93.803396, 31.687812}, {-93.805144, 31.686957}, {-93.805229, 31.686424}, {-93.804479, 31.685664}, {-93.804273, 31.684969}, {-93.804462, 31.684089}, {-93.806342, 31.683033}, {-93.806879, 31.681571}, {-93.810975, 31.679846}, {-93.811559, 31.678134}, {-93.812509, 31.677005}, + {-93.814997, 31.676595}, {-93.816285, 31.675360}, {-93.815629, 31.674035}, {-93.812516, 31.671900}, {-93.812433, 31.670245}, {-93.815834, 31.668058}, {-93.816651, 31.668010}, {-93.817372, 31.669166}, {-93.816957, 31.671031}, {-93.817425, 31.672146}, {-93.820109, 31.672552}, {-93.821140, 31.673611}, {-93.822051, 31.673967}, {-93.823169, 31.672893}, {-93.826462, 31.666919}, {-93.826297, 31.663050}, + {-93.825661, 31.661022}, {-93.822511, 31.658153}, {-93.821371, 31.656610}, {-93.819486, 31.652650}, {-93.818427, 31.651479}, {-93.817029, 31.650928}, {-93.813623, 31.650926}, {-93.812788, 31.650441}, {-93.811682, 31.649309}, {-93.811516, 31.647435}, {-93.813234, 31.646491}, {-93.816122, 31.648900}, {-93.816788, 31.648949}, {-93.818037, 31.647892}, {-93.818077, 31.646189}, {-93.817405, 31.643572}, + {-93.817464, 31.641496}, {-93.816555, 31.640276}, {-93.816369, 31.639258}, {-93.818132, 31.636820}, {-93.818062, 31.635568}, {-93.816592, 31.633669}, {-93.815955, 31.632279}, {-93.816528, 31.631787}, {-93.818527, 31.631473}, {-93.819992, 31.630626}, {-93.820848, 31.629020}, {-93.820344, 31.627089}, {-93.817615, 31.625843}, {-93.816987, 31.625175}, {-93.817264, 31.623752}, {-93.816838, 31.622509}, + {-93.817155, 31.621872}, {-93.820728, 31.621441}, {-93.823754, 31.620640}, {-93.825830, 31.621382}, {-93.826852, 31.621047}, {-93.827170, 31.620276}, {-93.825956, 31.619535}, {-93.822154, 31.618522}, {-93.818787, 31.616603}, {-93.818717, 31.614556}, {-93.819582, 31.614121}, {-93.822642, 31.614529}, {-93.827083, 31.612857}, {-93.827865, 31.613496}, {-93.827273, 31.614401}, {-93.826290, 31.614903}, + {-93.825854, 31.615910}, {-93.827852, 31.616551}, {-93.832782, 31.610750}, {-93.833103, 31.609038}, {-93.836404, 31.608002}, {-93.838057, 31.606795}, {-93.839130, 31.603540}, {-93.837606, 31.601826}, {-93.839262, 31.599981}, {-93.839416, 31.599200}, {-93.838263, 31.594709}, {-93.835805, 31.591449}, {-93.834918, 31.586212}, {-93.830363, 31.579355}, {-93.827150, 31.578477}, {-93.823399, 31.574980}, + {-93.820269, 31.572860}, {-93.818434, 31.570943}, {-93.818400, 31.569600}, {-93.819149, 31.568930}, {-93.822210, 31.568834}, {-93.822958, 31.568130}, {-93.822889, 31.565914}, {-93.821564, 31.563696}, {-93.817444, 31.564092}, {-93.816860, 31.563084}, {-93.816984, 31.561674}, {-93.818363, 31.560164}, {-93.821107, 31.560471}, {-93.820764, 31.558221}, {-93.818582, 31.554826}, {-93.813973, 31.550556}, + {-93.810250, 31.549878}, {-93.809475, 31.547863}, {-93.808305, 31.546383}, {-93.803337, 31.543622}, {-93.800515, 31.543449}, {-93.799536, 31.542977}, {-93.799894, 31.541870}, {-93.802331, 31.540733}, {-93.803197, 31.539828}, {-93.803359, 31.538787}, {-93.799206, 31.538075}, {-93.798821, 31.536530}, {-93.798828, 31.534817}, {-93.798087, 31.534044}, {-93.790298, 31.530773}, {-93.787687, 31.527344}, + {-93.785028, 31.525962}, {-93.780835, 31.525384}, {-93.776444, 31.525376}, {-93.767315, 31.524082}, {-93.760062, 31.523933}, {-93.754883, 31.524796}, {-93.753860, 31.525331}, {-93.754130, 31.526406}, {-93.757613, 31.527554}, {-93.758195, 31.528731}, {-93.758111, 31.529872}, {-93.756500, 31.530540}, {-93.754700, 31.529865}, {-93.753414, 31.528284}, {-93.751889, 31.527475}, {-93.749772, 31.529783}, + {-93.748319, 31.530560}, {-93.747437, 31.530682}, {-93.747043, 31.530175}, {-93.747361, 31.528415}, {-93.746901, 31.527940}, {-93.746029, 31.527434}, {-93.743403, 31.527260}, {-93.742652, 31.526560}, {-93.742313, 31.522890}, {-93.740529, 31.518142}, {-93.740978, 31.517568}, {-93.741841, 31.517236}, {-93.743876, 31.518064}, {-93.744448, 31.517853}, {-93.744232, 31.516829}, {-93.742693, 31.515694}, + {-93.737376, 31.513864}, {-93.735238, 31.513496}, {-93.732902, 31.513451}, {-93.730137, 31.513972}, {-93.721360, 31.513848}, {-93.714703, 31.514156}, {-93.712490, 31.513361}, {-93.711046, 31.512161}, {-93.710952, 31.511427}, {-93.711402, 31.510652}, {-93.713886, 31.508640}, {-93.714683, 31.508163}, {-93.718358, 31.507940}, {-93.719567, 31.506921}, {-93.719708, 31.505840}, {-93.717710, 31.503972}, + {-93.717176, 31.503029}, {-93.717119, 31.501916}, {-93.717485, 31.501496}, {-93.718938, 31.500953}, {-93.720925, 31.501862}, {-93.722200, 31.501981}, {-93.723447, 31.500938}, {-93.723794, 31.500178}, {-93.723662, 31.499252}, {-93.724081, 31.496401}, {-93.722238, 31.494967}, {-93.721941, 31.494283}, {-93.721974, 31.493653}, {-93.722596, 31.492769}, {-93.725160, 31.491397}, {-93.729613, 31.487922}, + {-93.731346, 31.487274}, {-93.735315, 31.489074}, {-93.735707, 31.488757}, {-93.736453, 31.485676}, {-93.737168, 31.484622}, {-93.741894, 31.483537}, {-93.742892, 31.483779}, {-93.745152, 31.485638}, {-93.748492, 31.485117}, {-93.752387, 31.482740}, {-93.752988, 31.481710}, {-93.752198, 31.479551}, {-93.751368, 31.479072}, {-93.750183, 31.479314}, {-93.748003, 31.481648}, {-93.746483, 31.481828}, + {-93.745542, 31.480396}, {-93.745401, 31.478120}, {-93.748474, 31.474808}, {-93.749775, 31.470747}, {-93.749476, 31.468690}, {-93.741864, 31.465872}, {-93.731513, 31.457474}, {-93.726609, 31.454864}, {-93.721931, 31.453447}, {-93.720994, 31.453752}, {-93.717734, 31.457106}, {-93.716750, 31.457548}, {-93.715619, 31.457054}, {-93.713637, 31.454474}, {-93.711663, 31.453049}, {-93.709970, 31.452760}, + {-93.709027, 31.453404}, {-93.708941, 31.454430}, {-93.709366, 31.455945}, {-93.709128, 31.456746}, {-93.707946, 31.456979}, {-93.706630, 31.456793}, {-93.703934, 31.455493}, {-93.703771, 31.453831}, {-93.709416, 31.442995}, {-93.709512, 31.442025}, {-93.708203, 31.441557}, {-93.693272, 31.440078}, {-93.692659, 31.439597}, {-93.692526, 31.438201}, {-93.692631, 31.437192}, {-93.701051, 31.424629}, + {-93.702953, 31.420371}, {-93.704678, 31.418900}, {-93.704811, 31.416703}, {-93.704460, 31.414953}, {-93.704904, 31.412924}, {-93.704879, 31.410881}, {-93.701611, 31.409334}, {-93.697895, 31.409679}, {-93.695866, 31.409392}, {-93.690326, 31.406591}, {-93.688134, 31.404762}, {-93.682097, 31.398332}, {-93.681279, 31.398049}, {-93.676555, 31.398733}, {-93.674117, 31.397681}, {-93.671644, 31.393352}, + {-93.670182, 31.387184}, {-93.669017, 31.371379}, {-93.669396, 31.370530}, {-93.672420, 31.371187}, {-93.673634, 31.370964}, {-93.673847, 31.370633}, {-93.673322, 31.368682}, {-93.670999, 31.366221}, {-93.669069, 31.365430}, {-93.667273, 31.365413}, {-93.658343, 31.366845}, {-93.652025, 31.369008}, {-93.648257, 31.370855}, {-93.647211, 31.371109}, {-93.644104, 31.370645}, {-93.641384, 31.372455}, + {-93.639552, 31.372591}, {-93.639222, 31.372020}, {-93.641722, 31.367377}, {-93.644124, 31.365528}, {-93.644980, 31.362966}, {-93.644891, 31.360174}, {-93.645866, 31.359008}, {-93.647638, 31.358097}, {-93.652905, 31.357116}, {-93.657834, 31.355620}, {-93.667395, 31.353501}, {-93.668439, 31.353012}, {-93.669547, 31.350570}, {-93.668707, 31.343939}, {-93.669024, 31.338829}, {-93.672840, 31.334278}, + {-93.674551, 31.332641}, {-93.676535, 31.331454}, {-93.677277, 31.330483}, {-93.676973, 31.329612}, {-93.673954, 31.326186}, {-93.673553, 31.324542}, {-93.675342, 31.322033}, {-93.676263, 31.321351}, {-93.680659, 31.320341}, {-93.682941, 31.318999}, {-93.683478, 31.318109}, {-93.683981, 31.314621}, {-93.687159, 31.311403}, {-93.687851, 31.309835}, {-93.687899, 31.308543}, {-93.687278, 31.306068}, + {-93.686220, 31.303990}, {-93.683561, 31.301507}, {-93.680441, 31.301592}, {-93.675440, 31.301040}, {-93.671676, 31.299586}, {-93.668928, 31.297975}, {-93.667651, 31.294986}, {-93.665592, 31.292702}, {-93.664100, 31.289731}, {-93.659285, 31.282106}, {-93.658685, 31.281624}, {-93.657004, 31.281736}, {-93.655095, 31.282857}, {-93.651152, 31.284487}, {-93.650042, 31.284677}, {-93.648648, 31.284230}, + {-93.647053, 31.281161}, {-93.644633, 31.277764}, {-93.644235, 31.276612}, {-93.643926, 31.271230}, {-93.642516, 31.269508}, {-93.641384, 31.269157}, {-93.639694, 31.269163}, {-93.635168, 31.269979}, {-93.628652, 31.270488}, {-93.622818, 31.271338}, {-93.620829, 31.271299}, {-93.620343, 31.271025}, {-93.620034, 31.270419}, {-93.620159, 31.266333}, {-93.619924, 31.265178}, {-93.617999, 31.263371}, + {-93.615346, 31.261862}, {-93.614402, 31.260869}, {-93.613942, 31.259375}, {-93.615637, 31.255127}, {-93.615402, 31.254166}, {-93.613944, 31.252858}, {-93.614288, 31.251631}, {-93.621202, 31.249642}, {-93.621757, 31.249127}, {-93.621330, 31.248100}, {-93.620127, 31.246953}, {-93.617735, 31.245744}, {-93.616308, 31.244595}, {-93.617109, 31.241005}, {-93.616205, 31.237548}, {-93.616274, 31.236838}, + {-93.618255, 31.233105}, {-93.618107, 31.232685}, {-93.617482, 31.232135}, {-93.616471, 31.231979}, {-93.612491, 31.232689}, {-93.610705, 31.232152}, {-93.609772, 31.229090}, {-93.608747, 31.228014}, {-93.608158, 31.227835}, {-93.601656, 31.229523}, {-93.598391, 31.231097}, {-93.597773, 31.231120}, {-93.596886, 31.230626}, {-93.596273, 31.229510}, {-93.596266, 31.228856}, {-93.596785, 31.227986}, + {-93.599353, 31.224949}, {-93.598740, 31.223850}, {-93.595305, 31.221815}, {-93.594970, 31.221314}, {-93.595255, 31.220314}, {-93.595678, 31.219798}, {-93.603496, 31.216710}, {-93.603816, 31.214161}, {-93.603379, 31.211343}, {-93.603541, 31.210673}, {-93.604993, 31.208053}, {-93.607061, 31.205766}, {-93.607243, 31.204806}, {-93.606517, 31.203755}, {-93.601986, 31.199910}, {-93.601857, 31.199393}, + {-93.601941, 31.192887}, {-93.601589, 31.187511}, {-93.602443, 31.182541}, {-93.600308, 31.176158}, {-93.589119, 31.165889}, {-93.587585, 31.165577}, {-93.583558, 31.166868}, {-93.579310, 31.167404}, {-93.578166, 31.167855}, {-93.577180, 31.168571}, {-93.572841, 31.174548}, {-93.569563, 31.177574}, {-93.564018, 31.180312}, {-93.560918, 31.182486}, {-93.554592, 31.184823}, {-93.551472, 31.185580}, + {-93.549984, 31.186627}, {-93.548584, 31.186476}, {-93.545836, 31.184215}, {-93.544365, 31.183478}, {-93.542367, 31.183193}, {-93.539280, 31.183547}, {-93.536287, 31.185312}, {-93.535098, 31.185612}, {-93.534280, 31.185425}, {-93.532545, 31.183314}, {-93.531318, 31.179806}, {-93.532023, 31.175978}, {-93.532807, 31.174265}, {-93.535953, 31.172409}, {-93.542309, 31.171640}, {-93.542933, 31.171277}, + {-93.543061, 31.170658}, {-93.542995, 31.169643}, {-93.542100, 31.167888}, {-93.538977, 31.165150}, {-93.537989, 31.163427}, {-93.536412, 31.160387}, {-93.536481, 31.158471}, {-93.540164, 31.156054}, {-93.546372, 31.156599}, {-93.548506, 31.156195}, {-93.548700, 31.155839}, {-93.548515, 31.148229}, {-93.546102, 31.143354}, {-93.544735, 31.136066}, {-93.543673, 31.134106}, {-93.537870, 31.126570}, + {-93.537824, 31.125181}, {-93.539366, 31.115207}, {-93.539832, 31.114152}, {-93.540737, 31.113573}, {-93.544377, 31.112404}, {-93.545935, 31.111197}, {-93.548696, 31.106035}, {-93.549125, 31.103231}, {-93.548632, 31.101203}, {-93.549067, 31.100219}, {-93.550341, 31.099651}, {-93.558179, 31.097983}, {-93.558623, 31.096926}, {-93.558316, 31.096481}, {-93.554628, 31.095188}, {-93.553852, 31.094584}, + {-93.553566, 31.094164}, {-93.553722, 31.093869}, {-93.554891, 31.094829}, {-93.557941, 31.095874}, {-93.559378, 31.097678}, {-93.561195, 31.097783}, {-93.562415, 31.097529}, {-93.563182, 31.096988}, {-93.563654, 31.096009}, {-93.563746, 31.094522}, {-93.563269, 31.093592}, {-93.560894, 31.092226}, {-93.558581, 31.091504}, {-93.557808, 31.090692}, {-93.556641, 31.087435}, {-93.555815, 31.086965}, + {-93.554638, 31.086870}, {-93.552279, 31.088327}, {-93.550714, 31.090985}, {-93.550083, 31.091072}, {-93.549262, 31.090412}, {-93.548699, 31.088637}, {-93.549399, 31.087602}, {-93.551436, 31.085735}, {-93.552467, 31.082489}, {-93.552205, 31.078931}, {-93.551311, 31.078661}, {-93.542454, 31.078336}, {-93.540130, 31.077940}, {-93.537766, 31.077213}, {-93.532627, 31.074552}, {-93.527645, 31.074510}, + {-93.527168, 31.073189}, {-93.528057, 31.071022}, {-93.528935, 31.070234}, {-93.528732, 31.069214}, {-93.525131, 31.066551}, {-93.523322, 31.066186}, {-93.521140, 31.066325}, {-93.520126, 31.065816}, {-93.518942, 31.063162}, {-93.518912, 31.061863}, {-93.521145, 31.057275}, {-93.522971, 31.056670}, {-93.528438, 31.057176}, {-93.530129, 31.056814}, {-93.531989, 31.055715}, {-93.532215, 31.055236}, + {-93.531628, 31.053627}, {-93.530730, 31.051749}, {-93.527743, 31.048317}, {-93.526289, 31.047272}, {-93.524202, 31.046350}, {-93.519961, 31.045168}, {-93.518899, 31.043736}, {-93.519196, 31.042694}, {-93.519941, 31.041876}, {-93.521932, 31.041413}, {-93.524048, 31.041520}, {-93.524450, 31.041139}, {-93.524904, 31.039947}, {-93.524739, 31.039007}, {-93.523383, 31.037716}, {-93.521598, 31.036301}, + {-93.518841, 31.036070}, {-93.510073, 31.033423}, {-93.508292, 31.032352}, {-93.508039, 31.031532}, {-93.508141, 31.030245}, {-93.508960, 31.029017}, {-93.515738, 31.025203}, {-93.522314, 31.020098}, {-93.524522, 31.019642}, {-93.531482, 31.020243}, {-93.532457, 31.019719}, {-93.533430, 31.018971}, {-93.534612, 31.017323}, {-93.537458, 31.012202}, {-93.539184, 31.008212}, {-93.540429, 31.007808}, + {-93.543086, 31.008299}, {-93.545760, 31.010033}, {-93.547626, 31.010373}, {-93.549377, 31.010143}, {-93.551466, 31.009237}, {-93.552303, 31.008310}, {-93.554265, 31.004547}, {-93.555927, 31.003744}, {-93.562664, 31.008907}, {-93.564164, 31.011512}, {-93.565631, 31.013156}, {-93.566561, 31.013057}, {-93.567548, 31.012672}, {-93.571465, 31.009599}, {-93.572729, 31.007743}, {-93.573044, 31.004712}, + {-93.573423, 31.003858}, {-93.574588, 31.002363}, {-93.577094, 31.001003}, {-93.578207, 30.999995}, {-93.577892, 30.998764}, {-93.575958, 30.995714}, {-93.574000, 30.993399}, {-93.573365, 30.991828}, {-93.572216, 30.991210}, {-93.570479, 30.990815}, {-93.565430, 30.990579}, {-93.564299, 30.989700}, {-93.564164, 30.988368}, {-93.565551, 30.986257}, {-93.565885, 30.985168}, {-93.566858, 30.979403}, + {-93.568296, 30.977096}, {-93.569295, 30.976191}, {-93.572798, 30.974724}, {-93.574332, 30.973708}, {-93.573537, 30.971206}, {-93.572159, 30.970273}, {-93.567737, 30.968971}, {-93.564841, 30.969527}, {-93.562556, 30.970910}, {-93.560537, 30.971289}, {-93.558861, 30.970861}, {-93.556106, 30.968639}, {-93.553540, 30.967374}, {-93.550651, 30.967499}, {-93.549841, 30.967118}, {-93.547314, 30.964755}, + {-93.546933, 30.960132}, {-93.546327, 30.957802}, {-93.543122, 30.956740}, {-93.539274, 30.956520}, {-93.534628, 30.957998}, {-93.531758, 30.958217}, {-93.530927, 30.957114}, {-93.530507, 30.955738}, {-93.530534, 30.954578}, {-93.532224, 30.951820}, {-93.532555, 30.950702}, {-93.532367, 30.948977}, {-93.530936, 30.944530}, {-93.529814, 30.942172}, {-93.526439, 30.939375}, {-93.526013, 30.937535}, + {-93.527262, 30.935634}, {-93.527837, 30.933066}, {-93.526457, 30.930365}, {-93.526474, 30.929464}, {-93.527471, 30.928035}, {-93.529636, 30.927538}, {-93.530703, 30.926778}, {-93.531234, 30.924388}, {-93.532008, 30.923754}, {-93.534173, 30.924033}, {-93.535832, 30.923691}, {-93.537974, 30.922742}, {-93.541782, 30.920207}, {-93.543423, 30.920153}, {-93.544116, 30.921214}, {-93.542073, 30.925849}, + {-93.542589, 30.926422}, {-93.545718, 30.927100}, {-93.546838, 30.926601}, {-93.549283, 30.924372}, {-93.552085, 30.918119}, {-93.558562, 30.913238}, {-93.558737, 30.911652}, {-93.558087, 30.910797}, {-93.556100, 30.909635}, {-93.554674, 30.909216}, {-93.553026, 30.908100}, {-93.552916, 30.907153}, {-93.553995, 30.902773}, {-93.553116, 30.901053}, {-93.548623, 30.899894}, {-93.548643, 30.898622}, + {-93.549202, 30.897439}, {-93.552475, 30.896704}, {-93.557806, 30.896746}, {-93.561564, 30.895684}, {-93.566316, 30.894839}, {-93.568158, 30.893374}, {-93.568508, 30.892364}, {-93.570222, 30.889912}, {-93.572957, 30.887570}, {-93.574080, 30.884529}, {-93.571855, 30.879389}, {-93.571700, 30.877963}, {-93.570604, 30.875193}, {-93.568137, 30.874565}, {-93.565590, 30.875640}, {-93.564795, 30.875675}, + {-93.563233, 30.874867}, {-93.563014, 30.872568}, {-93.564651, 30.870113}, {-93.564504, 30.868640}, {-93.563011, 30.868108}, {-93.559763, 30.869826}, {-93.559142, 30.869937}, {-93.558616, 30.869424}, {-93.558502, 30.867905}, {-93.561394, 30.861942}, {-93.562147, 30.859332}, {-93.561911, 30.858332}, {-93.559653, 30.855608}, {-93.557920, 30.854299}, {-93.557314, 30.852081}, {-93.564101, 30.846744}, + {-93.568195, 30.845515}, {-93.568717, 30.844182}, {-93.567970, 30.843026}, {-93.565803, 30.842070}, {-93.563494, 30.842049}, {-93.559854, 30.840988}, {-93.558024, 30.840075}, {-93.555603, 30.837908}, {-93.553626, 30.835140}, {-93.553104, 30.833111}, {-93.553311, 30.832542}, {-93.553744, 30.831785}, {-93.557320, 30.828419}, {-93.556179, 30.826387}, {-93.554057, 30.824941}, {-93.554433, 30.823524}, + {-93.557842, 30.821015}, {-93.561912, 30.819459}, {-93.564164, 30.817541}, {-93.564579, 30.816402}, {-93.563763, 30.813606}, {-93.561153, 30.810613}, {-93.560707, 30.809639}, {-93.560791, 30.808628}, {-93.561349, 30.807577}, {-93.562980, 30.806009}, {-93.569318, 30.802962}, {-93.572471, 30.802450}, {-93.574677, 30.801373}, {-93.578437, 30.801749}, {-93.580535, 30.803001}, {-93.581505, 30.803105}, + {-93.582229, 30.803117}, {-93.583775, 30.801779}, {-93.584150, 30.799141}, {-93.584094, 30.796689}, {-93.583188, 30.794284}, {-93.581388, 30.791627}, {-93.581921, 30.788979}, {-93.583693, 30.787762}, {-93.587160, 30.787859}, {-93.588939, 30.787550}, {-93.589381, 30.786676}, {-93.586956, 30.782895}, {-93.584450, 30.781164}, {-93.583165, 30.778596}, {-93.583815, 30.777052}, {-93.587143, 30.776815}, + {-93.590049, 30.777662}, {-93.591731, 30.776664}, {-93.592714, 30.775095}, {-93.593505, 30.772275}, {-93.592722, 30.769648}, {-93.591558, 30.767258}, {-93.592101, 30.763675}, {-93.600501, 30.762012}, {-93.602161, 30.760761}, {-93.603663, 30.760115}, {-93.604920, 30.761073}, {-93.605225, 30.764361}, {-93.605941, 30.764593}, {-93.606653, 30.763572}, {-93.606086, 30.760123}, {-93.606496, 30.758797}, + {-93.606785, 30.758157}, {-93.609178, 30.757260}, {-93.610447, 30.757858}, {-93.611365, 30.759871}, {-93.612372, 30.760597}, {-93.613474, 30.760850}, {-93.614346, 30.760195}, {-93.614728, 30.758775}, {-93.614777, 30.756064}, {-93.614038, 30.755107}, {-93.612424, 30.754123}, {-93.611723, 30.752331}, {-93.608364, 30.748127}, {-93.608414, 30.746664}, {-93.608998, 30.745507}, {-93.609946, 30.744649}, + {-93.613696, 30.744587}, {-93.617753, 30.743252}, {-93.619130, 30.741997}, {-93.619031, 30.740338}, {-93.617689, 30.738478}, {-93.612820, 30.736707}, {-93.610645, 30.734279}, {-93.609134, 30.730039}, {-93.610081, 30.728249}, {-93.610953, 30.727868}, {-93.612393, 30.727987}, {-93.615307, 30.729816}, {-93.616569, 30.729953}, {-93.617719, 30.728897}, {-93.616885, 30.727545}, {-93.614893, 30.726788}, + {-93.609814, 30.727339}, {-93.609047, 30.724272}, {-93.609544, 30.723139}, {-93.610637, 30.722259}, {-93.613274, 30.722814}, {-93.613746, 30.722548}, {-93.613644, 30.722143}, {-93.609699, 30.720388}, {-93.608326, 30.718655}, {-93.605391, 30.717159}, {-93.605817, 30.716594}, {-93.606868, 30.716241}, {-93.608298, 30.715195}, {-93.608720, 30.715235}, {-93.609569, 30.717892}, {-93.610046, 30.718293}, + {-93.611211, 30.718062}, {-93.612598, 30.716532}, {-93.613331, 30.717054}, {-93.614447, 30.719870}, {-93.615669, 30.719595}, {-93.616438, 30.718626}, {-93.617229, 30.718580}, {-93.616185, 30.713981}, {-93.616437, 30.712388}, {-93.619655, 30.708939}, {-93.620157, 30.707073}, {-93.620123, 30.705951}, {-93.620773, 30.704121}, {-93.621809, 30.702762}, {-93.624466, 30.701083}, {-93.624676, 30.699937}, + {-93.624384, 30.698643}, {-93.623279, 30.697360}, {-93.621020, 30.696018}, {-93.620373, 30.695130}, {-93.620442, 30.694142}, {-93.626647, 30.688357}, {-93.627228, 30.687412}, {-93.627709, 30.683731}, {-93.629887, 30.679935}, {-93.631345, 30.677872}, {-93.636054, 30.676320}, {-93.636974, 30.674550}, {-93.638215, 30.673060}, {-93.639640, 30.672505}, {-93.642697, 30.673395}, {-93.645306, 30.673481}, + {-93.646623, 30.671435}, {-93.646847, 30.668727}, {-93.647302, 30.668166}, {-93.648584, 30.667647}, {-93.651473, 30.669940}, {-93.653494, 30.670465}, {-93.654972, 30.670185}, {-93.658269, 30.664154}, {-93.659745, 30.662052}, {-93.661251, 30.661203}, {-93.663572, 30.661880}, {-93.665753, 30.661581}, {-93.668189, 30.660146}, {-93.670358, 30.658007}, {-93.670662, 30.656510}, {-93.670035, 30.654056}, + {-93.670536, 30.652626}, {-93.672094, 30.651697}, {-93.673838, 30.651297}, {-93.674224, 30.650166}, {-93.674134, 30.648686}, {-93.674761, 30.647683}, {-93.678600, 30.644190}, {-93.681086, 30.643108}, {-93.682972, 30.641251}, {-93.682868, 30.639346}, {-93.680780, 30.636786}, {-93.680415, 30.634877}, {-93.680799, 30.633782}, {-93.681763, 30.633033}, {-93.683819, 30.632578}, {-93.684211, 30.631881}, + {-93.684715, 30.630810}, {-93.683887, 30.628600}, {-93.685121, 30.625201}, {-93.682072, 30.620926}, {-93.681802, 30.619516}, {-93.681983, 30.618584}, {-93.685702, 30.616418}, {-93.686905, 30.613757}, {-93.689134, 30.611406}, {-93.689072, 30.610836}, {-93.688287, 30.610151}, {-93.684396, 30.609181}, {-93.683372, 30.608249}, {-93.682843, 30.604999}, {-93.682082, 30.603256}, {-93.679553, 30.600342}, + {-93.678767, 30.598629}, {-93.678662, 30.594097}, {-93.678949, 30.593637}, {-93.679609, 30.593402}, {-93.683649, 30.593520}, {-93.685066, 30.592086}, {-93.684933, 30.587145}, {-93.685466, 30.586030}, {-93.686573, 30.585720}, {-93.691552, 30.585482}, {-93.692222, 30.585706}, {-93.692437, 30.586196}, {-93.691751, 30.587959}, {-93.687804, 30.590138}, {-93.687912, 30.591099}, {-93.688739, 30.592123}, + {-93.692869, 30.594382}, {-93.693976, 30.594217}, {-93.700892, 30.591643}, {-93.703297, 30.588773}, {-93.703908, 30.588441}, {-93.706034, 30.588035}, {-93.708482, 30.588672}, {-93.711318, 30.588906}, {-93.712530, 30.588575}, {-93.713782, 30.587584}, {-93.713732, 30.583131}, {-93.714032, 30.580284}, {-93.715068, 30.579018}, {-93.716821, 30.577860}, {-93.717911, 30.577767}, {-93.720477, 30.580360}, + {-93.721493, 30.580553}, {-93.727844, 30.574070}, {-93.728062, 30.572403}, {-93.726165, 30.569520}, {-93.726423, 30.568105}, {-93.727616, 30.567248}, {-93.727818, 30.565919}, {-93.724300, 30.562015}, {-93.723945, 30.559224}, {-93.725403, 30.557104}, {-93.730144, 30.554218}, {-93.731423, 30.552861}, {-93.731155, 30.550366}, {-93.728763, 30.546438}, {-93.728760, 30.545296}, {-93.734801, 30.541731}, + {-93.739494, 30.540643}, {-93.740253, 30.539569}, {-93.739634, 30.538090}, {-93.738053, 30.537309}, {-93.733326, 30.536849}, {-93.732416, 30.536366}, {-93.732209, 30.535805}, {-93.732806, 30.534882}, {-93.733821, 30.531735}, {-93.732793, 30.529960}, {-93.727722, 30.525670}, {-93.724780, 30.524828}, {-93.720484, 30.524328}, {-93.719788, 30.523609}, {-93.718693, 30.520851}, {-93.716473, 30.519231}, + {-93.714322, 30.518562}, {-93.712870, 30.518613}, {-93.711579, 30.519065}, {-93.709155, 30.521845}, {-93.708633, 30.522034}, {-93.707683, 30.521337}, {-93.707692, 30.519971}, {-93.708903, 30.518823}, {-93.710223, 30.518263}, {-93.711022, 30.517275}, {-93.709892, 30.513851}, {-93.710019, 30.513330}, {-93.711412, 30.512220}, {-93.711762, 30.510915}, {-93.710106, 30.510558}, {-93.707653, 30.511066}, + {-93.707559, 30.510518}, {-93.706358, 30.509653}, {-93.706809, 30.507069}, {-93.707346, 30.506440}, {-93.709726, 30.506511}, {-93.710620, 30.506198}, {-93.710720, 30.505562}, {-93.715145, 30.501268}, {-93.714124, 30.499997}, {-93.708536, 30.500397}, {-93.708669, 30.499124}, {-93.710395, 30.497411}, {-93.714495, 30.497701}, {-93.715377, 30.497270}, {-93.716602, 30.494424}, {-93.717069, 30.494449}, + {-93.713819, 30.490521}, {-93.711995, 30.487635}, {-93.710247, 30.486253}, {-93.709550, 30.485093}, {-93.709763, 30.482649}, {-93.711727, 30.482299}, {-93.713178, 30.483398}, {-93.713842, 30.484615}, {-93.714830, 30.485235}, {-93.716173, 30.485085}, {-93.717421, 30.483822}, {-93.716811, 30.481495}, {-93.714455, 30.479481}, {-93.712853, 30.479032}, {-93.711081, 30.476877}, {-93.710519, 30.475498}, + {-93.710303, 30.473940}, {-93.712226, 30.470627}, {-93.711854, 30.469437}, {-93.711061, 30.468749}, {-93.707167, 30.467488}, {-93.706377, 30.466904}, {-93.704842, 30.464559}, {-93.703877, 30.463779}, {-93.702396, 30.461735}, {-93.699426, 30.459512}, {-93.699036, 30.458805}, {-93.699128, 30.458165}, {-93.700359, 30.457532}, {-93.703719, 30.458251}, {-93.706339, 30.458141}, {-93.707144, 30.456547}, + {-93.706936, 30.455341}, {-93.705367, 30.453233}, {-93.703076, 30.451567}, {-93.702103, 30.449759}, {-93.703802, 30.447756}, {-93.706835, 30.446491}, {-93.707440, 30.445933}, {-93.707465, 30.445045}, {-93.706945, 30.443994}, {-93.705852, 30.443334}, {-93.704633, 30.443118}, {-93.700836, 30.444915}, {-93.699332, 30.445200}, {-93.698306, 30.444928}, {-93.697725, 30.444321}, {-93.697578, 30.442233}, + {-93.698135, 30.438880}, {-93.698927, 30.437234}, {-93.699277, 30.437376}, {-93.702023, 30.436307}, {-93.703393, 30.435356}, {-93.704154, 30.434353}, {-93.703853, 30.432579}, {-93.702368, 30.430922}, {-93.702140, 30.430369}, {-93.702329, 30.429840}, {-93.703254, 30.429447}, {-93.705501, 30.430184}, {-93.708255, 30.430252}, {-93.709445, 30.430967}, {-93.711254, 30.433149}, {-93.711928, 30.433428}, + {-93.712747, 30.433175}, {-93.713369, 30.432163}, {-93.713359, 30.430741}, {-93.714039, 30.429729}, {-93.716472, 30.428442}, {-93.716905, 30.427679}, {-93.716874, 30.425976}, {-93.716369, 30.424755}, {-93.717062, 30.423114}, {-93.718051, 30.422579}, {-93.719178, 30.422722}, {-93.719622, 30.423513}, {-93.719274, 30.425467}, {-93.719799, 30.426803}, {-93.721934, 30.429040}, {-93.722767, 30.431983}, + {-93.723846, 30.432138}, {-93.726106, 30.430720}, {-93.726824, 30.429675}, {-93.727152, 30.427672}, {-93.726834, 30.423642}, {-93.722744, 30.421369}, {-93.722277, 30.420179}, {-93.722749, 30.419557}, {-93.726710, 30.419392}, {-93.727548, 30.418720}, {-93.727871, 30.417700}, {-93.727551, 30.416526}, {-93.727724, 30.415158}, {-93.728721, 30.413547}, {-93.730608, 30.413330}, {-93.731988, 30.413878}, + {-93.732988, 30.415128}, {-93.733917, 30.414935}, {-93.734984, 30.412266}, {-93.734892, 30.408339}, {-93.736764, 30.405478}, {-93.738273, 30.403963}, {-93.741679, 30.402975}, {-93.742037, 30.400515}, {-93.742882, 30.399758}, {-93.745498, 30.398747}, {-93.745338, 30.397028}, {-93.745681, 30.396570}, {-93.748372, 30.396840}, {-93.751016, 30.396484}, {-93.751997, 30.395753}, {-93.752242, 30.393415}, + {-93.752615, 30.392750}, {-93.755025, 30.391495}, {-93.756773, 30.391156}, {-93.757668, 30.390441}, {-93.758753, 30.387892}, {-93.758612, 30.386186}, {-93.757877, 30.385996}, {-93.755774, 30.387273}, {-93.755149, 30.387004}, {-93.754822, 30.386313}, {-93.755330, 30.385029}, {-93.757959, 30.384474}, {-93.758466, 30.384033}, {-93.755789, 30.380646}, {-93.755226, 30.378848}, {-93.754981, 30.372899}, + {-93.755356, 30.368956}, {-93.754697, 30.367191}, {-93.754857, 30.366091}, {-93.755967, 30.365794}, {-93.756495, 30.366253}, {-93.758051, 30.369692}, {-93.758633, 30.369670}, {-93.759215, 30.368754}, {-93.759216, 30.368020}, {-93.758505, 30.365842}, {-93.757687, 30.364581}, {-93.754939, 30.363478}, {-93.751744, 30.361046}, {-93.750979, 30.359899}, {-93.751429, 30.359441}, {-93.752169, 30.359373}, + {-93.754045, 30.359741}, {-93.755367, 30.359582}, {-93.755976, 30.358414}, {-93.756064, 30.356558}, {-93.756803, 30.356158}, {-93.757532, 30.356266}, {-93.758502, 30.357553}, {-93.758930, 30.360965}, {-93.759218, 30.361088}, {-93.759709, 30.360388}, {-93.760326, 30.360302}, {-93.760779, 30.359850}, {-93.761022, 30.359168}, {-93.760938, 30.357112}, {-93.758118, 30.354686}, {-93.758703, 30.354075}, + {-93.762183, 30.353653}, {-93.762480, 30.353039}, {-93.760727, 30.351788}, {-93.758210, 30.350782}, {-93.757718, 30.349977}, {-93.758541, 30.349255}, {-93.759810, 30.348788}, {-93.761779, 30.346774}, {-93.762308, 30.346460}, {-93.763413, 30.346563}, {-93.763655, 30.346040}, {-93.761112, 30.344964}, {-93.759533, 30.342323}, {-93.759880, 30.341869}, {-93.760749, 30.341809}, {-93.764356, 30.344448}, + {-93.765489, 30.344253}, {-93.765655, 30.343409}, {-93.763925, 30.340173}, {-93.763089, 30.339457}, {-93.761277, 30.339074}, {-93.759191, 30.339602}, {-93.758658, 30.339019}, {-93.764109, 30.333830}, {-93.765851, 30.333105}, {-93.765829, 30.332745}, {-93.764252, 30.330229}, {-93.762958, 30.329448}, {-93.759470, 30.330225}, {-93.758120, 30.332126}, {-93.757274, 30.332354}, {-93.755584, 30.331505}, + {-93.755374, 30.330542}, {-93.755561, 30.328869}, {-93.755244, 30.328387}, {-93.754478, 30.328226}, {-93.752337, 30.329485}, {-93.751676, 30.329347}, {-93.751783, 30.328361}, {-93.752392, 30.327605}, {-93.756013, 30.326784}, {-93.756198, 30.326004}, {-93.753321, 30.323526}, {-93.752688, 30.322609}, {-93.750711, 30.321753}, {-93.751239, 30.320402}, {-93.751041, 30.319852}, {-93.750262, 30.319443}, + {-93.748305, 30.319153}, {-93.747815, 30.318756}, {-93.748458, 30.318020}, {-93.750181, 30.317010}, {-93.750201, 30.316155}, {-93.747821, 30.315972}, {-93.746868, 30.315376}, {-93.747021, 30.314283}, {-93.748619, 30.312405}, {-93.748609, 30.311945}, {-93.745648, 30.311744}, {-93.744211, 30.310681}, {-93.743296, 30.309040}, {-93.740918, 30.307230}, {-93.740965, 30.306690}, {-93.741377, 30.306327}, + {-93.744352, 30.305451}, {-93.744293, 30.304418}, {-93.743364, 30.303220}, {-93.743029, 30.301772}, {-93.742424, 30.301139}, {-93.741490, 30.301150}, {-93.739643, 30.303507}, {-93.738520, 30.303745}, {-93.737129, 30.302059}, {-93.734546, 30.301675}, {-93.734855, 30.301079}, {-93.737938, 30.300247}, {-93.738113, 30.299586}, {-93.737556, 30.299369}, {-93.735891, 30.299435}, {-93.734228, 30.298813}, + {-93.732906, 30.298993}, {-93.731903, 30.298372}, {-93.729922, 30.297917}, {-93.729046, 30.296705}, {-93.727990, 30.296776}, {-93.726250, 30.298179}, {-93.725511, 30.298226}, {-93.724797, 30.297838}, {-93.724659, 30.295615}, {-93.724208, 30.295112}, {-93.722808, 30.294910}, {-93.720566, 30.295947}, {-93.718982, 30.296246}, {-93.718347, 30.295788}, {-93.717157, 30.293611}, {-93.715413, 30.292764}, + {-93.714779, 30.293155}, {-93.714780, 30.293773}, {-93.715996, 30.295171}, {-93.715996, 30.295537}, {-93.715521, 30.295629}, {-93.712269, 30.292331}, {-93.712558, 30.290978}, {-93.713385, 30.289862}, {-93.713653, 30.290003}, {-93.713652, 30.288582}, {-93.712908, 30.287939}, {-93.710573, 30.287987}, {-93.710439, 30.287765}, {-93.709532, 30.289312}, {-93.709477, 30.292001}, {-93.708354, 30.292563}, + {-93.707756, 30.292608}, {-93.706061, 30.291791}, {-93.704584, 30.289949}, {-93.704589, 30.288641}, {-93.705254, 30.286181}, {-93.706251, 30.284925}, {-93.708596, 30.284283}, {-93.709053, 30.283874}, {-93.709419, 30.281917}, {-93.708446, 30.281031}, {-93.704917, 30.280830}, {-93.704203, 30.280188}, {-93.704060, 30.279417}, {-93.705626, 30.277393}, {-93.706526, 30.273501}, {-93.707365, 30.272947}, + {-93.708165, 30.272904}, {-93.710084, 30.273759}, {-93.710416, 30.273172}, {-93.710152, 30.272274}, {-93.709663, 30.271892}, {-93.708005, 30.271818}, {-93.707757, 30.271569}, {-93.708618, 30.268673}, {-93.708666, 30.267428}, {-93.705732, 30.267934}, {-93.705452, 30.267756}, {-93.705911, 30.267115}, {-93.708054, 30.266673}, {-93.708819, 30.265923}, {-93.708717, 30.265081}, {-93.708054, 30.264793}, + {-93.707468, 30.265036}, {-93.706881, 30.265898}, {-93.706575, 30.265943}, {-93.706218, 30.265477}, {-93.706448, 30.264349}, {-93.707979, 30.263686}, {-93.707266, 30.262379}, {-93.707266, 30.261471}, {-93.705126, 30.260585}, {-93.703289, 30.261069}, {-93.702983, 30.260517}, {-93.703544, 30.259321}, {-93.705372, 30.257152}, {-93.705733, 30.256007}, {-93.705666, 30.254116}, {-93.706301, 30.252956}, + {-93.706950, 30.252417}, {-93.708789, 30.251949}, {-93.708928, 30.251653}, {-93.707326, 30.249668}, {-93.707432, 30.249221}, {-93.708916, 30.249043}, {-93.709050, 30.248048}, {-93.708310, 30.247532}, {-93.706266, 30.247441}, {-93.705542, 30.246502}, {-93.705932, 30.246127}, {-93.706439, 30.246464}, {-93.708249, 30.246352}, {-93.709081, 30.246017}, {-93.709531, 30.244932}, {-93.709018, 30.243963}, + {-93.706939, 30.244261}, {-93.706993, 30.243736}, {-93.705002, 30.242351}, {-93.703218, 30.239759}, {-93.703099, 30.238459}, {-93.703683, 30.238011}, {-93.704181, 30.238035}, {-93.706668, 30.239402}, {-93.707812, 30.239624}, {-93.709450, 30.238514}, {-93.709569, 30.237666}, {-93.708401, 30.236158}, {-93.707656, 30.235849}, {-93.706095, 30.236269}, {-93.705625, 30.235364}, {-93.704661, 30.234565}, + {-93.704490, 30.233787}, {-93.705288, 30.233359}, {-93.707623, 30.233791}, {-93.709513, 30.233548}, {-93.710130, 30.233948}, {-93.710130, 30.234466}, {-93.709679, 30.234915}, {-93.709749, 30.235512}, {-93.710323, 30.235726}, {-93.711098, 30.235247}, {-93.710837, 30.233837}, {-93.711433, 30.231593}, {-93.713427, 30.229466}, {-93.713496, 30.228900}, {-93.711856, 30.229092}, {-93.709509, 30.227834}, + {-93.709867, 30.227488}, {-93.711081, 30.227432}, {-93.711983, 30.227022}, {-93.713113, 30.226001}, {-93.713642, 30.224656}, {-93.713371, 30.224060}, {-93.711154, 30.222976}, {-93.711176, 30.222070}, {-93.712023, 30.221473}, {-93.712152, 30.219815}, {-93.713058, 30.218641}, {-93.713755, 30.218739}, {-93.714853, 30.220143}, {-93.716065, 30.220934}, {-93.718463, 30.219998}, {-93.719650, 30.218219}, + {-93.719938, 30.216234}, {-93.720689, 30.215481}, {-93.720544, 30.214935}, {-93.719861, 30.214850}, {-93.720287, 30.215395}, {-93.718199, 30.215504}, {-93.717020, 30.216131}, {-93.717788, 30.215472}, {-93.719542, 30.214985}, {-93.719557, 30.214751}, {-93.717517, 30.213244}, {-93.717119, 30.212040}, {-93.717693, 30.210984}, {-93.720413, 30.210781}, {-93.720997, 30.210409}, {-93.721259, 30.209611}, + {-93.720931, 30.208461}, {-93.718219, 30.206441}, {-93.717438, 30.206208}, {-93.717007, 30.206304}, {-93.715480, 30.208891}, {-93.714996, 30.209043}, {-93.714192, 30.208659}, {-93.715003, 30.204488}, {-93.714617, 30.202383}, {-93.716371, 30.202590}, {-93.717904, 30.200723}, {-93.717102, 30.199406}, {-93.715330, 30.198185}, {-93.714755, 30.197404}, {-93.716134, 30.196898}, {-93.717648, 30.194888}, + {-93.717839, 30.193999}, {-93.717523, 30.193085}, {-93.713227, 30.193245}, {-93.710622, 30.196541}, {-93.709121, 30.197208}, {-93.708222, 30.196224}, {-93.708230, 30.194982}, {-93.707940, 30.194454}, {-93.706044, 30.193978}, {-93.705495, 30.192921}, {-93.705663, 30.191771}, {-93.708992, 30.190959}, {-93.710858, 30.191342}, {-93.712024, 30.192729}, {-93.712552, 30.192456}, {-93.712810, 30.188827}, + {-93.712423, 30.187172}, {-93.711422, 30.186226}, {-93.709548, 30.186897}, {-93.708795, 30.186353}, {-93.708896, 30.185786}, {-93.709573, 30.185071}, {-93.709712, 30.182980}, {-93.710684, 30.179862}, {-93.708027, 30.179284}, {-93.707090, 30.179664}, {-93.706609, 30.180676}, {-93.705621, 30.180649}, {-93.705488, 30.180165}, {-93.706399, 30.178360}, {-93.706538, 30.176979}, {-93.705196, 30.174867}, + {-93.704240, 30.174185}, {-93.703429, 30.174086}, {-93.702612, 30.174406}, {-93.701239, 30.175820}, {-93.699953, 30.175529}, {-93.700254, 30.174549}, {-93.702094, 30.172960}, {-93.703000, 30.171695}, {-93.703046, 30.169753}, {-93.702658, 30.167824}, {-93.701925, 30.166711}, {-93.700817, 30.166226}, {-93.702162, 30.165104}, {-93.702716, 30.161748}, {-93.703050, 30.161231}, {-93.705838, 30.159268}, + {-93.706996, 30.157465}, {-93.707056, 30.154226}, {-93.706333, 30.152416}, {-93.706154, 30.150596}, {-93.707069, 30.149110}, {-93.709348, 30.147632}, {-93.709508, 30.146672}, {-93.708436, 30.146608}, {-93.706532, 30.147424}, {-93.703837, 30.149205}, {-93.701513, 30.152027}, {-93.700180, 30.152720}, {-93.697737, 30.152946}, {-93.696292, 30.151601}, {-93.695721, 30.148612}, {-93.693303, 30.141899}, + {-93.691923, 30.141088}, {-93.689527, 30.141751}, {-93.688205, 30.141357}, {-93.688834, 30.139945}, {-93.692849, 30.135213}, {-93.693821, 30.134990}, {-93.694952, 30.135181}, {-93.698274, 30.138619}, {-93.699911, 30.138626}, {-93.701210, 30.137458}, {-93.701999, 30.129995}, {-93.700692, 30.125105}, {-93.700872, 30.123114}, {-93.701430, 30.122295}, {-93.702587, 30.121841}, {-93.706884, 30.121554}, + {-93.707753, 30.120943}, {-93.707954, 30.120016}, {-93.707290, 30.119365}, {-93.703412, 30.117185}, {-93.702372, 30.114753}, {-93.702436, 30.112721}, {-93.702916, 30.111777}, {-93.704260, 30.110657}, {-93.706660, 30.110113}, {-93.710709, 30.109761}, {-93.712261, 30.109041}, {-93.712997, 30.107857}, {-93.714597, 30.101825}, {-93.715493, 30.100529}, {-93.718021, 30.098129}, {-93.723109, 30.094866}, + {-93.723765, 30.094130}, {-93.724101, 30.093010}, {-93.723141, 30.088722}, {-93.722949, 30.086130}, {-93.723237, 30.083890}, {-93.724005, 30.082626}, {-93.724805, 30.082098}, {-93.726859, 30.082268}, {-93.727717, 30.083410}, {-93.729544, 30.087867}, {-93.730313, 30.089034}, {-93.731079, 30.089518}, {-93.733127, 30.088374}, {-93.733765, 30.087570}, {-93.734085, 30.086130}, {-93.733745, 30.084590}, + {-93.732491, 30.082589}, {-93.725910, 30.078469}, {-93.720716, 30.072981}, {-93.717008, 30.069583}, {-93.715238, 30.068870}, {-93.706171, 30.067311}, {-93.703105, 30.066261}, {-93.700580, 30.063683}, {-93.699421, 30.060323}, {-93.699516, 30.058578}, {-93.700189, 30.057000}, {-93.701092, 30.056040}, {-93.704151, 30.054208}, {-93.706578, 30.053751}, {-93.711074, 30.054391}, {-93.711797, 30.055671}, + {-93.712112, 30.059221}, {-93.713772, 30.065687}, {-93.715461, 30.066579}, {-93.717884, 30.065554}, {-93.719453, 30.063808}, {-93.719714, 30.061943}, {-93.719685, 30.056050}, {-93.720805, 30.053043}, {-93.722309, 30.051059}, {-93.727187, 30.046500}, {-93.734486, 30.040803}, {-93.737446, 30.037283}, {-93.739158, 30.032627}, {-93.739734, 30.023987}, {-93.741078, 30.021571}, {-93.743958, 30.019539}, + {-93.748790, 30.017491}, {-93.755014, 30.015363}, {-93.756630, 30.014163}, {-93.761959, 30.009060}, {-93.767239, 30.003380}, {-93.768444, 30.001694}, {-93.769383, 29.997700}, {-93.770567, 29.996532}, {-93.772935, 29.995588}, {-93.776807, 29.995396}, {-93.780823, 29.994276}, {-93.785191, 29.991956}, {-93.788599, 29.988836}, {-93.790087, 29.986564}, {-93.792423, 29.979108}, {-93.794439, 29.974773}, + {-93.796151, 29.972037}, {-93.803591, 29.962309}, {-93.806551, 29.957445}, {-93.807815, 29.954549}, {-93.810375, 29.947349}, {-93.813735, 29.935126}, {-93.815558, 29.925094}, {-93.817286, 29.918422}, {-93.821478, 29.910182}, {-93.830374, 29.894358}, {-93.838374, 29.882853}, {-93.845079, 29.874835}, {-93.855140, 29.864099}, {-93.859896, 29.859921}, {-93.863570, 29.857177}, {-93.872446, 29.851650}, + {-93.880999, 29.848215}, {-93.886215, 29.845719}, {-93.895575, 29.840247}, {-93.903576, 29.835000}, {-93.913672, 29.827192}, {-93.922744, 29.818808}, {-93.925720, 29.814744}, {-93.927992, 29.809640}, {-93.929208, 29.802952}, {-93.928808, 29.797080}, {-93.926504, 29.789560}, {-93.922407, 29.785048}, {-93.898470, 29.771577}, {-93.893863, 29.767294}, {-93.892246, 29.765241}, {-93.890821, 29.761673}, + {-93.890789, 29.760873}, {-93.892773, 29.757033}, {-93.893829, 29.753033}, {-93.892805, 29.747129}, {-93.891637, 29.744618}, {-93.888821, 29.742234}, {-93.887557, 29.742090}, {-93.873941, 29.737770}, {-93.871908, 29.736922}, {-93.870020, 29.735482}, {-93.863204, 29.724059}, {-93.844500, 29.699644}, {-93.837412, 29.689885}, {-93.835540, 29.685661}, {-93.834196, 29.681069}, {-93.831076, 29.666879}, + {-93.828708, 29.659535}, {-93.814351, 29.596576}, {-93.825794, 29.595828}, {-93.836491, 29.596845}, {-93.848588, 29.600260}, {-93.857983, 29.605087}, {-93.866531, 29.611834}, {-93.875006, 29.621426}, {-93.906043, 29.625001}, {-94.033565, 29.625002}, {-94.125180, 29.595559}, {-94.250184, 29.546764}, {-94.250184, 29.547600}, {-94.370351, 29.500230}, {-94.372494, 29.499741}, {-94.375187, 29.500131}, + {-94.419249, 29.483452}, {-94.499452, 29.452337}, {-94.500190, 29.451590}, {-94.625192, 29.397662}, {-94.650311, 29.382820}, {-94.646981, 29.381008}, {-94.643555, 29.378486}, {-94.640821, 29.375235}, {-94.636046, 29.370636}, {-94.628585, 29.358279}, {-94.625192, 29.349517}, {-94.625192, 29.347758}, {-94.623898, 29.343143}, {-94.624169, 29.335198}, {-94.627306, 29.327899}, {-94.629026, 29.321640}, + {-94.633492, 29.314449}, {-94.638018, 29.307965}, {-94.642421, 29.300136}, {-94.650360, 29.291890}, {-94.659644, 29.285145}, {-94.666220, 29.281047}, {-94.680295, 29.277603}, {-94.692309, 29.277051}, {-94.707561, 29.280785}, {-94.713311, 29.282521}, {-94.741166, 29.259583}, {-94.747932, 29.250240}, {-94.750740, 29.246928}, {-94.772686, 29.231718}, {-94.840151, 29.198346}, {-94.912171, 29.159318}, + {-94.963661, 29.125241}, {-94.964240, 29.125241}, {-95.000202, 29.104452}, {-95.086398, 29.068240}, {-95.088357, 29.065200}, {-95.091622, 29.062739}, {-95.087257, 29.057996}, {-95.089036, 29.048046}, {-95.093799, 29.033327}, {-95.105447, 29.016048}, {-95.106528, 29.014935}, {-95.115124, 29.008631}, {-95.125770, 29.002989}, {-95.139469, 28.991697}, {-95.156695, 28.974271}, {-95.180835, 28.948931}, + {-95.206428, 28.926692}, {-95.227228, 28.910379}, {-95.241681, 28.899960}, {-95.252791, 28.892600}, {-95.267365, 28.885063}, {-95.290812, 28.871643}, {-95.308121, 28.858830}, {-95.310199, 28.850965}, {-95.315422, 28.842209}, {-95.322113, 28.834442}, {-95.329847, 28.828699}, {-95.340722, 28.823085}, {-95.353471, 28.817863}, {-95.365442, 28.813757}, {-95.377987, 28.810279}, {-95.383900, 28.809387}, + {-95.394172, 28.807000}, {-95.401159, 28.805969}, {-95.413068, 28.805317}, {-95.420071, 28.805625}, {-95.493888, 28.765340}, {-95.533784, 28.750256}, {-95.532019, 28.750256}, {-95.625232, 28.702877}, {-95.750238, 28.638419}, {-95.777845, 28.625266}, {-95.875240, 28.582788}, {-96.000243, 28.532071}, {-96.000243, 28.533433}, {-96.076048, 28.500002}, {-96.078331, 28.500001}, {-96.182242, 28.452036}, + {-96.199375, 28.442278}, {-96.217837, 28.431368}, {-96.235458, 28.421469}, {-96.258191, 28.407872}, {-96.259806, 28.400699}, {-96.262905, 28.394196}, {-96.267069, 28.388229}, {-96.271767, 28.382934}, {-96.282307, 28.375002}, {-96.294175, 28.368305}, {-96.303533, 28.365020}, {-96.312419, 28.363443}, {-96.320762, 28.362868}, {-96.321428, 28.360792}, {-96.324204, 28.356953}, {-96.328740, 28.352053}, + {-96.337945, 28.344300}, {-96.341807, 28.342381}, {-96.341290, 28.340699}, {-96.342291, 28.335135}, {-96.344731, 28.325989}, {-96.347637, 28.320170}, {-96.352686, 28.312923}, {-96.359293, 28.305753}, {-96.367967, 28.297222}, {-96.375252, 28.290760}, {-96.381092, 28.287098}, {-96.389290, 28.282543}, {-96.395645, 28.279524}, {-96.402403, 28.278056}, {-96.425807, 28.268687}, {-96.455074, 28.255279}, + {-96.500256, 28.235482}, {-96.510049, 28.230680}, {-96.531000, 28.221164}, {-96.556661, 28.206480}, {-96.596507, 28.182998}, {-96.618785, 28.169522}, {-96.640749, 28.155390}, {-96.666137, 28.137684}, {-96.730685, 28.091268}, {-96.746251, 28.079622}, {-96.784064, 28.048554}, {-96.844196, 28.000296}, {-96.875264, 27.969920}, {-96.908743, 27.934091}, {-96.938546, 27.901199}, {-96.959495, 27.875302}, + {-96.984281, 27.803783}, {-96.987006, 27.800842}, {-96.991680, 27.797166}, {-97.008898, 27.786790}, {-97.013046, 27.785288}, {-97.021855, 27.783364}, {-97.037789, 27.765692}, {-97.046727, 27.755049}, {-97.050221, 27.750308}, {-97.125265, 27.640597}, {-97.165097, 27.568300}, {-97.167381, 27.562816}, {-97.199816, 27.500319}, {-97.250264, 27.392308}, {-97.256726, 27.375321}, {-97.288337, 27.277584}, + {-97.296567, 27.250323}, {-97.304669, 27.216929}, {-97.306841, 27.205748}, {-97.309363, 27.196109}, {-97.309473, 27.190842}, {-97.311943, 27.179492}, {-97.313559, 27.169689}, {-97.315535, 27.148146}, {-97.316571, 27.141130}, {-97.319824, 27.110023}, {-97.321854, 27.079308}, {-97.323150, 27.051146}, {-97.322698, 27.036033}, {-97.323219, 27.010256}, {-97.321148, 27.000330}, {-97.320725, 26.984484}, + {-97.315950, 26.929289}, {-97.313879, 26.913126}, {-97.310842, 26.897682}, {-97.306988, 26.875334}, {-97.300423, 26.834613}, {-97.296629, 26.818647}, {-97.292374, 26.803255}, {-97.282725, 26.763423}, {-97.251705, 26.661866}, {-97.224429, 26.597694}, {-97.217341, 26.584670}, {-97.214056, 26.576766}, {-97.212676, 26.566880}, {-97.212817, 26.564030}, {-97.212548, 26.561651}, {-97.201806, 26.529057}, + {-97.196851, 26.512843}, {-97.184566, 26.475852}, {-97.167369, 26.413108}, {-97.156496, 26.375348}, {-97.126121, 26.250351}, {-97.125248, 26.248285}, {-97.125248, 26.246333}, {-97.124553, 26.244415}, {-97.121262, 26.225549}, {-97.117285, 26.197964}, {-97.113328, 26.166312}, {-97.112594, 26.157957}, {-97.110699, 26.145367}, {-97.110147, 26.140583}, {-97.109649, 26.129468}, {-97.109040, 26.125354}, + {-97.108301, 26.104510}, {-97.100252, 26.097664}, {-97.097386, 26.093508}, {-97.094220, 26.088376}, {-97.092921, 26.085102}, {-97.089989, 26.075340}, {-97.089340, 26.066115}, {-97.090205, 26.057169}, {-97.091287, 26.051598}, {-97.094143, 26.043044}, {-97.097858, 26.038620}, {-97.091121, 25.973840}, {-97.125247, 25.972593}, {-97.146654, 25.970969}, {-97.147012, 25.970516}, {-97.146340, 25.962614}, + {-97.146679, 25.959391}, {-97.146105, 25.956657}, {-97.147333, 25.955701}, {-97.147278, 25.953991}, {-97.147786, 25.953143}, {-97.150485, 25.951472}, {-97.156606, 25.949020}, {-97.158954, 25.949119}, {-97.160297, 25.950250}, {-97.160462, 25.950985}, {-97.158577, 25.957571}, {-97.158188, 25.960365}, {-97.158816, 25.962220}, {-97.160004, 25.962371}, {-97.164277, 25.960621}, {-97.166950, 25.960225}, + {-97.171100, 25.961640}, {-97.171879, 25.962280}, {-97.173479, 25.964570}, {-97.175584, 25.965573}, {-97.177088, 25.965346}, {-97.177743, 25.962709}, {-97.178601, 25.961459}, {-97.179674, 25.960749}, {-97.187079, 25.958483}, {-97.187513, 25.957584}, {-97.187143, 25.955227}, {-97.187895, 25.954513}, {-97.189587, 25.953795}, {-97.193392, 25.955091}, {-97.196753, 25.957449}, {-97.197888, 25.957926}, + {-97.201568, 25.958458}, {-97.203656, 25.957988}, {-97.205481, 25.958287}, {-97.206059, 25.958652}, {-97.206488, 25.960115}, {-97.206561, 25.963231}, {-97.207157, 25.963646}, {-97.209021, 25.963722}, {-97.209961, 25.963458}, {-97.214089, 25.960311}, {-97.220922, 25.957272}, {-97.222560, 25.957326}, {-97.224220, 25.958629}, {-97.225354, 25.959094}, {-97.228026, 25.958937}, {-97.229227, 25.958758}, + {-97.233001, 25.956711}, {-97.239872, 25.954975}, {-97.243572, 25.952786}, {-97.244775, 25.951272}, {-97.244853, 25.949951}, {-97.245243, 25.949225}, {-97.247010, 25.948234}, {-97.248033, 25.948097}, {-97.253586, 25.948366}, {-97.257597, 25.950112}, {-97.260864, 25.950933}, {-97.276701, 25.952146}, {-97.278580, 25.953428}, {-97.280000, 25.954926}, {-97.282197, 25.958807}, {-97.283013, 25.959271}, + {-97.284446, 25.959156}, {-97.285523, 25.958615}, {-97.289239, 25.955444}, {-97.289702, 25.954674}, {-97.289638, 25.954028}, {-97.288389, 25.952384}, {-97.284741, 25.949487}, {-97.281254, 25.947995}, {-97.278528, 25.947465}, {-97.278284, 25.946582}, {-97.280390, 25.945446}, {-97.281037, 25.943457}, {-97.278937, 25.941724}, {-97.277485, 25.939905}, {-97.276387, 25.936065}, {-97.276696, 25.935339}, + {-97.280046, 25.935070}, {-97.286873, 25.936227}, {-97.291786, 25.936302}, {-97.293243, 25.935710}, {-97.295005, 25.933739}, {-97.296108, 25.933425}, {-97.298150, 25.933865}, {-97.298659, 25.934422}, {-97.298893, 25.936246}, {-97.299615, 25.936865}, {-97.300716, 25.937224}, {-97.302541, 25.937230}, {-97.304210, 25.936114}, {-97.304590, 25.935156}, {-97.304691, 25.932168}, {-97.304216, 25.930274}, + {-97.304505, 25.928838}, {-97.305319, 25.927930}, {-97.306903, 25.927010}, {-97.308231, 25.926490}, {-97.309739, 25.926405}, {-97.310856, 25.926903}, {-97.311487, 25.927544}, {-97.311734, 25.928597}, {-97.312635, 25.929954}, {-97.313962, 25.931073}, {-97.315177, 25.931468}, {-97.316784, 25.931650}, {-97.318582, 25.931042}, {-97.320567, 25.929804}, {-97.323951, 25.925955}, {-97.324916, 25.924036}, + {-97.325102, 25.921667}, {-97.324651, 25.918592}, {-97.324869, 25.918041}, {-97.326003, 25.917689}, {-97.330249, 25.917709}, {-97.331241, 25.918556}, {-97.331805, 25.920029}, {-97.332678, 25.925578}, {-97.333623, 25.926112}, {-97.334077, 25.925687}, {-97.334510, 25.924280}, {-97.334267, 25.921457}, {-97.334483, 25.920203}, {-97.335936, 25.919802}, {-97.337834, 25.920477}, {-97.338437, 25.921624}, + {-97.338357, 25.922854}, {-97.336541, 25.928366}, {-97.336436, 25.928958}, {-97.336819, 25.929449}, {-97.338064, 25.929806}, {-97.340778, 25.928877}, {-97.342507, 25.928985}, {-97.344231, 25.929377}, {-97.347354, 25.931178}, {-97.348274, 25.931143}, {-97.348906, 25.930807}, {-97.350394, 25.925237}, {-97.352979, 25.921733}, {-97.355441, 25.919428}, {-97.361500, 25.915868}, {-97.367640, 25.915681}, + {-97.368517, 25.915326}, {-97.369113, 25.914622}, {-97.369393, 25.913126}, {-97.368351, 25.910639}, {-97.368420, 25.909671}, {-97.369123, 25.909049}, {-97.370215, 25.908915}, {-97.372158, 25.909463}, {-97.373490, 25.909252}, {-97.374914, 25.908006}, {-97.375118, 25.907196}, {-97.374844, 25.906536}, {-97.372365, 25.905016}, {-97.365976, 25.902447}, {-97.365675, 25.901056}, {-97.365909, 25.899358}, + {-97.367262, 25.893886}, {-97.366957, 25.891746}, {-97.366287, 25.890820}, {-97.364886, 25.889969}, {-97.363427, 25.889689}, {-97.361037, 25.889938}, {-97.358467, 25.889639}, {-97.356750, 25.888432}, {-97.356629, 25.887681}, {-97.357401, 25.886825}, {-97.358747, 25.886228}, {-97.363522, 25.885888}, {-97.366723, 25.885066}, {-97.369042, 25.884051}, {-97.373197, 25.881183}, {-97.373864, 25.880253}, + {-97.374130, 25.879039}, {-97.373337, 25.878484}, {-97.363823, 25.877917}, {-97.361702, 25.878398}, {-97.361367, 25.879302}, {-97.360657, 25.879828}, {-97.359497, 25.879683}, {-97.358573, 25.878129}, {-97.358605, 25.876669}, {-97.359263, 25.873873}, {-97.359192, 25.872426}, {-97.356693, 25.867354}, {-97.357193, 25.866680}, {-97.358752, 25.865708}, {-97.359384, 25.866550}, {-97.358933, 25.868101}, + {-97.359364, 25.869209}, {-97.360072, 25.869577}, {-97.361455, 25.869300}, {-97.363247, 25.867600}, {-97.363976, 25.866025}, {-97.365723, 25.864257}, {-97.369144, 25.858453}, {-97.372278, 25.857076}, {-97.374155, 25.856948}, {-97.375772, 25.856437}, {-97.376575, 25.854578}, {-97.375558, 25.853998}, {-97.369756, 25.854357}, {-97.366583, 25.853564}, {-97.365393, 25.852977}, {-97.364585, 25.852134}, + {-97.364099, 25.851108}, {-97.364100, 25.850034}, {-97.365185, 25.849854}, {-97.368197, 25.850705}, {-97.369403, 25.850484}, {-97.370778, 25.849656}, {-97.372042, 25.847422}, {-97.371842, 25.841919}, {-97.372279, 25.840781}, {-97.373187, 25.839956}, {-97.376551, 25.840410}, {-97.380663, 25.839613}, {-97.382063, 25.839760}, {-97.383381, 25.840646}, {-97.386200, 25.843552}, {-97.387138, 25.843853}, + {-97.387863, 25.843495}, {-97.388246, 25.842749}, {-97.389075, 25.839749}, {-97.390072, 25.838932}, {-97.393366, 25.837395}, {-97.395041, 25.837240}, {-97.398496, 25.838960}, {-97.399655, 25.839212}, {-97.401823, 25.838888}, {-97.403660, 25.837873}, {-97.405250, 25.837728}, {-97.406166, 25.837915}, {-97.406701, 25.838393}, {-97.407088, 25.839865}, {-97.405042, 25.840657}, {-97.403426, 25.840237}, + {-97.402272, 25.840341}, {-97.400663, 25.841512}, {-97.400158, 25.842759}, {-97.400776, 25.846198}, {-97.398744, 25.850490}, {-97.399212, 25.851202}, {-97.400694, 25.852281}, {-97.402253, 25.851929}, {-97.403585, 25.851012}, {-97.404314, 25.850030}, {-97.405798, 25.845292}, {-97.406731, 25.844722}, {-97.408247, 25.845633}, {-97.410051, 25.848350}, {-97.410564, 25.849964}, {-97.410214, 25.851084}, + {-97.408105, 25.854729}, {-97.408304, 25.856035}, {-97.409262, 25.857590}, {-97.408856, 25.859038}, {-97.407859, 25.860764}, {-97.408014, 25.861596}, {-97.408806, 25.862109}, {-97.410018, 25.862077}, {-97.411314, 25.861342}, {-97.412612, 25.859689}, {-97.413926, 25.856594}, {-97.413618, 25.852609}, {-97.414579, 25.847729}, {-97.413558, 25.843793}, {-97.412223, 25.842649}, {-97.412349, 25.841658}, + {-97.413055, 25.840881}, {-97.416718, 25.841349}, {-97.423016, 25.840259}, {-97.426090, 25.840947}, {-97.429761, 25.843869}, {-97.434598, 25.848774}, {-97.436046, 25.849827}, {-97.436937, 25.849796}, {-97.440442, 25.848320}, {-97.443362, 25.848180}, {-97.444515, 25.849147}, {-97.445849, 25.851373}, {-97.444116, 25.854750}, {-97.444451, 25.855746}, {-97.445130, 25.856224}, {-97.447497, 25.856256}, + {-97.452149, 25.853832}, {-97.453182, 25.853634}, {-97.453985, 25.854088}, {-97.453656, 25.856486}, {-97.451768, 25.858793}, {-97.451380, 25.859140}, {-97.448744, 25.859032}, {-97.448271, 25.859463}, {-97.447679, 25.863232}, {-97.445586, 25.865018}, {-97.444873, 25.866286}, {-97.444653, 25.867440}, {-97.445097, 25.868664}, {-97.447153, 25.871410}, {-97.448321, 25.872106}, {-97.449482, 25.872116}, + {-97.450267, 25.871747}, {-97.451526, 25.869481}, {-97.453491, 25.867367}, {-97.455598, 25.867290}, {-97.456690, 25.868343}, {-97.457130, 25.869473}, {-97.456331, 25.871872}, {-97.454724, 25.879336}, {-97.454895, 25.882070}, {-97.455601, 25.883949}, {-97.458332, 25.883946}, {-97.464181, 25.881716}, {-97.465338, 25.880875}, {-97.469507, 25.875539}, {-97.470122, 25.875413}, {-97.472321, 25.875596}, + {-97.474103, 25.876531}, {-97.474743, 25.878037}, {-97.474670, 25.879681}, {-97.473149, 25.880865}, {-97.469418, 25.882697}, {-97.468460, 25.883707}, {-97.467772, 25.886642}, {-97.468340, 25.888401}, {-97.469342, 25.888795}, {-97.470819, 25.888354}, {-97.477089, 25.883092}, {-97.481769, 25.879955}, {-97.484865, 25.878700}, {-97.486444, 25.878502}, {-97.487640, 25.879023}, {-97.487561, 25.880253}, + {-97.486273, 25.883334}, {-97.486749, 25.884781}, {-97.487161, 25.885158}, {-97.488472, 25.885384}, {-97.492814, 25.880806}, {-97.495094, 25.879665}, {-97.496687, 25.879680}, {-97.497355, 25.880092}, {-97.497457, 25.880563}, {-97.497615, 25.886255}, {-97.496513, 25.892892}, {-97.496430, 25.895805}, {-97.497386, 25.898644}, {-97.498204, 25.899039}, {-97.499553, 25.898056}, {-97.502073, 25.894740}, + {-97.505319, 25.891291}, {-97.510769, 25.887595}, {-97.515135, 25.887213}, {-97.520340, 25.885932}, {-97.521750, 25.886462}, {-97.522489, 25.887362}, {-97.523317, 25.889407}, {-97.523447, 25.890956}, {-97.522862, 25.895058}, {-97.523862, 25.899032}, {-97.526102, 25.901035}, {-97.527342, 25.902613}, {-97.528346, 25.906171}, {-97.528799, 25.906876}, {-97.530309, 25.907423}, {-97.532087, 25.907333}, + {-97.532863, 25.907626}, {-97.533482, 25.908383}, {-97.533757, 25.911919}, {-97.533055, 25.913316}, {-97.530144, 25.916756}, {-97.529855, 25.918954}, {-97.530182, 25.919854}, {-97.531623, 25.920475}, {-97.533987, 25.920457}, {-97.540380, 25.919131}, {-97.542125, 25.919433}, {-97.542962, 25.920031}, {-97.542975, 25.921489}, {-97.542530, 25.922373}, {-97.537349, 25.927396}, {-97.536987, 25.928855}, + {-97.537229, 25.930526}, {-97.537794, 25.930761}, {-97.538571, 25.930238}, {-97.539884, 25.927567}, {-97.542484, 25.923731}, {-97.542982, 25.923395}, {-97.543990, 25.923353}, {-97.545099, 25.924002}, {-97.545477, 25.925170}, {-97.545440, 25.931181}, {-97.546014, 25.933520}, {-97.546667, 25.934547}, {-97.549740, 25.936219}, {-97.553868, 25.937247}, {-97.555914, 25.937018}, {-97.557586, 25.935267}, + {-97.559355, 25.931464}, {-97.559324, 25.932247}, {-97.582865, 25.937857}, {-97.580908, 25.939368}, {-97.573474, 25.942970}, {-97.571468, 25.943632}, {-97.568993, 25.943820}, {-97.566663, 25.944714}, {-97.566113, 25.945414}, {-97.566062, 25.948192}, {-97.567957, 25.952350}, {-97.569342, 25.953147}, {-97.573998, 25.953969}, {-97.574406, 25.953810}, {-97.574668, 25.952777}, {-97.572998, 25.948650}, + {-97.573223, 25.945855}, {-97.573918, 25.944434}, {-97.575181, 25.943177}, {-97.578236, 25.942049}, {-97.578955, 25.942431}, {-97.580048, 25.944498}, {-97.581167, 25.950915}, {-97.582052, 25.952216}, {-97.583012, 25.954828}, {-97.582968, 25.956976}, {-97.581716, 25.960848}, {-97.581747, 25.961596}, {-97.582373, 25.962469}, {-97.583120, 25.962580}, {-97.587742, 25.961339}, {-97.591765, 25.961857}, + {-97.593888, 25.961575}, {-97.595678, 25.960593}, {-97.597562, 25.957663}, {-97.598311, 25.957038}, {-97.600561, 25.956747}, {-97.601478, 25.956919}, {-97.602591, 25.957735}, {-97.604484, 25.961047}, {-97.609107, 25.964537}, {-97.610572, 25.966303}, {-97.610389, 25.967326}, {-97.608725, 25.969762}, {-97.608077, 25.971280}, {-97.607650, 25.975887}, {-97.607957, 25.976635}, {-97.609992, 25.977582}, + {-97.612891, 25.977742}, {-97.615119, 25.978296}, {-97.617666, 25.979956}, {-97.619331, 25.982732}, {-97.620197, 25.985014}, {-97.620264, 25.986094}, {-97.619847, 25.987469}, {-97.620687, 25.988496}, {-97.621720, 25.989117}, {-97.623686, 25.989522}, {-97.627965, 25.988374}, {-97.629857, 25.988053}, {-97.631571, 25.988178}, {-97.633081, 25.988652}, {-97.634436, 25.990268}, {-97.634367, 25.991617}, + {-97.633215, 25.994500}, {-97.632379, 25.995689}, {-97.631137, 25.996331}, {-97.629786, 25.996331}, {-97.626111, 25.995229}, {-97.624565, 25.993970}, {-97.623937, 25.992345}, {-97.623015, 25.991613}, {-97.621267, 25.991856}, {-97.620309, 25.992357}, {-97.618712, 25.994625}, {-97.617834, 25.997895}, {-97.617467, 26.000356}, {-97.618282, 26.002092}, {-97.619127, 26.002890}, {-97.619816, 26.002703}, + {-97.622017, 25.998605}, {-97.623816, 25.997076}, {-97.626011, 25.996480}, {-97.627704, 25.996826}, {-97.628310, 25.997625}, {-97.628154, 25.998892}, {-97.626396, 26.002840}, {-97.626357, 26.005306}, {-97.626950, 26.005968}, {-97.628505, 26.006155}, {-97.629977, 26.005485}, {-97.631889, 26.003187}, {-97.631886, 26.001888}, {-97.632224, 26.001335}, {-97.635072, 25.999893}, {-97.637321, 26.001367}, + {-97.640706, 26.004722}, {-97.646769, 26.007609}, {-97.648411, 26.009327}, {-97.649377, 26.010862}, {-97.649562, 26.011324}, {-97.649268, 26.012164}, {-97.648177, 26.012666}, {-97.641221, 26.011828}, {-97.638874, 26.012760}, {-97.637609, 26.014161}, {-97.636767, 26.016439}, {-97.637330, 26.020165}, {-97.638293, 26.022020}, {-97.640578, 26.024515}, {-97.645220, 26.027758}, {-97.645991, 26.027522}, + {-97.647683, 26.026249}, {-97.649638, 26.024495}, {-97.650540, 26.023216}, {-97.651086, 26.020390}, {-97.650185, 26.017974}, {-97.650939, 26.016390}, {-97.651442, 26.015981}, {-97.652771, 26.016296}, {-97.654368, 26.018122}, {-97.655647, 26.019018}, {-97.658627, 26.020115}, {-97.659753, 26.020189}, {-97.666207, 26.018873}, {-97.667001, 26.018876}, {-97.668130, 26.019429}, {-97.668196, 26.020844}, + {-97.667428, 26.024384}, {-97.665826, 26.026108}, {-97.662378, 26.028511}, {-97.660699, 26.031252}, {-97.660524, 26.033708}, {-97.662728, 26.038078}, {-97.663510, 26.037862}, {-97.664178, 26.036785}, {-97.664193, 26.032970}, {-97.665052, 26.030750}, {-97.667693, 26.028954}, {-97.668480, 26.028949}, {-97.669550, 26.030149}, {-97.670379, 26.033235}, {-97.670859, 26.033819}, {-97.672436, 26.034023}, + {-97.674548, 26.033029}, {-97.675906, 26.031369}, {-97.676163, 26.030016}, {-97.675628, 26.029229}, {-97.673727, 26.028480}, {-97.673418, 26.027900}, {-97.674020, 26.026703}, {-97.675760, 26.025822}, {-97.681101, 26.025887}, {-97.685669, 26.026755}, {-97.687813, 26.027610}, {-97.689249, 26.028656}, {-97.692260, 26.032337}, {-97.692777, 26.032541}, {-97.693333, 26.032337}, {-97.694117, 26.030289}, + {-97.693705, 26.027100}, {-97.692702, 26.024879}, {-97.690471, 26.022939}, {-97.690123, 26.022282}, {-97.690684, 26.019588}, {-97.691369, 26.018990}, {-97.693099, 26.019247}, {-97.694889, 26.021800}, {-97.697422, 26.023836}, {-97.697840, 26.025910}, {-97.697232, 26.029087}, {-97.697287, 26.030946}, {-97.698816, 26.031204}, {-97.701710, 26.030888}, {-97.702521, 26.031208}, {-97.704708, 26.036101}, + {-97.705422, 26.036863}, {-97.707296, 26.037756}, {-97.708629, 26.036545}, {-97.709300, 26.034722}, {-97.709514, 26.031777}, {-97.709239, 26.029064}, {-97.709661, 26.026194}, {-97.714757, 26.025349}, {-97.718620, 26.023716}, {-97.721113, 26.023321}, {-97.721469, 26.023993}, {-97.721171, 26.024777}, {-97.718887, 26.027421}, {-97.718270, 26.029036}, {-97.718716, 26.030188}, {-97.719764, 26.031039}, + {-97.721702, 26.031310}, {-97.726071, 26.030775}, {-97.734900, 26.032018}, {-97.735419, 26.031340}, {-97.734889, 26.028948}, {-97.735071, 26.028355}, {-97.732600, 26.027313}, {-97.730174, 26.028012}, {-97.727702, 26.027985}, {-97.726268, 26.027313}, {-97.725551, 26.026551}, {-97.725276, 26.024880}, {-97.725719, 26.023496}, {-97.726470, 26.022656}, {-97.730800, 26.023408}, {-97.731700, 26.023313}, + {-97.735745, 26.021611}, {-97.737103, 26.021493}, {-97.738217, 26.021982}, {-97.738232, 26.022882}, {-97.737471, 26.024856}, {-97.736401, 26.026161}, {-97.736080, 26.027477}, {-97.736271, 26.028155}, {-97.737774, 26.028858}, {-97.740729, 26.029244}, {-97.742404, 26.029806}, {-97.744683, 26.029015}, {-97.745810, 26.027322}, {-97.746911, 26.026532}, {-97.748699, 26.026327}, {-97.749376, 26.027318}, + {-97.748508, 26.029197}, {-97.748663, 26.030002}, {-97.749717, 26.031123}, {-97.752072, 26.031475}, {-97.754055, 26.030827}, {-97.758523, 26.026559}, {-97.759422, 26.026131}, {-97.761583, 26.025792}, {-97.763425, 26.026819}, {-97.764081, 26.027958}, {-97.764136, 26.028992}, {-97.763103, 26.035161}, {-97.763272, 26.036355}, {-97.762671, 26.040955}, {-97.763120, 26.042450}, {-97.764219, 26.044079}, + {-97.768065, 26.047321}, {-97.769860, 26.047379}, {-97.773458, 26.045769}, {-97.775547, 26.045351}, {-97.776126, 26.045037}, {-97.776828, 26.043972}, {-97.776902, 26.042170}, {-97.776349, 26.040509}, {-97.774914, 26.039093}, {-97.770557, 26.037292}, {-97.769331, 26.036065}, {-97.769406, 26.035621}, {-97.770240, 26.034863}, {-97.772972, 26.034255}, {-97.774936, 26.032954}, {-97.775940, 26.031785}, + {-97.776194, 26.030064}, {-97.776816, 26.029452}, {-97.778356, 26.029433}, {-97.779144, 26.029931}, {-97.780836, 26.032848}, {-97.782526, 26.033894}, {-97.783695, 26.034174}, {-97.788170, 26.033375}, {-97.790246, 26.033359}, {-97.792955, 26.034536}, {-97.795194, 26.036731}, {-97.797261, 26.039441}, {-97.797869, 26.041164}, {-97.797649, 26.042686}, {-97.795830, 26.045526}, {-97.795122, 26.047207}, + {-97.794414, 26.047454}, {-97.791102, 26.047183}, {-97.789757, 26.047425}, {-97.789004, 26.048072}, {-97.789028, 26.049737}, {-97.789899, 26.051170}, {-97.792981, 26.053231}, {-97.796700, 26.052382}, {-97.798428, 26.052692}, {-97.799148, 26.053323}, {-97.799878, 26.054688}, {-97.800325, 26.059371}, {-97.801317, 26.060161}, {-97.803099, 26.060055}, {-97.807570, 26.058205}, {-97.809599, 26.057068}, + {-97.810972, 26.055512}, {-97.811997, 26.053418}, {-97.812428, 26.050342}, {-97.812380, 26.048407}, {-97.811160, 26.045742}, {-97.811547, 26.045020}, {-97.813389, 26.044199}, {-97.815196, 26.044453}, {-97.816457, 26.045637}, {-97.816451, 26.047058}, {-97.814942, 26.051638}, {-97.814890, 26.053415}, {-97.816253, 26.055512}, {-97.818054, 26.056397}, {-97.821533, 26.056534}, {-97.825940, 26.055878}, + {-97.829315, 26.049813}, {-97.832391, 26.047445}, {-97.832779, 26.046157}, {-97.833456, 26.045861}, {-97.834665, 26.046009}, {-97.835983, 26.047558}, {-97.836044, 26.049790}, {-97.836608, 26.051651}, {-97.837403, 26.052407}, {-97.839386, 26.053204}, {-97.842779, 26.053545}, {-97.849594, 26.052109}, {-97.852374, 26.052289}, {-97.857773, 26.053372}, {-97.859787, 26.052819}, {-97.860504, 26.052918}, + {-97.861390, 26.054184}, {-97.862294, 26.060419}, {-97.861765, 26.062195}, {-97.859910, 26.064570}, {-97.859391, 26.065758}, {-97.859330, 26.066956}, {-97.860702, 26.069676}, {-97.861776, 26.069946}, {-97.862828, 26.069608}, {-97.864076, 26.068024}, {-97.867372, 26.060105}, {-97.868268, 26.058685}, {-97.868606, 26.057156}, {-97.869061, 26.056955}, {-97.870744, 26.057598}, {-97.874070, 26.062439}, + {-97.876935, 26.064582}, {-97.878755, 26.065269}, {-97.884117, 26.065859}, {-97.887041, 26.066554}, {-97.887648, 26.066437}, {-97.888430, 26.065382}, {-97.888393, 26.062428}, {-97.888811, 26.061784}, {-97.890677, 26.060672}, {-97.893652, 26.060171}, {-97.897933, 26.061073}, {-97.901060, 26.061081}, {-97.903549, 26.060436}, {-97.909862, 26.056864}, {-97.911245, 26.056454}, {-97.913882, 26.056539}, + {-97.917994, 26.057902}, {-97.920123, 26.057984}, {-97.921388, 26.057768}, {-97.925230, 26.055693}, {-97.927190, 26.055316}, {-97.927981, 26.055906}, {-97.931397, 26.063420}, {-97.932286, 26.064662}, {-97.933924, 26.064834}, {-97.935984, 26.064007}, {-97.936787, 26.063007}, {-97.936967, 26.061926}, {-97.936098, 26.059990}, {-97.932868, 26.057308}, {-97.931801, 26.055944}, {-97.931981, 26.054848}, + {-97.932820, 26.053671}, {-97.934247, 26.052826}, {-97.936607, 26.052773}, {-97.937924, 26.053524}, {-97.943401, 26.058919}, {-97.945260, 26.060188}, {-97.948046, 26.061402}, {-97.951175, 26.061958}, {-97.954551, 26.061182}, {-97.957001, 26.059963}, {-97.958988, 26.057274}, {-97.965437, 26.052268}, {-97.967335, 26.051783}, {-97.969420, 26.052702}, {-97.975045, 26.058160}, {-97.978761, 26.059756}, + {-97.979831, 26.062450}, {-97.980163, 26.066510}, {-97.981339, 26.067227}, {-97.987302, 26.065722}, {-97.988121, 26.064640}, {-97.989766, 26.059846}, {-97.990477, 26.058998}, {-97.991493, 26.058585}, {-97.992491, 26.058585}, {-97.993553, 26.059197}, {-97.997253, 26.064141}, {-98.000285, 26.065042}, {-98.003516, 26.064453}, {-98.005919, 26.063293}, {-98.007386, 26.062160}, {-98.008820, 26.060422}, + {-98.010698, 26.057133}, {-98.011978, 26.056292}, {-98.012641, 26.056885}, {-98.012428, 26.058316}, {-98.011013, 26.060922}, {-98.010853, 26.061892}, {-98.010971, 26.063863}, {-98.011932, 26.065859}, {-98.012675, 26.066328}, {-98.013652, 26.066413}, {-98.014233, 26.066022}, {-98.016012, 26.061390}, {-98.017336, 26.060242}, {-98.020091, 26.058780}, {-98.020741, 26.058739}, {-98.021392, 26.058957}, + {-98.022703, 26.060420}, {-98.024203, 26.064104}, {-98.025537, 26.065965}, {-98.026355, 26.066544}, {-98.027801, 26.066849}, {-98.028973, 26.066487}, {-98.030132, 26.065673}, {-98.031487, 26.064217}, {-98.032717, 26.062124}, {-98.034115, 26.057556}, {-98.034403, 26.051375}, {-98.035451, 26.048490}, {-98.037506, 26.045606}, {-98.038367, 26.042107}, {-98.039239, 26.041275}, {-98.039845, 26.041275}, + {-98.044517, 26.043746}, {-98.046228, 26.044230}, {-98.054701, 26.044590}, {-98.061169, 26.046238}, {-98.063052, 26.045894}, {-98.065094, 26.044831}, {-98.069953, 26.038181}, {-98.071198, 26.037075}, {-98.073054, 26.036501}, {-98.076620, 26.036415}, {-98.077576, 26.036823}, {-98.079906, 26.041270}, {-98.080003, 26.041960}, {-98.078104, 26.042437}, {-98.075051, 26.042641}, {-98.072365, 26.043884}, + {-98.070857, 26.045947}, {-98.070021, 26.047992}, {-98.070025, 26.051466}, {-98.071162, 26.054705}, {-98.074069, 26.058109}, {-98.075073, 26.059871}, {-98.075680, 26.062168}, {-98.075981, 26.066741}, {-98.076544, 26.068042}, {-98.077740, 26.069496}, {-98.079087, 26.070513}, {-98.080495, 26.070932}, {-98.083113, 26.071140}, {-98.084689, 26.070860}, {-98.085387, 26.070355}, {-98.085849, 26.069208}, + {-98.085204, 26.068019}, {-98.082250, 26.067076}, {-98.081400, 26.066207}, {-98.081102, 26.064872}, {-98.081884, 26.063724}, {-98.083088, 26.062880}, {-98.091038, 26.059169}, {-98.094432, 26.058625}, {-98.096728, 26.059517}, {-98.098487, 26.060968}, {-98.103811, 26.067094}, {-98.105505, 26.067537}, {-98.106922, 26.067350}, {-98.111143, 26.065820}, {-98.119758, 26.064447}, {-98.122724, 26.063410}, + {-98.123996, 26.062375}, {-98.125844, 26.061791}, {-98.126872, 26.061852}, {-98.129269, 26.062692}, {-98.130369, 26.063153}, {-98.130776, 26.063689}, {-98.131250, 26.065381}, {-98.131014, 26.066843}, {-98.128964, 26.070238}, {-98.128367, 26.072399}, {-98.128869, 26.073547}, {-98.129797, 26.074236}, {-98.131829, 26.074170}, {-98.134432, 26.072898}, {-98.136013, 26.071022}, {-98.136802, 26.068615}, + {-98.136515, 26.065157}, {-98.136796, 26.062319}, {-98.137302, 26.060845}, {-98.140656, 26.054966}, {-98.142380, 26.052829}, {-98.143828, 26.050384}, {-98.144787, 26.049607}, {-98.146506, 26.049303}, {-98.147777, 26.049638}, {-98.149381, 26.051506}, {-98.149372, 26.055797}, {-98.149917, 26.056741}, {-98.151665, 26.058208}, {-98.152979, 26.058316}, {-98.154074, 26.057900}, {-98.155609, 26.056648}, + {-98.156682, 26.054979}, {-98.159262, 26.053743}, {-98.161880, 26.054539}, {-98.162489, 26.056096}, {-98.162096, 26.057089}, {-98.158334, 26.060580}, {-98.158422, 26.062700}, {-98.158839, 26.063309}, {-98.159679, 26.064021}, {-98.161684, 26.064239}, {-98.165337, 26.062533}, {-98.166550, 26.062356}, {-98.168829, 26.062987}, {-98.169960, 26.065431}, {-98.171150, 26.069457}, {-98.172126, 26.071137}, + {-98.176296, 26.075075}, {-98.178292, 26.074780}, {-98.179240, 26.074053}, {-98.180220, 26.071616}, {-98.180319, 26.070530}, {-98.180059, 26.068179}, {-98.180404, 26.065786}, {-98.179836, 26.064727}, {-98.177106, 26.063399}, {-98.176673, 26.062863}, {-98.176540, 26.061620}, {-98.176935, 26.060661}, {-98.177875, 26.060017}, {-98.178787, 26.059836}, {-98.179721, 26.060098}, {-98.181954, 26.062520}, + {-98.183606, 26.063658}, {-98.185405, 26.064264}, {-98.187835, 26.064005}, {-98.189061, 26.063257}, {-98.189508, 26.062399}, {-98.190006, 26.058093}, {-98.192305, 26.053453}, {-98.192981, 26.053268}, {-98.196572, 26.054138}, {-98.198466, 26.055397}, {-98.200871, 26.059161}, {-98.202560, 26.063075}, {-98.204959, 26.066418}, {-98.206878, 26.067781}, {-98.211395, 26.068513}, {-98.212791, 26.069154}, + {-98.214402, 26.070713}, {-98.215214, 26.072416}, {-98.216832, 26.074437}, {-98.220673, 26.076467}, {-98.224833, 26.077139}, {-98.230097, 26.077155}, {-98.240076, 26.075020}, {-98.243682, 26.073745}, {-98.245997, 26.072361}, {-98.248737, 26.072042}, {-98.249954, 26.072718}, {-98.250957, 26.074089}, {-98.251541, 26.079031}, {-98.263170, 26.084494}, {-98.264514, 26.085507}, {-98.265394, 26.087011}, + {-98.265906, 26.089507}, {-98.266754, 26.091554}, {-98.268386, 26.093138}, {-98.277218, 26.098802}, {-98.284291, 26.101090}, {-98.286211, 26.102402}, {-98.288535, 26.105312}, {-98.288209, 26.105892}, {-98.286547, 26.106914}, {-98.281187, 26.107730}, {-98.278851, 26.107602}, {-98.274482, 26.106370}, {-98.271298, 26.106338}, {-98.270482, 26.106610}, {-98.269944, 26.107496}, {-98.271578, 26.110525}, + {-98.271829, 26.112996}, {-98.270338, 26.115150}, {-98.269144, 26.115965}, {-98.267260, 26.116241}, {-98.265974, 26.117138}, {-98.265314, 26.119010}, {-98.265698, 26.120370}, {-98.266898, 26.120866}, {-98.268540, 26.120613}, {-98.270235, 26.119355}, {-98.271816, 26.117057}, {-98.273743, 26.116072}, {-98.275161, 26.115918}, {-98.290595, 26.120657}, {-98.295187, 26.120641}, {-98.296195, 26.120321}, + {-98.299345, 26.118088}, {-98.302979, 26.110050}, {-98.302787, 26.108450}, {-98.301827, 26.106898}, {-98.300643, 26.105730}, {-98.298179, 26.105010}, {-98.297875, 26.104386}, {-98.298019, 26.103298}, {-98.299571, 26.102146}, {-98.302803, 26.102354}, {-98.305027, 26.103090}, {-98.305971, 26.103682}, {-98.307075, 26.105250}, {-98.309447, 26.113001}, {-98.310339, 26.114396}, {-98.313171, 26.115995}, + {-98.317301, 26.116405}, {-98.320878, 26.118238}, {-98.324868, 26.122305}, {-98.326296, 26.125097}, {-98.329905, 26.130036}, {-98.332388, 26.132753}, {-98.335204, 26.137617}, {-98.335284, 26.140449}, {-98.334532, 26.141121}, {-98.332724, 26.141585}, {-98.327908, 26.141809}, {-98.326916, 26.142769}, {-98.327060, 26.143809}, {-98.328436, 26.144833}, {-98.333108, 26.145489}, {-98.335620, 26.146401}, + {-98.337428, 26.148081}, {-98.338244, 26.149936}, {-98.338420, 26.151344}, {-98.337508, 26.154080}, {-98.334532, 26.156784}, {-98.333316, 26.158800}, {-98.332948, 26.161280}, {-98.333156, 26.162336}, {-98.333444, 26.163456}, {-98.334564, 26.165136}, {-98.336837, 26.166432}, {-98.339413, 26.166896}, {-98.342923, 26.166896}, {-98.345781, 26.166016}, {-98.347525, 26.164000}, {-98.348703, 26.160106}, + {-98.348981, 26.153120}, {-98.349749, 26.152000}, {-98.350437, 26.151568}, {-98.352341, 26.151120}, {-98.353109, 26.151392}, {-98.354645, 26.153040}, {-98.356805, 26.156448}, {-98.358021, 26.159232}, {-98.358533, 26.161920}, {-98.357447, 26.166544}, {-98.357685, 26.168832}, {-98.358069, 26.170112}, {-98.359157, 26.171832}, {-98.360645, 26.172544}, {-98.361925, 26.172368}, {-98.362933, 26.171840}, + {-98.364901, 26.169888}, {-98.366600, 26.168782}, {-98.367125, 26.166944}, {-98.366858, 26.165847}, {-98.365409, 26.164221}, {-98.364927, 26.163063}, {-98.364702, 26.160891}, {-98.365029, 26.159156}, {-98.367237, 26.157680}, {-98.368597, 26.157088}, {-98.374677, 26.157568}, {-98.380486, 26.156816}, {-98.383814, 26.156960}, {-98.386694, 26.157872}, {-98.388838, 26.160464}, {-98.391446, 26.165856}, + {-98.393542, 26.168016}, {-98.397830, 26.170624}, {-98.401542, 26.171648}, {-98.402208, 26.172170}, {-98.402745, 26.173259}, {-98.402844, 26.174875}, {-98.402249, 26.179523}, {-98.402646, 26.180761}, {-98.404433, 26.182564}, {-98.406222, 26.183229}, {-98.414657, 26.183794}, {-98.418120, 26.184648}, {-98.420439, 26.186735}, {-98.423271, 26.189983}, {-98.425175, 26.191535}, {-98.431559, 26.195071}, + {-98.439048, 26.197471}, {-98.440616, 26.198367}, {-98.442040, 26.198751}, {-98.444072, 26.200895}, {-98.444680, 26.202783}, {-98.444296, 26.204671}, {-98.442923, 26.206869}, {-98.442456, 26.206989}, {-98.440733, 26.208726}, {-98.439203, 26.210923}, {-98.438822, 26.212022}, {-98.438822, 26.214220}, {-98.440251, 26.219442}, {-98.443406, 26.223653}, {-98.445222, 26.224571}, {-98.446658, 26.224389}, + {-98.450010, 26.223019}, {-98.453165, 26.219724}, {-98.453892, 26.217424}, {-98.454456, 26.217062}, {-98.456072, 26.217598}, {-98.456724, 26.222883}, {-98.458402, 26.225317}, {-98.459716, 26.225961}, {-98.461216, 26.226120}, {-98.463657, 26.225272}, {-98.468217, 26.221589}, {-98.471782, 26.220187}, {-98.479871, 26.220103}, {-98.481582, 26.219379}, {-98.483082, 26.217546}, {-98.483129, 26.215309}, + {-98.482502, 26.213511}, {-98.479344, 26.209941}, {-98.478495, 26.208292}, {-98.477961, 26.203359}, {-98.478377, 26.202303}, {-98.479593, 26.201471}, {-98.482201, 26.201375}, {-98.482973, 26.202043}, {-98.483186, 26.203348}, {-98.481868, 26.205476}, {-98.481617, 26.206469}, {-98.482021, 26.208588}, {-98.482601, 26.209822}, {-98.483993, 26.211438}, {-98.485401, 26.212366}, {-98.486988, 26.212923}, + {-98.490138, 26.213177}, {-98.492428, 26.212921}, {-98.495133, 26.212128}, {-98.500333, 26.209750}, {-98.503766, 26.208800}, {-98.505042, 26.208793}, {-98.506769, 26.209404}, {-98.507332, 26.210171}, {-98.507105, 26.211675}, {-98.503859, 26.217451}, {-98.501081, 26.221347}, {-98.500916, 26.222211}, {-98.501269, 26.222922}, {-98.502025, 26.223344}, {-98.506343, 26.224135}, {-98.511623, 26.226995}, + {-98.514847, 26.227218}, {-98.516550, 26.226708}, {-98.519058, 26.224939}, {-98.521219, 26.223037}, {-98.521918, 26.221574}, {-98.522540, 26.220996}, {-98.523414, 26.220894}, {-98.524278, 26.221245}, {-98.525369, 26.222651}, {-98.526723, 26.225443}, {-98.526435, 26.227723}, {-98.525956, 26.228662}, {-98.521788, 26.232530}, {-98.521526, 26.234423}, {-98.521814, 26.235642}, {-98.523053, 26.235934}, + {-98.524456, 26.235565}, {-98.525429, 26.234805}, {-98.529841, 26.227936}, {-98.531592, 26.226544}, {-98.534080, 26.225604}, {-98.535241, 26.225677}, {-98.537109, 26.227721}, {-98.539492, 26.234224}, {-98.543549, 26.242436}, {-98.544479, 26.243753}, {-98.545328, 26.244536}, {-98.551970, 26.246346}, {-98.554577, 26.247380}, {-98.557349, 26.247482}, {-98.559097, 26.245519}, {-98.559053, 26.244976}, + {-98.557444, 26.243411}, {-98.552335, 26.241192}, {-98.547599, 26.241135}, {-98.546752, 26.240460}, {-98.546023, 26.239200}, {-98.545922, 26.237097}, {-98.546550, 26.235512}, {-98.547683, 26.234277}, {-98.548718, 26.233714}, {-98.554080, 26.232853}, {-98.556002, 26.232105}, {-98.558012, 26.230016}, {-98.559181, 26.226274}, {-98.560442, 26.225237}, {-98.561701, 26.224811}, {-98.562959, 26.224908}, + {-98.564549, 26.226880}, {-98.564737, 26.227818}, {-98.564236, 26.232459}, {-98.564985, 26.238619}, {-98.566231, 26.240833}, {-98.567392, 26.241624}, {-98.571006, 26.240471}, {-98.574106, 26.238585}, {-98.576643, 26.233834}, {-98.578390, 26.233840}, {-98.579845, 26.234776}, {-98.581720, 26.238759}, {-98.583920, 26.242023}, {-98.584982, 26.244301}, {-98.584145, 26.246178}, {-98.579958, 26.249430}, + {-98.579517, 26.252245}, {-98.580817, 26.254741}, {-98.582917, 26.256472}, {-98.583882, 26.257008}, {-98.587545, 26.257816}, {-98.591377, 26.257563}, {-98.596334, 26.257972}, {-98.599154, 26.257612}, {-98.608362, 26.253388}, {-98.611271, 26.253282}, {-98.612913, 26.252727}, {-98.613465, 26.252028}, {-98.614188, 26.248864}, {-98.614799, 26.247727}, {-98.616487, 26.246761}, {-98.617266, 26.246809}, + {-98.618205, 26.247562}, {-98.618755, 26.249254}, {-98.618915, 26.252412}, {-98.619519, 26.254659}, {-98.621654, 26.258774}, {-98.622199, 26.259481}, {-98.623467, 26.259666}, {-98.624759, 26.259221}, {-98.625837, 26.258338}, {-98.627709, 26.255763}, {-98.632277, 26.252140}, {-98.632900, 26.250348}, {-98.633486, 26.243265}, {-98.634180, 26.242612}, {-98.636253, 26.241866}, {-98.642402, 26.241829}, + {-98.644095, 26.241356}, {-98.648528, 26.239481}, {-98.652068, 26.236897}, {-98.654451, 26.235933}, {-98.659343, 26.235330}, {-98.664633, 26.235351}, {-98.669149, 26.236530}, {-98.672967, 26.238423}, {-98.676562, 26.241562}, {-98.677770, 26.243236}, {-98.678852, 26.245576}, {-98.679298, 26.247308}, {-98.679361, 26.249210}, {-98.677813, 26.254784}, {-98.677845, 26.256915}, {-98.678958, 26.260064}, + {-98.681090, 26.262803}, {-98.685037, 26.264620}, {-98.687156, 26.265120}, {-98.689489, 26.265296}, {-98.693174, 26.264849}, {-98.700737, 26.265966}, {-98.710942, 26.268692}, {-98.718412, 26.271417}, {-98.719413, 26.272304}, {-98.720181, 26.274507}, {-98.719765, 26.275911}, {-98.719033, 26.276853}, {-98.717792, 26.277624}, {-98.715661, 26.278423}, {-98.712157, 26.278977}, {-98.710621, 26.278929}, + {-98.705680, 26.277418}, {-98.703226, 26.277170}, {-98.701159, 26.278023}, {-98.699943, 26.280005}, {-98.699898, 26.280858}, {-98.700639, 26.283731}, {-98.701773, 26.286143}, {-98.704267, 26.287982}, {-98.708443, 26.289181}, {-98.709353, 26.289189}, {-98.712227, 26.287220}, {-98.714802, 26.286077}, {-98.716170, 26.286197}, {-98.718218, 26.287253}, {-98.718951, 26.288237}, {-98.719907, 26.291052}, + {-98.721989, 26.295046}, {-98.724135, 26.297054}, {-98.727765, 26.298770}, {-98.729196, 26.299027}, {-98.732250, 26.299108}, {-98.734822, 26.298352}, {-98.736559, 26.297517}, {-98.740000, 26.294944}, {-98.742621, 26.294197}, {-98.747755, 26.294853}, {-98.749407, 26.295323}, {-98.750973, 26.296321}, {-98.751974, 26.297697}, {-98.753012, 26.300213}, {-98.755067, 26.301926}, {-98.756123, 26.304249}, + {-98.755948, 26.305590}, {-98.755209, 26.307037}, {-98.754216, 26.307928}, {-98.752708, 26.308237}, {-98.750353, 26.307657}, {-98.743685, 26.303009}, {-98.740419, 26.302777}, {-98.738469, 26.303700}, {-98.737235, 26.304960}, {-98.736528, 26.306406}, {-98.736161, 26.309199}, {-98.736652, 26.311410}, {-98.738457, 26.313654}, {-98.739927, 26.314460}, {-98.743477, 26.314897}, {-98.745069, 26.315929}, + {-98.745821, 26.316845}, {-98.748049, 26.320294}, {-98.749379, 26.324831}, {-98.749469, 26.329988}, {-98.751127, 26.331584}, {-98.753776, 26.331666}, {-98.755002, 26.331368}, {-98.763488, 26.326663}, {-98.768240, 26.325308}, {-98.771321, 26.325081}, {-98.777028, 26.325729}, {-98.780245, 26.326724}, {-98.788955, 26.330433}, {-98.789706, 26.331527}, {-98.789673, 26.332059}, {-98.788333, 26.334811}, + {-98.788018, 26.336114}, {-98.788313, 26.338337}, {-98.793073, 26.343515}, {-98.796252, 26.349104}, {-98.797257, 26.352061}, {-98.798211, 26.360166}, {-98.802238, 26.365456}, {-98.803946, 26.367223}, {-98.806618, 26.369109}, {-98.807348, 26.369421}, {-98.808354, 26.369078}, {-98.811396, 26.366904}, {-98.812928, 26.366309}, {-98.814000, 26.366292}, {-98.815070, 26.366478}, {-98.816622, 26.367733}, + {-98.818125, 26.369530}, {-98.820452, 26.371002}, {-98.824565, 26.370833}, {-98.826857, 26.369624}, {-98.830652, 26.365262}, {-98.832909, 26.363338}, {-98.836587, 26.361197}, {-98.841562, 26.358943}, {-98.844115, 26.358730}, {-98.845703, 26.358930}, {-98.847570, 26.359790}, {-98.848930, 26.360720}, {-98.851022, 26.363810}, {-98.853415, 26.365023}, {-98.856076, 26.365776}, {-98.861910, 26.365990}, + {-98.866097, 26.364713}, {-98.870285, 26.362365}, {-98.878494, 26.360694}, {-98.889045, 26.357096}, {-98.890895, 26.356362}, {-98.895694, 26.353491}, {-98.896734, 26.353330}, {-98.898647, 26.355050}, {-98.900893, 26.359404}, {-98.900993, 26.361385}, {-98.899551, 26.366588}, {-98.901677, 26.371229}, {-98.903899, 26.372130}, {-98.911058, 26.371767}, {-98.913276, 26.372300}, {-98.917377, 26.374471}, + {-98.920316, 26.377827}, {-98.922194, 26.379080}, {-98.923402, 26.381371}, {-98.923967, 26.383281}, {-98.923650, 26.386426}, {-98.924106, 26.387728}, {-98.923777, 26.392048}, {-98.925024, 26.393718}, {-98.925879, 26.394357}, {-98.927050, 26.394462}, {-98.929283, 26.393361}, {-98.933348, 26.388860}, {-98.935636, 26.385047}, {-98.935891, 26.383091}, {-98.934684, 26.379824}, {-98.934307, 26.377464}, + {-98.934586, 26.374375}, {-98.935134, 26.372971}, {-98.936164, 26.371533}, {-98.939165, 26.369929}, {-98.942152, 26.369687}, {-98.946694, 26.370003}, {-98.948963, 26.371253}, {-98.953375, 26.375341}, {-98.955846, 26.376982}, {-98.956128, 26.377561}, {-98.955984, 26.379466}, {-98.954037, 26.382666}, {-98.953322, 26.384712}, {-98.952973, 26.390926}, {-98.953384, 26.392948}, {-98.954504, 26.394368}, + {-98.956815, 26.396005}, {-98.961886, 26.398373}, {-98.966348, 26.399081}, {-98.970083, 26.399193}, {-98.973187, 26.400841}, {-98.975227, 26.401140}, {-98.976423, 26.400986}, {-98.979153, 26.399924}, {-98.983619, 26.395097}, {-98.986298, 26.392782}, {-98.987204, 26.392197}, {-98.989085, 26.391667}, {-98.990505, 26.391560}, {-98.997099, 26.392375}, {-99.000307, 26.392345}, {-99.003783, 26.393476}, + {-99.005749, 26.393199}, {-99.008517, 26.392313}, {-99.010640, 26.392135}, {-99.012429, 26.393016}, {-99.014315, 26.395822}, {-99.015646, 26.399521}, {-99.016414, 26.400781}, {-99.021935, 26.407902}, {-99.023694, 26.409142}, {-99.026938, 26.410265}, {-99.032011, 26.412821}, {-99.037058, 26.413603}, {-99.040441, 26.412674}, {-99.045466, 26.409816}, {-99.047575, 26.406922}, {-99.050831, 26.404612}, + {-99.054614, 26.401262}, {-99.062111, 26.397385}, {-99.063179, 26.397120}, {-99.069041, 26.397639}, {-99.073790, 26.396976}, {-99.078575, 26.397255}, {-99.082006, 26.396667}, {-99.083628, 26.397441}, {-99.085126, 26.398782}, {-99.086712, 26.402084}, {-99.086997, 26.403534}, {-99.089413, 26.408100}, {-99.090367, 26.408974}, {-99.094783, 26.410953}, {-99.096011, 26.411828}, {-99.099574, 26.417248}, + {-99.104198, 26.421162}, {-99.105723, 26.423216}, {-99.108723, 26.425300}, {-99.110854, 26.426282}, {-99.113546, 26.432530}, {-99.113808, 26.434002}, {-99.113163, 26.435180}, {-99.112694, 26.435457}, {-99.107253, 26.437597}, {-99.105656, 26.438667}, {-99.104037, 26.440186}, {-99.102376, 26.442726}, {-99.102567, 26.445788}, {-99.102359, 26.447981}, {-99.100822, 26.450212}, {-99.100647, 26.451918}, + {-99.100046, 26.453587}, {-99.100357, 26.458748}, {-99.099226, 26.461606}, {-99.098127, 26.463347}, {-99.093618, 26.468300}, {-99.091976, 26.472376}, {-99.091572, 26.477054}, {-99.091956, 26.477912}, {-99.095479, 26.481678}, {-99.095803, 26.483068}, {-99.102237, 26.495318}, {-99.103437, 26.496896}, {-99.104851, 26.500383}, {-99.112292, 26.507703}, {-99.113557, 26.510174}, {-99.115989, 26.512839}, + {-99.118566, 26.516583}, {-99.121150, 26.518295}, {-99.123613, 26.520494}, {-99.127782, 26.525199}, {-99.128379, 26.525501}, {-99.135664, 26.526259}, {-99.137325, 26.527440}, {-99.143658, 26.527929}, {-99.155517, 26.531956}, {-99.162790, 26.535211}, {-99.166742, 26.536079}, {-99.169170, 26.538332}, {-99.171230, 26.541965}, {-99.171822, 26.544590}, {-99.171380, 26.549249}, {-99.169665, 26.553928}, + {-99.168255, 26.555918}, {-99.167410, 26.560001}, {-99.178064, 26.620547}, {-99.200522, 26.656443}, {-99.209948, 26.693938}, {-99.208907, 26.724761}, {-99.240023, 26.745851}, {-99.242444, 26.788262}, {-99.262208, 26.815668}, {-99.268613, 26.843213}, {-99.280471, 26.858053}, {-99.295146, 26.865440}, {-99.310232, 26.864983}, {-99.316753, 26.865831}, {-99.323837, 26.873902}, {-99.325882, 26.877376}, + {-99.328619, 26.878592}, {-99.328900, 26.879761}, {-99.326574, 26.884784}, {-99.326216, 26.890403}, {-99.321819, 26.906846}, {-99.322970, 26.912464}, {-99.324684, 26.915973}, {-99.329852, 26.919343}, {-99.337297, 26.922759}, {-99.347147, 26.925004}, {-99.351650, 26.925140}, {-99.361144, 26.928921}, {-99.367054, 26.929034}, {-99.373374, 26.931256}, {-99.379149, 26.934490}, {-99.385159, 26.940455}, + {-99.388253, 26.944217}, {-99.390605, 26.951465}, {-99.393033, 26.957060}, {-99.393748, 26.960730}, {-99.392954, 26.963803}, {-99.390189, 26.966348}, {-99.381102, 26.970816}, {-99.377312, 26.973819}, {-99.376593, 26.977717}, {-99.378435, 26.980034}, {-99.384808, 26.981069}, {-99.387367, 26.982399}, {-99.392178, 26.987492}, {-99.403694, 26.997356}, {-99.405922, 27.002798}, {-99.407073, 27.006995}, + {-99.408507, 27.009520}, {-99.411175, 27.011597}, {-99.412625, 27.013949}, {-99.415476, 27.017240}, {-99.418357, 27.017107}, {-99.420447, 27.016568}, {-99.422232, 27.015715}, {-99.424892, 27.013483}, {-99.429380, 27.010833}, {-99.430182, 27.010544}, {-99.432155, 27.010699}, {-99.435547, 27.012164}, {-99.443632, 27.018820}, {-99.445310, 27.021050}, {-99.446123, 27.023048}, {-99.445668, 27.026009}, + {-99.444062, 27.031253}, {-99.443973, 27.036458}, {-99.445646, 27.043441}, {-99.447128, 27.047495}, {-99.449948, 27.053068}, {-99.450926, 27.055653}, {-99.452316, 27.060957}, {-99.452316, 27.062669}, {-99.451035, 27.066765}, {-99.450282, 27.067705}, {-99.444748, 27.072003}, {-99.435487, 27.077476}, {-99.434470, 27.078517}, {-99.432812, 27.081875}, {-99.429209, 27.090982}, {-99.429096, 27.092632}, + {-99.430275, 27.094872}, {-99.435324, 27.098373}, {-99.437646, 27.100442}, {-99.439876, 27.103423}, {-99.441810, 27.105394}, {-99.442125, 27.106856}, {-99.441922, 27.108405}, {-99.441109, 27.110042}, {-99.435746, 27.116081}, {-99.433370, 27.119218}, {-99.431153, 27.124719}, {-99.430672, 27.125307}, {-99.430581, 27.126612}, {-99.430685, 27.131543}, {-99.431355, 27.137580}, {-99.434495, 27.140359}, + {-99.435514, 27.142020}, {-99.438265, 27.144792}, {-99.439973, 27.148773}, {-99.439971, 27.151072}, {-99.439276, 27.152694}, {-99.437951, 27.154121}, {-99.435932, 27.155435}, {-99.432166, 27.157086}, {-99.429984, 27.159149}, {-99.427806, 27.167273}, {-99.426348, 27.176262}, {-99.426418, 27.178287}, {-99.428212, 27.185923}, {-99.430395, 27.193551}, {-99.431050, 27.196981}, {-99.432775, 27.201841}, + {-99.433160, 27.204552}, {-99.432589, 27.209698}, {-99.434307, 27.212131}, {-99.437405, 27.214216}, {-99.442755, 27.218780}, {-99.445238, 27.223341}, {-99.445228, 27.225479}, {-99.442934, 27.231223}, {-99.441407, 27.236758}, {-99.441041, 27.242987}, {-99.441545, 27.249915}, {-99.442314, 27.251607}, {-99.444362, 27.253831}, {-99.446811, 27.257580}, {-99.449888, 27.260400}, {-99.452391, 27.264046}, + {-99.455584, 27.266198}, {-99.458418, 27.266236}, {-99.460509, 27.267838}, {-99.461476, 27.268175}, {-99.463309, 27.268437}, {-99.465825, 27.267970}, {-99.469275, 27.265974}, {-99.471231, 27.265274}, {-99.473678, 27.263175}, {-99.476401, 27.262360}, {-99.481192, 27.259835}, {-99.483520, 27.259562}, {-99.486011, 27.259854}, {-99.489059, 27.261477}, {-99.492407, 27.264118}, {-99.494022, 27.266230}, + {-99.495658, 27.269053}, {-99.496690, 27.272435}, {-99.494678, 27.277595}, {-99.489139, 27.285760}, {-99.487592, 27.289602}, {-99.487505, 27.292777}, {-99.487937, 27.294941}, {-99.490784, 27.298334}, {-99.492020, 27.300666}, {-99.493758, 27.301856}, {-99.494604, 27.303542}, {-99.495754, 27.304444}, {-99.497419, 27.305279}, {-99.500951, 27.306008}, {-99.505247, 27.305983}, {-99.511531, 27.304077}, + {-99.521071, 27.303824}, {-99.523658, 27.304138}, {-99.525756, 27.305257}, {-99.528473, 27.305417}, {-99.529654, 27.306051}, {-99.533564, 27.310452}, {-99.535183, 27.311120}, {-99.535999, 27.311990}, {-99.536715, 27.313030}, {-99.538035, 27.316961}, {-99.537702, 27.317704}, {-99.533992, 27.320673}, {-99.531376, 27.323809}, {-99.530355, 27.324117}, {-99.525589, 27.323857}, {-99.523583, 27.324143}, + {-99.521639, 27.324581}, {-99.518912, 27.326305}, {-99.509016, 27.333859}, {-99.506145, 27.336511}, {-99.504575, 27.338741}, {-99.504397, 27.339896}, {-99.507831, 27.348637}, {-99.507565, 27.351398}, {-99.507984, 27.353013}, {-99.507779, 27.354247}, {-99.506960, 27.356420}, {-99.505895, 27.357818}, {-99.503727, 27.364620}, {-99.499076, 27.373140}, {-99.498347, 27.373918}, {-99.495999, 27.375204}, + {-99.494875, 27.377424}, {-99.493136, 27.379030}, {-99.492144, 27.380517}, {-99.491717, 27.383668}, {-99.492709, 27.386049}, {-99.492709, 27.391954}, {-99.489611, 27.399041}, {-99.488818, 27.405556}, {-99.487521, 27.411572}, {-99.487643, 27.413071}, {-99.489199, 27.416340}, {-99.489931, 27.418663}, {-99.490984, 27.424294}, {-99.493899, 27.432578}, {-99.495699, 27.438884}, {-99.495882, 27.440147}, + {-99.495577, 27.444732}, {-99.494646, 27.449310}, {-99.495104, 27.451518}, {-99.494636, 27.452539}, {-99.493495, 27.453098}, {-99.492175, 27.455818}, {-99.487631, 27.460672}, {-99.485404, 27.464067}, {-99.485027, 27.464869}, {-99.485239, 27.465696}, {-99.483973, 27.467244}, {-99.483119, 27.469396}, {-99.482965, 27.473129}, {-99.482282, 27.476315}, {-99.479251, 27.478635}, {-99.479376, 27.481606}, + {-99.480119, 27.485340}, {-99.481288, 27.488468}, {-99.483708, 27.491405}, {-99.485806, 27.493188}, {-99.491911, 27.497304}, {-99.496826, 27.500106}, {-99.497505, 27.500033}, {-99.499002, 27.499154}, {-99.500977, 27.500076}, {-99.502519, 27.500396}, {-99.510319, 27.499096}, {-99.513576, 27.499412}, {-99.516120, 27.498594}, {-99.519850, 27.496763}, {-99.526137, 27.496589}, {-99.528392, 27.498550}, + {-99.527420, 27.503395}, {-99.525320, 27.510395}, {-99.525120, 27.515695}, {-99.525420, 27.518495}, {-99.524835, 27.523795}, {-99.522220, 27.534794}, {-99.521919, 27.544094}, {-99.518819, 27.553194}, {-99.514016, 27.556842}, {-99.512367, 27.562627}, {-99.511049, 27.564507}, {-99.512132, 27.568288}, {-99.516405, 27.572467}, {-99.521731, 27.574611}, {-99.523959, 27.576102}, {-99.524950, 27.576327}, + {-99.529711, 27.579761}, {-99.531878, 27.582858}, {-99.536197, 27.591682}, {-99.536136, 27.592692}, {-99.536945, 27.596103}, {-99.539722, 27.603281}, {-99.541889, 27.606001}, {-99.551425, 27.611643}, {-99.554945, 27.614456}, {-99.556117, 27.614408}, {-99.557563, 27.612976}, {-99.558640, 27.611132}, {-99.558701, 27.609181}, {-99.561233, 27.607569}, {-99.569065, 27.605883}, {-99.578626, 27.602286}, + {-99.580008, 27.602246}, {-99.582278, 27.602734}, {-99.584843, 27.603903}, {-99.584973, 27.605321}, {-99.584432, 27.606213}, {-99.582469, 27.606646}, {-99.579977, 27.607823}, {-99.577802, 27.610146}, {-99.577182, 27.611300}, {-99.577173, 27.615714}, {-99.577636, 27.618597}, {-99.578892, 27.619978}, {-99.581713, 27.620646}, {-99.584782, 27.622007}, {-99.588896, 27.625292}, {-99.589481, 27.626314}, + {-99.591372, 27.627464}, {-99.592353, 27.628613}, {-99.592757, 27.634654}, {-99.594038, 27.638573}, {-99.596231, 27.639858}, {-99.603533, 27.641992}, {-99.610311, 27.640006}, {-99.614252, 27.637950}, {-99.616069, 27.637681}, {-99.617582, 27.638358}, {-99.619093, 27.640148}, {-99.621397, 27.643763}, {-99.623478, 27.644136}, {-99.624311, 27.644001}, {-99.626294, 27.642450}, {-99.627057, 27.640778}, + {-99.627123, 27.638412}, {-99.626349, 27.637365}, {-99.623753, 27.635367}, {-99.623246, 27.634644}, {-99.623415, 27.633558}, {-99.625112, 27.631380}, {-99.626857, 27.629963}, {-99.636553, 27.626989}, {-99.638929, 27.626758}, {-99.650057, 27.630582}, {-99.654300, 27.629778}, {-99.658853, 27.631138}, {-99.659586, 27.632565}, {-99.666108, 27.636088}, {-99.666175, 27.638324}, {-99.665422, 27.640275}, + {-99.661159, 27.643128}, {-99.659500, 27.645246}, {-99.658068, 27.648003}, {-99.658295, 27.650158}, {-99.659353, 27.652281}, {-99.661467, 27.654069}, {-99.661845, 27.655753}, {-99.664223, 27.657575}, {-99.668942, 27.659974}, {-99.671471, 27.660248}, {-99.678949, 27.658979}, {-99.685813, 27.661015}, {-99.687814, 27.662263}, {-99.688569, 27.663173}, {-99.689514, 27.665093}, {-99.689403, 27.668560}, + {-99.690158, 27.668864}, {-99.691253, 27.668732}, {-99.694383, 27.666078}, {-99.697060, 27.662481}, {-99.699356, 27.655417}, {-99.704601, 27.654954}, {-99.711511, 27.658365}, {-99.713248, 27.660185}, {-99.716173, 27.661836}, {-99.721519, 27.666155}, {-99.723634, 27.669532}, {-99.723902, 27.673559}, {-99.728440, 27.679260}, {-99.732448, 27.685425}, {-99.738459, 27.693108}, {-99.752615, 27.706844}, + {-99.757645, 27.712112}, {-99.758512, 27.717041}, {-99.761620, 27.722063}, {-99.768239, 27.729850}, {-99.770740, 27.732134}, {-99.774901, 27.733540}, {-99.785366, 27.730355}, {-99.788845, 27.730718}, {-99.791531, 27.731858}, {-99.796342, 27.735586}, {-99.801651, 27.741771}, {-99.803427, 27.750288}, {-99.805670, 27.758688}, {-99.809963, 27.769272}, {-99.813118, 27.773997}, {-99.815584, 27.775595}, + {-99.817549, 27.776296}, {-99.819092, 27.776019}, {-99.822114, 27.767274}, {-99.822905, 27.766278}, {-99.826022, 27.764327}, {-99.833054, 27.762890}, {-99.835127, 27.762881}, {-99.838791, 27.764181}, {-99.841708, 27.766464}, {-99.842967, 27.768518}, {-99.843441, 27.771521}, {-99.843195, 27.773655}, {-99.844737, 27.778809}, {-99.848870, 27.787399}, {-99.849660, 27.792460}, {-99.850877, 27.793974}, + {-99.852465, 27.794608}, {-99.854342, 27.794689}, {-99.860380, 27.793673}, {-99.862940, 27.794256}, {-99.866407, 27.794206}, {-99.870066, 27.794627}, {-99.872294, 27.795258}, {-99.875330, 27.796770}, {-99.876761, 27.797845}, {-99.878024, 27.800505}, {-99.877143, 27.804385}, {-99.874845, 27.808584}, {-99.873785, 27.813012}, {-99.874222, 27.816014}, {-99.876452, 27.819895}, {-99.877894, 27.825352}, + {-99.878050, 27.827612}, {-99.877061, 27.832234}, {-99.876241, 27.833820}, {-99.876003, 27.837968}, {-99.877200, 27.842180}, {-99.882015, 27.850392}, {-99.883375, 27.850490}, {-99.884400, 27.851262}, {-99.890805, 27.854380}, {-99.893650, 27.856193}, {-99.895284, 27.857706}, {-99.895755, 27.859230}, {-99.897079, 27.860600}, {-99.901486, 27.864162}, {-99.902333, 27.866486}, {-99.904385, 27.875284}, + {-99.903953, 27.878533}, {-99.903326, 27.880544}, {-99.901232, 27.884406}, {-99.897013, 27.888240}, {-99.894091, 27.892950}, {-99.893456, 27.899208}, {-99.896309, 27.905186}, {-99.896778, 27.907011}, {-99.900080, 27.912142}, {-99.903125, 27.913486}, {-99.908331, 27.914619}, {-99.917461, 27.917973}, {-99.922046, 27.922119}, {-99.926035, 27.927484}, {-99.924350, 27.929514}, {-99.924130, 27.931379}, + {-99.926555, 27.935701}, {-99.927623, 27.936407}, {-99.932866, 27.937270}, {-99.934619, 27.938245}, {-99.937142, 27.940537}, {-99.938958, 27.949323}, {-99.938541, 27.954059}, {-99.934388, 27.961480}, {-99.932161, 27.967710}, {-99.931229, 27.979482}, {-99.931811, 27.980968}, {-99.934816, 27.981770}, {-99.941236, 27.982493}, {-99.947117, 27.982170}, {-99.950209, 27.983266}, {-99.962768, 27.983537}, + {-99.970113, 27.985879}, {-99.977281, 27.989505}, {-99.978659, 27.989289}, {-99.984923, 27.990729}, {-99.989762, 27.992876}, {-99.991447, 27.994560}, {-99.996388, 28.001582}, {-100.000024, 28.010478}, {-100.006148, 28.018419}, {-100.008631, 28.023964}, {-100.012839, 28.037203}, {-100.013088, 28.043820}, {-100.015000, 28.048880}, {-100.016068, 28.053296}, {-100.017143, 28.062274}, {-100.017913, 28.064788}, + {-100.018951, 28.066441}, {-100.021859, 28.068881}, {-100.028724, 28.073119}, {-100.031775, 28.074391}, {-100.040905, 28.076969}, {-100.046107, 28.079069}, {-100.053122, 28.084731}, {-100.055063, 28.088182}, {-100.056982, 28.094208}, {-100.057048, 28.095657}, {-100.055595, 28.101142}, {-100.055834, 28.102720}, {-100.056492, 28.104187}, {-100.058808, 28.107292}, {-100.065885, 28.111738}, {-100.067651, 28.113603}, + {-100.071769, 28.120842}, {-100.075474, 28.124882}, {-100.076596, 28.127629}, {-100.077784, 28.128663}, {-100.078950, 28.131028}, {-100.080629, 28.135474}, {-100.081301, 28.138475}, {-100.082553, 28.139743}, {-100.082958, 28.141059}, {-100.082932, 28.143099}, {-100.083393, 28.144035}, {-100.084328, 28.144819}, {-100.086898, 28.146783}, {-100.090289, 28.148313}, {-100.109091, 28.153586}, {-100.110448, 28.153573}, + {-100.114395, 28.154740}, {-100.119627, 28.155588}, {-100.124318, 28.158111}, {-100.126782, 28.160037}, {-100.128308, 28.160574}, {-100.129148, 28.161806}, {-100.133157, 28.163563}, {-100.134383, 28.163737}, {-100.136405, 28.165362}, {-100.141099, 28.168148}, {-100.142252, 28.168372}, {-100.155975, 28.167383}, {-100.159190, 28.167647}, {-100.160589, 28.168160}, {-100.168437, 28.171542}, {-100.171322, 28.176230}, + {-100.174412, 28.179448}, {-100.177552, 28.181585}, {-100.178623, 28.181623}, {-100.185694, 28.185781}, {-100.196499, 28.190218}, {-100.197886, 28.190004}, {-100.201085, 28.190558}, {-100.202447, 28.190554}, {-100.203807, 28.190049}, {-100.208058, 28.190383}, {-100.210217, 28.192248}, {-100.211313, 28.193899}, {-100.212104, 28.196510}, {-100.213609, 28.204703}, {-100.214112, 28.205694}, {-100.213481, 28.207140}, + {-100.213452, 28.210416}, {-100.214248, 28.211769}, {-100.215464, 28.218872}, {-100.216532, 28.221821}, {-100.216547, 28.223503}, {-100.217565, 28.226934}, {-100.220288, 28.232210}, {-100.223629, 28.235223}, {-100.227573, 28.235858}, {-100.230517, 28.236034}, {-100.231479, 28.235697}, {-100.232228, 28.236058}, {-100.234966, 28.235180}, {-100.241397, 28.234938}, {-100.244273, 28.234130}, {-100.246205, 28.234091}, + {-100.251636, 28.236180}, {-100.257789, 28.240340}, {-100.260910, 28.244832}, {-100.264710, 28.247843}, {-100.265998, 28.249337}, {-100.267603, 28.250269}, {-100.270129, 28.253721}, {-100.272630, 28.255876}, {-100.277630, 28.262453}, {-100.280519, 28.267968}, {-100.283177, 28.270634}, {-100.284301, 28.271074}, {-100.285102, 28.271808}, {-100.289390, 28.273490}, {-100.291397, 28.275397}, {-100.293474, 28.278474}, + {-100.294113, 28.280404}, {-100.294296, 28.284380}, {-100.293468, 28.285980}, {-100.293276, 28.287368}, {-100.292228, 28.288647}, {-100.292151, 28.289899}, {-100.290436, 28.295089}, {-100.289798, 28.295774}, {-100.287553, 28.301095}, {-100.285986, 28.309100}, {-100.286476, 28.312295}, {-100.288639, 28.316976}, {-100.289553, 28.317580}, {-100.293084, 28.321908}, {-100.294838, 28.323213}, {-100.299284, 28.328716}, + {-100.300887, 28.330242}, {-100.303849, 28.334188}, {-100.305428, 28.335196}, {-100.309123, 28.338674}, {-100.309837, 28.341557}, {-100.310732, 28.343227}, {-100.313139, 28.344788}, {-100.314199, 28.345859}, {-100.315712, 28.349662}, {-100.317245, 28.357381}, {-100.320392, 28.362117}, {-100.322914, 28.364679}, {-100.323724, 28.366026}, {-100.325114, 28.367245}, {-100.329590, 28.372652}, {-100.331401, 28.373939}, + {-100.332208, 28.375265}, {-100.339712, 28.382491}, {-100.341871, 28.384953}, {-100.344402, 28.389662}, {-100.346799, 28.395752}, {-100.348178, 28.398189}, {-100.349588, 28.402604}, {-100.349450, 28.403714}, {-100.346988, 28.407232}, {-100.345248, 28.407889}, {-100.343947, 28.410119}, {-100.341915, 28.414607}, {-100.337063, 28.427152}, {-100.336187, 28.430181}, {-100.337616, 28.437723}, {-100.337433, 28.441217}, + {-100.337798, 28.442960}, {-100.339440, 28.446436}, {-100.341534, 28.449571}, {-100.348425, 28.456047}, {-100.350786, 28.459246}, {-100.357498, 28.463642}, {-100.361046, 28.467332}, {-100.362060, 28.469303}, {-100.367765, 28.475019}, {-100.368288, 28.477196}, {-100.368197, 28.478910}, {-100.365982, 28.481116}, {-100.361890, 28.482301}, {-100.355080, 28.482105}, {-100.352235, 28.482638}, {-100.350650, 28.483629}, + {-100.344181, 28.486249}, {-100.337140, 28.491729}, {-100.334864, 28.495111}, {-100.333682, 28.497878}, {-100.333814, 28.499252}, {-100.334740, 28.500261}, {-100.338518, 28.501833}, {-100.346748, 28.503841}, {-100.353142, 28.504247}, {-100.356037, 28.504871}, {-100.357982, 28.505600}, {-100.360356, 28.507770}, {-100.362148, 28.508399}, {-100.364738, 28.508621}, {-100.376959, 28.512023}, {-100.379079, 28.511639}, + {-100.386963, 28.514023}, {-100.388262, 28.514863}, {-100.394988, 28.528358}, {-100.396177, 28.529619}, {-100.397736, 28.530424}, {-100.400209, 28.531095}, {-100.402214, 28.532657}, {-100.405058, 28.535780}, {-100.409887, 28.545605}, {-100.411269, 28.550705}, {-100.411388, 28.551960}, {-100.408888, 28.557281}, {-100.405171, 28.560712}, {-100.404250, 28.564233}, {-100.402866, 28.565887}, {-100.397301, 28.575578}, + {-100.396800, 28.580401}, {-100.397211, 28.582069}, {-100.398096, 28.583508}, {-100.398462, 28.585169}, {-100.400266, 28.586530}, {-100.402236, 28.587350}, {-100.406214, 28.588666}, {-100.408968, 28.588924}, {-100.419389, 28.593429}, {-100.426603, 28.595145}, {-100.429856, 28.596441}, {-100.431871, 28.597841}, {-100.433623, 28.606117}, {-100.435334, 28.606986}, {-100.444975, 28.607806}, {-100.447320, 28.609325}, + {-100.448624, 28.617050}, {-100.447557, 28.621163}, {-100.446581, 28.633599}, {-100.445529, 28.637144}, {-100.445526, 28.637765}, {-100.446163, 28.638598}, {-100.445749, 28.640626}, {-100.447091, 28.642197}, {-100.449519, 28.642962}, {-100.454700, 28.642186}, {-100.455719, 28.642275}, {-100.460287, 28.641021}, {-100.462866, 28.641364}, {-100.474494, 28.647071}, {-100.476171, 28.649219}, {-100.478878, 28.654416}, + {-100.479636, 28.655225}, {-100.483946, 28.655811}, {-100.486033, 28.656948}, {-100.489260, 28.658002}, {-100.492639, 28.657869}, {-100.495863, 28.658569}, {-100.498115, 28.659605}, {-100.500354, 28.661960}, {-100.501798, 28.667380}, {-100.507691, 28.682875}, {-100.509005, 28.687974}, {-100.510055, 28.690723}, {-100.510534, 28.692669}, {-100.510532, 28.697972}, {-100.510922, 28.701539}, {-100.511998, 28.705352}, + {-100.511419, 28.708024}, {-100.508986, 28.711014}, {-100.507750, 28.713320}, {-100.506701, 28.716745}, {-100.507877, 28.721571}, {-100.507706, 28.727769}, {-100.507113, 28.728910}, {-100.506791, 28.732015}, {-100.507613, 28.740599}, {-100.508255, 28.741199}, {-100.509150, 28.741435}, {-100.513516, 28.741726}, {-100.515156, 28.742947}, {-100.515565, 28.743943}, {-100.515536, 28.744943}, {-100.514504, 28.748190}, + {-100.514399, 28.750252}, {-100.515472, 28.752480}, {-100.519226, 28.756161}, {-100.521570, 28.757640}, {-100.522483, 28.757786}, {-100.527216, 28.756168}, {-100.528475, 28.756106}, {-100.531567, 28.757181}, {-100.532862, 28.758118}, {-100.533408, 28.759207}, {-100.533551, 28.761078}, {-100.533017, 28.763280}, {-100.532211, 28.764763}, {-100.530281, 28.767052}, {-100.530046, 28.770423}, {-100.530464, 28.771681}, + {-100.531940, 28.773771}, {-100.535023, 28.775870}, {-100.536445, 28.777622}, {-100.537772, 28.780776}, {-100.537733, 28.782688}, {-100.536944, 28.784292}, {-100.535170, 28.785758}, {-100.533450, 28.787821}, {-100.532431, 28.791063}, {-100.533402, 28.799282}, {-100.534246, 28.802556}, {-100.535438, 28.805195}, {-100.536459, 28.806276}, {-100.538815, 28.806673}, {-100.542173, 28.808466}, {-100.545200, 28.812082}, + {-100.545819, 28.813308}, {-100.546216, 28.815970}, {-100.545479, 28.819954}, {-100.546576, 28.824923}, {-100.547324, 28.825817}, {-100.548895, 28.826214}, {-100.553130, 28.828249}, {-100.561443, 28.829174}, {-100.562777, 28.829183}, {-100.565830, 28.828338}, {-100.570475, 28.826036}, {-100.573158, 28.827310}, {-100.574728, 28.828788}, {-100.575891, 28.832949}, {-100.576866, 28.835157}, {-100.576846, 28.836168}, + {-100.576294, 28.838950}, {-100.574397, 28.843148}, {-100.572878, 28.848206}, {-100.575154, 28.850941}, {-100.580502, 28.856008}, {-100.588192, 28.860238}, {-100.591040, 28.863054}, {-100.591468, 28.864272}, {-100.590761, 28.867369}, {-100.590855, 28.871052}, {-100.590369, 28.871890}, {-100.590768, 28.872697}, {-100.592033, 28.873232}, {-100.596804, 28.873402}, {-100.598101, 28.874314}, {-100.598877, 28.875591}, + {-100.598731, 28.878096}, {-100.597941, 28.880146}, {-100.594848, 28.882215}, {-100.591041, 28.883008}, {-100.589705, 28.884988}, {-100.589499, 28.886358}, {-100.591517, 28.889286}, {-100.592504, 28.889448}, {-100.594287, 28.888831}, {-100.594929, 28.888166}, {-100.597290, 28.887811}, {-100.599870, 28.886469}, {-100.600990, 28.886331}, {-100.602172, 28.886617}, {-100.603011, 28.887227}, {-100.603841, 28.888391}, + {-100.603793, 28.889458}, {-100.602294, 28.894154}, {-100.602562, 28.895315}, {-100.602320, 28.898055}, {-100.600749, 28.900076}, {-100.601090, 28.901288}, {-100.602595, 28.902214}, {-100.604249, 28.902380}, {-100.607763, 28.902323}, {-100.612180, 28.901292}, {-100.614222, 28.901718}, {-100.615792, 28.903049}, {-100.617016, 28.907072}, {-100.618216, 28.908522}, {-100.620942, 28.909546}, {-100.622436, 28.909773}, + {-100.623482, 28.909601}, {-100.624899, 28.908270}, {-100.624706, 28.906600}, {-100.625407, 28.904898}, {-100.627729, 28.903363}, {-100.631611, 28.902839}, {-100.632778, 28.903766}, {-100.633277, 28.904583}, {-100.634691, 28.908708}, {-100.636619, 28.911508}, {-100.639242, 28.912603}, {-100.640568, 28.914212}, {-100.640672, 28.915249}, {-100.639170, 28.916289}, {-100.636015, 28.916567}, {-100.632956, 28.916245}, + {-100.631061, 28.916915}, {-100.629785, 28.918790}, {-100.629706, 28.921146}, {-100.630188, 28.922177}, {-100.630228, 28.923333}, {-100.630720, 28.924006}, {-100.632401, 28.925032}, {-100.636855, 28.926306}, {-100.638857, 28.927622}, {-100.640794, 28.930333}, {-100.642552, 28.933674}, {-100.643750, 28.937528}, {-100.645841, 28.939867}, {-100.650894, 28.942128}, {-100.651512, 28.943432}, {-100.651542, 28.945577}, + {-100.648911, 28.952632}, {-100.647268, 28.955457}, {-100.646993, 28.957079}, {-100.647143, 28.958306}, {-100.648285, 28.960656}, {-100.648828, 28.961017}, {-100.649932, 28.961092}, {-100.650334, 28.961544}, {-100.650319, 28.962336}, {-100.649430, 28.964152}, {-100.647153, 28.966120}, {-100.646636, 28.966929}, {-100.646382, 28.970724}, {-100.647192, 28.973056}, {-100.648113, 28.974004}, {-100.648654, 28.977422}, + {-100.648104, 28.983860}, {-100.647286, 28.985298}, {-100.645894, 28.986421}, {-100.646040, 28.987981}, {-100.646903, 28.991777}, {-100.647826, 28.992933}, {-100.649164, 28.996285}, {-100.648529, 28.998941}, {-100.648664, 29.000243}, {-100.650946, 29.008254}, {-100.653758, 29.015356}, {-100.656110, 29.017224}, {-100.660208, 29.031497}, {-100.660351, 29.035849}, {-100.661885, 29.039655}, {-100.663212, 29.048042}, + {-100.663059, 29.053466}, {-100.662508, 29.058107}, {-100.664021, 29.074164}, {-100.664609, 29.075392}, {-100.665464, 29.075973}, {-100.665873, 29.076782}, {-100.666848, 29.082596}, {-100.670597, 29.092045}, {-100.675200, 29.100741}, {-100.680526, 29.107006}, {-100.683927, 29.110239}, {-100.688505, 29.112591}, {-100.691790, 29.114854}, {-100.692978, 29.115384}, {-100.702309, 29.117482}, {-100.709966, 29.119684}, + {-100.727462, 29.129123}, {-100.729049, 29.131466}, {-100.737795, 29.139079}, {-100.739116, 29.141658}, {-100.738860, 29.143271}, {-100.737709, 29.145489}, {-100.737419, 29.147443}, {-100.738023, 29.148815}, {-100.739681, 29.150486}, {-100.743105, 29.152686}, {-100.746140, 29.154149}, {-100.750365, 29.155241}, {-100.758072, 29.156321}, {-100.759726, 29.157150}, {-100.763350, 29.160084}, {-100.763676, 29.164845}, + {-100.765448, 29.166326}, {-100.768960, 29.166045}, {-100.772604, 29.168369}, {-100.773855, 29.170836}, {-100.775905, 29.173344}, {-100.775189, 29.174996}, {-100.768493, 29.181832}, {-100.766004, 29.185847}, {-100.765824, 29.187473}, {-100.766306, 29.190446}, {-100.766121, 29.191296}, {-100.767059, 29.195287}, {-100.768110, 29.197364}, {-100.769341, 29.198911}, {-100.773952, 29.199846}, {-100.775294, 29.199698}, + {-100.776254, 29.200929}, {-100.777020, 29.204329}, {-100.774539, 29.209728}, {-100.774627, 29.210634}, {-100.775042, 29.211637}, {-100.777088, 29.213506}, {-100.779697, 29.218698}, {-100.780039, 29.219120}, {-100.781036, 29.219432}, {-100.781142, 29.220511}, {-100.782410, 29.222511}, {-100.783779, 29.225997}, {-100.785521, 29.228137}, {-100.786329, 29.228501}, {-100.787131, 29.228346}, {-100.790071, 29.226391}, + {-100.791372, 29.225945}, {-100.793677, 29.226466}, {-100.795681, 29.227730}, {-100.796860, 29.231386}, {-100.797046, 29.235586}, {-100.794800, 29.239587}, {-100.794627, 29.241612}, {-100.795647, 29.244111}, {-100.797619, 29.246776}, {-100.800060, 29.248797}, {-100.806653, 29.252264}, {-100.809279, 29.253152}, {-100.816277, 29.253864}, {-100.816723, 29.254557}, {-100.816693, 29.255639}, {-100.813306, 29.261057}, + {-100.813321, 29.262158}, {-100.813959, 29.263137}, {-100.815062, 29.263926}, {-100.816994, 29.264332}, {-100.818773, 29.263915}, {-100.823533, 29.261742}, {-100.826006, 29.261513}, {-100.831214, 29.261826}, {-100.832315, 29.261342}, {-100.834040, 29.261400}, {-100.839016, 29.263259}, {-100.840829, 29.264457}, {-100.842782, 29.266981}, {-100.845476, 29.269145}, {-100.849908, 29.272110}, {-100.851100, 29.271732}, + {-100.853396, 29.272606}, {-100.855067, 29.274946}, {-100.855841, 29.275546}, {-100.857898, 29.275879}, {-100.864659, 29.276076}, {-100.873343, 29.278490}, {-100.876049, 29.279585}, {-100.878126, 29.280860}, {-100.878883, 29.282193}, {-100.880240, 29.286296}, {-100.880580, 29.292108}, {-100.881958, 29.299057}, {-100.882293, 29.299873}, {-100.885785, 29.304286}, {-100.886291, 29.305966}, {-100.886312, 29.307322}, + {-100.886842, 29.307848}, {-100.891072, 29.308765}, {-100.894931, 29.310351}, {-100.896646, 29.309105}, {-100.899033, 29.309643}, {-100.904734, 29.312136}, {-100.906419, 29.313032}, {-100.907422, 29.314528}, {-100.908394, 29.316927}, {-100.909457, 29.317608}, {-100.913131, 29.317821}, {-100.916628, 29.319204}, {-100.917279, 29.320462}, {-100.917602, 29.322547}, {-100.918284, 29.324147}, {-100.919499, 29.325318}, + {-100.921343, 29.326197}, {-100.923600, 29.326785}, {-100.926677, 29.326583}, {-100.928505, 29.326976}, {-100.930482, 29.328531}, {-100.934451, 29.330818}, {-100.940616, 29.333109}, {-100.941265, 29.333926}, {-100.941691, 29.337422}, {-100.943198, 29.341985}, {-100.945383, 29.344517}, {-100.948974, 29.347246}, {-100.950727, 29.347711}, {-100.960790, 29.346914}, {-100.964326, 29.347342}, {-100.970212, 29.350802}, + {-100.971744, 29.351371}, {-100.972540, 29.352349}, {-100.972914, 29.354521}, {-100.983246, 29.359121}, {-100.995607, 29.363403}, {-101.002948, 29.365083}, {-101.004207, 29.364772}, {-101.009976, 29.368267}, {-101.012627, 29.371282}, {-101.014274, 29.374123}, {-101.013273, 29.380347}, {-101.013542, 29.381188}, {-101.014473, 29.382110}, {-101.016726, 29.387275}, {-101.019258, 29.390907}, {-101.021023, 29.392790}, + {-101.024016, 29.393698}, {-101.030464, 29.399584}, {-101.035146, 29.405334}, {-101.036604, 29.406108}, {-101.038600, 29.410714}, {-101.037642, 29.414681}, {-101.039461, 29.416505}, {-101.040637, 29.422447}, {-101.043364, 29.429880}, {-101.050505, 29.434621}, {-101.054422, 29.437949}, {-101.056957, 29.440773}, {-101.056844, 29.447599}, {-101.058149, 29.447834}, {-101.059727, 29.455097}, {-101.060151, 29.458661}, + {-101.077184, 29.464190}, {-101.087149, 29.469414}, {-101.103699, 29.470550}, {-101.115254, 29.468459}, {-101.123794, 29.473721}, {-101.127280, 29.477071}, {-101.130038, 29.478420}, {-101.132634, 29.477314}, {-101.137503, 29.473542}, {-101.140750, 29.472993}, {-101.143447, 29.473155}, {-101.146798, 29.474187}, {-101.151877, 29.477005}, {-101.152191, 29.478449}, {-101.152741, 29.478609}, {-101.161948, 29.489524}, + {-101.168102, 29.500220}, {-101.171664, 29.503951}, {-101.173824, 29.514568}, {-101.179646, 29.515444}, {-101.192716, 29.520285}, {-101.227417, 29.522350}, {-101.235273, 29.524854}, {-101.250382, 29.520701}, {-101.254895, 29.520341}, {-101.260838, 29.529932}, {-101.261176, 29.536776}, {-101.260482, 29.538968}, {-101.255275, 29.549307}, {-101.250385, 29.556621}, {-101.248524, 29.559211}, {-101.242981, 29.564129}, + {-101.241030, 29.565028}, {-101.251660, 29.600247}, {-101.252297, 29.604167}, {-101.250383, 29.609193}, {-101.249636, 29.610204}, {-101.247333, 29.618480}, {-101.250383, 29.624171}, {-101.254036, 29.626879}, {-101.262233, 29.630607}, {-101.263391, 29.630962}, {-101.266469, 29.631010}, {-101.269102, 29.630128}, {-101.274145, 29.625214}, {-101.277709, 29.621025}, {-101.278759, 29.619467}, {-101.280494, 29.615181}, + {-101.280079, 29.613273}, {-101.278097, 29.609371}, {-101.277439, 29.606593}, {-101.278008, 29.596549}, {-101.277610, 29.593231}, {-101.277878, 29.590810}, {-101.279449, 29.588238}, {-101.280623, 29.587249}, {-101.281138, 29.585900}, {-101.285336, 29.582018}, {-101.290962, 29.571537}, {-101.292489, 29.571574}, {-101.296162, 29.572778}, {-101.305533, 29.577925}, {-101.305969, 29.578792}, {-101.307565, 29.580214}, + {-101.311636, 29.585127}, {-101.311997, 29.586063}, {-101.312363, 29.587367}, {-101.313373, 29.602259}, {-101.312617, 29.607640}, {-101.312896, 29.610657}, {-101.311706, 29.615372}, {-101.305863, 29.625213}, {-101.301427, 29.634624}, {-101.300027, 29.640702}, {-101.301444, 29.647233}, {-101.302516, 29.650425}, {-101.303558, 29.652281}, {-101.306944, 29.655768}, {-101.310372, 29.657682}, {-101.314135, 29.659054}, + {-101.333486, 29.660890}, {-101.337483, 29.660786}, {-101.346017, 29.662241}, {-101.347863, 29.662209}, {-101.349351, 29.661681}, {-101.350327, 29.660660}, {-101.355450, 29.651003}, {-101.357140, 29.649267}, {-101.358713, 29.649106}, {-101.361312, 29.650063}, {-101.363218, 29.652638}, {-101.365551, 29.666912}, {-101.367105, 29.670898}, {-101.368718, 29.672408}, {-101.372696, 29.678057}, {-101.373054, 29.680191}, + {-101.373108, 29.684137}, {-101.371782, 29.693258}, {-101.372773, 29.699428}, {-101.375386, 29.701807}, {-101.388481, 29.707629}, {-101.396948, 29.713947}, {-101.398362, 29.717000}, {-101.398175, 29.720485}, {-101.396294, 29.727055}, {-101.396271, 29.730465}, {-101.397009, 29.733963}, {-101.398599, 29.736432}, {-101.400636, 29.738079}, {-101.407663, 29.740357}, {-101.410024, 29.741498}, {-101.413227, 29.743599}, + {-101.414875, 29.745234}, {-101.415584, 29.746534}, {-101.415936, 29.748638}, {-101.415722, 29.750206}, {-101.414588, 29.752418}, {-101.413211, 29.753381}, {-101.403926, 29.757587}, {-101.400155, 29.760083}, {-101.398983, 29.762175}, {-101.398445, 29.764630}, {-101.398672, 29.766898}, {-101.399736, 29.768961}, {-101.402319, 29.771190}, {-101.404067, 29.771766}, {-101.405960, 29.771653}, {-101.407983, 29.771343}, + {-101.410933, 29.770152}, {-101.424208, 29.761312}, {-101.436270, 29.751921}, {-101.438146, 29.750920}, {-101.442589, 29.749722}, {-101.446202, 29.749933}, {-101.448881, 29.750699}, {-101.450298, 29.752846}, {-101.451215, 29.755792}, {-101.451974, 29.760363}, {-101.453074, 29.764719}, {-101.454588, 29.768703}, {-101.455224, 29.771874}, {-101.455125, 29.773936}, {-101.452947, 29.781947}, {-101.452806, 29.783791}, + {-101.453780, 29.785911}, {-101.455799, 29.788049}, {-101.458309, 29.789228}, {-101.460906, 29.789663}, {-101.464403, 29.789286}, {-101.469489, 29.787894}, {-101.476275, 29.785518}, {-101.478864, 29.784085}, {-101.482515, 29.780095}, {-101.484457, 29.776885}, {-101.487089, 29.773868}, {-101.500389, 29.765868}, {-101.503223, 29.764582}, {-101.504956, 29.764238}, {-101.508274, 29.764220}, {-101.517580, 29.765005}, + {-101.520351, 29.764354}, {-101.522811, 29.763338}, {-101.525648, 29.761583}, {-101.530256, 29.759584}, {-101.533874, 29.758728}, {-101.535998, 29.758950}, {-101.537276, 29.759411}, {-101.538638, 29.760506}, {-101.539191, 29.761774}, {-101.539432, 29.766421}, {-101.538466, 29.770038}, {-101.537920, 29.788065}, {-101.536916, 29.791649}, {-101.535218, 29.795404}, {-101.535010, 29.797679}, {-101.536166, 29.801284}, + {-101.541901, 29.810781}, {-101.543881, 29.811840}, {-101.544769, 29.811868}, {-101.547382, 29.810716}, {-101.550918, 29.805714}, {-101.552485, 29.804233}, {-101.554059, 29.803422}, {-101.565446, 29.799911}, {-101.567749, 29.798727}, {-101.573216, 29.790334}, {-101.574119, 29.788222}, {-101.574332, 29.786482}, {-101.572910, 29.783380}, {-101.572789, 29.775757}, {-101.572223, 29.773319}, {-101.573629, 29.771304}, + {-101.575807, 29.769319}, {-101.577540, 29.769074}, {-101.578627, 29.769703}, {-101.580247, 29.769660}, {-101.586899, 29.771124}, {-101.588593, 29.770746}, {-101.589443, 29.770875}, {-101.595863, 29.772207}, {-101.598890, 29.773827}, {-101.603681, 29.774399}, {-101.606648, 29.774113}, {-101.615674, 29.770412}, {-101.617906, 29.769930}, {-101.620336, 29.769814}, {-101.625133, 29.770988}, {-101.626900, 29.770762}, + {-101.628769, 29.770025}, {-101.630290, 29.768335}, {-101.632715, 29.764265}, {-101.632961, 29.763013}, {-101.632796, 29.761638}, {-101.633951, 29.759802}, {-101.635464, 29.758360}, {-101.637748, 29.757437}, {-101.640177, 29.756918}, {-101.642883, 29.754548}, {-101.644567, 29.754233}, {-101.646936, 29.754565}, {-101.650758, 29.757093}, {-101.652965, 29.759911}, {-101.653145, 29.762195}, {-101.654459, 29.765121}, + {-101.656004, 29.765888}, {-101.657663, 29.766235}, {-101.659687, 29.767411}, {-101.660268, 29.768971}, {-101.661724, 29.770857}, {-101.662994, 29.771060}, {-101.666140, 29.769926}, {-101.670623, 29.768928}, {-101.673629, 29.766458}, {-101.674564, 29.765232}, {-101.674965, 29.764158}, {-101.675299, 29.761084}, {-101.677804, 29.760292}, {-101.680002, 29.760198}, {-101.681440, 29.760784}, {-101.684555, 29.764381}, + {-101.686219, 29.768590}, {-101.689634, 29.771035}, {-101.690109, 29.771051}, {-101.693945, 29.768930}, {-101.698177, 29.765672}, {-101.702393, 29.764605}, {-101.706113, 29.762861}, {-101.707177, 29.761793}, {-101.708873, 29.761642}, {-101.710724, 29.762020}, {-101.712056, 29.762777}, {-101.712781, 29.763668}, {-101.714439, 29.768456}, {-101.717778, 29.772535}, {-101.718713, 29.774738}, {-101.719730, 29.775738}, + {-101.722610, 29.776247}, {-101.724956, 29.775897}, {-101.726329, 29.774152}, {-101.728289, 29.772685}, {-101.731236, 29.771415}, {-101.733352, 29.771180}, {-101.736078, 29.771862}, {-101.738692, 29.772985}, {-101.739228, 29.773535}, {-101.739864, 29.776207}, {-101.741335, 29.778278}, {-101.744315, 29.779195}, {-101.746280, 29.778141}, {-101.748894, 29.777549}, {-101.753881, 29.777327}, {-101.754392, 29.777527}, + {-101.755347, 29.778984}, {-101.756124, 29.779583}, {-101.758591, 29.780331}, {-101.760308, 29.781702}, {-101.762899, 29.782125}, {-101.764299, 29.782736}, {-101.771100, 29.787512}, {-101.773825, 29.788675}, {-101.777045, 29.789251}, {-101.780556, 29.789376}, {-101.782767, 29.789321}, {-101.784900, 29.788648}, {-101.785957, 29.787409}, {-101.785586, 29.784160}, {-101.785773, 29.783247}, {-101.787141, 29.780840}, + {-101.788531, 29.779272}, {-101.790762, 29.779496}, {-101.794631, 29.779134}, {-101.799496, 29.779218}, {-101.805114, 29.780350}, {-101.808059, 29.781594}, {-101.810810, 29.783627}, {-101.813135, 29.787207}, {-101.813977, 29.792330}, {-101.813462, 29.794412}, {-101.806515, 29.799296}, {-101.805000, 29.801642}, {-101.804919, 29.802512}, {-101.803976, 29.802562}, {-101.803613, 29.803446}, {-101.805745, 29.805240}, + {-101.809739, 29.807198}, {-101.812516, 29.809786}, {-101.816284, 29.811350}, {-101.817777, 29.811635}, {-101.819986, 29.811394}, {-101.822559, 29.810341}, {-101.825062, 29.808255}, {-101.825608, 29.807053}, {-101.825830, 29.805520}, {-101.825533, 29.803963}, {-101.823966, 29.800478}, {-101.821851, 29.798466}, {-101.820306, 29.796310}, {-101.818694, 29.791500}, {-101.819289, 29.790058}, {-101.820422, 29.788875}, + {-101.823656, 29.787398}, {-101.824844, 29.787426}, {-101.828671, 29.788532}, {-101.837595, 29.793364}, {-101.842932, 29.798972}, {-101.846922, 29.802442}, {-101.847406, 29.803243}, {-101.847159, 29.804002}, {-101.847353, 29.804660}, {-101.848948, 29.806743}, {-101.850726, 29.807542}, {-101.852720, 29.807791}, {-101.855862, 29.807217}, {-101.859472, 29.803485}, {-101.861087, 29.802638}, {-101.866502, 29.798014}, + {-101.870297, 29.795895}, {-101.875772, 29.793796}, {-101.880588, 29.795760}, {-101.882451, 29.793627}, {-101.883984, 29.793542}, {-101.885185, 29.794099}, {-101.888160, 29.796425}, {-101.890511, 29.797380}, {-101.892883, 29.797688}, {-101.898696, 29.798201}, {-101.902729, 29.798027}, {-101.908795, 29.798467}, {-101.911595, 29.798040}, {-101.913966, 29.796885}, {-101.915723, 29.795213}, {-101.917420, 29.792540}, + {-101.921707, 29.788240}, {-101.921428, 29.787473}, {-101.922250, 29.786026}, {-101.926637, 29.783109}, {-101.929323, 29.782576}, {-101.931675, 29.784222}, {-101.933061, 29.784480}, {-101.933528, 29.785193}, {-101.934174, 29.787993}, {-101.932541, 29.789769}, {-101.932672, 29.793243}, {-101.933806, 29.797033}, {-101.935606, 29.799561}, {-101.937595, 29.800544}, {-101.939329, 29.800864}, {-101.943860, 29.800667}, + {-101.945890, 29.801755}, {-101.947566, 29.800929}, {-101.949642, 29.798745}, {-101.954090, 29.795777}, {-101.955480, 29.795333}, {-101.957515, 29.795766}, {-101.960968, 29.797618}, {-101.963377, 29.800149}, {-101.963342, 29.801192}, {-101.964012, 29.803601}, {-101.965465, 29.805995}, {-101.964717, 29.809314}, {-101.965435, 29.810994}, {-101.966740, 29.812698}, {-101.967677, 29.813347}, {-101.969615, 29.813834}, + {-101.971005, 29.813555}, {-101.971783, 29.814079}, {-101.972232, 29.815245}, {-101.972838, 29.815658}, {-101.973974, 29.815579}, {-101.976264, 29.816040}, {-101.979137, 29.815319}, {-101.980779, 29.815625}, {-101.981567, 29.815020}, {-101.982795, 29.811869}, {-101.983052, 29.809943}, {-101.982960, 29.808766}, {-101.981725, 29.806807}, {-101.981808, 29.806167}, {-101.984303, 29.799262}, {-101.985445, 29.797959}, + {-101.987305, 29.796842}, {-101.988607, 29.796666}, {-101.989796, 29.796960}, {-101.991032, 29.797723}, {-101.992990, 29.800964}, {-101.994147, 29.804516}, {-101.994724, 29.805165}, {-101.997021, 29.806109}, {-102.000656, 29.805198}, {-102.006007, 29.801489}, {-102.009659, 29.800441}, {-102.011603, 29.799571}, {-102.014465, 29.797643}, {-102.015227, 29.797491}, {-102.017928, 29.798127}, {-102.018412, 29.798517}, + {-102.019847, 29.801171}, {-102.021906, 29.802467}, {-102.024555, 29.802922}, {-102.027436, 29.801883}, {-102.028424, 29.801880}, {-102.030684, 29.802538}, {-102.032571, 29.803832}, {-102.035818, 29.804063}, {-102.038758, 29.803029}, {-102.039827, 29.802047}, {-102.040732, 29.800553}, {-102.041189, 29.799058}, {-102.040930, 29.797565}, {-102.039462, 29.794367}, {-102.038736, 29.793625}, {-102.038386, 29.792368}, + {-102.038626, 29.791619}, {-102.040385, 29.790104}, {-102.041418, 29.789656}, {-102.043783, 29.790298}, {-102.046293, 29.789940}, {-102.048289, 29.788182}, {-102.048605, 29.787722}, {-102.048787, 29.786091}, {-102.049652, 29.785276}, {-102.051958, 29.784977}, {-102.053162, 29.785745}, {-102.053765, 29.786564}, {-102.055001, 29.787030}, {-102.057891, 29.786436}, {-102.067455, 29.786923}, {-102.068434, 29.786376}, + {-102.071246, 29.787012}, {-102.072046, 29.786913}, {-102.072618, 29.786458}, {-102.073187, 29.786524}, {-102.075668, 29.788392}, {-102.076317, 29.790682}, {-102.077109, 29.792051}, {-102.078982, 29.793084}, {-102.080596, 29.792772}, {-102.082058, 29.793140}, {-102.082475, 29.793425}, {-102.082784, 29.794351}, {-102.084379, 29.795064}, {-102.088630, 29.793522}, {-102.091149, 29.793287}, {-102.093567, 29.791497}, + {-102.094678, 29.791245}, {-102.098732, 29.792264}, {-102.101383, 29.793612}, {-102.107362, 29.792820}, {-102.108782, 29.791845}, {-102.110895, 29.792412}, {-102.112266, 29.793557}, {-102.114196, 29.793451}, {-102.114766, 29.792466}, {-102.115706, 29.792301}, {-102.116723, 29.793038}, {-102.117916, 29.796161}, {-102.117730, 29.797447}, {-102.118365, 29.799210}, {-102.117141, 29.800363}, {-102.119754, 29.802479}, + {-102.123610, 29.801884}, {-102.127641, 29.798797}, {-102.128904, 29.798530}, {-102.130461, 29.798983}, {-102.135562, 29.801589}, {-102.137847, 29.802108}, {-102.140769, 29.802264}, {-102.143566, 29.803320}, {-102.149206, 29.810122}, {-102.150893, 29.811592}, {-102.152059, 29.811983}, {-102.153207, 29.811605}, {-102.155610, 29.812042}, {-102.157890, 29.813094}, {-102.159796, 29.814685}, {-102.161279, 29.818683}, + {-102.165745, 29.823046}, {-102.165830, 29.824212}, {-102.166445, 29.825385}, {-102.167666, 29.826043}, {-102.168603, 29.826160}, {-102.172682, 29.825633}, {-102.177527, 29.825551}, {-102.179560, 29.825970}, {-102.179964, 29.826375}, {-102.181305, 29.831150}, {-102.180008, 29.833063}, {-102.178437, 29.836665}, {-102.178509, 29.839454}, {-102.181131, 29.844484}, {-102.182281, 29.845889}, {-102.187027, 29.848656}, + {-102.189394, 29.848662}, {-102.191655, 29.846638}, {-102.192469, 29.845266}, {-102.193254, 29.842783}, {-102.193357, 29.839356}, {-102.194201, 29.837894}, {-102.196086, 29.836576}, {-102.197642, 29.836417}, {-102.199614, 29.836870}, {-102.202257, 29.839453}, {-102.204561, 29.840993}, {-102.204987, 29.841954}, {-102.206645, 29.843201}, {-102.208021, 29.843919}, {-102.209938, 29.844359}, {-102.211834, 29.844417}, + {-102.214598, 29.843990}, {-102.219797, 29.840869}, {-102.221853, 29.840365}, {-102.223528, 29.840530}, {-102.227891, 29.843634}, {-102.229466, 29.845598}, {-102.229973, 29.847035}, {-102.230857, 29.847868}, {-102.231791, 29.848251}, {-102.234142, 29.848579}, {-102.241380, 29.847686}, {-102.243830, 29.848172}, {-102.244892, 29.848946}, {-102.245319, 29.850403}, {-102.244940, 29.852718}, {-102.243225, 29.858413}, + {-102.245307, 29.863191}, {-102.246458, 29.864128}, {-102.248310, 29.864341}, {-102.249170, 29.864002}, {-102.249775, 29.863293}, {-102.250550, 29.861416}, {-102.251604, 29.859912}, {-102.251891, 29.858291}, {-102.251294, 29.856751}, {-102.251491, 29.855155}, {-102.253178, 29.853068}, {-102.254581, 29.852702}, {-102.256398, 29.852714}, {-102.261495, 29.853241}, {-102.263480, 29.854227}, {-102.264269, 29.855630}, + {-102.264278, 29.857797}, {-102.263161, 29.861435}, {-102.262134, 29.862667}, {-102.261882, 29.863570}, {-102.262081, 29.864693}, {-102.263134, 29.866231}, {-102.264842, 29.867496}, {-102.266758, 29.868039}, {-102.268995, 29.867765}, {-102.274040, 29.864583}, {-102.274764, 29.863788}, {-102.276591, 29.862954}, {-102.280892, 29.862819}, {-102.282359, 29.863452}, {-102.283305, 29.864292}, {-102.285828, 29.870053}, + {-102.286875, 29.871202}, {-102.295871, 29.873610}, {-102.299707, 29.877167}, {-102.300558, 29.877597}, {-102.304401, 29.877815}, {-102.309540, 29.877099}, {-102.312298, 29.877119}, {-102.313741, 29.879090}, {-102.314818, 29.879804}, {-102.315940, 29.879993}, {-102.320272, 29.879301}, {-102.321186, 29.878685}, {-102.324340, 29.873712}, {-102.324739, 29.872617}, {-102.324602, 29.870980}, {-102.322711, 29.865660}, + {-102.323463, 29.863917}, {-102.324856, 29.863081}, {-102.326905, 29.862674}, {-102.330758, 29.863110}, {-102.331931, 29.862824}, {-102.332642, 29.863675}, {-102.332949, 29.865607}, {-102.332801, 29.867072}, {-102.333310, 29.868071}, {-102.338298, 29.868874}, {-102.339382, 29.869475}, {-102.340921, 29.869373}, {-102.342185, 29.868441}, {-102.342113, 29.866565}, {-102.343091, 29.865237}, {-102.349678, 29.862634}, + {-102.350207, 29.861203}, {-102.350245, 29.857008}, {-102.350854, 29.854849}, {-102.352272, 29.852756}, {-102.354553, 29.851275}, {-102.359135, 29.850242}, {-102.361236, 29.849130}, {-102.363355, 29.847085}, {-102.364557, 29.845335}, {-102.364491, 29.843381}, {-102.363956, 29.841953}, {-102.364202, 29.841294}, {-102.362445, 29.835655}, {-102.362407, 29.834148}, {-102.362929, 29.831489}, {-102.364255, 29.829579}, + {-102.364503, 29.828261}, {-102.365832, 29.825592}, {-102.369643, 29.820662}, {-102.369627, 29.819703}, {-102.372084, 29.813605}, {-102.373195, 29.807878}, {-102.374339, 29.806417}, {-102.375752, 29.802914}, {-102.377512, 29.799997}, {-102.377820, 29.797168}, {-102.377361, 29.794514}, {-102.377471, 29.791219}, {-102.377200, 29.789889}, {-102.378960, 29.788468}, {-102.379379, 29.787762}, {-102.385189, 29.785769}, + {-102.388554, 29.783610}, {-102.390044, 29.781921}, {-102.389510, 29.780608}, {-102.387263, 29.778473}, {-102.386586, 29.777446}, {-102.386234, 29.774879}, {-102.385408, 29.773398}, {-102.385039, 29.771744}, {-102.385028, 29.770502}, {-102.385455, 29.769242}, {-102.386961, 29.766570}, {-102.386854, 29.765476}, {-102.387347, 29.764740}, {-102.386361, 29.762412}, {-102.387227, 29.761392}, {-102.388323, 29.761266}, + {-102.389679, 29.761880}, {-102.390299, 29.762639}, {-102.391791, 29.766100}, {-102.393428, 29.768296}, {-102.394807, 29.769163}, {-102.397619, 29.769642}, {-102.398924, 29.769522}, {-102.404537, 29.765181}, {-102.408183, 29.765011}, {-102.408976, 29.764531}, {-102.410639, 29.765373}, {-102.411973, 29.768168}, {-102.413289, 29.768269}, {-102.414934, 29.769281}, {-102.416032, 29.769575}, {-102.417545, 29.769591}, + {-102.420082, 29.768939}, {-102.421474, 29.769258}, {-102.423119, 29.770008}, {-102.424043, 29.771361}, {-102.425922, 29.773100}, {-102.427012, 29.773654}, {-102.428494, 29.773859}, {-102.429474, 29.774657}, {-102.432281, 29.775547}, {-102.433666, 29.776853}, {-102.435636, 29.776479}, {-102.436597, 29.775743}, {-102.437271, 29.774242}, {-102.438272, 29.773496}, {-102.439297, 29.773259}, {-102.440787, 29.773735}, + {-102.444578, 29.776812}, {-102.447382, 29.777502}, {-102.450082, 29.777171}, {-102.455006, 29.774810}, {-102.457692, 29.774585}, {-102.458889, 29.775398}, {-102.462194, 29.781290}, {-102.465453, 29.783390}, {-102.466268, 29.783402}, {-102.467487, 29.782870}, {-102.469728, 29.780796}, {-102.470649, 29.779664}, {-102.471314, 29.777078}, {-102.471994, 29.776387}, {-102.473233, 29.776018}, {-102.480665, 29.776069}, + {-102.482285, 29.777961}, {-102.481345, 29.780574}, {-102.481271, 29.781325}, {-102.481772, 29.782838}, {-102.483565, 29.784784}, {-102.485665, 29.785681}, {-102.486377, 29.786467}, {-102.487252, 29.786585}, {-102.497987, 29.781448}, {-102.506048, 29.782370}, {-102.514939, 29.784299}, {-102.516551, 29.784121}, {-102.517616, 29.783580}, {-102.519186, 29.780368}, {-102.518382, 29.778664}, {-102.514571, 29.775609}, + {-102.511517, 29.774565}, {-102.510727, 29.773882}, {-102.510727, 29.771354}, {-102.511765, 29.769785}, {-102.513103, 29.769089}, {-102.513290, 29.768398}, {-102.513000, 29.766569}, {-102.513503, 29.765439}, {-102.514562, 29.764994}, {-102.520514, 29.760739}, {-102.523414, 29.759546}, {-102.527363, 29.758426}, {-102.534235, 29.754938}, {-102.540715, 29.748069}, {-102.543417, 29.746505}, {-102.546441, 29.745769}, + {-102.547978, 29.744644}, {-102.550704, 29.745191}, {-102.551815, 29.746039}, {-102.553106, 29.747873}, {-102.554669, 29.748728}, {-102.555127, 29.751329}, {-102.554408, 29.753769}, {-102.555184, 29.755862}, {-102.557482, 29.757877}, {-102.558360, 29.759096}, {-102.560429, 29.763718}, {-102.562720, 29.767509}, {-102.563781, 29.768400}, {-102.564937, 29.768772}, {-102.565544, 29.769628}, {-102.565697, 29.770928}, + {-102.567663, 29.771463}, {-102.569190, 29.771455}, {-102.570568, 29.771005}, {-102.571815, 29.768964}, {-102.573629, 29.768202}, {-102.574077, 29.767358}, {-102.574451, 29.765969}, {-102.573301, 29.762486}, {-102.573850, 29.761631}, {-102.572054, 29.757189}, {-102.572558, 29.755885}, {-102.573796, 29.754896}, {-102.576011, 29.754691}, {-102.576904, 29.755119}, {-102.578982, 29.755417}, {-102.580338, 29.754991}, + {-102.582585, 29.753522}, {-102.582836, 29.752958}, {-102.585380, 29.750897}, {-102.589207, 29.749069}, {-102.591014, 29.749093}, {-102.591915, 29.749514}, {-102.592869, 29.749140}, {-102.594654, 29.750699}, {-102.595741, 29.751211}, {-102.597618, 29.751415}, {-102.599755, 29.751229}, {-102.604412, 29.750155}, {-102.606108, 29.748858}, {-102.609942, 29.747785}, {-102.612426, 29.748272}, {-102.614960, 29.746654}, + {-102.617042, 29.743449}, {-102.622790, 29.736339}, {-102.626110, 29.735418}, {-102.629342, 29.735310}, {-102.630410, 29.734181}, {-102.631913, 29.734270}, {-102.634193, 29.733805}, {-102.638922, 29.733932}, {-102.639968, 29.733628}, {-102.645184, 29.733839}, {-102.651403, 29.734765}, {-102.653927, 29.735509}, {-102.656868, 29.735160}, {-102.660245, 29.735629}, {-102.664048, 29.736667}, {-102.667148, 29.739129}, + {-102.667634, 29.740150}, {-102.667280, 29.744164}, {-102.667571, 29.745099}, {-102.668403, 29.745930}, {-102.669034, 29.746106}, {-102.670080, 29.745684}, {-102.672285, 29.744363}, {-102.673465, 29.744732}, {-102.673997, 29.744574}, {-102.675018, 29.743969}, {-102.677021, 29.741523}, {-102.678987, 29.734020}, {-102.681763, 29.728073}, {-102.682680, 29.727291}, {-102.683972, 29.727311}, {-102.685062, 29.726867}, + {-102.686833, 29.723774}, {-102.687737, 29.722753}, {-102.689676, 29.722098}, {-102.690532, 29.722673}, {-102.691373, 29.721372}, {-102.691061, 29.720751}, {-102.690378, 29.713829}, {-102.690471, 29.710091}, {-102.690166, 29.707308}, {-102.689019, 29.706036}, {-102.689035, 29.705038}, {-102.690552, 29.703550}, {-102.694239, 29.701104}, {-102.695107, 29.699989}, {-102.697027, 29.698776}, {-102.698137, 29.695637}, + {-102.698551, 29.695545}, {-102.699252, 29.691254}, {-102.699300, 29.687269}, {-102.699672, 29.686287}, {-102.699443, 29.684999}, {-102.696169, 29.681927}, {-102.695493, 29.679914}, {-102.693758, 29.677785}, {-102.693644, 29.676685}, {-102.696233, 29.673129}, {-102.696723, 29.671958}, {-102.698307, 29.671634}, {-102.700510, 29.672561}, {-102.704083, 29.671340}, {-102.706755, 29.668474}, {-102.708646, 29.666987}, + {-102.709167, 29.664874}, {-102.711283, 29.660467}, {-102.712536, 29.658694}, {-102.715209, 29.656405}, {-102.718293, 29.653998}, {-102.721347, 29.652607}, {-102.722828, 29.651427}, {-102.724224, 29.651143}, {-102.725740, 29.651417}, {-102.725528, 29.650051}, {-102.724066, 29.648515}, {-102.724467, 29.645844}, {-102.725906, 29.643242}, {-102.729784, 29.640652}, {-102.734510, 29.642143}, {-102.736574, 29.641825}, + {-102.737021, 29.641496}, {-102.736848, 29.639834}, {-102.741949, 29.633193}, {-102.742289, 29.631978}, {-102.741957, 29.630319}, {-102.741048, 29.628712}, {-102.740120, 29.627782}, {-102.739053, 29.625557}, {-102.738557, 29.618176}, {-102.738791, 29.608828}, {-102.739515, 29.606392}, {-102.740973, 29.604509}, {-102.741565, 29.602828}, {-102.741451, 29.602069}, {-102.739721, 29.599925}, {-102.740543, 29.597499}, + {-102.743416, 29.594876}, {-102.744340, 29.593559}, {-102.746331, 29.592648}, {-102.749695, 29.595085}, {-102.753667, 29.596245}, {-102.756303, 29.597598}, {-102.760323, 29.598603}, {-102.761886, 29.598643}, {-102.765374, 29.597757}, {-102.768302, 29.595209}, {-102.768641, 29.594680}, {-102.768405, 29.593334}, {-102.765929, 29.589527}, {-102.763504, 29.582012}, {-102.762343, 29.580548}, {-102.762263, 29.579751}, + {-102.763242, 29.577818}, {-102.763953, 29.574747}, {-102.764779, 29.573340}, {-102.766201, 29.572206}, {-102.767627, 29.571798}, {-102.768901, 29.572743}, {-102.769121, 29.573303}, {-102.770649, 29.573838}, {-102.773250, 29.573674}, {-102.773866, 29.573124}, {-102.773897, 29.569659}, {-102.773423, 29.566865}, {-102.774882, 29.565136}, {-102.775100, 29.563722}, {-102.776568, 29.561809}, {-102.777603, 29.561377}, + {-102.777766, 29.560980}, {-102.777339, 29.554749}, {-102.775437, 29.551588}, {-102.773831, 29.550371}, {-102.771110, 29.549031}, {-102.771228, 29.548397}, {-102.773468, 29.547184}, {-102.776809, 29.547055}, {-102.778382, 29.543592}, {-102.780266, 29.542466}, {-102.783332, 29.541918}, {-102.789266, 29.543679}, {-102.789883, 29.543672}, {-102.791452, 29.542824}, {-102.792443, 29.541770}, {-102.793027, 29.539946}, + {-102.792941, 29.538837}, {-102.791526, 29.536016}, {-102.792007, 29.533004}, {-102.792567, 29.532444}, {-102.795039, 29.531237}, {-102.797180, 29.529398}, {-102.798443, 29.526667}, {-102.803907, 29.523954}, {-102.808320, 29.522930}, {-102.808856, 29.521198}, {-102.808738, 29.519948}, {-102.807869, 29.517554}, {-102.806388, 29.515381}, {-102.807219, 29.512826}, {-102.806561, 29.507012}, {-102.807474, 29.503851}, + {-102.806388, 29.502887}, {-102.805133, 29.502497}, {-102.804337, 29.501177}, {-102.804744, 29.498785}, {-102.807268, 29.494295}, {-102.806571, 29.493594}, {-102.805748, 29.491436}, {-102.804874, 29.490353}, {-102.803239, 29.489082}, {-102.802856, 29.488396}, {-102.801958, 29.488233}, {-102.800673, 29.486235}, {-102.804210, 29.483716}, {-102.808352, 29.483305}, {-102.810695, 29.483816}, {-102.812565, 29.483636}, + {-102.814251, 29.482398}, {-102.814385, 29.479900}, {-102.815527, 29.476004}, {-102.815374, 29.474444}, {-102.814001, 29.472408}, {-102.813997, 29.471401}, {-102.815482, 29.471160}, {-102.816414, 29.470522}, {-102.817971, 29.467113}, {-102.820380, 29.466009}, {-102.822185, 29.464558}, {-102.823474, 29.462581}, {-102.823858, 29.459955}, {-102.825637, 29.457945}, {-102.825355, 29.456072}, {-102.825579, 29.455463}, + {-102.823954, 29.452112}, {-102.821086, 29.451890}, {-102.819528, 29.450720}, {-102.822243, 29.449123}, {-102.823892, 29.448806}, {-102.825593, 29.447421}, {-102.829393, 29.445884}, {-102.831045, 29.444254}, {-102.830827, 29.441028}, {-102.831214, 29.439933}, {-102.831767, 29.439391}, {-102.831827, 29.438652}, {-102.831656, 29.436532}, {-102.830862, 29.434353}, {-102.830943, 29.433879}, {-102.832215, 29.433393}, + {-102.832480, 29.432431}, {-102.830798, 29.427879}, {-102.829948, 29.427277}, {-102.828572, 29.423584}, {-102.826925, 29.417700}, {-102.828539, 29.415518}, {-102.830583, 29.413967}, {-102.831537, 29.412082}, {-102.833102, 29.411140}, {-102.833443, 29.410498}, {-102.833251, 29.409291}, {-102.832582, 29.408445}, {-102.826518, 29.407563}, {-102.826522, 29.406341}, {-102.825727, 29.404186}, {-102.826126, 29.402962}, + {-102.825855, 29.402544}, {-102.824621, 29.402603}, {-102.823136, 29.403425}, {-102.819003, 29.403599}, {-102.813676, 29.402326}, {-102.812362, 29.401443}, {-102.812381, 29.400525}, {-102.813383, 29.399085}, {-102.815859, 29.397016}, {-102.820040, 29.398348}, {-102.825612, 29.396946}, {-102.826598, 29.395941}, {-102.829741, 29.390769}, {-102.832520, 29.388922}, {-102.833060, 29.388086}, {-102.834023, 29.379056}, + {-102.834712, 29.378492}, {-102.837029, 29.377894}, {-102.840690, 29.377641}, {-102.843980, 29.375749}, {-102.845018, 29.374441}, {-102.844898, 29.369600}, {-102.845545, 29.366937}, {-102.845317, 29.364732}, {-102.842943, 29.363004}, {-102.841378, 29.362720}, {-102.840078, 29.361851}, {-102.839375, 29.358922}, {-102.843356, 29.357775}, {-102.845303, 29.357699}, {-102.849183, 29.355921}, {-102.856661, 29.353603}, + {-102.859893, 29.351771}, {-102.862127, 29.351343}, {-102.872932, 29.352259}, {-102.875824, 29.353580}, {-102.876861, 29.354711}, {-102.877505, 29.354891}, {-102.880711, 29.352639}, {-102.884365, 29.347946}, {-102.884439, 29.347412}, {-102.883880, 29.345990}, {-102.882508, 29.344603}, {-102.880749, 29.342088}, {-102.879875, 29.339148}, {-102.880627, 29.337185}, {-102.881158, 29.334315}, {-102.884737, 29.325995}, + {-102.884990, 29.322133}, {-102.884320, 29.321197}, {-102.884450, 29.320678}, {-102.885250, 29.320147}, {-102.886205, 29.320316}, {-102.887433, 29.318755}, {-102.887625, 29.316783}, {-102.888263, 29.314836}, {-102.887544, 29.313217}, {-102.887338, 29.311630}, {-102.890195, 29.309472}, {-102.892234, 29.309499}, {-102.893041, 29.308907}, {-102.893109, 29.307473}, {-102.892635, 29.305885}, {-102.890123, 29.302415}, + {-102.888862, 29.296517}, {-102.888450, 29.295832}, {-102.888178, 29.293359}, {-102.889736, 29.288748}, {-102.890466, 29.287978}, {-102.891219, 29.287390}, {-102.894883, 29.285956}, {-102.896514, 29.284831}, {-102.897544, 29.281992}, {-102.898670, 29.280730}, {-102.899238, 29.280456}, {-102.901643, 29.280659}, {-102.902978, 29.279225}, {-102.903328, 29.277842}, {-102.903065, 29.276081}, {-102.902153, 29.274644}, + {-102.900182, 29.272783}, {-102.899287, 29.269092}, {-102.902249, 29.264820}, {-102.903273, 29.264681}, {-102.905478, 29.263623}, {-102.906382, 29.262055}, {-102.906384, 29.260083}, {-102.905981, 29.258269}, {-102.904208, 29.255471}, {-102.903066, 29.254335}, {-102.900322, 29.253964}, {-102.898706, 29.254424}, {-102.896626, 29.254114}, {-102.892660, 29.249172}, {-102.887818, 29.245796}, {-102.884061, 29.246173}, + {-102.880280, 29.246066}, {-102.880254, 29.245505}, {-102.879724, 29.245176}, {-102.874388, 29.243552}, {-102.871749, 29.241776}, {-102.871032, 29.240706}, {-102.871176, 29.238767}, {-102.870597, 29.236109}, {-102.870799, 29.233949}, {-102.871525, 29.232744}, {-102.871716, 29.231719}, {-102.868529, 29.230365}, {-102.866762, 29.227744}, {-102.866544, 29.226258}, {-102.867474, 29.223883}, {-102.873736, 29.218868}, + {-102.877286, 29.217087}, {-102.877894, 29.216334}, {-102.878346, 29.214056}, {-102.878821, 29.213627}, {-102.882829, 29.213003}, {-102.884595, 29.212207}, {-102.885596, 29.211116}, {-102.890054, 29.209959}, {-102.890388, 29.209332}, {-102.891414, 29.209424}, {-102.895101, 29.208521}, {-102.897783, 29.208636}, {-102.899491, 29.209460}, {-102.900956, 29.212364}, {-102.903375, 29.214712}, {-102.905631, 29.215826}, + {-102.907677, 29.218136}, {-102.907970, 29.219155}, {-102.908653, 29.219663}, {-102.912324, 29.219418}, {-102.914944, 29.217476}, {-102.916081, 29.215743}, {-102.916023, 29.211960}, {-102.915740, 29.211213}, {-102.913712, 29.208907}, {-102.912862, 29.207554}, {-102.912734, 29.206730}, {-102.913623, 29.204384}, {-102.913305, 29.202560}, {-102.914306, 29.201082}, {-102.914195, 29.200143}, {-102.917513, 29.199294}, + {-102.918689, 29.199349}, {-102.918936, 29.199030}, {-102.918588, 29.194186}, {-102.918125, 29.193007}, {-102.917086, 29.192235}, {-102.917823, 29.190887}, {-102.918600, 29.190515}, {-102.919747, 29.190760}, {-102.924166, 29.193440}, {-102.925329, 29.193760}, {-102.932511, 29.194735}, {-102.934468, 29.192997}, {-102.935891, 29.191119}, {-102.937967, 29.189429}, {-102.937749, 29.188534}, {-102.938271, 29.188198}, + {-102.939081, 29.188151}, {-102.939835, 29.188551}, {-102.940889, 29.189939}, {-102.941465, 29.191735}, {-102.942334, 29.191852}, {-102.943378, 29.191410}, {-102.944328, 29.190599}, {-102.945431, 29.188905}, {-102.945476, 29.187915}, {-102.944822, 29.186724}, {-102.945711, 29.183075}, {-102.946827, 29.182432}, {-102.947641, 29.181083}, {-102.949465, 29.180493}, {-102.950888, 29.178162}, {-102.950998, 29.177385}, + {-102.950413, 29.174442}, {-102.950886, 29.173620}, {-102.952786, 29.173940}, {-102.954004, 29.175201}, {-102.954393, 29.176485}, {-102.955349, 29.177904}, {-102.957274, 29.179040}, {-102.958508, 29.178827}, {-102.962518, 29.180176}, {-102.966074, 29.182575}, {-102.968038, 29.182740}, {-102.973241, 29.185657}, {-102.975527, 29.186298}, {-102.978740, 29.186433}, {-102.981148, 29.185738}, {-102.983989, 29.183994}, + {-102.986044, 29.183219}, {-102.989985, 29.183472}, {-102.991413, 29.182061}, {-102.992276, 29.180603}, {-102.994519, 29.180013}, {-102.995618, 29.179291}, {-102.996759, 29.177729}, {-102.997902, 29.175119}, {-102.998121, 29.173769}, {-102.996993, 29.170479}, {-102.995785, 29.164008}, {-102.995914, 29.161134}, {-102.996268, 29.160220}, {-102.999575, 29.155897}, {-103.002506, 29.149959}, {-103.008108, 29.148382}, + {-103.008991, 29.141378}, {-103.008797, 29.137569}, {-103.011018, 29.137052}, {-103.012086, 29.137179}, {-103.014859, 29.135347}, {-103.015651, 29.133995}, {-103.015571, 29.132715}, {-103.014790, 29.130614}, {-103.013624, 29.130236}, {-103.012171, 29.128703}, {-103.011748, 29.127465}, {-103.012093, 29.126187}, {-103.013228, 29.125265}, {-103.014259, 29.124978}, {-103.016633, 29.126204}, {-103.016978, 29.125925}, + {-103.018952, 29.126148}, {-103.020472, 29.125628}, {-103.021810, 29.124745}, {-103.022856, 29.123505}, {-103.023528, 29.122048}, {-103.024030, 29.119753}, {-103.025905, 29.117387}, {-103.027592, 29.116021}, {-103.028610, 29.115509}, {-103.032719, 29.115168}, {-103.034318, 29.114294}, {-103.035656, 29.114222}, {-103.037014, 29.112901}, {-103.036117, 29.110780}, {-103.032109, 29.106827}, {-103.032353, 29.103832}, + {-103.033879, 29.100972}, {-103.036301, 29.098872}, {-103.039319, 29.097044}, {-103.041844, 29.096108}, {-103.045427, 29.095427}, {-103.047636, 29.096032}, {-103.049319, 29.098013}, {-103.054249, 29.100798}, {-103.055680, 29.099654}, {-103.056869, 29.097723}, {-103.058670, 29.095814}, {-103.061632, 29.094250}, {-103.064179, 29.091476}, {-103.066028, 29.091437}, {-103.068968, 29.093488}, {-103.069989, 29.093708}, + {-103.071835, 29.093015}, {-103.073005, 29.092199}, {-103.075824, 29.091537}, {-103.078679, 29.088079}, {-103.079590, 29.087946}, {-103.080183, 29.087437}, {-103.076630, 29.079770}, {-103.077019, 29.076017}, {-103.079154, 29.073564}, {-103.083562, 29.070849}, {-103.087024, 29.067887}, {-103.087425, 29.067108}, {-103.085506, 29.059961}, {-103.085404, 29.058383}, {-103.085868, 29.056633}, {-103.085584, 29.054645}, + {-103.085900, 29.053736}, {-103.088699, 29.053729}, {-103.089689, 29.053352}, {-103.090535, 29.054098}, {-103.090775, 29.055541}, {-103.091473, 29.056981}, {-103.091908, 29.057256}, {-103.092033, 29.061644}, {-103.091501, 29.063523}, {-103.092390, 29.064704}, {-103.094098, 29.065121}, {-103.095480, 29.064727}, {-103.096218, 29.063845}, {-103.096166, 29.062595}, {-103.096891, 29.060768}, {-103.097927, 29.060477}, + {-103.100134, 29.060675}, {-103.100626, 29.060323}, {-103.100599, 29.057835}, {-103.100136, 29.057180}, {-103.099920, 29.055613}, {-103.100437, 29.054716}, {-103.100435, 29.051375}, {-103.101069, 29.049301}, {-103.103004, 29.047598}, {-103.105046, 29.044468}, {-103.105868, 29.042454}, {-103.106506, 29.038179}, {-103.104720, 29.034205}, {-103.103465, 29.032661}, {-103.100651, 29.030910}, {-103.097891, 29.027174}, + {-103.097993, 29.026264}, {-103.099501, 29.022201}, {-103.101503, 29.018099}, {-103.103249, 29.016353}, {-103.106830, 29.014704}, {-103.108484, 29.013329}, {-103.112482, 29.007610}, {-103.113491, 29.005643}, {-103.116039, 29.002552}, {-103.117191, 29.000484}, {-103.117478, 28.997774}, {-103.116478, 28.996298}, {-103.115928, 28.993401}, {-103.115246, 28.992024}, {-103.114732, 28.991623}, {-103.113814, 28.988604}, + {-103.114247, 28.986642}, {-103.115360, 28.985398}, {-103.116814, 28.984416}, {-103.118833, 28.983709}, {-103.121863, 28.984450}, {-103.123582, 28.984301}, {-103.125379, 28.982439}, {-103.127006, 28.982056}, {-103.129714, 28.982734}, {-103.131689, 28.984045}, {-103.132761, 28.983652}, {-103.134552, 28.983755}, {-103.135989, 28.983371}, {-103.139313, 28.980552}, {-103.144408, 28.977467}, {-103.151022, 28.975448}, + {-103.152700, 28.972656}, {-103.153896, 28.971606}, {-103.154418, 28.971547}, {-103.155352, 28.972044}, {-103.156231, 28.972981}, {-103.160319, 28.973844}, {-103.163935, 28.972204}, {-103.166084, 28.974397}, {-103.166031, 28.975598}, {-103.165608, 28.976548}, {-103.165776, 28.977496}, {-103.166788, 28.978513}, {-103.170321, 28.980422}, {-103.172436, 28.980694}, {-103.174459, 28.977795}, {-103.176281, 28.977804}, + {-103.176755, 28.978069}, {-103.178373, 28.980824}, {-103.180842, 28.982398}, {-103.190822, 28.984163}, {-103.191639, 28.983946}, {-103.198843, 28.985872}, {-103.200557, 28.986777}, {-103.202412, 28.987195}, {-103.203834, 28.987230}, {-103.206289, 28.986575}, {-103.207759, 28.986547}, {-103.212275, 28.987622}, {-103.214782, 28.987726}, {-103.218981, 28.985652}, {-103.219848, 28.983318}, {-103.220731, 28.983601}, + {-103.221594, 28.984368}, {-103.225548, 28.989978}, {-103.227888, 28.991857}, {-103.229378, 28.990301}, {-103.229338, 28.989810}, {-103.229913, 28.988935}, {-103.231555, 28.987380}, {-103.236191, 28.984622}, {-103.236285, 28.983683}, {-103.237635, 28.981862}, {-103.238976, 28.981331}, {-103.250416, 28.980430}, {-103.251189, 28.981735}, {-103.251242, 28.983247}, {-103.251971, 28.985348}, {-103.253393, 28.987019}, + {-103.255260, 28.988474}, {-103.256747, 28.989028}, {-103.259317, 28.990680}, {-103.259512, 28.991643}, {-103.262216, 28.993490}, {-103.266333, 28.995981}, {-103.267334, 28.996072}, {-103.269359, 28.993309}, {-103.269930, 28.991105}, {-103.269844, 28.989268}, {-103.271386, 28.985362}, {-103.274447, 28.981581}, {-103.277960, 28.979176}, {-103.279338, 28.977002}, {-103.281773, 28.976641}, {-103.285083, 28.977491}, + {-103.286241, 28.978211}, {-103.286986, 28.978172}, {-103.288163, 28.979382}, {-103.288305, 28.981385}, {-103.284996, 28.982084}, {-103.283009, 28.982971}, {-103.282293, 28.984490}, {-103.281553, 28.987696}, {-103.281918, 28.989638}, {-103.284675, 28.996976}, {-103.286160, 29.000258}, {-103.286941, 29.001273}, {-103.288116, 29.002409}, {-103.290560, 29.003150}, {-103.293941, 29.004920}, {-103.295666, 29.006399}, + {-103.298981, 29.006678}, {-103.300422, 29.008210}, {-103.301662, 29.008426}, {-103.304671, 29.006520}, {-103.306798, 29.004194}, {-103.308664, 29.003426}, {-103.310067, 29.004085}, {-103.313765, 29.006987}, {-103.314955, 29.008449}, {-103.316162, 29.010689}, {-103.316318, 29.011581}, {-103.316000, 29.011994}, {-103.312748, 29.012963}, {-103.311219, 29.013978}, {-103.308628, 29.017860}, {-103.307780, 29.019455}, + {-103.306710, 29.022775}, {-103.306735, 29.023401}, {-103.307657, 29.024537}, {-103.309141, 29.025484}, {-103.311237, 29.026076}, {-103.314341, 29.026064}, {-103.315208, 29.025593}, {-103.315881, 29.024217}, {-103.318590, 29.021940}, {-103.320921, 29.019103}, {-103.324815, 29.016847}, {-103.326359, 29.016315}, {-103.332551, 29.016590}, {-103.336486, 29.018315}, {-103.338751, 29.020277}, {-103.340695, 29.023641}, + {-103.342010, 29.028436}, {-103.340210, 29.028641}, {-103.337960, 29.027471}, {-103.336108, 29.027396}, {-103.333589, 29.028956}, {-103.330110, 29.029534}, {-103.327248, 29.032721}, {-103.327696, 29.034044}, {-103.327122, 29.038269}, {-103.327492, 29.039924}, {-103.328702, 29.041559}, {-103.330613, 29.043219}, {-103.333972, 29.043991}, {-103.336700, 29.043394}, {-103.339281, 29.043704}, {-103.340758, 29.045209}, + {-103.342294, 29.046168}, {-103.343678, 29.046369}, {-103.344334, 29.045950}, {-103.344901, 29.045161}, {-103.345868, 29.042459}, {-103.347802, 29.040259}, {-103.349144, 29.036853}, {-103.350528, 29.028914}, {-103.352595, 29.026401}, {-103.353504, 29.022892}, {-103.353901, 29.022225}, {-103.355608, 29.020883}, {-103.359603, 29.018644}, {-103.361934, 29.018690}, {-103.366763, 29.020227}, {-103.374597, 29.023703}, + {-103.375829, 29.023830}, {-103.376512, 29.023213}, {-103.377277, 29.023183}, {-103.382961, 29.024512}, {-103.384568, 29.023933}, {-103.385637, 29.022208}, {-103.386505, 29.021648}, {-103.387384, 29.022245}, {-103.388120, 29.023798}, {-103.387050, 29.027461}, {-103.387745, 29.033023}, {-103.388644, 29.033974}, {-103.392261, 29.034955}, {-103.393013, 29.034599}, {-103.394142, 29.033145}, {-103.394982, 29.032575}, + {-103.399365, 29.031250}, {-103.401050, 29.031056}, {-103.401917, 29.031452}, {-103.402448, 29.032127}, {-103.402785, 29.033850}, {-103.403442, 29.034476}, {-103.404323, 29.036493}, {-103.404332, 29.038874}, {-103.405079, 29.039577}, {-103.408339, 29.039738}, {-103.410968, 29.038842}, {-103.411883, 29.038126}, {-103.415414, 29.037881}, {-103.417396, 29.041061}, {-103.418999, 29.042324}, {-103.423557, 29.043487}, + {-103.427601, 29.042164}, {-103.432095, 29.043356}, {-103.432072, 29.043944}, {-103.430677, 29.044938}, {-103.431126, 29.045990}, {-103.429584, 29.047006}, {-103.428869, 29.047918}, {-103.428894, 29.049455}, {-103.429895, 29.050337}, {-103.433707, 29.052198}, {-103.435125, 29.055216}, {-103.435111, 29.055610}, {-103.433987, 29.056899}, {-103.434221, 29.057287}, {-103.435494, 29.057829}, {-103.438396, 29.058186}, + {-103.439546, 29.057542}, {-103.441077, 29.057900}, {-103.442269, 29.058532}, {-103.446978, 29.062334}, {-103.449667, 29.065092}, {-103.450019, 29.065734}, {-103.450246, 29.068457}, {-103.450614, 29.068920}, {-103.450319, 29.071172}, {-103.449548, 29.072190}, {-103.449930, 29.072714}, {-103.454046, 29.072294}, {-103.459023, 29.072704}, {-103.461694, 29.072604}, {-103.462307, 29.072110}, {-103.462228, 29.071425}, + {-103.459097, 29.068174}, {-103.459684, 29.067048}, {-103.460935, 29.067388}, {-103.462596, 29.067302}, {-103.465878, 29.066646}, {-103.467094, 29.066079}, {-103.468483, 29.066003}, {-103.470107, 29.066194}, {-103.471350, 29.066727}, {-103.472151, 29.067710}, {-103.472618, 29.067794}, {-103.473930, 29.070315}, {-103.471289, 29.075064}, {-103.470039, 29.079182}, {-103.469743, 29.081070}, {-103.469936, 29.085317}, + {-103.470348, 29.085738}, {-103.471448, 29.085878}, {-103.476202, 29.087812}, {-103.475257, 29.090315}, {-103.475558, 29.091003}, {-103.476495, 29.091671}, {-103.476955, 29.091732}, {-103.477283, 29.091276}, {-103.478551, 29.091253}, {-103.480683, 29.091697}, {-103.483406, 29.093496}, {-103.486453, 29.097109}, {-103.489700, 29.101891}, {-103.492351, 29.104729}, {-103.493639, 29.107953}, {-103.494612, 29.108419}, + {-103.497562, 29.108245}, {-103.498762, 29.108539}, {-103.500165, 29.110386}, {-103.499763, 29.114778}, {-103.500377, 29.116196}, {-103.500934, 29.116536}, {-103.503233, 29.115732}, {-103.505884, 29.116048}, {-103.507822, 29.117048}, {-103.508321, 29.118418}, {-103.510674, 29.120374}, {-103.512852, 29.121255}, {-103.515261, 29.121317}, {-103.516854, 29.120279}, {-103.519870, 29.119676}, {-103.521535, 29.119996}, + {-103.523855, 29.121247}, {-103.523959, 29.122794}, {-103.523297, 29.129033}, {-103.523849, 29.131681}, {-103.523772, 29.133079}, {-103.523029, 29.133596}, {-103.522804, 29.134646}, {-103.523378, 29.136021}, {-103.524835, 29.137123}, {-103.527249, 29.138337}, {-103.528424, 29.139474}, {-103.531739, 29.140543}, {-103.535085, 29.142795}, {-103.538212, 29.144133}, {-103.539806, 29.146224}, {-103.542237, 29.145869}, + {-103.544671, 29.144837}, {-103.546375, 29.143807}, {-103.547616, 29.142394}, {-103.548444, 29.142382}, {-103.550211, 29.143639}, {-103.552380, 29.146202}, {-103.552965, 29.148656}, {-103.552055, 29.151563}, {-103.550081, 29.153613}, {-103.550372, 29.155512}, {-103.551315, 29.156668}, {-103.553692, 29.157996}, {-103.554482, 29.157740}, {-103.556108, 29.155989}, {-103.557020, 29.155451}, {-103.561401, 29.155300}, + {-103.565417, 29.154163}, {-103.566214, 29.154772}, {-103.568876, 29.155316}, {-103.571962, 29.154113}, {-103.573684, 29.153823}, {-103.576078, 29.151400}, {-103.577319, 29.150986}, {-103.578073, 29.150126}, {-103.580009, 29.149524}, {-103.582856, 29.149389}, {-103.584486, 29.150013}, {-103.587444, 29.150391}, {-103.591100, 29.150472}, {-103.592241, 29.150069}, {-103.594581, 29.151331}, {-103.596132, 29.151701}, + {-103.597147, 29.153888}, {-103.598293, 29.154483}, {-103.600418, 29.157227}, {-103.600667, 29.157960}, {-103.603182, 29.158097}, {-103.606179, 29.160275}, {-103.608875, 29.164799}, {-103.612231, 29.165369}, {-103.618985, 29.163261}, {-103.622180, 29.162954}, {-103.624631, 29.163261}, {-103.628384, 29.163067}, {-103.630188, 29.162102}, {-103.631319, 29.159780}, {-103.633657, 29.158580}, {-103.634830, 29.158821}, + {-103.637590, 29.160531}, {-103.639760, 29.161202}, {-103.640745, 29.161142}, {-103.646174, 29.158996}, {-103.647564, 29.157103}, {-103.649178, 29.156529}, {-103.650586, 29.156480}, {-103.651676, 29.157206}, {-103.651975, 29.157907}, {-103.651903, 29.159331}, {-103.651109, 29.159864}, {-103.650620, 29.160824}, {-103.651287, 29.161978}, {-103.653050, 29.162823}, {-103.653633, 29.163417}, {-103.655264, 29.166413}, + {-103.656141, 29.167389}, {-103.655984, 29.167902}, {-103.656712, 29.169360}, {-103.659619, 29.170569}, {-103.661164, 29.171005}, {-103.664632, 29.170937}, {-103.667830, 29.173145}, {-103.669307, 29.173652}, {-103.673492, 29.173247}, {-103.677740, 29.173883}, {-103.682553, 29.176275}, {-103.684610, 29.176062}, {-103.686280, 29.176347}, {-103.687771, 29.178108}, {-103.688568, 29.178570}, {-103.690233, 29.178408}, + {-103.693611, 29.177217}, {-103.698164, 29.177473}, {-103.700317, 29.178434}, {-103.700241, 29.179362}, {-103.700649, 29.180346}, {-103.702553, 29.182566}, {-103.703295, 29.184041}, {-103.705162, 29.184968}, {-103.706013, 29.184846}, {-103.707345, 29.183713}, {-103.708965, 29.185259}, {-103.711134, 29.184677}, {-103.711747, 29.185239}, {-103.712958, 29.185222}, {-103.714438, 29.184272}, {-103.715460, 29.182076}, + {-103.715559, 29.181102}, {-103.716618, 29.180591}, {-103.718561, 29.180826}, {-103.719180, 29.181395}, {-103.720121, 29.184310}, {-103.721656, 29.186636}, {-103.722843, 29.189167}, {-103.724947, 29.191986}, {-103.726213, 29.192581}, {-103.728725, 29.192885}, {-103.729610, 29.194638}, {-103.729457, 29.196745}, {-103.729792, 29.197428}, {-103.730779, 29.198273}, {-103.733892, 29.199676}, {-103.737055, 29.201861}, + {-103.739187, 29.204110}, {-103.741098, 29.207713}, {-103.742545, 29.209010}, {-103.742646, 29.210583}, {-103.742216, 29.213515}, {-103.742318, 29.213927}, {-103.743086, 29.214445}, {-103.743182, 29.215082}, {-103.741102, 29.217573}, {-103.740875, 29.218785}, {-103.740924, 29.219424}, {-103.742246, 29.221694}, {-103.743459, 29.222120}, {-103.747629, 29.220171}, {-103.752874, 29.219486}, {-103.756362, 29.219937}, + {-103.757365, 29.220668}, {-103.757657, 29.223098}, {-103.754878, 29.225013}, {-103.754261, 29.226890}, {-103.755117, 29.228725}, {-103.757395, 29.231176}, {-103.758549, 29.232939}, {-103.760166, 29.233520}, {-103.762782, 29.232748}, {-103.768393, 29.227578}, {-103.768508, 29.226673}, {-103.767530, 29.225830}, {-103.767533, 29.225089}, {-103.768831, 29.222896}, {-103.769706, 29.220224}, {-103.771574, 29.219348}, + {-103.775086, 29.220136}, {-103.776698, 29.220054}, {-103.777147, 29.220509}, {-103.777534, 29.222994}, {-103.777955, 29.223806}, {-103.779874, 29.225923}, {-103.780692, 29.226518}, {-103.781443, 29.226591}, {-103.782029, 29.227264}, {-103.780591, 29.236185}, {-103.780730, 29.238034}, {-103.781489, 29.239035}, {-103.781681, 29.241278}, {-103.780716, 29.243822}, {-103.776733, 29.243273}, {-103.776253, 29.243465}, + {-103.775916, 29.244112}, {-103.776089, 29.245319}, {-103.775072, 29.247954}, {-103.775542, 29.248947}, {-103.778477, 29.252039}, {-103.778807, 29.253468}, {-103.778732, 29.256131}, {-103.779969, 29.257697}, {-103.783626, 29.265324}, {-103.788613, 29.265379}, {-103.790566, 29.264048}, {-103.790909, 29.263410}, {-103.793487, 29.262214}, {-103.796176, 29.259486}, {-103.799364, 29.258437}, {-103.806369, 29.261089}, + {-103.806945, 29.261900}, {-103.806782, 29.264043}, {-103.808879, 29.265433}, {-103.809173, 29.265939}, {-103.807612, 29.270159}, {-103.809396, 29.272713}, {-103.810111, 29.273050}, {-103.812178, 29.273084}, {-103.813517, 29.274032}, {-103.816142, 29.273129}, {-103.820753, 29.270527}, {-103.822634, 29.270495}, {-103.826207, 29.269203}, {-103.829628, 29.271313}, {-103.832317, 29.271430}, {-103.832511, 29.271819}, + {-103.833772, 29.272455}, {-103.835491, 29.272687}, {-103.836956, 29.274169}, {-103.837348, 29.276139}, {-103.836708, 29.277225}, {-103.837638, 29.278252}, {-103.842300, 29.278514}, {-103.843887, 29.279520}, {-103.845589, 29.279891}, {-103.846985, 29.279538}, {-103.849315, 29.279853}, {-103.854307, 29.281472}, {-103.857412, 29.281661}, {-103.859373, 29.280442}, {-103.861384, 29.280025}, {-103.863178, 29.279197}, + {-103.863876, 29.279755}, {-103.864546, 29.281315}, {-103.865763, 29.282155}, {-103.868945, 29.280648}, {-103.870076, 29.280684}, {-103.872515, 29.283116}, {-103.880120, 29.285149}, {-103.884487, 29.284698}, {-103.885689, 29.283490}, {-103.885637, 29.282536}, {-103.885239, 29.282074}, {-103.885624, 29.281296}, {-103.887584, 29.279494}, {-103.889395, 29.278824}, {-103.891972, 29.280286}, {-103.892863, 29.281142}, + {-103.893164, 29.281938}, {-103.895921, 29.284027}, {-103.898600, 29.287074}, {-103.899292, 29.287410}, {-103.902212, 29.287420}, {-103.902886, 29.286956}, {-103.904609, 29.286773}, {-103.908867, 29.284674}, {-103.912078, 29.283744}, {-103.918887, 29.284844}, {-103.919116, 29.285506}, {-103.918940, 29.286278}, {-103.917690, 29.288213}, {-103.918288, 29.289820}, {-103.919703, 29.291111}, {-103.922755, 29.292473}, + {-103.923667, 29.293560}, {-103.925720, 29.294372}, {-103.930071, 29.293604}, {-103.933679, 29.293486}, {-103.937011, 29.292712}, {-103.943774, 29.295099}, {-103.946333, 29.294969}, {-103.949239, 29.295485}, {-103.950033, 29.295347}, {-103.950861, 29.296563}, {-103.952194, 29.297616}, {-103.954560, 29.296713}, {-103.956082, 29.297457}, {-103.958146, 29.297573}, {-103.961152, 29.298684}, {-103.963476, 29.298334}, + {-103.964806, 29.298488}, {-103.965995, 29.298834}, {-103.967648, 29.300029}, {-103.968397, 29.299740}, {-103.969694, 29.297397}, {-103.970834, 29.296556}, {-103.972294, 29.295996}, {-103.975808, 29.296249}, {-103.978094, 29.297373}, {-103.983342, 29.299175}, {-103.985491, 29.301211}, {-103.987869, 29.301814}, {-103.991659, 29.303807}, {-103.992867, 29.304255}, {-104.000678, 29.305337}, {-104.003356, 29.306965}, + {-104.008177, 29.308132}, {-104.009118, 29.308940}, {-104.010806, 29.309349}, {-104.016238, 29.312139}, {-104.019398, 29.311851}, {-104.020218, 29.312048}, {-104.027460, 29.315567}, {-104.028730, 29.315796}, {-104.030258, 29.317100}, {-104.031571, 29.317159}, {-104.034340, 29.318689}, {-104.037497, 29.319707}, {-104.038307, 29.320250}, {-104.039190, 29.321756}, {-104.041354, 29.322690}, {-104.045958, 29.325611}, + {-104.047069, 29.325836}, {-104.049672, 29.327384}, {-104.051147, 29.327763}, {-104.055165, 29.330442}, {-104.056691, 29.333265}, {-104.056141, 29.336443}, {-104.055523, 29.337021}, {-104.056100, 29.337727}, {-104.059360, 29.338194}, {-104.062190, 29.339314}, {-104.063717, 29.340550}, {-104.066746, 29.341005}, {-104.068873, 29.342308}, {-104.069302, 29.343350}, {-104.071222, 29.344649}, {-104.072332, 29.346110}, + {-104.073532, 29.346557}, {-104.074428, 29.346331}, {-104.074774, 29.345658}, {-104.075779, 29.345136}, {-104.077902, 29.345278}, {-104.078839, 29.345653}, {-104.080379, 29.345375}, {-104.082256, 29.346064}, {-104.086172, 29.350431}, {-104.088109, 29.351309}, {-104.089244, 29.352484}, {-104.090909, 29.353528}, {-104.090900, 29.353971}, {-104.089487, 29.355929}, {-104.090092, 29.356720}, {-104.089500, 29.357989}, + {-104.090025, 29.359221}, {-104.091208, 29.359913}, {-104.092264, 29.359904}, {-104.093671, 29.360372}, {-104.094475, 29.361179}, {-104.097754, 29.361642}, {-104.098207, 29.363147}, {-104.097740, 29.365185}, {-104.098201, 29.366547}, {-104.099495, 29.367242}, {-104.100925, 29.367465}, {-104.102991, 29.370312}, {-104.106579, 29.373313}, {-104.109560, 29.374085}, {-104.113371, 29.373623}, {-104.115110, 29.372896}, + {-104.117715, 29.370661}, {-104.121318, 29.371423}, {-104.121707, 29.372661}, {-104.121741, 29.377445}, {-104.122289, 29.379352}, {-104.122941, 29.380306}, {-104.123215, 29.380484}, {-104.125974, 29.379447}, {-104.126923, 29.378358}, {-104.127979, 29.378250}, {-104.129447, 29.378589}, {-104.130758, 29.379259}, {-104.131422, 29.379979}, {-104.130909, 29.381327}, {-104.131383, 29.382346}, {-104.133266, 29.383677}, + {-104.134919, 29.384220}, {-104.136329, 29.383911}, {-104.137809, 29.382655}, {-104.137918, 29.381416}, {-104.137285, 29.379933}, {-104.138199, 29.378564}, {-104.141158, 29.377823}, {-104.142158, 29.378607}, {-104.143202, 29.382424}, {-104.143152, 29.383322}, {-104.142481, 29.384593}, {-104.142424, 29.386098}, {-104.143339, 29.388268}, {-104.143787, 29.388371}, {-104.144455, 29.387956}, {-104.146438, 29.385425}, + {-104.148373, 29.385101}, {-104.150158, 29.385897}, {-104.151519, 29.387738}, {-104.152045, 29.388911}, {-104.151310, 29.390509}, {-104.151568, 29.391542}, {-104.155195, 29.394569}, {-104.155981, 29.394609}, {-104.156569, 29.393936}, {-104.156343, 29.390810}, {-104.157393, 29.390267}, {-104.160378, 29.389828}, {-104.161553, 29.390411}, {-104.162320, 29.391632}, {-104.165004, 29.392722}, {-104.167443, 29.394744}, + {-104.168430, 29.396489}, {-104.166697, 29.398621}, {-104.165376, 29.399052}, {-104.164691, 29.399645}, {-104.164512, 29.400955}, {-104.164771, 29.401517}, {-104.166089, 29.402038}, {-104.167484, 29.401514}, {-104.168508, 29.401507}, {-104.170364, 29.402646}, {-104.171133, 29.403780}, {-104.171068, 29.404351}, {-104.170257, 29.406387}, {-104.169391, 29.407332}, {-104.169674, 29.408161}, {-104.170605, 29.408914}, + {-104.173757, 29.410108}, {-104.175938, 29.409280}, {-104.177550, 29.409553}, {-104.179438, 29.410400}, {-104.180136, 29.411528}, {-104.180058, 29.413176}, {-104.180407, 29.414470}, {-104.182306, 29.415105}, {-104.183889, 29.417200}, {-104.182824, 29.421429}, {-104.181504, 29.421613}, {-104.180901, 29.422575}, {-104.180792, 29.425861}, {-104.181065, 29.426460}, {-104.182306, 29.427531}, {-104.186114, 29.428298}, + {-104.188881, 29.432115}, {-104.192924, 29.435270}, {-104.193603, 29.436121}, {-104.192823, 29.439248}, {-104.193109, 29.440295}, {-104.194142, 29.441678}, {-104.197548, 29.442773}, {-104.200600, 29.444793}, {-104.203656, 29.445534}, {-104.204189, 29.446422}, {-104.206225, 29.446803}, {-104.207846, 29.448128}, {-104.210077, 29.448843}, {-104.211166, 29.449755}, {-104.212023, 29.451726}, {-104.212899, 29.452582}, + {-104.215641, 29.454081}, {-104.217705, 29.456559}, {-104.218262, 29.458175}, {-104.213583, 29.461528}, {-104.212433, 29.464928}, {-104.212914, 29.467508}, {-104.210808, 29.471753}, {-104.210954, 29.476918}, {-104.208923, 29.480732}, {-104.209153, 29.481468}, {-104.210044, 29.482243}, {-104.211690, 29.482842}, {-104.212900, 29.484087}, {-104.214147, 29.484638}, {-104.215529, 29.484763}, {-104.219669, 29.483170}, + {-104.221539, 29.483046}, {-104.223470, 29.482285}, {-104.225240, 29.482036}, {-104.226724, 29.481140}, {-104.227521, 29.480022}, {-104.230698, 29.478669}, {-104.232210, 29.481284}, {-104.233523, 29.485628}, {-104.233904, 29.487909}, {-104.233219, 29.491339}, {-104.233820, 29.493654}, {-104.235005, 29.495777}, {-104.237134, 29.497556}, {-104.237968, 29.497844}, {-104.240317, 29.497390}, {-104.244623, 29.500112}, + {-104.247178, 29.502781}, {-104.249505, 29.504404}, {-104.254295, 29.506711}, {-104.258305, 29.506104}, {-104.260659, 29.506671}, {-104.261441, 29.507409}, {-104.262034, 29.509002}, {-104.262278, 29.510204}, {-104.261301, 29.510958}, {-104.261243, 29.511548}, {-104.262175, 29.513501}, {-104.265700, 29.514181}, {-104.268850, 29.513582}, {-104.270482, 29.514567}, {-104.270773, 29.515564}, {-104.272244, 29.516349}, + {-104.274246, 29.516331}, {-104.276481, 29.517013}, {-104.277730, 29.516919}, {-104.282072, 29.517492}, {-104.284827, 29.518892}, {-104.286621, 29.518469}, {-104.287098, 29.518820}, {-104.291929, 29.519455}, {-104.292436, 29.519776}, {-104.292703, 29.520449}, {-104.292874, 29.521612}, {-104.292571, 29.521680}, {-104.292895, 29.522290}, {-104.294195, 29.523209}, {-104.296789, 29.524128}, {-104.302801, 29.524256}, + {-104.309184, 29.523268}, {-104.311475, 29.523202}, {-104.311696, 29.524423}, {-104.310631, 29.526574}, {-104.310946, 29.528792}, {-104.313114, 29.531651}, {-104.315476, 29.532754}, {-104.317925, 29.532356}, {-104.320896, 29.529539}, {-104.320510, 29.527066}, {-104.320618, 29.524068}, {-104.321486, 29.521862}, {-104.322480, 29.521901}, {-104.324095, 29.522935}, {-104.326598, 29.523282}, {-104.330727, 29.522656}, + {-104.332533, 29.521319}, {-104.332769, 29.520381}, {-104.334553, 29.519412}, {-104.336221, 29.519333}, {-104.338201, 29.520182}, {-104.347405, 29.526236}, {-104.350243, 29.527677}, {-104.352684, 29.529691}, {-104.357532, 29.532400}, {-104.362498, 29.535885}, {-104.370421, 29.543141}, {-104.373135, 29.543800}, {-104.375044, 29.543748}, {-104.378932, 29.542103}, {-104.380531, 29.542637}, {-104.381563, 29.543369}, + {-104.386143, 29.548175}, {-104.392374, 29.553958}, {-104.394268, 29.555180}, {-104.394913, 29.556527}, {-104.393910, 29.558432}, {-104.393893, 29.559694}, {-104.394905, 29.561206}, {-104.395859, 29.564045}, {-104.397269, 29.566423}, {-104.397440, 29.567391}, {-104.396925, 29.568369}, {-104.396928, 29.569500}, {-104.397543, 29.570749}, {-104.398921, 29.571897}, {-104.404747, 29.575677}, {-104.418129, 29.583272}, + {-104.429355, 29.590244}, {-104.452025, 29.603525}, {-104.454848, 29.605808}, {-104.455262, 29.607083}, {-104.456369, 29.608734}, {-104.457436, 29.609213}, {-104.460726, 29.608948}, {-104.462952, 29.609586}, {-104.463819, 29.610240}, {-104.464867, 29.610080}, {-104.465508, 29.609049}, {-104.466549, 29.609216}, {-104.467582, 29.611236}, {-104.467591, 29.612938}, {-104.466666, 29.614624}, {-104.465816, 29.614903}, + {-104.465430, 29.615525}, {-104.465619, 29.617118}, {-104.465397, 29.618209}, {-104.466387, 29.618934}, {-104.468495, 29.619067}, {-104.471787, 29.618360}, {-104.472807, 29.617732}, {-104.473404, 29.617887}, {-104.474142, 29.619472}, {-104.474150, 29.621494}, {-104.474752, 29.623626}, {-104.476049, 29.624866}, {-104.476470, 29.626011}, {-104.476428, 29.626756}, {-104.475810, 29.627370}, {-104.475991, 29.627979}, + {-104.477326, 29.628619}, {-104.483292, 29.627631}, {-104.486141, 29.628658}, {-104.487391, 29.630918}, {-104.486961, 29.632980}, {-104.487179, 29.633691}, {-104.487822, 29.634024}, {-104.490060, 29.633939}, {-104.491696, 29.634284}, {-104.494934, 29.633826}, {-104.495303, 29.634971}, {-104.496750, 29.635741}, {-104.496752, 29.637819}, {-104.496266, 29.638296}, {-104.496231, 29.639049}, {-104.498003, 29.639695}, + {-104.501832, 29.638425}, {-104.502646, 29.637271}, {-104.503199, 29.637057}, {-104.504055, 29.637025}, {-104.505123, 29.637570}, {-104.506923, 29.637011}, {-104.507152, 29.636670}, {-104.506920, 29.635955}, {-104.505749, 29.634722}, {-104.505633, 29.634044}, {-104.507124, 29.632349}, {-104.507659, 29.632278}, {-104.508462, 29.632697}, {-104.510223, 29.635223}, {-104.512756, 29.637030}, {-104.514031, 29.638630}, + {-104.513982, 29.639762}, {-104.515589, 29.640282}, {-104.515891, 29.640963}, {-104.515320, 29.641582}, {-104.513934, 29.640842}, {-104.513194, 29.640906}, {-104.512447, 29.641873}, {-104.512893, 29.642612}, {-104.512636, 29.643126}, {-104.511663, 29.643571}, {-104.510101, 29.643295}, {-104.509878, 29.644213}, {-104.510684, 29.645492}, {-104.512001, 29.645637}, {-104.512430, 29.646717}, {-104.513745, 29.646456}, + {-104.515423, 29.647216}, {-104.517107, 29.647066}, {-104.517597, 29.647391}, {-104.518466, 29.649150}, {-104.517059, 29.650193}, {-104.517245, 29.651809}, {-104.516594, 29.652102}, {-104.516581, 29.652420}, {-104.517203, 29.653203}, {-104.518476, 29.653492}, {-104.519408, 29.654124}, {-104.519964, 29.655353}, {-104.521890, 29.656976}, {-104.522269, 29.658089}, {-104.524112, 29.658582}, {-104.524682, 29.659236}, + {-104.525263, 29.661987}, {-104.524785, 29.665043}, {-104.524980, 29.665974}, {-104.525686, 29.667053}, {-104.525227, 29.667412}, {-104.525397, 29.667843}, {-104.526176, 29.668021}, {-104.527766, 29.667337}, {-104.528224, 29.667422}, {-104.528875, 29.668413}, {-104.528427, 29.669452}, {-104.528614, 29.669792}, {-104.530116, 29.670632}, {-104.531202, 29.670775}, {-104.532283, 29.670419}, {-104.533386, 29.668673}, + {-104.532599, 29.667777}, {-104.532656, 29.667178}, {-104.533616, 29.666349}, {-104.535575, 29.666553}, {-104.536415, 29.667777}, {-104.535184, 29.672254}, {-104.535864, 29.674354}, {-104.538870, 29.677849}, {-104.540337, 29.678919}, {-104.541277, 29.679123}, {-104.542387, 29.679939}, {-104.542604, 29.680441}, {-104.543880, 29.680375}, {-104.544099, 29.680693}, {-104.543842, 29.682182}, {-104.542273, 29.684086}, + {-104.540617, 29.684831}, {-104.537724, 29.684495}, {-104.534299, 29.685852}, {-104.530824, 29.688393}, {-104.530119, 29.690027}, {-104.530210, 29.690464}, {-104.530624, 29.691072}, {-104.531709, 29.691720}, {-104.534217, 29.692623}, {-104.536095, 29.692997}, {-104.537053, 29.692861}, {-104.543832, 29.696299}, {-104.544181, 29.697114}, {-104.543561, 29.697512}, {-104.543607, 29.697832}, {-104.545123, 29.698290}, + {-104.545932, 29.699909}, {-104.544530, 29.701770}, {-104.543382, 29.704039}, {-104.543829, 29.705103}, {-104.545075, 29.706339}, {-104.545221, 29.707363}, {-104.546163, 29.708475}, {-104.546250, 29.708999}, {-104.545761, 29.709897}, {-104.544023, 29.710311}, {-104.543909, 29.711189}, {-104.544559, 29.712742}, {-104.545451, 29.713758}, {-104.546720, 29.713752}, {-104.547535, 29.714252}, {-104.545009, 29.715247}, + {-104.544881, 29.716098}, {-104.545599, 29.717043}, {-104.546358, 29.717122}, {-104.547143, 29.717663}, {-104.547662, 29.717627}, {-104.549997, 29.716170}, {-104.551670, 29.716413}, {-104.552287, 29.717342}, {-104.551344, 29.719204}, {-104.549998, 29.719425}, {-104.549159, 29.720175}, {-104.549120, 29.720773}, {-104.552155, 29.722754}, {-104.552448, 29.723707}, {-104.552041, 29.725074}, {-104.549549, 29.726569}, + {-104.548905, 29.727460}, {-104.548920, 29.728723}, {-104.549546, 29.729745}, {-104.549326, 29.730571}, {-104.550063, 29.731269}, {-104.552666, 29.731941}, {-104.553600, 29.731184}, {-104.554129, 29.731120}, {-104.555149, 29.731191}, {-104.556248, 29.731811}, {-104.556507, 29.733132}, {-104.555668, 29.734584}, {-104.556393, 29.735488}, {-104.558611, 29.734793}, {-104.560435, 29.735188}, {-104.560246, 29.736707}, + {-104.558934, 29.738060}, {-104.556416, 29.737768}, {-104.555716, 29.738078}, {-104.555368, 29.737790}, {-104.553997, 29.738707}, {-104.552562, 29.738368}, {-104.551154, 29.738847}, {-104.550955, 29.738145}, {-104.550327, 29.738008}, {-104.550058, 29.738234}, {-104.550382, 29.739614}, {-104.549848, 29.740895}, {-104.550401, 29.741194}, {-104.551559, 29.740653}, {-104.551339, 29.741290}, {-104.551859, 29.742062}, + {-104.553877, 29.743054}, {-104.554390, 29.742774}, {-104.554482, 29.742102}, {-104.555390, 29.741871}, {-104.556698, 29.744358}, {-104.556624, 29.744888}, {-104.557144, 29.745474}, {-104.557932, 29.745784}, {-104.559343, 29.745688}, {-104.560036, 29.747210}, {-104.561950, 29.747538}, {-104.565165, 29.749890}, {-104.568838, 29.751465}, {-104.569941, 29.752597}, {-104.570182, 29.754276}, {-104.569299, 29.756111}, + {-104.569614, 29.757089}, {-104.569011, 29.757414}, {-104.568481, 29.756821}, {-104.567887, 29.756697}, {-104.567118, 29.757062}, {-104.566577, 29.758270}, {-104.565661, 29.759208}, {-104.565942, 29.759501}, {-104.566817, 29.759448}, {-104.566892, 29.760092}, {-104.565732, 29.760280}, {-104.563883, 29.761283}, {-104.564194, 29.761747}, {-104.565164, 29.761442}, {-104.565812, 29.761859}, {-104.566246, 29.762853}, + {-104.566158, 29.765131}, {-104.566513, 29.765451}, {-104.568207, 29.765744}, {-104.568517, 29.766177}, {-104.567841, 29.767389}, {-104.566468, 29.768323}, {-104.565573, 29.769381}, {-104.565929, 29.771468}, {-104.568451, 29.773842}, {-104.569089, 29.775457}, {-104.570300, 29.776920}, {-104.570473, 29.778342}, {-104.571446, 29.778868}, {-104.572252, 29.778828}, {-104.572538, 29.779796}, {-104.573218, 29.780210}, + {-104.573668, 29.780159}, {-104.574082, 29.779508}, {-104.574410, 29.779601}, {-104.574260, 29.781087}, {-104.574714, 29.781788}, {-104.577197, 29.782091}, {-104.578921, 29.782835}, {-104.578990, 29.784724}, {-104.578603, 29.785293}, {-104.578950, 29.786842}, {-104.578234, 29.787779}, {-104.579926, 29.788881}, {-104.579281, 29.789891}, {-104.579874, 29.790787}, {-104.581744, 29.791757}, {-104.582463, 29.792501}, + {-104.582270, 29.793356}, {-104.581402, 29.793805}, {-104.581506, 29.795112}, {-104.585193, 29.796232}, {-104.587602, 29.796121}, {-104.589000, 29.795662}, {-104.589352, 29.795871}, {-104.589586, 29.796915}, {-104.590915, 29.798115}, {-104.590470, 29.799673}, {-104.588664, 29.799783}, {-104.587503, 29.800190}, {-104.586443, 29.800871}, {-104.586014, 29.802035}, {-104.586320, 29.803165}, {-104.589270, 29.806156}, + {-104.590138, 29.808108}, {-104.591347, 29.809809}, {-104.592550, 29.810453}, {-104.593060, 29.809770}, {-104.593978, 29.809437}, {-104.593950, 29.808213}, {-104.593360, 29.808173}, {-104.592480, 29.808931}, {-104.591642, 29.808477}, {-104.591645, 29.808103}, {-104.592264, 29.807712}, {-104.591886, 29.806888}, {-104.592403, 29.806178}, {-104.593412, 29.806012}, {-104.593884, 29.805318}, {-104.594928, 29.805331}, + {-104.595638, 29.806324}, {-104.596300, 29.808200}, {-104.599193, 29.811392}, {-104.599950, 29.813033}, {-104.600436, 29.815133}, {-104.601168, 29.816183}, {-104.603324, 29.817322}, {-104.603642, 29.817160}, {-104.603957, 29.816063}, {-104.605076, 29.815998}, {-104.606945, 29.817099}, {-104.607245, 29.819365}, {-104.607755, 29.819384}, {-104.608755, 29.818584}, {-104.609642, 29.818851}, {-104.610082, 29.819359}, + {-104.610414, 29.820582}, {-104.609575, 29.822327}, {-104.609526, 29.823813}, {-104.608423, 29.827001}, {-104.608486, 29.827949}, {-104.609702, 29.828363}, {-104.609379, 29.827427}, {-104.610032, 29.827056}, {-104.610440, 29.827502}, {-104.612842, 29.827340}, {-104.613844, 29.828004}, {-104.614031, 29.829142}, {-104.614775, 29.829799}, {-104.617616, 29.829424}, {-104.619637, 29.830217}, {-104.620256, 29.833082}, + {-104.615271, 29.833445}, {-104.613939, 29.833935}, {-104.610499, 29.836275}, {-104.610706, 29.837832}, {-104.609956, 29.838222}, {-104.610492, 29.838720}, {-104.610193, 29.839216}, {-104.609484, 29.839144}, {-104.609445, 29.839754}, {-104.610968, 29.841202}, {-104.610746, 29.842430}, {-104.612345, 29.842497}, {-104.613609, 29.844196}, {-104.614019, 29.845087}, {-104.613890, 29.846109}, {-104.615371, 29.846569}, + {-104.616120, 29.846393}, {-104.617796, 29.844733}, {-104.619110, 29.844502}, {-104.619579, 29.843648}, {-104.619028, 29.842791}, {-104.619488, 29.841722}, {-104.620173, 29.842220}, {-104.620093, 29.842853}, {-104.620523, 29.843238}, {-104.621098, 29.842894}, {-104.621218, 29.842064}, {-104.621912, 29.841843}, {-104.624750, 29.842783}, {-104.624333, 29.844979}, {-104.623579, 29.845712}, {-104.622017, 29.846431}, + {-104.621325, 29.847527}, {-104.621869, 29.848496}, {-104.621585, 29.849565}, {-104.622623, 29.849866}, {-104.623102, 29.850822}, {-104.624930, 29.851243}, {-104.626844, 29.850964}, {-104.629271, 29.852201}, {-104.630138, 29.853931}, {-104.630519, 29.856397}, {-104.629948, 29.860998}, {-104.630125, 29.864520}, {-104.631303, 29.865438}, {-104.631650, 29.867752}, {-104.633421, 29.870758}, {-104.634347, 29.871464}, + {-104.637039, 29.872503}, {-104.637239, 29.873964}, {-104.639493, 29.873539}, {-104.641084, 29.873821}, {-104.641841, 29.874543}, {-104.642003, 29.875665}, {-104.642528, 29.876399}, {-104.646112, 29.878542}, {-104.646270, 29.879077}, {-104.645675, 29.881171}, {-104.646520, 29.883537}, {-104.647182, 29.883578}, {-104.647673, 29.884257}, {-104.650740, 29.886444}, {-104.650941, 29.887661}, {-104.652639, 29.888937}, + {-104.652446, 29.889491}, {-104.651105, 29.890318}, {-104.650560, 29.891127}, {-104.651662, 29.892384}, {-104.653333, 29.892295}, {-104.653348, 29.890769}, {-104.655852, 29.889211}, {-104.656784, 29.889372}, {-104.658257, 29.890798}, {-104.658551, 29.892179}, {-104.657836, 29.895297}, {-104.659048, 29.898680}, {-104.659002, 29.899087}, {-104.658262, 29.899702}, {-104.659008, 29.901191}, {-104.658079, 29.902104}, + {-104.656153, 29.902159}, {-104.655157, 29.902808}, {-104.654783, 29.903761}, {-104.655086, 29.905398}, {-104.656549, 29.906080}, {-104.657501, 29.905909}, {-104.659202, 29.904960}, {-104.662299, 29.905388}, {-104.662618, 29.905168}, {-104.662671, 29.904540}, {-104.661686, 29.903123}, {-104.661824, 29.902188}, {-104.663986, 29.901114}, {-104.665589, 29.901708}, {-104.666094, 29.902404}, {-104.666197, 29.903257}, + {-104.665893, 29.905351}, {-104.666118, 29.906731}, {-104.665542, 29.908271}, {-104.665606, 29.908879}, {-104.666622, 29.909276}, {-104.666968, 29.910049}, {-104.668569, 29.911245}, {-104.670796, 29.911865}, {-104.673504, 29.910809}, {-104.674530, 29.910830}, {-104.677167, 29.913035}, {-104.677293, 29.914083}, {-104.676968, 29.914846}, {-104.676406, 29.914907}, {-104.675861, 29.915537}, {-104.677436, 29.917370}, + {-104.677208, 29.917903}, {-104.677455, 29.918703}, {-104.679236, 29.920183}, {-104.679921, 29.921176}, {-104.679741, 29.924679}, {-104.678798, 29.925649}, {-104.678501, 29.926622}, {-104.679851, 29.926906}, {-104.679982, 29.927777}, {-104.680535, 29.928281}, {-104.682429, 29.928350}, {-104.683272, 29.929107}, {-104.682877, 29.930574}, {-104.683298, 29.931559}, {-104.682640, 29.931766}, {-104.681719, 29.930505}, + {-104.680472, 29.930404}, {-104.679467, 29.931732}, {-104.679300, 29.932879}, {-104.679863, 29.933686}, {-104.681746, 29.934203}, {-104.682628, 29.935381}, {-104.682548, 29.936038}, {-104.681275, 29.937962}, {-104.682345, 29.939174}, {-104.682318, 29.939691}, {-104.681181, 29.940210}, {-104.680465, 29.939692}, {-104.680231, 29.938664}, {-104.678949, 29.938131}, {-104.678339, 29.938619}, {-104.677790, 29.940432}, + {-104.678631, 29.941781}, {-104.678044, 29.942755}, {-104.677971, 29.943602}, {-104.680783, 29.945732}, {-104.680364, 29.946471}, {-104.680735, 29.947326}, {-104.679380, 29.947618}, {-104.678890, 29.948145}, {-104.678668, 29.949709}, {-104.677110, 29.950300}, {-104.677232, 29.950740}, {-104.678226, 29.951117}, {-104.678168, 29.951691}, {-104.676397, 29.952033}, {-104.675152, 29.950759}, {-104.674288, 29.950795}, + {-104.674715, 29.952742}, {-104.673502, 29.953999}, {-104.673180, 29.954969}, {-104.673435, 29.956001}, {-104.674174, 29.956734}, {-104.676964, 29.956056}, {-104.679036, 29.956022}, {-104.680058, 29.955414}, {-104.680930, 29.955692}, {-104.680170, 29.956648}, {-104.680729, 29.957124}, {-104.681781, 29.957100}, {-104.683747, 29.956059}, {-104.685049, 29.956335}, {-104.685253, 29.956731}, {-104.683553, 29.960734}, + {-104.683086, 29.963885}, {-104.683237, 29.966586}, {-104.682134, 29.967585}, {-104.682213, 29.968392}, {-104.681853, 29.969067}, {-104.682056, 29.969512}, {-104.683163, 29.969315}, {-104.683956, 29.970212}, {-104.685326, 29.970293}, {-104.685503, 29.970843}, {-104.684345, 29.971673}, {-104.683514, 29.971316}, {-104.682387, 29.971850}, {-104.681996, 29.973346}, {-104.680106, 29.974224}, {-104.679518, 29.975307}, + {-104.680152, 29.977003}, {-104.680663, 29.977201}, {-104.682650, 29.976978}, {-104.685334, 29.974724}, {-104.687638, 29.973922}, {-104.691737, 29.973986}, {-104.692012, 29.975022}, {-104.693127, 29.975530}, {-104.693080, 29.975854}, {-104.691528, 29.976791}, {-104.690483, 29.978431}, {-104.690406, 29.979203}, {-104.688353, 29.980050}, {-104.688826, 29.982133}, {-104.688197, 29.983773}, {-104.688272, 29.984721}, + {-104.687919, 29.985015}, {-104.686393, 29.985169}, {-104.685736, 29.987142}, {-104.685670, 29.990451}, {-104.690314, 29.990846}, {-104.691552, 29.991300}, {-104.692787, 29.992536}, {-104.693182, 29.993525}, {-104.692864, 29.994000}, {-104.689087, 29.993931}, {-104.688139, 29.994573}, {-104.687533, 29.995673}, {-104.687717, 29.997598}, {-104.688481, 29.998768}, {-104.688639, 29.999662}, {-104.689508, 29.999868}, + {-104.689825, 30.000854}, {-104.688798, 30.001748}, {-104.688798, 30.002229}, {-104.690088, 30.003398}, {-104.690615, 30.003398}, {-104.692063, 30.002183}, {-104.691194, 30.001106}, {-104.691906, 30.000781}, {-104.693637, 30.001376}, {-104.694104, 30.002516}, {-104.693364, 30.004294}, {-104.693207, 30.005826}, {-104.693851, 30.006984}, {-104.693694, 30.007740}, {-104.693096, 30.008172}, {-104.692198, 30.008276}, + {-104.691922, 30.008792}, {-104.690475, 30.008229}, {-104.690724, 30.009571}, {-104.689564, 30.010455}, {-104.689652, 30.011343}, {-104.690277, 30.012150}, {-104.690463, 30.013497}, {-104.689581, 30.014457}, {-104.689549, 30.015105}, {-104.692796, 30.017808}, {-104.693254, 30.019128}, {-104.694126, 30.019433}, {-104.697604, 30.019443}, {-104.700653, 30.021131}, {-104.702066, 30.021230}, {-104.703818, 30.023417}, + {-104.703899, 30.025808}, {-104.702203, 30.027701}, {-104.702493, 30.028990}, {-104.702049, 30.030230}, {-104.702112, 30.031028}, {-104.700331, 30.034039}, {-104.700900, 30.035643}, {-104.699412, 30.036016}, {-104.697900, 30.036994}, {-104.696046, 30.037125}, {-104.695675, 30.037664}, {-104.696748, 30.039006}, {-104.697681, 30.041242}, {-104.698457, 30.041994}, {-104.699733, 30.042532}, {-104.705111, 30.048623}, + {-104.706581, 30.048842}, {-104.707264, 30.049867}, {-104.705247, 30.053781}, {-104.703629, 30.055055}, {-104.704791, 30.058325}, {-104.703821, 30.059351}, {-104.702468, 30.059790}, {-104.702213, 30.060190}, {-104.703553, 30.062963}, {-104.703662, 30.064196}, {-104.702169, 30.064972}, {-104.699495, 30.064773}, {-104.697541, 30.065243}, {-104.695477, 30.066676}, {-104.693915, 30.068721}, {-104.693849, 30.069458}, + {-104.691027, 30.070496}, {-104.693223, 30.071694}, {-104.693612, 30.073219}, {-104.693159, 30.074015}, {-104.690507, 30.074150}, {-104.687986, 30.073771}, {-104.687368, 30.075685}, {-104.686600, 30.076581}, {-104.687072, 30.076782}, {-104.687963, 30.075871}, {-104.689545, 30.075649}, {-104.690404, 30.076135}, {-104.690772, 30.076727}, {-104.690772, 30.078015}, {-104.690078, 30.078929}, {-104.687303, 30.080666}, + {-104.685829, 30.082063}, {-104.685857, 30.082581}, {-104.686634, 30.083250}, {-104.686676, 30.083950}, {-104.685930, 30.084859}, {-104.684965, 30.085301}, {-104.686210, 30.085970}, {-104.686422, 30.087177}, {-104.687300, 30.087500}, {-104.687801, 30.087469}, {-104.687940, 30.086843}, {-104.686971, 30.086640}, {-104.687124, 30.086014}, {-104.687977, 30.085911}, {-104.688656, 30.085336}, {-104.689223, 30.085384}, + {-104.689425, 30.085869}, {-104.688951, 30.087487}, {-104.689868, 30.089342}, {-104.689537, 30.090200}, {-104.687853, 30.090407}, {-104.687066, 30.090835}, {-104.685535, 30.093209}, {-104.685876, 30.095240}, {-104.685033, 30.097694}, {-104.685068, 30.099849}, {-104.685552, 30.099988}, {-104.686535, 30.099517}, {-104.686575, 30.097946}, {-104.687282, 30.097019}, {-104.688057, 30.096869}, {-104.687921, 30.095657}, + {-104.688951, 30.095259}, {-104.690460, 30.096278}, {-104.691531, 30.095901}, {-104.692153, 30.096084}, {-104.692521, 30.098062}, {-104.692392, 30.099378}, {-104.694404, 30.101979}, {-104.692896, 30.104023}, {-104.692497, 30.106359}, {-104.691798, 30.107200}, {-104.691742, 30.108725}, {-104.691285, 30.108783}, {-104.689954, 30.107808}, {-104.690644, 30.106990}, {-104.690254, 30.106308}, {-104.687131, 30.105867}, + {-104.686358, 30.106373}, {-104.685632, 30.107855}, {-104.685468, 30.110032}, {-104.685703, 30.110590}, {-104.686859, 30.111036}, {-104.689835, 30.111473}, {-104.691574, 30.111222}, {-104.691867, 30.112256}, {-104.691382, 30.113049}, {-104.691801, 30.113512}, {-104.692093, 30.114991}, {-104.691302, 30.116506}, {-104.690817, 30.116510}, {-104.690484, 30.115756}, {-104.689859, 30.115980}, {-104.689947, 30.117021}, + {-104.690985, 30.117888}, {-104.692172, 30.117740}, {-104.693027, 30.118353}, {-104.693457, 30.118189}, {-104.693536, 30.117098}, {-104.693825, 30.116884}, {-104.694319, 30.116986}, {-104.695078, 30.117811}, {-104.695157, 30.118940}, {-104.694264, 30.119981}, {-104.693902, 30.121590}, {-104.694824, 30.124560}, {-104.695683, 30.125613}, {-104.695217, 30.130053}, {-104.695312, 30.132282}, {-104.696706, 30.134457}, + {-104.696146, 30.135128}, {-104.695366, 30.134410}, {-104.694603, 30.134223}, {-104.694253, 30.134688}, {-104.694343, 30.135752}, {-104.693033, 30.135451}, {-104.692428, 30.136848}, {-104.692399, 30.138406}, {-104.691900, 30.138852}, {-104.690969, 30.139069}, {-104.689619, 30.137622}, {-104.689069, 30.137724}, {-104.689043, 30.139860}, {-104.689609, 30.140372}, {-104.689628, 30.140940}, {-104.688323, 30.141788}, + {-104.686287, 30.141457}, {-104.686538, 30.143297}, {-104.685728, 30.143707}, {-104.685312, 30.144733}, {-104.685352, 30.145280}, {-104.685930, 30.145700}, {-104.687674, 30.145298}, {-104.689326, 30.145635}, {-104.689607, 30.146571}, {-104.688505, 30.146868}, {-104.688248, 30.147371}, {-104.688633, 30.147996}, {-104.689836, 30.148624}, {-104.690113, 30.149226}, {-104.689909, 30.149860}, {-104.687442, 30.150878}, + {-104.686885, 30.152139}, {-104.687044, 30.152743}, {-104.689051, 30.153421}, {-104.689563, 30.154269}, {-104.687529, 30.159552}, {-104.687387, 30.161571}, {-104.687848, 30.164180}, {-104.688531, 30.165666}, {-104.688376, 30.166822}, {-104.687273, 30.168283}, {-104.686782, 30.169618}, {-104.686725, 30.172403}, {-104.686332, 30.173281}, {-104.687149, 30.175570}, {-104.687978, 30.176390}, {-104.687076, 30.179342}, + {-104.688741, 30.180910}, {-104.691512, 30.184627}, {-104.692691, 30.187092}, {-104.693147, 30.189260}, {-104.695912, 30.190159}, {-104.697138, 30.191568}, {-104.697196, 30.192233}, {-104.696117, 30.195570}, {-104.697041, 30.197800}, {-104.699000, 30.198757}, {-104.699808, 30.198782}, {-104.700404, 30.203591}, {-104.700327, 30.205303}, {-104.701928, 30.206383}, {-104.704122, 30.206806}, {-104.705066, 30.208381}, + {-104.703766, 30.210445}, {-104.703888, 30.210989}, {-104.702471, 30.211650}, {-104.702475, 30.212779}, {-104.705260, 30.216933}, {-104.707949, 30.218801}, {-104.709415, 30.220772}, {-104.710164, 30.221078}, {-104.711269, 30.223083}, {-104.711092, 30.224338}, {-104.707900, 30.226231}, {-104.706078, 30.233835}, {-104.706255, 30.234812}, {-104.706699, 30.235258}, {-104.708285, 30.235492}, {-104.713195, 30.237632}, + {-104.714157, 30.240363}, {-104.714921, 30.241293}, {-104.716303, 30.241892}, {-104.717348, 30.243786}, {-104.718554, 30.244958}, {-104.718950, 30.247086}, {-104.721274, 30.247671}, {-104.723999, 30.249619}, {-104.724509, 30.250414}, {-104.724474, 30.251231}, {-104.723877, 30.251904}, {-104.724146, 30.253046}, {-104.727516, 30.254438}, {-104.729740, 30.258497}, {-104.732117, 30.259882}, {-104.733722, 30.261399}, + {-104.734788, 30.261693}, {-104.736837, 30.261635}, {-104.737501, 30.261285}, {-104.736826, 30.259648}, {-104.737511, 30.258420}, {-104.736523, 30.257917}, {-104.735988, 30.257249}, {-104.736011, 30.256602}, {-104.736645, 30.255905}, {-104.737871, 30.256245}, {-104.739637, 30.258037}, {-104.740392, 30.259582}, {-104.745505, 30.260452}, {-104.746872, 30.261208}, {-104.749012, 30.261070}, {-104.750712, 30.262229}, + {-104.750878, 30.263090}, {-104.751632, 30.263853}, {-104.751586, 30.264419}, {-104.749580, 30.266972}, {-104.748607, 30.271660}, {-104.747858, 30.273159}, {-104.748593, 30.274407}, {-104.748957, 30.274279}, {-104.748814, 30.272994}, {-104.750094, 30.272366}, {-104.750768, 30.271543}, {-104.751349, 30.271718}, {-104.751472, 30.272117}, {-104.751080, 30.272697}, {-104.751097, 30.273881}, {-104.752057, 30.273937}, + {-104.752746, 30.273406}, {-104.753229, 30.273555}, {-104.753862, 30.272890}, {-104.754111, 30.271807}, {-104.754725, 30.271741}, {-104.756249, 30.272580}, {-104.756241, 30.273647}, {-104.756780, 30.274316}, {-104.757184, 30.274343}, {-104.757994, 30.273879}, {-104.756915, 30.272773}, {-104.757194, 30.271876}, {-104.758479, 30.271393}, {-104.758990, 30.271732}, {-104.760166, 30.271666}, {-104.760534, 30.271983}, + {-104.760456, 30.272794}, {-104.759673, 30.273870}, {-104.760076, 30.274359}, {-104.760607, 30.274610}, {-104.762509, 30.274278}, {-104.763176, 30.274836}, {-104.762980, 30.275287}, {-104.761332, 30.276319}, {-104.760419, 30.277325}, {-104.758275, 30.281158}, {-104.757893, 30.283370}, {-104.758139, 30.286412}, {-104.759209, 30.288255}, {-104.759580, 30.293404}, {-104.760939, 30.298114}, {-104.761288, 30.300611}, + {-104.763018, 30.301827}, {-104.764088, 30.303082}, {-104.764888, 30.303387}, {-104.766158, 30.303077}, {-104.767091, 30.303713}, {-104.766690, 30.306000}, {-104.768884, 30.304446}, {-104.770424, 30.303688}, {-104.771502, 30.303578}, {-104.772645, 30.302691}, {-104.773466, 30.302786}, {-104.775081, 30.304163}, {-104.775908, 30.304471}, {-104.777191, 30.304273}, {-104.778409, 30.303371}, {-104.779318, 30.303269}, + {-104.779633, 30.303756}, {-104.779631, 30.305731}, {-104.776850, 30.307384}, {-104.776427, 30.308900}, {-104.776807, 30.310300}, {-104.777599, 30.311104}, {-104.778740, 30.311628}, {-104.779182, 30.312873}, {-104.782824, 30.316051}, {-104.783921, 30.316475}, {-104.786221, 30.316314}, {-104.788494, 30.317893}, {-104.789306, 30.318979}, {-104.788802, 30.321843}, {-104.790978, 30.321905}, {-104.791485, 30.322286}, + {-104.791107, 30.324068}, {-104.791399, 30.325518}, {-104.794797, 30.326960}, {-104.797444, 30.330437}, {-104.802987, 30.331086}, {-104.805748, 30.332763}, {-104.807917, 30.333314}, {-104.809409, 30.333431}, {-104.810299, 30.332838}, {-104.811578, 30.332811}, {-104.811854, 30.333703}, {-104.810933, 30.335499}, {-104.810876, 30.337853}, {-104.810096, 30.339498}, {-104.809995, 30.341511}, {-104.814041, 30.343893}, + {-104.815710, 30.343981}, {-104.818756, 30.347527}, {-104.819104, 30.347395}, {-104.819196, 30.346933}, {-104.819868, 30.346860}, {-104.821201, 30.348907}, {-104.822291, 30.349663}, {-104.822581, 30.350624}, {-104.822595, 30.351267}, {-104.821755, 30.351861}, {-104.818401, 30.351865}, {-104.815685, 30.352751}, {-104.814131, 30.353691}, {-104.814215, 30.357416}, {-104.813879, 30.359896}, {-104.811655, 30.361283}, + {-104.810993, 30.362472}, {-104.811355, 30.366701}, {-104.812885, 30.366905}, {-104.814665, 30.366335}, {-104.815301, 30.365830}, {-104.816171, 30.365864}, {-104.819155, 30.367769}, {-104.819674, 30.368722}, {-104.819231, 30.370657}, {-104.818596, 30.371077}, {-104.818205, 30.372027}, {-104.816784, 30.372724}, {-104.816160, 30.373750}, {-104.816423, 30.375187}, {-104.817602, 30.375746}, {-104.820057, 30.374642}, + {-104.825694, 30.373910}, {-104.827919, 30.373049}, {-104.829478, 30.373262}, {-104.830564, 30.372853}, {-104.834318, 30.373604}, {-104.837438, 30.373847}, {-104.838646, 30.375854}, {-104.839384, 30.376407}, {-104.841205, 30.377081}, {-104.842630, 30.378096}, {-104.845613, 30.379209}, {-104.846324, 30.380176}, {-104.846584, 30.381209}, {-104.848505, 30.383579}, {-104.850247, 30.384366}, {-104.853814, 30.385321}, + {-104.859296, 30.390253}, {-104.859397, 30.391125}, {-104.857350, 30.393522}, {-104.856046, 30.395695}, {-104.854813, 30.399566}, {-104.854281, 30.400366}, {-104.854278, 30.400940}, {-104.855281, 30.401380}, {-104.856033, 30.403319}, {-104.856443, 30.407103}, {-104.857198, 30.407999}, {-104.857182, 30.408871}, {-104.856695, 30.409839}, {-104.855546, 30.410523}, {-104.855113, 30.411183}, {-104.853947, 30.411761}, + {-104.852321, 30.412043}, {-104.852031, 30.413066}, {-104.851258, 30.413829}, {-104.847679, 30.413805}, {-104.848166, 30.416432}, {-104.848971, 30.417986}, {-104.847831, 30.419775}, {-104.848333, 30.421012}, {-104.849422, 30.421549}, {-104.849913, 30.421121}, {-104.849721, 30.419953}, {-104.850063, 30.419262}, {-104.851533, 30.418651}, {-104.852151, 30.418739}, {-104.852742, 30.419231}, {-104.852723, 30.420066}, + {-104.853067, 30.420512}, {-104.855760, 30.420967}, {-104.856626, 30.421517}, {-104.856879, 30.422226}, {-104.856470, 30.423822}, {-104.856886, 30.424475}, {-104.859211, 30.425680}, {-104.859382, 30.426045}, {-104.860354, 30.425597}, {-104.861305, 30.426121}, {-104.861535, 30.426927}, {-104.860826, 30.427978}, {-104.860485, 30.429337}, {-104.859875, 30.429689}, {-104.859207, 30.430908}, {-104.858394, 30.431409}, + {-104.858149, 30.432186}, {-104.858167, 30.433213}, {-104.858723, 30.433783}, {-104.859033, 30.434923}, {-104.858171, 30.436844}, {-104.858333, 30.437636}, {-104.860220, 30.439913}, {-104.862656, 30.440209}, {-104.864511, 30.441276}, {-104.866687, 30.440902}, {-104.867781, 30.440974}, {-104.868047, 30.441407}, {-104.867856, 30.442630}, {-104.867087, 30.444037}, {-104.865458, 30.444907}, {-104.865850, 30.446495}, + {-104.864444, 30.450549}, {-104.865410, 30.452555}, {-104.868057, 30.453623}, {-104.868382, 30.454158}, {-104.869636, 30.459195}, {-104.869465, 30.459970}, {-104.868283, 30.460526}, {-104.868534, 30.463481}, {-104.867489, 30.464207}, {-104.866596, 30.464061}, {-104.866063, 30.464384}, {-104.865880, 30.466890}, {-104.866493, 30.469394}, {-104.869383, 30.474117}, {-104.869082, 30.475864}, {-104.869153, 30.478738}, + {-104.869660, 30.479414}, {-104.871393, 30.479697}, {-104.871667, 30.480345}, {-104.869504, 30.484641}, {-104.868749, 30.484993}, {-104.867548, 30.484671}, {-104.867174, 30.485071}, {-104.867383, 30.488332}, {-104.866317, 30.492435}, {-104.866632, 30.494499}, {-104.867182, 30.494825}, {-104.870320, 30.494088}, {-104.873921, 30.495258}, {-104.872911, 30.497758}, {-104.872224, 30.501502}, {-104.872364, 30.502346}, + {-104.873520, 30.504545}, {-104.872869, 30.507563}, {-104.872213, 30.508481}, {-104.871955, 30.511090}, {-104.873047, 30.511816}, {-104.873190, 30.513304}, {-104.873719, 30.513737}, {-104.874241, 30.513459}, {-104.874855, 30.512153}, {-104.876754, 30.511096}, {-104.877041, 30.511204}, {-104.879147, 30.515358}, {-104.879258, 30.517813}, {-104.879965, 30.517751}, {-104.881004, 30.516900}, {-104.882885, 30.516769}, + {-104.883680, 30.516961}, {-104.885702, 30.518270}, {-104.885690, 30.518798}, {-104.884599, 30.520331}, {-104.883053, 30.520583}, {-104.882356, 30.521032}, {-104.881602, 30.520557}, {-104.881023, 30.520976}, {-104.881180, 30.521629}, {-104.882117, 30.522378}, {-104.883359, 30.522957}, {-104.884180, 30.522791}, {-104.883873, 30.523815}, {-104.885320, 30.523998}, {-104.885884, 30.524523}, {-104.884505, 30.525846}, + {-104.883715, 30.526135}, {-104.883238, 30.524598}, {-104.881409, 30.523955}, {-104.881121, 30.524368}, {-104.880559, 30.528925}, {-104.880648, 30.529821}, {-104.882359, 30.532255}, {-104.884531, 30.533239}, {-104.886354, 30.533557}, {-104.888836, 30.534775}, {-104.889271, 30.535378}, {-104.889678, 30.537067}, {-104.889636, 30.538385}, {-104.890786, 30.541010}, {-104.888889, 30.542162}, {-104.887685, 30.544089}, + {-104.887697, 30.546727}, {-104.888143, 30.547098}, {-104.888646, 30.547057}, {-104.889624, 30.545977}, {-104.890254, 30.546037}, {-104.890535, 30.546799}, {-104.889819, 30.549157}, {-104.891711, 30.550269}, {-104.893279, 30.550063}, {-104.894061, 30.550918}, {-104.893794, 30.551917}, {-104.892786, 30.551140}, {-104.892374, 30.551293}, {-104.892277, 30.551858}, {-104.892898, 30.552925}, {-104.896244, 30.552983}, + {-104.897946, 30.554548}, {-104.897817, 30.556332}, {-104.896965, 30.557686}, {-104.896907, 30.559877}, {-104.896322, 30.560582}, {-104.896433, 30.561088}, {-104.895838, 30.562272}, {-104.896818, 30.565042}, {-104.898929, 30.568362}, {-104.898835, 30.570473}, {-104.901489, 30.572203}, {-104.902232, 30.573627}, {-104.904039, 30.575020}, {-104.905226, 30.576496}, {-104.906888, 30.577164}, {-104.907774, 30.582307}, + {-104.909552, 30.585143}, {-104.912705, 30.586050}, {-104.915520, 30.585837}, {-104.917643, 30.585093}, {-104.918762, 30.585542}, {-104.919115, 30.586537}, {-104.919931, 30.587275}, {-104.921378, 30.590032}, {-104.920205, 30.590486}, {-104.919447, 30.593572}, {-104.918741, 30.594815}, {-104.919320, 30.596471}, {-104.918550, 30.597038}, {-104.918798, 30.597791}, {-104.920025, 30.597434}, {-104.921777, 30.598465}, + {-104.922096, 30.602245}, {-104.922677, 30.603693}, {-104.924704, 30.605112}, {-104.926521, 30.605212}, {-104.928300, 30.603035}, {-104.929329, 30.599649}, {-104.929927, 30.599202}, {-104.930975, 30.599066}, {-104.934584, 30.600013}, {-104.935129, 30.600807}, {-104.936260, 30.601557}, {-104.936852, 30.602660}, {-104.938415, 30.603633}, {-104.940313, 30.602752}, {-104.941118, 30.602867}, {-104.941351, 30.603764}, + {-104.941768, 30.604076}, {-104.942878, 30.603849}, {-104.943427, 30.603143}, {-104.944093, 30.602895}, {-104.945394, 30.603077}, {-104.945840, 30.604143}, {-104.946747, 30.604664}, {-104.947952, 30.604712}, {-104.950076, 30.603973}, {-104.950821, 30.604051}, {-104.953806, 30.606417}, {-104.953940, 30.608000}, {-104.955030, 30.609357}, {-104.956764, 30.609276}, {-104.957390, 30.609881}, {-104.959831, 30.610595}, + {-104.963816, 30.608595}, {-104.966902, 30.607895}, {-104.971664, 30.610014}, {-104.973271, 30.611702}, {-104.974185, 30.613234}, {-104.975508, 30.614245}, {-104.974360, 30.616235}, {-104.974403, 30.616780}, {-104.974947, 30.617253}, {-104.976533, 30.617534}, {-104.977664, 30.618130}, {-104.978765, 30.621070}, {-104.980213, 30.621952}, {-104.979616, 30.624605}, {-104.980778, 30.625520}, {-104.979521, 30.626706}, + {-104.979496, 30.628598}, {-104.980469, 30.629833}, {-104.982141, 30.630275}, {-104.982632, 30.631701}, {-104.982734, 30.633874}, {-104.983900, 30.635025}, {-104.983880, 30.637111}, {-104.984513, 30.641342}, {-104.983892, 30.642798}, {-104.984561, 30.644245}, {-104.984680, 30.646726}, {-104.985514, 30.649899}, {-104.985181, 30.653127}, {-104.985850, 30.654575}, {-104.985658, 30.655268}, {-104.984716, 30.655636}, + {-104.985076, 30.656795}, {-104.986176, 30.658076}, {-104.985393, 30.658332}, {-104.985537, 30.659158}, {-104.986557, 30.659547}, {-104.985945, 30.661233}, {-104.986245, 30.662150}, {-104.986110, 30.662767}, {-104.990294, 30.663326}, {-104.991423, 30.665132}, {-104.993052, 30.664914}, {-104.993485, 30.665960}, {-104.993120, 30.667018}, {-104.993846, 30.668520}, {-104.995681, 30.668714}, {-104.996552, 30.669158}, + {-104.997758, 30.668994}, {-104.999555, 30.671465}, {-105.001542, 30.672704}, {-105.001270, 30.675199}, {-105.002539, 30.680087}, {-105.002048, 30.680426}, {-105.002043, 30.680878}, {-105.004508, 30.683090}, {-105.005628, 30.683076}, {-105.006147, 30.685476}, {-105.006579, 30.686060}, {-105.007796, 30.686221}, {-105.010423, 30.684584}, {-105.012296, 30.683832}, {-105.013199, 30.682562}, {-105.016008, 30.682878}, + {-105.016594, 30.683509}, {-105.016759, 30.684875}, {-105.017878, 30.685086}, {-105.019829, 30.683931}, {-105.021270, 30.681509}, {-105.025218, 30.681287}, {-105.026247, 30.680722}, {-105.026618, 30.679428}, {-105.027325, 30.678881}, {-105.028127, 30.680004}, {-105.028784, 30.680264}, {-105.030388, 30.680143}, {-105.031962, 30.680990}, {-105.033953, 30.683335}, {-105.036089, 30.686658}, {-105.038318, 30.688082}, + {-105.040819, 30.688935}, {-105.041274, 30.688874}, {-105.042063, 30.687663}, {-105.042709, 30.687353}, {-105.044555, 30.687810}, {-105.045357, 30.688957}, {-105.047161, 30.688074}, {-105.047076, 30.687433}, {-105.044781, 30.684964}, {-105.044428, 30.684183}, {-105.044402, 30.683205}, {-105.045316, 30.682668}, {-105.045784, 30.681876}, {-105.045704, 30.681029}, {-105.045220, 30.680559}, {-105.045681, 30.679917}, + {-105.048321, 30.680285}, {-105.048449, 30.680728}, {-105.047537, 30.681704}, {-105.048406, 30.682458}, {-105.050809, 30.682174}, {-105.051972, 30.681569}, {-105.055307, 30.682156}, {-105.055392, 30.682753}, {-105.054421, 30.684180}, {-105.054269, 30.685104}, {-105.055642, 30.685544}, {-105.056155, 30.687206}, {-105.056698, 30.687864}, {-105.059040, 30.688015}, {-105.060011, 30.687285}, {-105.059277, 30.686812}, + {-105.059289, 30.686452}, {-105.061247, 30.685738}, {-105.061762, 30.685845}, {-105.062486, 30.686532}, {-105.063388, 30.688938}, {-105.062904, 30.691385}, {-105.061685, 30.694660}, {-105.061820, 30.695953}, {-105.062657, 30.697015}, {-105.062552, 30.698502}, {-105.065076, 30.698372}, {-105.066885, 30.699256}, {-105.068085, 30.701572}, {-105.068232, 30.703702}, {-105.068730, 30.703668}, {-105.069131, 30.702549}, + {-105.070062, 30.701885}, {-105.073349, 30.701527}, {-105.075332, 30.701893}, {-105.076640, 30.701239}, {-105.077401, 30.700012}, {-105.078560, 30.699708}, {-105.080941, 30.699846}, {-105.081129, 30.700336}, {-105.080786, 30.701441}, {-105.082065, 30.701301}, {-105.082578, 30.701678}, {-105.082592, 30.702088}, {-105.081864, 30.702981}, {-105.080104, 30.703315}, {-105.079383, 30.704343}, {-105.079461, 30.705605}, + {-105.080462, 30.706389}, {-105.080227, 30.707724}, {-105.081028, 30.708471}, {-105.082405, 30.708648}, {-105.083349, 30.709970}, {-105.087463, 30.709877}, {-105.087892, 30.711437}, {-105.086976, 30.714451}, {-105.087614, 30.715488}, {-105.088955, 30.715897}, {-105.091063, 30.717177}, {-105.094911, 30.717197}, {-105.095310, 30.716058}, {-105.095778, 30.715891}, {-105.098179, 30.716479}, {-105.098675, 30.717477}, + {-105.098381, 30.720045}, {-105.099281, 30.720868}, {-105.099622, 30.721953}, {-105.100916, 30.722544}, {-105.103628, 30.722755}, {-105.105547, 30.723808}, {-105.105471, 30.724597}, {-105.103901, 30.725300}, {-105.103314, 30.726337}, {-105.103358, 30.727076}, {-105.103889, 30.727828}, {-105.103344, 30.728598}, {-105.103371, 30.729129}, {-105.105890, 30.730483}, {-105.106023, 30.731821}, {-105.106614, 30.732221}, + {-105.107443, 30.731633}, {-105.106889, 30.731098}, {-105.107097, 30.730150}, {-105.107988, 30.729908}, {-105.108805, 30.730087}, {-105.110180, 30.731295}, {-105.110409, 30.732116}, {-105.111679, 30.732868}, {-105.111825, 30.733261}, {-105.111676, 30.735716}, {-105.110836, 30.737681}, {-105.110985, 30.738563}, {-105.110018, 30.739108}, {-105.109810, 30.739564}, {-105.110220, 30.740248}, {-105.111562, 30.740104}, + {-105.112211, 30.740502}, {-105.111476, 30.742351}, {-105.110897, 30.742648}, {-105.110939, 30.743552}, {-105.113933, 30.745980}, {-105.116139, 30.744660}, {-105.115935, 30.743578}, {-105.116942, 30.743009}, {-105.119790, 30.743159}, {-105.120443, 30.743853}, {-105.120851, 30.745894}, {-105.119432, 30.747519}, {-105.119456, 30.748354}, {-105.118021, 30.749083}, {-105.118295, 30.749726}, {-105.119676, 30.749995}, + {-105.121242, 30.749563}, {-105.122877, 30.749545}, {-105.124278, 30.749227}, {-105.125399, 30.748101}, {-105.127659, 30.747845}, {-105.128329, 30.748161}, {-105.129060, 30.749992}, {-105.129598, 30.750376}, {-105.133366, 30.750734}, {-105.137217, 30.751737}, {-105.139592, 30.753023}, {-105.140727, 30.752214}, {-105.141294, 30.752676}, {-105.142175, 30.754696}, {-105.143771, 30.755205}, {-105.147155, 30.753648}, + {-105.148677, 30.752258}, {-105.150628, 30.752003}, {-105.151976, 30.751462}, {-105.153070, 30.751689}, {-105.156846, 30.753972}, {-105.158184, 30.752862}, {-105.161390, 30.752133}, {-105.161951, 30.753351}, {-105.161259, 30.754789}, {-105.161304, 30.756263}, {-105.160667, 30.757546}, {-105.158744, 30.759394}, {-105.157844, 30.761305}, {-105.157430, 30.765512}, {-105.157566, 30.766819}, {-105.156191, 30.769773}, + {-105.158912, 30.771014}, {-105.160487, 30.770360}, {-105.162815, 30.770719}, {-105.165184, 30.772897}, {-105.166479, 30.775539}, {-105.166464, 30.776441}, {-105.167082, 30.776594}, {-105.168853, 30.775823}, {-105.169896, 30.773017}, {-105.170723, 30.771728}, {-105.172504, 30.770530}, {-105.172451, 30.769936}, {-105.173992, 30.767531}, {-105.175202, 30.767353}, {-105.176601, 30.767975}, {-105.179536, 30.771369}, + {-105.180635, 30.774021}, {-105.182134, 30.776272}, {-105.184215, 30.776452}, {-105.184896, 30.776960}, {-105.185172, 30.780593}, {-105.184391, 30.781573}, {-105.185591, 30.782518}, {-105.185616, 30.783736}, {-105.185999, 30.784550}, {-105.190738, 30.785697}, {-105.191667, 30.787138}, {-105.192619, 30.789970}, {-105.194373, 30.791034}, {-105.195191, 30.792304}, {-105.195891, 30.792111}, {-105.196845, 30.790727}, + {-105.199101, 30.789016}, {-105.199792, 30.787731}, {-105.200607, 30.787365}, {-105.202198, 30.788004}, {-105.203680, 30.787930}, {-105.206550, 30.786346}, {-105.207078, 30.785576}, {-105.207017, 30.784320}, {-105.209091, 30.782655}, {-105.209989, 30.782650}, {-105.211687, 30.781766}, {-105.213462, 30.782201}, {-105.214126, 30.782631}, {-105.214319, 30.783350}, {-105.214851, 30.783726}, {-105.216744, 30.783385}, + {-105.216952, 30.783634}, {-105.216568, 30.784611}, {-105.218787, 30.785360}, {-105.219687, 30.786262}, {-105.220150, 30.787763}, {-105.220047, 30.788898}, {-105.218836, 30.790434}, {-105.218431, 30.792113}, {-105.217635, 30.793167}, {-105.217065, 30.793416}, {-105.215209, 30.792858}, {-105.212999, 30.793232}, {-105.211566, 30.794334}, {-105.211517, 30.795311}, {-105.210761, 30.796813}, {-105.209920, 30.797068}, + {-105.209598, 30.797628}, {-105.211160, 30.799626}, {-105.212249, 30.799798}, {-105.213288, 30.800806}, {-105.213985, 30.802183}, {-105.213885, 30.803838}, {-105.215017, 30.805893}, {-105.218285, 30.805759}, {-105.223303, 30.800872}, {-105.224445, 30.800741}, {-105.225767, 30.799570}, {-105.227443, 30.799191}, {-105.228019, 30.799272}, {-105.229016, 30.800162}, {-105.228392, 30.801393}, {-105.228391, 30.802867}, + {-105.230019, 30.803827}, {-105.230756, 30.803788}, {-105.232261, 30.802969}, {-105.233729, 30.803056}, {-105.234490, 30.802647}, {-105.238178, 30.803660}, {-105.239139, 30.802419}, {-105.241656, 30.802215}, {-105.242782, 30.801601}, {-105.244172, 30.799812}, {-105.246155, 30.800477}, {-105.247941, 30.800115}, {-105.249928, 30.799236}, {-105.252731, 30.795867}, {-105.253909, 30.795875}, {-105.255673, 30.795095}, + {-105.256013, 30.794402}, {-105.257160, 30.794354}, {-105.258820, 30.795489}, {-105.259982, 30.797396}, {-105.261232, 30.798497}, {-105.261930, 30.798648}, {-105.264148, 30.800335}, {-105.265533, 30.802636}, {-105.265805, 30.806495}, {-105.266835, 30.808481}, {-105.268768, 30.809309}, {-105.271694, 30.809382}, {-105.272760, 30.808274}, {-105.274229, 30.808167}, {-105.274663, 30.807550}, {-105.275799, 30.807003}, + {-105.276701, 30.808497}, {-105.277301, 30.811284}, {-105.279221, 30.812905}, {-105.279401, 30.815698}, {-105.282514, 30.818279}, {-105.285275, 30.818682}, {-105.286632, 30.817891}, {-105.287953, 30.817877}, {-105.288797, 30.819124}, {-105.289484, 30.821584}, {-105.289258, 30.823779}, {-105.289894, 30.824780}, {-105.291795, 30.826193}, {-105.294783, 30.826344}, {-105.299041, 30.825399}, {-105.300136, 30.824771}, + {-105.300984, 30.822504}, {-105.300988, 30.820615}, {-105.300534, 30.819190}, {-105.302683, 30.816133}, {-105.303547, 30.813665}, {-105.304863, 30.812438}, {-105.305462, 30.811421}, {-105.306159, 30.811320}, {-105.306147, 30.809838}, {-105.307588, 30.810032}, {-105.308862, 30.811089}, {-105.308780, 30.813730}, {-105.307869, 30.814732}, {-105.309288, 30.815647}, {-105.309070, 30.816706}, {-105.309363, 30.817040}, + {-105.309936, 30.816814}, {-105.310538, 30.816031}, {-105.311366, 30.814017}, {-105.311952, 30.813391}, {-105.314248, 30.812452}, {-105.314938, 30.811363}, {-105.316869, 30.810913}, {-105.317984, 30.811944}, {-105.319094, 30.812259}, {-105.318421, 30.813444}, {-105.318328, 30.814552}, {-105.317486, 30.816573}, {-105.317553, 30.817261}, {-105.315958, 30.818717}, {-105.316522, 30.820414}, {-105.317899, 30.821577}, + {-105.318896, 30.824046}, {-105.318924, 30.825056}, {-105.318010, 30.827175}, {-105.318179, 30.827816}, {-105.319453, 30.829084}, {-105.320047, 30.828804}, {-105.319946, 30.827789}, {-105.320678, 30.827372}, {-105.324592, 30.828303}, {-105.326850, 30.826084}, {-105.327043, 30.824705}, {-105.329297, 30.824975}, {-105.330194, 30.826134}, {-105.328309, 30.829066}, {-105.327762, 30.830310}, {-105.327825, 30.831087}, + {-105.328333, 30.831486}, {-105.332223, 30.832382}, {-105.333063, 30.833190}, {-105.334135, 30.833633}, {-105.335827, 30.833341}, {-105.337192, 30.833481}, {-105.337947, 30.838023}, {-105.339293, 30.840475}, {-105.341676, 30.840470}, {-105.342692, 30.840998}, {-105.345755, 30.840612}, {-105.347372, 30.839516}, {-105.347169, 30.838708}, {-105.347658, 30.838044}, {-105.348823, 30.837639}, {-105.351409, 30.835625}, + {-105.351964, 30.835544}, {-105.353153, 30.835927}, {-105.354442, 30.837781}, {-105.353523, 30.839569}, {-105.353057, 30.841278}, {-105.353184, 30.842021}, {-105.354447, 30.843339}, {-105.356401, 30.844328}, {-105.357774, 30.845433}, {-105.359720, 30.845942}, {-105.360408, 30.848584}, {-105.361592, 30.850545}, {-105.363976, 30.849701}, {-105.367638, 30.847693}, {-105.370497, 30.847500}, {-105.372073, 30.847612}, + {-105.373436, 30.848422}, {-105.374477, 30.848617}, {-105.375047, 30.849965}, {-105.376288, 30.848851}, {-105.377628, 30.848814}, {-105.376725, 30.850546}, {-105.377066, 30.851262}, {-105.379890, 30.850615}, {-105.382124, 30.851202}, {-105.382708, 30.851966}, {-105.383263, 30.852106}, {-105.383718, 30.851819}, {-105.384163, 30.850585}, {-105.384696, 30.850497}, {-105.385585, 30.851325}, {-105.386840, 30.853502}, + {-105.387322, 30.853734}, {-105.387840, 30.853638}, {-105.389229, 30.852255}, {-105.391036, 30.852289}, {-105.391952, 30.850594}, {-105.394018, 30.850913}, {-105.394514, 30.850234}, {-105.394051, 30.849358}, {-105.394620, 30.849021}, {-105.395889, 30.849295}, {-105.398151, 30.851277}, {-105.399837, 30.852077}, {-105.401635, 30.854266}, {-105.401639, 30.854658}, {-105.400802, 30.856314}, {-105.399270, 30.857737}, + {-105.395482, 30.859089}, {-105.394946, 30.859757}, {-105.394523, 30.861288}, {-105.395027, 30.862192}, {-105.394404, 30.863107}, {-105.395007, 30.863990}, {-105.395860, 30.863933}, {-105.395993, 30.864305}, {-105.393640, 30.866930}, {-105.393916, 30.868007}, {-105.394723, 30.868992}, {-105.394817, 30.869619}, {-105.394399, 30.870563}, {-105.394960, 30.871194}, {-105.394353, 30.871891}, {-105.392893, 30.872519}, + {-105.392383, 30.873160}, {-105.391653, 30.873309}, {-105.391299, 30.873853}, {-105.392681, 30.875766}, {-105.393628, 30.876381}, {-105.397308, 30.876219}, {-105.399936, 30.876674}, {-105.402580, 30.878791}, {-105.404303, 30.879676}, {-105.402372, 30.881658}, {-105.400113, 30.882940}, {-105.399132, 30.884255}, {-105.398828, 30.885329}, {-105.400151, 30.886629}, {-105.399951, 30.887694}, {-105.399415, 30.888305}, + {-105.400185, 30.889382}, {-105.401293, 30.890174}, {-105.402142, 30.890280}, {-105.403760, 30.892106}, {-105.408026, 30.889361}, {-105.412093, 30.888560}, {-105.412308, 30.888922}, {-105.412143, 30.890443}, {-105.410617, 30.891572}, {-105.410552, 30.892048}, {-105.412166, 30.893843}, {-105.413802, 30.899567}, {-105.416205, 30.900188}, {-105.421917, 30.900483}, {-105.424762, 30.900996}, {-105.425817, 30.900396}, + {-105.426786, 30.901920}, {-105.427624, 30.902562}, {-105.428698, 30.904533}, {-105.430136, 30.905604}, {-105.432634, 30.906100}, {-105.434559, 30.907685}, {-105.435592, 30.907832}, {-105.436172, 30.907480}, {-105.439857, 30.908877}, {-105.440539, 30.908349}, {-105.441158, 30.908449}, {-105.442361, 30.909442}, {-105.444773, 30.913336}, {-105.445560, 30.916124}, {-105.444320, 30.917258}, {-105.446379, 30.919355}, + {-105.446918, 30.919613}, {-105.447468, 30.919392}, {-105.448665, 30.917723}, {-105.450063, 30.917039}, {-105.451348, 30.915727}, {-105.452091, 30.915653}, {-105.453152, 30.916206}, {-105.453400, 30.917517}, {-105.454395, 30.919345}, {-105.453928, 30.919768}, {-105.452224, 30.919971}, {-105.451453, 30.921041}, {-105.451147, 30.922064}, {-105.451205, 30.923963}, {-105.451604, 30.924827}, {-105.452971, 30.925085}, + {-105.456916, 30.924515}, {-105.457787, 30.924923}, {-105.459613, 30.924802}, {-105.460521, 30.925408}, {-105.462775, 30.924964}, {-105.464827, 30.925857}, {-105.465310, 30.925386}, {-105.465462, 30.924551}, {-105.466771, 30.924279}, {-105.467339, 30.925552}, {-105.467552, 30.929668}, {-105.469240, 30.930505}, {-105.469765, 30.931119}, {-105.470789, 30.931239}, {-105.470794, 30.933712}, {-105.473603, 30.934388}, + {-105.474373, 30.933885}, {-105.475923, 30.933817}, {-105.478028, 30.937984}, {-105.479749, 30.939536}, {-105.481918, 30.940740}, {-105.483937, 30.940983}, {-105.486358, 30.942625}, {-105.486793, 30.942481}, {-105.487632, 30.940962}, {-105.488731, 30.940815}, {-105.490644, 30.942261}, {-105.491654, 30.942407}, {-105.491690, 30.943450}, {-105.491355, 30.943806}, {-105.490327, 30.943709}, {-105.488677, 30.942515}, + {-105.488082, 30.942856}, {-105.488114, 30.943725}, {-105.490138, 30.946469}, {-105.493224, 30.948663}, {-105.493728, 30.949461}, {-105.496272, 30.949273}, {-105.497742, 30.950169}, {-105.498623, 30.950134}, {-105.498872, 30.950418}, {-105.498980, 30.950826}, {-105.497697, 30.952396}, {-105.497492, 30.953239}, {-105.496463, 30.954325}, {-105.496810, 30.955420}, {-105.495544, 30.955655}, {-105.494810, 30.956255}, + {-105.494305, 30.955261}, {-105.493950, 30.955624}, {-105.493893, 30.956424}, {-105.494462, 30.958045}, {-105.495969, 30.959542}, {-105.496991, 30.961401}, {-105.498617, 30.962725}, {-105.499883, 30.965719}, {-105.501968, 30.967737}, {-105.503436, 30.968026}, {-105.506674, 30.966507}, {-105.508236, 30.966295}, {-105.511606, 30.966500}, {-105.513764, 30.966261}, {-105.515718, 30.964857}, {-105.516865, 30.967207}, + {-105.516888, 30.969428}, {-105.517731, 30.970322}, {-105.517864, 30.971098}, {-105.520338, 30.972974}, {-105.521319, 30.974689}, {-105.521799, 30.976286}, {-105.522561, 30.976801}, {-105.525360, 30.977332}, {-105.527083, 30.978178}, {-105.532676, 30.984697}, {-105.535789, 30.985022}, {-105.538042, 30.985734}, {-105.539230, 30.985507}, {-105.541003, 30.984634}, {-105.543018, 30.984644}, {-105.555148, 30.988470}, + {-105.557226, 30.989673}, {-105.563446, 30.997165}, {-105.565684, 31.002336}, {-105.570096, 31.008203}, {-105.570200, 31.009427}, {-105.568756, 31.014606}, {-105.570532, 31.018007}, {-105.571146, 31.018656}, {-105.571976, 31.018936}, {-105.576419, 31.019387}, {-105.578008, 31.020078}, {-105.581246, 31.026653}, {-105.581175, 31.027902}, {-105.579913, 31.030390}, {-105.579524, 31.035695}, {-105.584994, 31.056964}, + {-105.585362, 31.057670}, {-105.588417, 31.060457}, {-105.593854, 31.063243}, {-105.595286, 31.064310}, {-105.596003, 31.065285}, {-105.596672, 31.066924}, {-105.598367, 31.074318}, {-105.602682, 31.081817}, {-105.604467, 31.083744}, {-105.606438, 31.085278}, {-105.627102, 31.098405}, {-105.628519, 31.098655}, {-105.640048, 31.097639}, {-105.641829, 31.098333}, {-105.642684, 31.099583}, {-105.646488, 31.113080}, + {-105.647604, 31.114979}, {-105.648839, 31.115877}, {-105.659637, 31.119655}, {-105.661688, 31.119950}, {-105.663473, 31.121113}, {-105.665712, 31.121999}, {-105.679183, 31.125937}, {-105.708400, 31.135930}, {-105.710526, 31.136975}, {-105.716180, 31.140567}, {-105.717098, 31.141534}, {-105.717525, 31.142415}, {-105.717856, 31.145653}, {-105.718417, 31.147635}, {-105.719273, 31.148894}, {-105.741929, 31.164602}, + {-105.743685, 31.165254}, {-105.745725, 31.165363}, {-105.763370, 31.164127}, {-105.769560, 31.164876}, {-105.772990, 31.166702}, {-105.774028, 31.168474}, {-105.775795, 31.175829}, {-105.780030, 31.182740}, {-105.780253, 31.184941}, {-105.779491, 31.188338}, {-105.779478, 31.190228}, {-105.780132, 31.192326}, {-105.782798, 31.197459}, {-105.784684, 31.198980}, {-105.792344, 31.201202}, {-105.794287, 31.202286}, + {-105.818381, 31.230146}, {-105.836392, 31.247722}, {-105.850332, 31.264968}, {-105.853814, 31.272521}, {-105.857962, 31.275561}, {-105.869294, 31.288955}, {-105.870715, 31.289939}, {-105.874456, 31.291309}, {-105.876387, 31.291668}, {-105.891040, 31.290095}, {-105.894146, 31.290749}, {-105.895649, 31.291758}, {-105.896700, 31.293122}, {-105.901978, 31.303434}, {-105.903322, 31.306788}, {-105.906689, 31.310045}, + {-105.907388, 31.312214}, {-105.908629, 31.312745}, {-105.913327, 31.313058}, {-105.922307, 31.311952}, {-105.931585, 31.312781}, {-105.932823, 31.313362}, {-105.937894, 31.318223}, {-105.938510, 31.319070}, {-105.947752, 31.339589}, {-105.947680, 31.342008}, {-105.945453, 31.351015}, {-105.945732, 31.352950}, {-105.953786, 31.364749}, {-105.955596, 31.365304}, {-105.968706, 31.365619}, {-105.970046, 31.365977}, + {-105.987067, 31.379750}, {-106.002304, 31.391147}, {-106.004921, 31.392456}, {-106.016192, 31.393520}, {-106.075307, 31.397615}, {-106.081665, 31.399223}, {-106.084977, 31.401446}, {-106.103087, 31.418478}, {-106.108615, 31.422375}, {-106.112169, 31.423567}, {-106.124541, 31.423729}, {-106.129672, 31.424348}, {-106.132291, 31.425081}, {-106.154608, 31.435894}, {-106.156934, 31.437360}, {-106.159420, 31.439308}, + {-106.170276, 31.451289}, {-106.175625, 31.455206}, {-106.180304, 31.457107}, {-106.200119, 31.462411}, {-106.204393, 31.464605}, {-106.207631, 31.467104}, {-106.217435, 31.477921}, {-106.219074, 31.480280}, {-106.219620, 31.481561}, {-106.223562, 31.498074}, {-106.224764, 31.500929}, {-106.225704, 31.502252}, {-106.235235, 31.511238}, {-106.237224, 31.513858}, {-106.245236, 31.539123}, {-106.246571, 31.541661}, + {-106.248245, 31.543488}, {-106.254742, 31.548043}, {-106.279589, 31.561138}, {-106.280790, 31.562096}, {-106.288740, 31.587157}, {-106.292933, 31.594306}, {-106.296671, 31.603509}, {-106.297761, 31.604306}, {-106.300889, 31.609624}, {-106.300617, 31.613262}, {-106.302832, 31.618677}, {-106.302868, 31.620785}, {-106.303433, 31.622193}, {-106.307944, 31.629519}, {-106.324357, 31.649739}, {-106.329850, 31.658490}, + {-106.334435, 31.663555}, {-106.349565, 31.696712}, {-106.351748, 31.698592}, {-106.370139, 31.710710}, {-106.372370, 31.712569}, {-106.374036, 31.714845}, {-106.378039, 31.728310}, {-106.380321, 31.731378}, {-106.381074, 31.732082}, {-106.387829, 31.735902}, {-106.394719, 31.739250}, {-106.408116, 31.746995}, {-106.412058, 31.748789}, {-106.415684, 31.750893}, {-106.421849, 31.752928}, {-106.431606, 31.754103}, + {-106.435718, 31.755275}, {-106.447752, 31.762669}, {-106.451430, 31.764427}, {-106.452806, 31.764571}, {-106.455059, 31.764169}, {-106.466135, 31.760007}, {-106.467556, 31.759210}, {-106.468500, 31.758249}, {-106.469242, 31.756834}, {-106.470566, 31.752737}, {-106.472076, 31.751049}, {-106.473207, 31.750493}, {-106.484642, 31.747809}, {-106.487746, 31.747809}, {-106.490549, 31.748924}, {-106.494677, 31.751804}, + {-106.496479, 31.753843}, {-106.500089, 31.755527}, {-106.501391, 31.757042}, {-106.503482, 31.758168}, {-106.505818, 31.760472}, {-106.507326, 31.761192}, {-106.508774, 31.761018}, {-106.510060, 31.761258}, {-106.511301, 31.764089}, {-106.511899, 31.767746}, {-106.513687, 31.770711}, {-106.514871, 31.771588}, {-106.517521, 31.771643}, {-106.520988, 31.773829}, {-106.523647, 31.776223}, {-106.525318, 31.778681}, + {-106.528498, 31.781497}, {-106.528641, 31.782307}, {-106.528124, 31.783394}, {-106.528075, 31.785241}, {-106.527061, 31.788712}, {-106.527402, 31.790011}, {-106.528391, 31.791000}, {-106.530362, 31.792009}, {-106.531800, 31.791891}, {-106.532795, 31.792444}, {-106.533937, 31.795189}, {-106.535431, 31.797097}, {-106.536005, 31.798506}, {-106.538377, 31.800415}, {-106.542127, 31.802068}, {-106.544157, 31.803470}, + {-106.547140, 31.807299}, {-106.551237, 31.808200}, {-106.558444, 31.810405}, {-106.562951, 31.811105}, {-106.563410, 31.812736}, {-106.566472, 31.813433}, {-106.570944, 31.810206}, {-106.577244, 31.810406}, {-106.581345, 31.813901}, {-106.582140, 31.815504}, {-106.588051, 31.822108}, {-106.589061, 31.822708}, {-106.593825, 31.824898}, {-106.597307, 31.824700}, {-106.602806, 31.825018}, {-106.605297, 31.827720}, + {-106.603841, 31.832999}, {-106.603446, 31.835910}, {-106.601944, 31.839601}, {-106.602043, 31.844406}, {-106.606200, 31.846326}, {-106.609098, 31.846655}, {-106.614788, 31.846413}, {-106.621840, 31.852624}, {-106.622026, 31.853189}, {-106.625773, 31.856165}, {-106.627460, 31.860158}, {-106.628173, 31.861120}, {-106.635922, 31.866237}, {-106.635892, 31.871504}, {-106.634874, 31.874477}, {-106.630804, 31.879690}, + {-106.629191, 31.883699}, {-106.630686, 31.886407}, {-106.633922, 31.889180}, {-106.638155, 31.891663}, {-106.642901, 31.892933}, {-106.645584, 31.895328}, {-106.645478, 31.898669}, {-106.642643, 31.902642}, {-106.640062, 31.905273}, {-106.633667, 31.909793}, {-106.628933, 31.911072}, {-106.625568, 31.912432}, {-106.618751, 31.917795}, {-106.616146, 31.917303}, {-106.614349, 31.918005}, {-106.611847, 31.920003}, + {-106.618409, 31.922253}, {-106.622117, 31.924713}, {-106.623932, 31.925334}, {-106.624996, 31.925248}, {-106.628654, 31.923608}, {-106.629742, 31.926567}, {-106.625339, 31.930031}, {-106.623826, 31.932082}, {-106.622529, 31.934863}, {-106.622117, 31.936324}, {-106.622371, 31.940863}, {-106.623659, 31.945510}, {-106.618229, 31.947363}, {-106.615253, 31.948965}, {-106.614321, 31.951615}, {-106.614375, 31.955991}, + {-106.617707, 31.956008}, {-106.622818, 31.952891}, {-106.625123, 31.954530}, {-106.625534, 31.957476}, {-106.624298, 31.961056}, {-106.620453, 31.963403}, {-106.619371, 31.964777}, {-106.618745, 31.966955}, {-106.619569, 31.971577}, {-106.621841, 31.972918}, {-106.623186, 31.972918}, {-106.626523, 31.970675}, {-106.630102, 31.971258}, {-106.633868, 31.974188}, {-106.638193, 31.976824}, {-106.639536, 31.980343}, + {-106.636497, 31.985711}, {-106.631186, 31.989810}, {-106.623568, 31.990998}, {-106.619449, 31.994735}, {-106.618486, 32.000495}, {-106.394127, 32.001449}, {-106.377165, 32.001168}, {-106.298762, 32.001351}, {-106.206157, 32.001739}, {-106.125534, 32.002533}, {-105.923200, 32.002081}, {-105.920223, 32.001635}, {-105.908383, 32.001286}, {-105.900751, 32.001977}, {-105.875288, 32.001948}, {-105.854059, 32.002350}, + {-105.750511, 32.002206}, {-105.731353, 32.001565}, {-105.679527, 32.001362}, {-105.250514, 32.000278}, {-104.800928, 32.000663}, {-104.776185, 32.000420}, {-104.252421, 31.999939}, {-103.915663, 32.000124}, {-103.875476, 32.000554}, {-103.750471, 32.000411}, {-103.748317, 32.000197}, {-103.064422, 32.000517}, {-103.064348, 32.123041}, {-103.064566, 32.127577}, {-103.064380, 32.137540}, {-103.064788, 32.240487}, + {-103.064842, 32.619155}, {-103.064815, 32.624537}, {-103.064537, 32.625110}, {-103.064871, 32.682647}, {-103.064691, 32.733769}, {-103.064968, 32.754674}, {-103.064682, 32.784584}, {-103.065075, 32.784773}, {-103.065078, 32.785155}, {-103.064725, 32.785640}, {-103.064722, 32.786625}, {-103.064916, 32.857259}, {-103.064566, 32.899909}, {-103.064657, 32.959097}, +} +var ri = []Point{ + {-71.317770, 41.776113}, {-71.310366, 41.772994}, {-71.308879, 41.772080}, {-71.306279, 41.771266}, {-71.260982, 41.752118}, {-71.249492, 41.738573}, {-71.226218, 41.712512}, {-71.195555, 41.675152}, {-71.175880, 41.671542}, {-71.176017, 41.668269}, {-71.132507, 41.660356}, {-71.134464, 41.644012}, {-71.135514, 41.628294}, {-71.140270, 41.623792}, {-71.141875, 41.612544}, {-71.141560, 41.612154}, + {-71.141598, 41.611089}, {-71.140442, 41.605298}, {-71.140078, 41.604665}, {-71.138464, 41.603635}, {-71.131727, 41.593762}, {-71.126989, 41.555795}, {-71.126580, 41.555707}, {-71.126868, 41.554861}, {-71.126199, 41.551515}, {-71.122495, 41.522139}, {-71.120617, 41.497901}, {-71.119874, 41.494604}, {-71.121116, 41.492787}, {-71.116404, 41.484568}, {-71.088571, 41.431315}, {-71.103980, 41.421957}, + {-71.119322, 41.413321}, {-71.124594, 41.411474}, {-71.249960, 41.377713}, {-71.374495, 41.339011}, {-71.479876, 41.312599}, {-71.482996, 41.310974}, {-71.489356, 41.308802}, {-71.494805, 41.307355}, {-71.506610, 41.307356}, {-71.512060, 41.308080}, {-71.526594, 41.308804}, {-71.543851, 41.312598}, {-71.575253, 41.320955}, {-71.585067, 41.316775}, {-71.598151, 41.312594}, {-71.624504, 41.305997}, + {-71.632188, 41.304842}, {-71.636129, 41.303157}, {-71.651523, 41.298362}, {-71.656976, 41.296897}, {-71.659565, 41.296653}, {-71.666319, 41.292487}, {-71.672595, 41.289079}, {-71.695043, 41.282647}, {-71.705744, 41.280739}, {-71.711661, 41.280601}, {-71.719994, 41.281149}, {-71.723435, 41.279617}, {-71.728969, 41.277964}, {-71.739560, 41.275449}, {-71.765812, 41.274245}, {-71.778430, 41.275111}, + {-71.791480, 41.272165}, {-71.790972, 41.184101}, {-71.854462, 41.250100}, {-71.858513, 41.253697}, {-71.879847, 41.276246}, {-71.907258, 41.304483}, {-71.901658, 41.312683}, {-71.900301, 41.316046}, {-71.892831, 41.327806}, {-71.892005, 41.330275}, {-71.874513, 41.321868}, {-71.866538, 41.319185}, {-71.857458, 41.320789}, {-71.851923, 41.324664}, {-71.847709, 41.329604}, {-71.844611, 41.330730}, + {-71.837280, 41.335068}, {-71.829365, 41.342432}, {-71.829779, 41.346229}, {-71.831303, 41.351295}, {-71.836044, 41.353988}, {-71.837286, 41.356708}, {-71.837873, 41.360145}, {-71.837633, 41.365499}, {-71.837034, 41.366286}, {-71.832776, 41.369169}, {-71.831844, 41.370344}, {-71.831652, 41.372226}, {-71.832656, 41.375850}, {-71.832134, 41.377059}, {-71.830637, 41.378772}, {-71.832082, 41.380139}, + {-71.833527, 41.382264}, {-71.833395, 41.383830}, {-71.832591, 41.386385}, {-71.832655, 41.387156}, {-71.833130, 41.387839}, {-71.835708, 41.389852}, {-71.838118, 41.390554}, {-71.838749, 41.391054}, {-71.842209, 41.395380}, {-71.842244, 41.396879}, {-71.841068, 41.399658}, {-71.841553, 41.403046}, {-71.843256, 41.404461}, {-71.843591, 41.405331}, {-71.842352, 41.408084}, {-71.842627, 41.409757}, + {-71.842426, 41.410260}, {-71.839258, 41.412390}, {-71.835797, 41.412232}, {-71.834107, 41.411582}, {-71.827902, 41.414334}, {-71.825384, 41.414687}, {-71.824384, 41.415461}, {-71.824146, 41.416764}, {-71.823731, 41.417282}, {-71.821765, 41.418248}, {-71.820330, 41.419382}, {-71.818088, 41.419852}, {-71.816904, 41.419927}, {-71.812150, 41.419213}, {-71.806812, 41.416673}, {-71.803684, 41.417428}, + {-71.801439, 41.415545}, {-71.799513, 41.415838}, {-71.797745, 41.416727}, {-71.796296, 41.451516}, {-71.794052, 41.487902}, {-71.792485, 41.519772}, {-71.790978, 41.561551}, {-71.788213, 41.621017}, {-71.787239, 41.656111}, {-71.791066, 41.770202}, {-71.795863, 41.877688}, {-71.797883, 41.931011}, {-71.798068, 41.953730}, {-71.799242, 42.008065}, {-71.790218, 42.008737}, {-71.766010, 42.009745}, + {-71.676150, 42.011538}, {-71.582810, 42.013997}, {-71.527606, 42.014998}, {-71.516906, 42.015598}, {-71.499908, 42.017199}, {-71.381401, 42.018798}, {-71.381505, 42.000011}, {-71.381201, 41.981198}, {-71.381505, 41.976793}, {-71.381344, 41.965111}, {-71.381700, 41.922699}, {-71.381400, 41.913499}, {-71.381700, 41.903299}, {-71.381400, 41.895699}, {-71.381700, 41.893199}, {-71.365086, 41.895584}, + {-71.359491, 41.895901}, {-71.352062, 41.896997}, {-71.348667, 41.897125}, {-71.338698, 41.898399}, {-71.340698, 41.882900}, {-71.340857, 41.878721}, {-71.337818, 41.873171}, {-71.333997, 41.862300}, {-71.341298, 41.847100}, {-71.342198, 41.844300}, {-71.335197, 41.835500}, {-71.341197, 41.830600}, {-71.344897, 41.828000}, {-71.347197, 41.823101}, {-71.338897, 41.808300}, {-71.339796, 41.805566}, + {-71.339568, 41.805004}, {-71.339802, 41.804206}, {-71.340616, 41.802834}, {-71.340894, 41.802108}, {-71.340660, 41.801644}, {-71.341297, 41.800344}, {-71.340790, 41.799156}, {-71.341193, 41.798234}, {-71.340305, 41.797761}, {-71.340430, 41.797196}, {-71.339461, 41.796040}, {-71.337972, 41.795787}, {-71.336666, 41.795151}, {-71.335053, 41.795117}, {-71.334136, 41.794653}, {-71.334290, 41.794394}, + {-71.333395, 41.793119}, {-71.332771, 41.793078}, {-71.333050, 41.792681}, {-71.332602, 41.792046}, {-71.333042, 41.791722}, {-71.331803, 41.790932}, {-71.332448, 41.790477}, {-71.332367, 41.790141}, {-71.331252, 41.789394}, {-71.330922, 41.789594}, {-71.330892, 41.789136}, {-71.330235, 41.788883}, {-71.330415, 41.788654}, {-71.329432, 41.788274}, {-71.329627, 41.787018}, {-71.330391, 41.785381}, + {-71.329869, 41.784315}, {-71.329803, 41.782038}, {-71.329418, 41.781884}, {-71.329096, 41.782719}, {-71.328639, 41.780872}, {-71.317770, 41.776113}, +} diff --git a/vendor/github.com/tidwall/geojson/geometrycollection.go b/vendor/github.com/tidwall/geojson/geometrycollection.go new file mode 100644 index 00000000..1d2cec49 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometrycollection.go @@ -0,0 +1,76 @@ +package geojson + +import ( + "strings" + + "github.com/tidwall/gjson" +) + +// GeometryCollection ... +type GeometryCollection struct{ collection } + +// NewGeometryCollection ... +func NewGeometryCollection(geometries []Object) *GeometryCollection { + g := new(GeometryCollection) + g.children = geometries + g.parseInitRectIndex(DefaultParseOptions) + return g +} + +// AppendJSON appends the GeoJSON reprensentation to dst +func (g *GeometryCollection) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"GeometryCollection","geometries":[`...) + for i := 0; i < len(g.children); i++ { + if i > 0 { + dst = append(dst, ',') + } + dst = g.children[i].AppendJSON(dst) + } + dst = append(dst, ']') + if g.extra != nil { + dst = g.extra.appendJSONExtra(dst) + } + dst = append(dst, '}') + strings.Index("", " ") + return dst +} + +// String ... +func (g *GeometryCollection) String() string { + return string(g.AppendJSON(nil)) +} + +// JSON ... +func (g *GeometryCollection) JSON() string { + return string(g.AppendJSON(nil)) +} + +func parseJSONGeometryCollection( + keys *parseKeys, opts *ParseOptions, +) (Object, error) { + var g GeometryCollection + if !keys.rGeometries.Exists() { + return nil, errGeometriesMissing + } + if !keys.rGeometries.IsArray() { + return nil, errGeometriesInvalid + } + var err error + keys.rGeometries.ForEach(func(key, value gjson.Result) bool { + var f Object + f, err = Parse(value.Raw, opts) + if err != nil { + return false + } + g.children = append(g.children, f) + return true + }) + if err != nil { + return nil, err + } + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + g.parseInitRectIndex(opts) + return &g, nil +} diff --git a/vendor/github.com/tidwall/geojson/geometrycollection_test.go b/vendor/github.com/tidwall/geojson/geometrycollection_test.go new file mode 100644 index 00000000..eb6ddd12 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/geometrycollection_test.go @@ -0,0 +1,18 @@ +package geojson + +import "testing" + +func TestGeometryCollection(t *testing.T) { + p := expectJSON(t, `{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[1,2,3]}]}`, nil) + expect(t, p.Center() == P(1, 2)) + expectJSON(t, `{"type":"GeometryCollection"}`, errGeometriesMissing) + expectJSON(t, `{"type":"GeometryCollection","geometries":null}`, errGeometriesInvalid) + expectJSON(t, `{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[1,2,3]}],"bbox":null}`, nil) + expectJSON(t, `{"type":"GeometryCollection","geometries":[{"type":"Point"}]}`, errCoordinatesMissing) +} + +func TestGeometryCollectionPoly(t *testing.T) { + p := expectJSON(t, `{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[1,2]}]}`, nil) + expect(t, p.Intersects(PO(1, 2))) + expect(t, p.Contains(PO(1, 2))) +} diff --git a/vendor/github.com/tidwall/geojson/linestring.go b/vendor/github.com/tidwall/geojson/linestring.go new file mode 100644 index 00000000..a849a91c --- /dev/null +++ b/vendor/github.com/tidwall/geojson/linestring.go @@ -0,0 +1,240 @@ +package geojson + +import ( + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" +) + +// LineString ... +type LineString struct { + base geometry.Line + extra *extra +} + +// NewLineString ... +func NewLineString(line *geometry.Line) *LineString { + return &LineString{base: *line} +} + +// Empty ... +func (g *LineString) Empty() bool { + return g.base.Empty() +} + +// Rect ... +func (g *LineString) Rect() geometry.Rect { + return g.base.Rect() +} + +// Center ... +func (g *LineString) Center() geometry.Point { + return g.Rect().Center() +} + +// Base ... +func (g *LineString) Base() *geometry.Line { + return &g.base +} + +// AppendJSON ... +func (g *LineString) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"LineString","coordinates":`...) + dst, _ = appendJSONSeries(dst, &g.base, g.extra, 0) + if g.extra != nil { + dst = g.extra.appendJSONExtra(dst) + } + dst = append(dst, '}') + return dst +} + +// String ... +func (g *LineString) String() string { + return string(g.AppendJSON(nil)) +} + +// JSON ... +func (g *LineString) JSON() string { + return string(g.AppendJSON(nil)) +} + +// Spatial ... +func (g *LineString) Spatial() Spatial { + return g +} + +// ForEach ... +func (g *LineString) ForEach(iter func(geom Object) bool) bool { + return iter(g) +} + +// Within ... +func (g *LineString) Within(obj Object) bool { + return obj.Contains(g) +} + +// Contains ... +func (g *LineString) Contains(obj Object) bool { + return obj.Spatial().WithinLine(&g.base) +} + +// Intersects ... +func (g *LineString) Intersects(obj Object) bool { + return obj.Spatial().IntersectsLine(&g.base) +} + +// WithinRect ... +func (g *LineString) WithinRect(rect geometry.Rect) bool { + return rect.ContainsLine(&g.base) +} + +// WithinPoint ... +func (g *LineString) WithinPoint(point geometry.Point) bool { + return point.ContainsLine(&g.base) +} + +// WithinLine ... +func (g *LineString) WithinLine(line *geometry.Line) bool { + return line.ContainsLine(&g.base) +} + +// WithinPoly ... +func (g *LineString) WithinPoly(poly *geometry.Poly) bool { + return poly.ContainsLine(&g.base) +} + +// IntersectsPoint ... +func (g *LineString) IntersectsPoint(point geometry.Point) bool { + return g.base.IntersectsPoint(point) +} + +// IntersectsRect ... +func (g *LineString) IntersectsRect(rect geometry.Rect) bool { + return g.base.IntersectsRect(rect) +} + +// IntersectsLine ... +func (g *LineString) IntersectsLine(line *geometry.Line) bool { + return g.base.IntersectsLine(line) +} + +// IntersectsPoly ... +func (g *LineString) IntersectsPoly(poly *geometry.Poly) bool { + return g.base.IntersectsPoly(poly) +} + +// NumPoints ... +func (g *LineString) NumPoints() int { + return g.base.NumPoints() +} + +func parseJSONLineString(keys *parseKeys, opts *ParseOptions) (Object, error) { + var g LineString + points, ex, err := parseJSONLineStringCoords(keys, gjson.Result{}, opts) + if err != nil { + return nil, err + } + if len(points) < 2 { + // Must have at least two points + // https://tools.ietf.org/html/rfc7946#section-3.1.4 + return nil, errCoordinatesInvalid + } + line := geometry.NewLine(points, opts.IndexGeometry) + g.base = *line + g.extra = ex + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + return &g, nil +} + +func parseJSONLineStringCoords( + keys *parseKeys, rcoords gjson.Result, opts *ParseOptions, +) ([]geometry.Point, *extra, error) { + var err error + var coords []geometry.Point + var ex *extra + var dims int + if !rcoords.Exists() { + rcoords = keys.rCoordinates + if !rcoords.Exists() { + return nil, nil, errCoordinatesMissing + } + if !rcoords.IsArray() { + return nil, nil, errCoordinatesInvalid + } + } + rcoords.ForEach(func(key, value gjson.Result) bool { + if !value.IsArray() { + err = errCoordinatesInvalid + return false + } + var count int + var nums [4]float64 + value.ForEach(func(key, value gjson.Result) bool { + if count == 4 { + return false + } + if value.Type != gjson.Number { + err = errCoordinatesInvalid + return false + } + nums[count] = value.Float() + count++ + return true + }) + if err != nil { + return false + } + if count < 2 { + err = errCoordinatesInvalid + return false + } + coords = append(coords, geometry.Point{X: nums[0], Y: nums[1]}) + if ex == nil { + if count > 2 { + ex = new(extra) + if count > 3 { + ex.dims = 2 + } else { + ex.dims = 1 + } + dims = int(ex.dims) + } + } + if ex != nil { + for i := 0; i < dims; i++ { + ex.values = append(ex.values, nums[2+i]) + } + } + return true + }) + if err != nil { + return nil, nil, err + } + return coords, ex, err +} + +// Distance ... +func (g *LineString) Distance(obj Object) float64 { + return obj.Spatial().DistanceLine(&g.base) +} + +// DistancePoint ... +func (g *LineString) DistancePoint(point geometry.Point) float64 { + return geoDistancePoints(g.Center(), point) +} + +// DistanceRect .. +func (g *LineString) DistanceRect(rect geometry.Rect) float64 { + return geoDistancePoints(g.Center(), rect.Center()) +} + +// DistanceLine ... +func (g *LineString) DistanceLine(line *geometry.Line) float64 { + return geoDistancePoints(g.Center(), line.Rect().Center()) +} + +// DistancePoly ... +func (g *LineString) DistancePoly(poly *geometry.Poly) float64 { + return geoDistancePoints(g.Center(), poly.Rect().Center()) +} diff --git a/vendor/github.com/tidwall/geojson/linestring_test.go b/vendor/github.com/tidwall/geojson/linestring_test.go new file mode 100644 index 00000000..4ae93553 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/linestring_test.go @@ -0,0 +1,58 @@ +package geojson + +import "testing" + +func TestLineStringParse(t *testing.T) { + expectJSON(t, `{"type":"LineString","coordinates":[[1,2,3]]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"LineString","coordinates":[[1,null]]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"LineString","coordinates":[[1,2]],"bbox":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"LineString"}`, errCoordinatesMissing) + expectJSON(t, `{"type":"LineString","coordinates":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"LineString","coordinates":[[null]]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"LineString","coordinates":[null]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"LineString","coordinates":[[1,2,3,4,5]]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"LineString","coordinates":[[1]]}`, errCoordinatesInvalid) + g := expectJSON(t, `{"type":"LineString","coordinates":[[3,4],[1,2]]}`, nil) + expect(t, g.Rect() == R(1, 2, 3, 4)) + expectJSON(t, `{"type":"LineString","coordinates":[[3,4],[1,2]],"bbox":null}`, nil) + expectJSON(t, `{"type":"LineString","coordinates":[[3,4],[1,2]],"bbox":[1,2,3,4]}`, nil) +} + +func TestLineStringVarious(t *testing.T) { + var g = expectJSON(t, `{"type":"LineString","coordinates":[[3,4],[1,2]]}`, nil) + expect(t, string(g.AppendJSON(nil)) == `{"type":"LineString","coordinates":[[3,4],[1,2]]}`) + expect(t, g.Rect() == R(1, 2, 3, 4)) + expect(t, g.Center() == P(2, 3)) + expect(t, !g.Empty()) + g = expectJSON(t, `{"type":"LineString","coordinates":[[3,4],[1,2]],"bbox":[1,2,3,4]}`, nil) + expect(t, !g.Empty()) + expect(t, g.Rect() == R(1, 2, 3, 4)) + expect(t, g.Center() == R(1, 2, 3, 4).Center()) +} + +// func TestLineStringPoly(t *testing.T) { +// ls := expectJSON(t, `{"type":"LineString","coordinates":[ +// [10,10],[20,20],[20,10] +// ]}`, nil) +// expect(t, ls.(*LineString).Contains(ls)) +// expect(t, ls.Contains(PO(10, 10))) +// expect(t, ls.Contains(PO(15, 15))) +// expect(t, ls.Contains(PO(20, 20))) +// expect(t, ls.Contains(PO(20, 15))) +// expect(t, !ls.Contains(PO(12, 13))) +// expect(t, !ls.Contains(RO(10, 10, 20, 20))) +// expect(t, ls.Intersects(PO(10, 10))) +// expect(t, ls.Intersects(PO(15, 15))) +// expect(t, ls.Intersects(PO(20, 20))) +// expect(t, !ls.Intersects(PO(12, 13))) +// expect(t, ls.Intersects(RO(10, 10, 20, 20))) +// expect(t, ls.Intersects( +// expectJSON(t, `{"type":"Point","coordinates":[15,15,0]}`, nil), +// )) +// expect(t, ls.Intersects(ls)) +// lsb := expectJSON(t, `{"type":"LineString","coordinates":[ +// [10,10],[20,20],[20,10] +// ],"bbox":[10,10,20,20]}`, nil) +// expect(t, lsb.Contains(PO(12, 13))) +// expect(t, ls.Contains(PO(20, 20))) +// } diff --git a/vendor/github.com/tidwall/geojson/multilinestring.go b/vendor/github.com/tidwall/geojson/multilinestring.go new file mode 100644 index 00000000..01365d9e --- /dev/null +++ b/vendor/github.com/tidwall/geojson/multilinestring.go @@ -0,0 +1,84 @@ +package geojson + +import ( + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" +) + +// MultiLineString ... +type MultiLineString struct{ collection } + +// NewMultiLineString ... +func NewMultiLineString(lines []*geometry.Line) *MultiLineString { + g := new(MultiLineString) + for _, line := range lines { + g.children = append(g.children, NewLineString(line)) + } + g.parseInitRectIndex(DefaultParseOptions) + return g +} + +// AppendJSON ... +func (g *MultiLineString) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"MultiLineString","coordinates":[`...) + for i, g := range g.children { + if i > 0 { + dst = append(dst, ',') + } + dst = append(dst, + gjson.GetBytes(g.AppendJSON(nil), "coordinates").String()...) + } + dst = append(dst, ']') + if g.extra != nil { + dst = g.extra.appendJSONExtra(dst) + } + dst = append(dst, '}') + return dst + +} + +// String ... +func (g *MultiLineString) String() string { + return string(g.AppendJSON(nil)) +} + +// JSON ... +func (g *MultiLineString) JSON() string { + return string(g.AppendJSON(nil)) +} + +func parseJSONMultiLineString( + keys *parseKeys, opts *ParseOptions, +) (Object, error) { + var g MultiLineString + var err error + if !keys.rCoordinates.Exists() { + return nil, errCoordinatesMissing + } + if !keys.rCoordinates.IsArray() { + return nil, errCoordinatesInvalid + } + var coords []geometry.Point + var ex *extra + keys.rCoordinates.ForEach(func(_, value gjson.Result) bool { + coords, ex, err = parseJSONLineStringCoords(keys, value, opts) + if err != nil { + return false + } + if len(coords) < 2 { + err = errCoordinatesInvalid + return false + } + line := geometry.NewLine(coords, opts.IndexGeometry) + g.children = append(g.children, &LineString{base: *line, extra: ex}) + return true + }) + if err != nil { + return nil, err + } + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + g.parseInitRectIndex(opts) + return &g, nil +} diff --git a/vendor/github.com/tidwall/geojson/multilinestring_test.go b/vendor/github.com/tidwall/geojson/multilinestring_test.go new file mode 100644 index 00000000..41838159 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/multilinestring_test.go @@ -0,0 +1,22 @@ +package geojson + +import "testing" + +func TestMultiLineString(t *testing.T) { + expectJSON(t, `{"type":"MultiLineString","coordinates":[[[1,2,3]]]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"MultiLineString","coordinates":[[[1,2]]],"bbox":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"MultiLineString"}`, errCoordinatesMissing) + expectJSON(t, `{"type":"MultiLineString","coordinates":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"MultiLineString","coordinates":[1,null]}`, errCoordinatesInvalid) +} + +func TestMultiLineStringPoly(t *testing.T) { + p := expectJSON(t, `{"type":"MultiLineString","coordinates":[ + [[10,10],[20,20]], + [[50,50],[100,100]] + ]}`, nil) + expect(t, p.Intersects(PO(15, 15))) + expect(t, p.Contains(PO(15, 15))) + expect(t, p.Contains(PO(70, 70))) + expect(t, !p.Contains(PO(40, 40))) +} diff --git a/vendor/github.com/tidwall/geojson/multipoint.go b/vendor/github.com/tidwall/geojson/multipoint.go new file mode 100644 index 00000000..d0d299df --- /dev/null +++ b/vendor/github.com/tidwall/geojson/multipoint.go @@ -0,0 +1,76 @@ +package geojson + +import ( + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" +) + +// MultiPoint ... +type MultiPoint struct{ collection } + +// NewMultiPoint ... +func NewMultiPoint(points []geometry.Point) *MultiPoint { + g := new(MultiPoint) + for _, point := range points { + g.children = append(g.children, NewPoint(point)) + } + g.parseInitRectIndex(DefaultParseOptions) + return g +} + +// AppendJSON ... +func (g *MultiPoint) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"MultiPoint","coordinates":[`...) + for i, g := range g.children { + if i > 0 { + dst = append(dst, ',') + } + dst = append(dst, + gjson.GetBytes(g.AppendJSON(nil), "coordinates").String()...) + } + dst = append(dst, ']') + if g.extra != nil { + dst = g.extra.appendJSONExtra(dst) + } + dst = append(dst, '}') + return dst +} + +// String ... +func (g *MultiPoint) String() string { + return string(g.AppendJSON(nil)) +} + +// JSON ... +func (g *MultiPoint) JSON() string { + return string(g.AppendJSON(nil)) +} + +func parseJSONMultiPoint(keys *parseKeys, opts *ParseOptions) (Object, error) { + var g MultiPoint + var err error + if !keys.rCoordinates.Exists() { + return nil, errCoordinatesMissing + } + if !keys.rCoordinates.IsArray() { + return nil, errCoordinatesInvalid + } + var coords geometry.Point + var ex *extra + keys.rCoordinates.ForEach(func(_, value gjson.Result) bool { + coords, ex, err = parseJSONPointCoords(keys, value, opts) + if err != nil { + return false + } + g.children = append(g.children, &Point{base: coords, extra: ex}) + return true + }) + if err != nil { + return nil, err + } + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + g.parseInitRectIndex(opts) + return &g, nil +} diff --git a/vendor/github.com/tidwall/geojson/multipoint_test.go b/vendor/github.com/tidwall/geojson/multipoint_test.go new file mode 100644 index 00000000..cbf1bc17 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/multipoint_test.go @@ -0,0 +1,21 @@ +package geojson + +import "testing" + +func TestMultiPoint(t *testing.T) { + p := expectJSON(t, `{"type":"MultiPoint","coordinates":[[1,2,3]]}`, nil) + expect(t, p.Center() == P(1, 2)) + expectJSON(t, `{"type":"MultiPoint","coordinates":[1,null]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"MultiPoint","coordinates":[[1,2]],"bbox":null}`, nil) + expectJSON(t, `{"type":"MultiPoint"}`, errCoordinatesMissing) + expectJSON(t, `{"type":"MultiPoint","coordinates":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"MultiPoint","coordinates":[[1,2,3],[4,5,6]],"bbox":[1,2,3,4]}`, nil) +} + +// func TestMultiPointPoly(t *testing.T) { +// p := expectJSON(t, `{"type":"MultiPoint","coordinates":[[1,2],[2,2]]}`, nil) +// expect(t, p.Intersects(PO(1, 2))) +// expect(t, p.Contains(PO(1, 2))) +// expect(t, p.Contains(PO(2, 2))) +// expect(t, !p.Contains(PO(3, 2))) +// } diff --git a/vendor/github.com/tidwall/geojson/multipolygon.go b/vendor/github.com/tidwall/geojson/multipolygon.go new file mode 100644 index 00000000..7c22cddc --- /dev/null +++ b/vendor/github.com/tidwall/geojson/multipolygon.go @@ -0,0 +1,94 @@ +package geojson + +import ( + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" +) + +// MultiPolygon ... +type MultiPolygon struct{ collection } + +// NewMultiPolygon ... +func NewMultiPolygon(polys []*geometry.Poly) *MultiPolygon { + g := new(MultiPolygon) + for _, poly := range polys { + g.children = append(g.children, NewPolygon(poly)) + } + g.parseInitRectIndex(DefaultParseOptions) + return g +} + +// AppendJSON ... +func (g *MultiPolygon) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"MultiPolygon","coordinates":[`...) + for i, g := range g.children { + if i > 0 { + dst = append(dst, ',') + } + dst = append(dst, + gjson.GetBytes(g.AppendJSON(nil), "coordinates").String()...) + } + dst = append(dst, ']') + if g.extra != nil { + dst = g.extra.appendJSONExtra(dst) + } + dst = append(dst, '}') + return dst +} + +// String ... +func (g *MultiPolygon) String() string { + return string(g.AppendJSON(nil)) +} + +// JSON ... +func (g *MultiPolygon) JSON() string { + return string(g.AppendJSON(nil)) +} + +func parseJSONMultiPolygon( + keys *parseKeys, opts *ParseOptions, +) (Object, error) { + var g MultiPolygon + var err error + if !keys.rCoordinates.Exists() { + return nil, errCoordinatesMissing + } + if !keys.rCoordinates.IsArray() { + return nil, errCoordinatesInvalid + } + var coords [][]geometry.Point + var ex *extra + keys.rCoordinates.ForEach(func(_, value gjson.Result) bool { + coords, ex, err = parseJSONPolygonCoords(keys, value, opts) + if err != nil { + return false + } + if len(coords) == 0 { + err = errCoordinatesInvalid // must be a linear ring + return false + } + for _, p := range coords { + if len(p) < 4 || p[0] != p[len(p)-1] { + err = errCoordinatesInvalid // must be a linear ring + return false + } + } + exterior := coords[0] + var holes [][]geometry.Point + if len(coords) > 1 { + holes = coords[1:] + } + poly := geometry.NewPoly(exterior, holes, opts.IndexGeometry) + g.children = append(g.children, &Polygon{base: *poly, extra: ex}) + return true + }) + if err != nil { + return nil, err + } + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + g.parseInitRectIndex(opts) + return &g, nil +} diff --git a/vendor/github.com/tidwall/geojson/multipolygon_test.go b/vendor/github.com/tidwall/geojson/multipolygon_test.go new file mode 100644 index 00000000..7fb68b00 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/multipolygon_test.go @@ -0,0 +1,41 @@ +package geojson + +import "testing" + +func TestMultiPolygon(t *testing.T) { + json := `{"type":"MultiPolygon","coordinates":[ + [ + [[0,0],[10,0],[10,10],[0,10],[0,0]], + [[2,2],[8,2],[8,8],[2,8],[2,2]] + ],[ + [[0,0],[10,0],[10,10],[0,10],[0,0]], + [[2,2],[8,2],[8,8],[2,8],[2,2]] + ] + ]}` + p := expectJSON(t, json, nil) + if p.Center() != P(5, 5) { + t.Fatalf("expected '%v', got '%v'", P(5, 5), p.Center()) + } + if cleanJSON(string(p.AppendJSON(nil))) != cleanJSON(json) { + t.Fatalf("expectect '%v', got '%v'", cleanJSON(json), cleanJSON(string(p.AppendJSON(nil)))) + } + expectJSON(t, `{"type":"MultiPolygon","coordinates":[[[[0,0],[10,0],[5,10],[0,0]],[[1,1]]]],"bbox":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"MultiPolygon"}`, errCoordinatesMissing) + expectJSON(t, `{"type":"MultiPolygon","coordinates":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"MultiPolygon","coordinates":[1,null]}`, errCoordinatesInvalid) +} + +func TestMultiPolygonPoly(t *testing.T) { + p := expectJSON(t, `{"type":"MultiPolygon","coordinates":[ + [ + [[10,10],[20,20],[30,10],[10,10]] + ],[ + [[100,100],[200,200],[300,100],[100,100]] + ] + ]}`, nil) + expect(t, p.Intersects(PO(15, 15))) + expect(t, p.Contains(PO(15, 15))) + expect(t, p.Contains(PO(150, 150))) + expect(t, !p.Contains(PO(40, 40))) + expect(t, p.Within(RO(-100, -100, 1000, 1000))) +} diff --git a/vendor/github.com/tidwall/geojson/object.go b/vendor/github.com/tidwall/geojson/object.go new file mode 100644 index 00000000..c643b3f2 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/object.go @@ -0,0 +1,277 @@ +package geojson + +import ( + "errors" + "fmt" + "strconv" + + "github.com/tidwall/geojson/geo" + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" + "github.com/tidwall/pretty" +) + +var ( + fmtErrTypeIsUnknown = "type '%s' is unknown" + errDataInvalid = errors.New("invalid data") + errTypeInvalid = errors.New("invalid type") + errTypeMissing = errors.New("missing type") + errCoordinatesInvalid = errors.New("invalid coordinates") + errCoordinatesMissing = errors.New("missing coordinates") + errGeometryInvalid = errors.New("invalid geometry") + errGeometryMissing = errors.New("missing geometry") + errFeaturesMissing = errors.New("missing features") + errFeaturesInvalid = errors.New("invalid features") + errGeometriesMissing = errors.New("missing geometries") + errGeometriesInvalid = errors.New("invalid geometries") + errCircleRadiusUnitsInvalid = errors.New("invalid circle radius units") +) + +// Object is a GeoJSON type +type Object interface { + Empty() bool + Rect() geometry.Rect + Center() geometry.Point + Contains(other Object) bool + Within(other Object) bool + Intersects(other Object) bool + AppendJSON(dst []byte) []byte + JSON() string + String() string + Distance(obj Object) float64 + NumPoints() int + ForEach(iter func(geom Object) bool) bool + Spatial() Spatial +} + +var _ = []Object{ + &Point{}, &LineString{}, &Polygon{}, &Feature{}, + &MultiPoint{}, &MultiLineString{}, &MultiPolygon{}, + &GeometryCollection{}, &FeatureCollection{}, + &Rect{}, &Circle{}, +} + +// Collection is a searchable collection type. +type Collection interface { + Children() []Object + Indexed() bool + Search(rect geometry.Rect, iter func(child Object) bool) +} + +var _ = []Collection{ + &MultiPoint{}, &MultiLineString{}, &MultiPolygon{}, + &FeatureCollection{}, &GeometryCollection{}, +} + +type extra struct { + dims byte // number of extra coordinate values, 1 or 2 + values []float64 // extra coordinate values + // valid json object that includes extra members such as + // "bbox", "id", "properties", and foreign members + members string +} + +// ParseOptions ... +type ParseOptions struct { + // IndexChildren option will cause the object to index their children + // objects when the number of children is greater than or equal to the + // provided value. Setting this value to 0 will disable indexing. + // The default is 64. + IndexChildren int + // IndexGeometry option will cause the object to index it's geometry + // when the number of points in it's base polygon or linestring is greater + // that or equal to the provided value. Setting this value to 0 will + // disable indexing. + // The default is 64. + IndexGeometry int +} + +// DefaultParseOptions ... +var DefaultParseOptions = &ParseOptions{ + IndexChildren: geometry.DefaultIndex, + IndexGeometry: geometry.DefaultIndex, +} + +// Parse a GeoJSON object +func Parse(data string, opts *ParseOptions) (Object, error) { + if opts == nil { + // opts should never be nil + opts = DefaultParseOptions + } + // look at the first byte + for i := 0; ; i++ { + if len(data) == 0 { + return nil, errDataInvalid + } + switch data[0] { + default: + // well-known text is not supported yet + return nil, errDataInvalid + case 0, 1: + if i > 0 { + // 0x00 or 0x01 must be the first bytes + return nil, errDataInvalid + } + // well-known binary is not supported yet + return nil, errDataInvalid + case ' ', '\t', '\n', '\r': + // strip whitespace + data = data[1:] + continue + case '{': + return parseJSON(data, opts) + } + } +} + +type parseKeys struct { + rCoordinates gjson.Result + rGeometries gjson.Result + rGeometry gjson.Result + rFeatures gjson.Result + members string // a valid payload with all extra members +} + +func parseJSON(data string, opts *ParseOptions) (Object, error) { + if !gjson.Valid(data) { + return nil, errDataInvalid + } + var keys parseKeys + var fmembers []byte + var rType gjson.Result + gjson.Parse(data).ForEach(func(key, val gjson.Result) bool { + switch key.String() { + case "type": + rType = val + case "coordinates": + keys.rCoordinates = val + case "geometries": + keys.rGeometries = val + case "geometry": + keys.rGeometry = val + case "features": + keys.rFeatures = val + default: + if len(fmembers) == 0 { + fmembers = append(fmembers, '{') + } else { + fmembers = append(fmembers, ',') + } + fmembers = append(fmembers, pretty.UglyInPlace([]byte(key.Raw))...) + fmembers = append(fmembers, ':') + fmembers = append(fmembers, pretty.UglyInPlace([]byte(val.Raw))...) + } + return true + }) + if len(fmembers) > 0 { + fmembers = append(fmembers, '}') + keys.members = string(fmembers) + } + if !rType.Exists() { + return nil, errTypeMissing + } + if rType.Type != gjson.String { + return nil, errTypeInvalid + } + switch rType.String() { + default: + return nil, fmt.Errorf(fmtErrTypeIsUnknown, rType.String()) + case "Point": + return parseJSONPoint(&keys, opts) + case "LineString": + return parseJSONLineString(&keys, opts) + case "Polygon": + return parseJSONPolygon(&keys, opts) + case "Feature": + return parseJSONFeature(&keys, opts) + case "MultiPoint": + return parseJSONMultiPoint(&keys, opts) + case "MultiLineString": + return parseJSONMultiLineString(&keys, opts) + case "MultiPolygon": + return parseJSONMultiPolygon(&keys, opts) + case "GeometryCollection": + return parseJSONGeometryCollection(&keys, opts) + case "FeatureCollection": + return parseJSONFeatureCollection(&keys, opts) + } +} + +func parseBBoxAndExtras(ex **extra, keys *parseKeys, opts *ParseOptions) error { + if keys.members == "" { + return nil + } + if *ex == nil { + *ex = new(extra) + } + (*ex).members = keys.members + return nil +} + +func appendJSONPoint(dst []byte, point geometry.Point, ex *extra, idx int) []byte { + dst = append(dst, '[') + dst = strconv.AppendFloat(dst, point.X, 'f', -1, 64) + dst = append(dst, ',') + dst = strconv.AppendFloat(dst, point.Y, 'f', -1, 64) + if ex != nil { + dims := int(ex.dims) + for i := 0; i < dims; i++ { + dst = append(dst, ',') + dst = strconv.AppendFloat( + dst, ex.values[idx*dims+i], 'f', -1, 64, + ) + } + } + dst = append(dst, ']') + return dst +} + +func (ex *extra) appendJSONExtra(dst []byte) []byte { + if ex != nil && ex.members != "" { + dst = append(dst, ',') + dst = append(dst, ex.members[1:len(ex.members)-1]...) + } + + return dst +} + +func appendJSONSeries( + dst []byte, series geometry.Series, ex *extra, pidx int, +) (ndst []byte, npidx int) { + dst = append(dst, '[') + nPoints := series.NumPoints() + for i := 0; i < nPoints; i++ { + if i > 0 { + dst = append(dst, ',') + } + dst = appendJSONPoint(dst, series.PointAt(i), ex, pidx) + pidx++ + } + dst = append(dst, ']') + return dst, pidx +} + +func unionRects(a, b geometry.Rect) geometry.Rect { + if b.Min.X < a.Min.X { + a.Min.X = b.Min.X + } else if b.Max.X > a.Max.X { + a.Max.X = b.Max.X + } + if b.Min.Y < a.Min.Y { + a.Min.Y = b.Min.Y + } else if b.Max.Y > a.Max.Y { + a.Max.Y = b.Max.Y + } + return a +} + +func geoDistancePoints(a, b geometry.Point) float64 { + return geo.DistanceTo(a.Y, a.X, b.Y, b.X) +} + +// func geoDistanceCenterToPoint(obj Object, point geometry.Point) float64 { +// if obj.Empty() { +// return 0 +// } +// return geoDistancePointsA(obj.Center(), point) +// } diff --git a/vendor/github.com/tidwall/geojson/object_test.go b/vendor/github.com/tidwall/geojson/object_test.go new file mode 100644 index 00000000..9713184a --- /dev/null +++ b/vendor/github.com/tidwall/geojson/object_test.go @@ -0,0 +1,112 @@ +package geojson + +import ( + "encoding/json" + "fmt" + "math/rand" + "testing" + "time" + + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/pretty" +) + +func init() { + seed := time.Now().UnixNano() + println(seed) + rand.Seed(seed) +} + +func R(minX, minY, maxX, maxY float64) geometry.Rect { + return geometry.Rect{ + Min: geometry.Point{X: minX, Y: minY}, + Max: geometry.Point{X: maxX, Y: maxY}, + } +} +func P(x, y float64) geometry.Point { + return geometry.Point{X: x, Y: y} +} + +func PO(x, y float64) *Point { + return NewPoint(P(x, y)) +} + +func RO(minX, minY, maxX, maxY float64) *Rect { + return NewRect(R(minX, minY, maxX, maxY)) +} + +func LO(points []geometry.Point) *LineString { + return NewLineString(geometry.NewLine(points, geometry.DefaultIndex)) +} + +func PPO(exterior []geometry.Point, holes [][]geometry.Point) *Polygon { + return NewPolygon(geometry.NewPoly(exterior, holes, geometry.DefaultIndex)) +} + +func expectJSON(t testing.TB, data string, expect interface{}) Object { + if t != nil { + t.Helper() + } + return expectJSONOpts(t, data, expect, nil) +} +func expectJSONOpts(t testing.TB, data string, expect interface{}, opts *ParseOptions) Object { + if t != nil { + t.Helper() + } + var exerr error + var exstr string + switch expect := expect.(type) { + case string: + exstr = expect + case error: + exerr = expect + case nil: + exstr = data + } + obj, err := Parse(data, opts) + if err != exerr { + if t == nil { + panic(fmt.Sprintf("expected '%v', got '%v'", exerr, err)) + } else { + t.Fatalf("expected '%v', got '%v'", exerr, err) + } + } + if exstr != "" { + if cleanJSON(exstr) != cleanJSON(string(obj.AppendJSON(nil))) { + if t == nil { + panic("json mismatch") + } else { + t.Fatal("json mismatch") + } + } + } + return obj +} + +func expect(t testing.TB, what bool) { + if t != nil { + t.Helper() + } + if !what { + if t == nil { + panic("exception failure") + } else { + t.Fatal("expection failure") + } + } +} + +func cleanJSON(data string) string { + var v interface{} + if err := json.Unmarshal([]byte(data), &v); err != nil { + println(string(data)) + panic(err) + } + dst, err := json.Marshal(v) + if err != nil { + panic(err) + } + opts := *pretty.DefaultOptions + opts.Width = 99999999 + return string(pretty.PrettyOptions(dst, &opts)) +} diff --git a/vendor/github.com/tidwall/geojson/point.go b/vendor/github.com/tidwall/geojson/point.go new file mode 100644 index 00000000..fbd70b03 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/point.go @@ -0,0 +1,231 @@ +package geojson + +import ( + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" +) + +// Point ... +type Point struct { + base geometry.Point + extra *extra +} + +// NewPoint ... +func NewPoint(point geometry.Point) *Point { + return &Point{base: point} +} + +// NewPointZ ... +func NewPointZ(point geometry.Point, z float64) *Point { + return &Point{ + base: point, + extra: &extra{dims: 1, values: []float64{z}}, + } +} + +// ForEach ... +func (g *Point) ForEach(iter func(geom Object) bool) bool { + return iter(g) +} + +// Empty ... +func (g *Point) Empty() bool { + return g.base.Empty() +} + +// Rect ... +func (g *Point) Rect() geometry.Rect { + return g.base.Rect() +} + +// Spatial ... +func (g *Point) Spatial() Spatial { + return g +} + +// Center ... +func (g *Point) Center() geometry.Point { + return g.base +} + +// Base ... +func (g *Point) Base() geometry.Point { + return g.base +} + +// AppendJSON ... +func (g *Point) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"Point","coordinates":`...) + dst = appendJSONPoint(dst, g.base, g.extra, 0) + dst = g.extra.appendJSONExtra(dst) + dst = append(dst, '}') + return dst +} + +// JSON ... +func (g *Point) JSON() string { + return string(g.AppendJSON(nil)) +} + +// String ... +func (g *Point) String() string { + return string(g.AppendJSON(nil)) +} + +// Within ... +func (g *Point) Within(obj Object) bool { + return obj.Contains(g) +} + +// Contains ... +func (g *Point) Contains(obj Object) bool { + return obj.Spatial().WithinPoint(g.base) +} + +// Intersects ... +func (g *Point) Intersects(obj Object) bool { + return obj.Spatial().IntersectsPoint(g.base) +} + +// WithinRect ... +func (g *Point) WithinRect(rect geometry.Rect) bool { + return rect.ContainsPoint(g.base) +} + +// WithinPoint ... +func (g *Point) WithinPoint(point geometry.Point) bool { + return point.ContainsPoint(g.base) +} + +// WithinLine ... +func (g *Point) WithinLine(line *geometry.Line) bool { + return line.ContainsPoint(g.base) +} + +// WithinPoly ... +func (g *Point) WithinPoly(poly *geometry.Poly) bool { + return poly.ContainsPoint(g.base) +} + +// IntersectsPoint ... +func (g *Point) IntersectsPoint(point geometry.Point) bool { + return g.base.IntersectsPoint(point) +} + +// IntersectsRect ... +func (g *Point) IntersectsRect(rect geometry.Rect) bool { + return g.base.IntersectsRect(rect) +} + +// IntersectsLine ... +func (g *Point) IntersectsLine(line *geometry.Line) bool { + return g.base.IntersectsLine(line) +} + +// IntersectsPoly ... +func (g *Point) IntersectsPoly(poly *geometry.Poly) bool { + return g.base.IntersectsPoly(poly) +} + +// NumPoints ... +func (g *Point) NumPoints() int { + return 1 +} + +// Z ... +func (g *Point) Z() float64 { + if g.extra != nil && len(g.extra.values) > 0 { + return g.extra.values[0] + } + return 0 +} + +func parseJSONPoint(keys *parseKeys, opts *ParseOptions) (Object, error) { + var g Point + var err error + g.base, g.extra, err = parseJSONPointCoords(keys, gjson.Result{}, opts) + if err != nil { + return nil, err + } + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + return &g, nil +} + +func parseJSONPointCoords( + keys *parseKeys, rcoords gjson.Result, opts *ParseOptions, +) (geometry.Point, *extra, error) { + var coords geometry.Point + var ex *extra + if !rcoords.Exists() { + rcoords = keys.rCoordinates + if !rcoords.Exists() { + return coords, nil, errCoordinatesMissing + } + if !rcoords.IsArray() { + return coords, nil, errCoordinatesInvalid + } + } + var err error + var count int + var nums [4]float64 + rcoords.ForEach(func(key, value gjson.Result) bool { + if count == 4 { + return false + } + if value.Type != gjson.Number { + err = errCoordinatesInvalid + return false + } + nums[count] = value.Float() + count++ + return true + }) + if err != nil { + return coords, nil, err + } + if count < 2 { + return coords, nil, errCoordinatesInvalid + } + coords = geometry.Point{X: nums[0], Y: nums[1]} + if count > 2 { + ex = new(extra) + if count > 3 { + ex.dims = 2 + } else { + ex.dims = 1 + } + ex.values = make([]float64, count-2) + for i := 2; i < count; i++ { + ex.values[i-2] = nums[i] + } + } + return coords, ex, nil +} + +// Distance ... +func (g *Point) Distance(obj Object) float64 { + return obj.Spatial().DistancePoint(g.base) +} + +// DistancePoint ... +func (g *Point) DistancePoint(point geometry.Point) float64 { + return geoDistancePoints(g.Center(), point) +} + +// DistanceRect ... +func (g *Point) DistanceRect(rect geometry.Rect) float64 { + return geoDistancePoints(g.Center(), rect.Center()) +} + +// DistanceLine ... +func (g *Point) DistanceLine(line *geometry.Line) float64 { + return geoDistancePoints(g.Center(), line.Rect().Center()) +} + +// DistancePoly ... +func (g *Point) DistancePoly(poly *geometry.Poly) float64 { + return geoDistancePoints(g.Center(), poly.Rect().Center()) +} diff --git a/vendor/github.com/tidwall/geojson/point_test.go b/vendor/github.com/tidwall/geojson/point_test.go new file mode 100644 index 00000000..dd8da290 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/point_test.go @@ -0,0 +1,98 @@ +package geojson + +import "testing" + +func TestPointParse(t *testing.T) { + p := expectJSON(t, `{"type":"Point","coordinates":[1,2,3]}`, nil) + expect(t, p.Center() == P(1, 2)) + expectJSON(t, `{"type":"Point","coordinates":[1,null]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Point","coordinates":[1,2],"bbox":null}`, nil) + expectJSON(t, `{"type":"Point"}`, errCoordinatesMissing) + expectJSON(t, `{"type":"Point","coordinates":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Point","coordinates":[1,2,3,4,5]}`, `{"type":"Point","coordinates":[1,2,3,4]}`) + expectJSON(t, `{"type":"Point","coordinates":[1]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4]}`, `{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4]}`) +} + +func TestPointVarious(t *testing.T) { + var g Object = PO(10, 20) + expect(t, string(g.AppendJSON(nil)) == `{"type":"Point","coordinates":[10,20]}`) + expect(t, g.Rect() == R(10, 20, 10, 20)) + expect(t, g.Center() == P(10, 20)) + expect(t, !g.Empty()) +} + +// func TestPointPoly(t *testing.T) { +// p := expectJSON(t, `{"type":"Point","coordinates":[15,15,0]}`, nil) +// expect(t, p.Within(PO(15, 15))) +// expect(t, p.Contains(PO(15, 15))) +// expect(t, p.Contains(RO(15, 15, 15, 15))) +// expect(t, !p.Contains(RO(10, 10, 15, 15))) +// expect(t, !p.Contains(PO(10, 10))) +// expect(t, p.Intersects(PO(15, 15))) +// expect(t, p.Intersects(RO(10, 10, 20, 20))) +// expect(t, p.Intersects( +// expectJSON(t, `{"type":"Point","coordinates":[15,15,10]}`, nil), +// )) +// expect(t, !p.Intersects( +// expectJSON(t, `{"type":"Point","coordinates":[9,15,10]}`, nil), +// )) +// expect(t, p.Intersects( +// expectJSON(t, `{"type":"Point","coordinates":[9,15,10],"bbox":[10,10,20,20]}`, nil), +// )) +// expect(t, p.Intersects( +// expectJSON(t, `{"type":"LineString","coordinates":[ +// [10,10],[20,20] +// ]}`, nil), +// )) +// expect(t, !p.Intersects( +// expectJSON(t, `{"type":"LineString","coordinates":[ +// [9,10],[20,20] +// ]}`, nil), +// )) +// expect(t, p.Intersects( +// expectJSON(t, `{"type":"Polygon","coordinates":[ +// [[9,9],[9,21],[21,21],[21,9],[9,9]] +// ]}`, nil), +// )) + +// expect(t, !p.Intersects( +// expectJSON(t, `{"type":"Polygon","coordinates":[ +// [[9,9],[9,21],[21,21],[21,9],[9,9]], +// [[9.5,9.5],[9.5,20.5],[20.5,20.5],[20.5,9.5],[9.5,9.5]] +// ]}`, nil), +// )) +// expect(t, p.Intersects( +// expectJSON(t, `{"type":"Feature","geometry": +// {"type":"Point","coordinates":[15,15,10]} +// }`, nil), +// )) +// expect(t, p.Intersects( +// expectJSON(t, `{"type":"Feature","geometry": +// {"type":"Polygon","coordinates":[ +// [[9,9],[9,21],[21,21],[21,9],[9,9]] +// ]} +// }`, nil), +// )) +// expect(t, !p.Intersects( +// expectJSON(t, `{"type":"Feature","geometry": +// {"type":"Polygon","coordinates":[ +// [[9,9],[9,21],[21,21],[21,9],[9,9]], +// [[9.5,9.5],[9.5,20.5],[20.5,20.5],[20.5,9.5],[9.5,9.5]] +// ]} +// }`, nil), +// )) +// expect(t, !expectJSON(t, +// `{"type":"Point","coordinates":[15,15],"bbox":[10,10,15,15]}`, nil, +// ).Contains(PO(7, 7))) +// expect(t, expectJSON(t, +// `{"type":"Point","coordinates":[15,15],"bbox":[10,10,15,15]}`, nil, +// ).Contains(PO(12, 12))) + +// expect(t, !expectJSON(t, +// `{"type":"Point","coordinates":[15,15],"bbox":[10,10,15,15]}`, nil, +// ).Intersects(PO(7, 7))) +// expect(t, expectJSON(t, +// `{"type":"Point","coordinates":[15,15],"bbox":[10,10,15,15]}`, nil, +// ).Intersects(PO(12, 12))) +// } diff --git a/vendor/github.com/tidwall/geojson/polygon.go b/vendor/github.com/tidwall/geojson/polygon.go new file mode 100644 index 00000000..50fd73ad --- /dev/null +++ b/vendor/github.com/tidwall/geojson/polygon.go @@ -0,0 +1,265 @@ +package geojson + +import ( + "github.com/tidwall/geojson/geometry" + "github.com/tidwall/gjson" +) + +// Polygon ... +type Polygon struct { + base geometry.Poly + extra *extra +} + +// NewPolygon ... +func NewPolygon(poly *geometry.Poly) *Polygon { + return &Polygon{base: *poly} +} + +// Empty ... +func (g *Polygon) Empty() bool { + return g.base.Empty() +} + +// Rect ... +func (g *Polygon) Rect() geometry.Rect { + return g.base.Rect() +} + +// Center ... +func (g *Polygon) Center() geometry.Point { + return g.Rect().Center() +} + +// Base ... +func (g *Polygon) Base() *geometry.Poly { + return &g.base +} + +// AppendJSON ... +func (g *Polygon) AppendJSON(dst []byte) []byte { + dst = append(dst, `{"type":"Polygon","coordinates":[`...) + var pidx int + dst, pidx = appendJSONSeries(dst, g.base.Exterior, g.extra, pidx) + for _, hole := range g.base.Holes { + dst = append(dst, ',') + dst, pidx = appendJSONSeries(dst, hole, g.extra, pidx) + } + dst = append(dst, ']') + if g.extra != nil { + dst = g.extra.appendJSONExtra(dst) + } + dst = append(dst, '}') + return dst +} + +// JSON ... +func (g *Polygon) JSON() string { + return string(g.AppendJSON(nil)) +} + +// String ... +func (g *Polygon) String() string { + return string(g.AppendJSON(nil)) +} + +// Spatial ... +func (g *Polygon) Spatial() Spatial { + return g +} + +// ForEach ... +func (g *Polygon) ForEach(iter func(geom Object) bool) bool { + return iter(g) +} + +// Within ... +func (g *Polygon) Within(obj Object) bool { + return obj.Contains(g) +} + +// Contains ... +func (g *Polygon) Contains(obj Object) bool { + return obj.Spatial().WithinPoly(&g.base) +} + +// WithinRect ... +func (g *Polygon) WithinRect(rect geometry.Rect) bool { + return rect.ContainsPoly(&g.base) +} + +// WithinPoint ... +func (g *Polygon) WithinPoint(point geometry.Point) bool { + return point.ContainsPoly(&g.base) +} + +// WithinLine ... +func (g *Polygon) WithinLine(line *geometry.Line) bool { + return line.ContainsPoly(&g.base) +} + +// WithinPoly ... +func (g *Polygon) WithinPoly(poly *geometry.Poly) bool { + return poly.ContainsPoly(&g.base) +} + +// Intersects ... +func (g *Polygon) Intersects(obj Object) bool { + return obj.Spatial().IntersectsPoly(&g.base) +} + +// IntersectsPoint ... +func (g *Polygon) IntersectsPoint(point geometry.Point) bool { + return g.base.IntersectsPoint(point) +} + +// IntersectsRect ... +func (g *Polygon) IntersectsRect(rect geometry.Rect) bool { + return g.base.IntersectsRect(rect) +} + +// IntersectsLine ... +func (g *Polygon) IntersectsLine(line *geometry.Line) bool { + return g.base.IntersectsLine(line) +} + +// IntersectsPoly ... +func (g *Polygon) IntersectsPoly(poly *geometry.Poly) bool { + return g.base.IntersectsPoly(poly) +} + +// NumPoints ... +func (g *Polygon) NumPoints() int { + n := g.base.Exterior.NumPoints() + for _, hole := range g.base.Holes { + n += hole.NumPoints() + } + return n +} + +func parseJSONPolygon(keys *parseKeys, opts *ParseOptions) (Object, error) { + var g Polygon + coords, ex, err := parseJSONPolygonCoords(keys, gjson.Result{}, opts) + if err != nil { + return nil, err + } + if len(coords) == 0 { + return nil, errCoordinatesInvalid // must be a linear ring + } + for _, p := range coords { + if len(p) < 4 || p[0] != p[len(p)-1] { + return nil, errCoordinatesInvalid // must be a linear ring + } + } + exterior := coords[0] + var holes [][]geometry.Point + if len(coords) > 1 { + holes = coords[1:] + } + poly := geometry.NewPoly(exterior, holes, opts.IndexGeometry) + g.base = *poly + g.extra = ex + if err := parseBBoxAndExtras(&g.extra, keys, opts); err != nil { + return nil, err + } + return &g, nil +} + +func parseJSONPolygonCoords( + keys *parseKeys, rcoords gjson.Result, opts *ParseOptions, +) ( + [][]geometry.Point, *extra, error, +) { + var err error + var coords [][]geometry.Point + var ex *extra + var dims int + if !rcoords.Exists() { + rcoords = keys.rCoordinates + if !rcoords.Exists() { + return nil, nil, errCoordinatesMissing + } + if !rcoords.IsArray() { + return nil, nil, errCoordinatesInvalid + } + } + rcoords.ForEach(func(key, value gjson.Result) bool { + if !value.IsArray() { + err = errCoordinatesInvalid + return false + } + coords = append(coords, []geometry.Point{}) + ii := len(coords) - 1 + value.ForEach(func(key, value gjson.Result) bool { + var count int + var nums [4]float64 + value.ForEach(func(key, value gjson.Result) bool { + if count == 4 { + return false + } + if value.Type != gjson.Number { + err = errCoordinatesInvalid + return false + } + nums[count] = value.Float() + count++ + return true + }) + if err != nil { + return false + } + if count < 2 { + err = errCoordinatesInvalid + return false + } + coords[ii] = append(coords[ii], geometry.Point{X: nums[0], Y: nums[1]}) + if ex == nil { + if count > 2 { + ex = new(extra) + if count > 3 { + ex.dims = 2 + } else { + ex.dims = 1 + } + dims = int(ex.dims) + } + } + if ex != nil { + for i := 0; i < dims; i++ { + ex.values = append(ex.values, nums[2+i]) + } + } + return true + }) + return err == nil + }) + if err != nil { + return nil, nil, err + } + return coords, ex, err +} + +// Distance ... +func (g *Polygon) Distance(obj Object) float64 { + return obj.Spatial().DistancePoly(&g.base) +} + +// DistancePoint ... +func (g *Polygon) DistancePoint(point geometry.Point) float64 { + return geoDistancePoints(g.Center(), point) +} + +// DistanceRect ... +func (g *Polygon) DistanceRect(rect geometry.Rect) float64 { + return geoDistancePoints(g.Center(), rect.Center()) +} + +// DistanceLine ... +func (g *Polygon) DistanceLine(line *geometry.Line) float64 { + return geoDistancePoints(g.Center(), line.Rect().Center()) +} + +// DistancePoly ... +func (g *Polygon) DistancePoly(poly *geometry.Poly) float64 { + return geoDistancePoints(g.Center(), poly.Rect().Center()) +} diff --git a/vendor/github.com/tidwall/geojson/polygon_test.go b/vendor/github.com/tidwall/geojson/polygon_test.go new file mode 100644 index 00000000..e681059b --- /dev/null +++ b/vendor/github.com/tidwall/geojson/polygon_test.go @@ -0,0 +1,62 @@ +package geojson + +import "testing" + +func TestPolygonParse(t *testing.T) { + json := `{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}` + g := expectJSON(t, json, nil) + json = `{"type":"Polygon","coordinates":[ + [[0,0],[10,0],[10,10],[0,10],[0,0]], + [[2,2],[8,2],[8,8],[2,8],[2,2]] + ]}` + g = expectJSON(t, json, nil) + if g.Center() != P(5, 5) { + t.Fatalf("expected '%v', got '%v'", P(5, 5), g.Center()) + } + expectJSON(t, `{"type":"Polygon","coordinates":[[1,null]]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Polygon","coordinates":[]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Polygon","coordinates":[[[0,0],[10,0],[5,10],[0,0]],[[1,1]]]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Polygon","coordinates":[[[0,0],[10,0],[5,10],[0,0]]],"bbox":null}`, nil) + expectJSON(t, `{"type":"Polygon"}`, errCoordinatesMissing) + expectJSON(t, `{"type":"Polygon","coordinates":null}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Polygon","coordinates":[null]}`, errCoordinatesInvalid) + expectJSON(t, `{"type":"Polygon","coordinates":[[null]]}`, errCoordinatesInvalid) + expectJSON(t, + `{"type":"Polygon","coordinates":[[[0,0,0,0,0],[10,0],[5,10],[0,0]]]}`, + `{"type":"Polygon","coordinates":[[[0,0,0,0],[10,0,0,0],[5,10,0,0],[0,0,0,0]]]}`) + expectJSON(t, + `{"type":"Polygon","coordinates":[[[0,0,0],[10,0,4,5],[5,10],[0,0]]]}`, + `{"type":"Polygon","coordinates":[[[0,0,0],[10,0,4],[5,10,0],[0,0,0]]]}`) +} + +func TestPolygonVarious(t *testing.T) { + var g = expectJSON(t, `{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}`, nil) + expect(t, string(g.AppendJSON(nil)) == `{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}`) + expect(t, g.Rect() == R(0, 0, 10, 10)) + expect(t, g.Center() == P(5, 5)) + expect(t, !g.Empty()) +} + +// func TestPolygonPoly(t *testing.T) { +// json := `{"type":"Polygon","coordinates":[[[0,0],[10,0],[10,10],[0,10],[0,0]]]}` +// g := expectJSON(t, json, nil) +// expect(t, g.Contains(PO(5, 5))) +// expect(t, g.Contains(RO(5, 5, 6, 6))) +// expect(t, g.Contains(expectJSON(t, `{"type":"LineString","coordinates":[ +// [5,5],[5,6],[6,5] +// ]}`, nil))) +// expect(t, g.Intersects(PO(5, 5))) +// expect(t, g.Intersects(RO(5, 5, 6, 6))) +// expect(t, g.Intersects(expectJSON(t, `{"type":"LineString","coordinates":[ +// [5,5],[5,6],[6,5],[50,50] +// ]}`, nil))) +// expect(t, g.Intersects(expectJSON(t, `{"type":"Polygon","coordinates":[[ +// [5,5],[5,6],[6,5],[50,50],[5,5] +// ]]}`, nil))) +// expect(t, !g.Contains(expectJSON(t, `{"type":"Polygon","coordinates":[[ +// [5,5],[5,6],[6,5],[50,50],[5,5] +// ]]}`, nil))) +// expect(t, g.Contains(expectJSON(t, `{"type":"Polygon","coordinates":[[ +// [5,5],[5,6],[6,5],[5,5] +// ]]}`, nil))) +// } diff --git a/vendor/github.com/tidwall/geojson/rect.go b/vendor/github.com/tidwall/geojson/rect.go new file mode 100644 index 00000000..04b593a6 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/rect.go @@ -0,0 +1,145 @@ +package geojson + +import "github.com/tidwall/geojson/geometry" + +// Rect ... +type Rect struct { + base geometry.Rect +} + +// NewRect ... +func NewRect(rect geometry.Rect) *Rect { + return &Rect{base: rect} +} + +// ForEach ... +func (g *Rect) ForEach(iter func(geom Object) bool) bool { + return iter(g) +} + +// Empty ... +func (g *Rect) Empty() bool { + return g.base.Empty() +} + +// Rect ... +func (g *Rect) Rect() geometry.Rect { + return g.base +} + +// Base ... +func (g *Rect) Base() geometry.Rect { + return g.base +} + +// Center ... +func (g *Rect) Center() geometry.Point { + return g.base.Center() +} + +// AppendJSON ... +func (g *Rect) AppendJSON(dst []byte) []byte { + var gPoly Polygon + gPoly.base.Exterior = g.base + return gPoly.AppendJSON(dst) +} + +// JSON ... +func (g *Rect) JSON() string { + return string(g.AppendJSON(nil)) +} + +// String ... +func (g *Rect) String() string { + return string(g.AppendJSON(nil)) +} + +// Contains ... +func (g *Rect) Contains(obj Object) bool { + return obj.Spatial().WithinRect(g.base) +} + +// Within ... +func (g *Rect) Within(obj Object) bool { + return obj.Contains(g) +} + +// WithinRect ... +func (g *Rect) WithinRect(rect geometry.Rect) bool { + return rect.ContainsRect(g.base) +} + +// WithinPoint ... +func (g *Rect) WithinPoint(point geometry.Point) bool { + return point.ContainsRect(g.base) +} + +// WithinLine ... +func (g *Rect) WithinLine(line *geometry.Line) bool { + return line.ContainsRect(g.base) +} + +// WithinPoly ... +func (g *Rect) WithinPoly(poly *geometry.Poly) bool { + return poly.ContainsRect(g.base) +} + +// Intersects ... +func (g *Rect) Intersects(obj Object) bool { + return obj.Spatial().IntersectsRect(g.base) +} + +// IntersectsPoint ... +func (g *Rect) IntersectsPoint(point geometry.Point) bool { + return g.base.IntersectsPoint(point) +} + +// IntersectsRect ... +func (g *Rect) IntersectsRect(rect geometry.Rect) bool { + return g.base.IntersectsRect(rect) +} + +// IntersectsLine ... +func (g *Rect) IntersectsLine(line *geometry.Line) bool { + return g.base.IntersectsLine(line) +} + +// IntersectsPoly ... +func (g *Rect) IntersectsPoly(poly *geometry.Poly) bool { + return g.base.IntersectsPoly(poly) +} + +// NumPoints ... +func (g *Rect) NumPoints() int { + return 2 +} + +// Spatial ... +func (g *Rect) Spatial() Spatial { + return g +} + +// Distance ... +func (g *Rect) Distance(obj Object) float64 { + return obj.Spatial().DistanceRect(g.base) +} + +// DistancePoint ... +func (g *Rect) DistancePoint(point geometry.Point) float64 { + return geoDistancePoints(g.Center(), point) +} + +// DistanceRect ... +func (g *Rect) DistanceRect(rect geometry.Rect) float64 { + return geoDistancePoints(g.Center(), rect.Center()) +} + +// DistanceLine ... +func (g *Rect) DistanceLine(line *geometry.Line) float64 { + return geoDistancePoints(g.Center(), line.Rect().Center()) +} + +// DistancePoly ... +func (g *Rect) DistancePoly(poly *geometry.Poly) float64 { + return geoDistancePoints(g.Center(), poly.Rect().Center()) +} diff --git a/vendor/github.com/tidwall/geojson/rect_test.go b/vendor/github.com/tidwall/geojson/rect_test.go new file mode 100644 index 00000000..e4ad7075 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/rect_test.go @@ -0,0 +1,43 @@ +package geojson + +import "testing" + +func TestRect(t *testing.T) { + rect := RO(10, 20, 30, 40) + expect(t, !rect.Empty()) + expect(t, string(rect.AppendJSON(nil)) == + `{"type":"Polygon","coordinates":[[[10,20],[30,20],[30,40],[10,40],[10,20]]]}`) + expect(t, rect.String() == string(rect.AppendJSON(nil))) + // expect(t, !rect.Contains(NewString(""))) + // expect(t, !rect.Within(NewString(""))) + // expect(t, !rect.Intersects(NewString(""))) + // expect(t, rect.Distance(NewString("")) == 0) + + expect(t, rect.Rect() == R(10, 20, 30, 40)) + expect(t, rect.Center() == P(20, 30)) + var g Object + rect.ForEach(func(o Object) bool { + expect(t, g == nil) + g = o + return true + }) + expect(t, g == rect) + + expect(t, rect.NumPoints() == 2) + + expect(t, !(&Point{}).Contains(rect)) + expect(t, !(&Rect{}).Contains(rect)) + expect(t, !(&LineString{}).Contains(rect)) + expect(t, !(&Polygon{}).Contains(rect)) + + expect(t, !(&Point{}).Intersects(rect)) + expect(t, !(&Rect{}).Intersects(rect)) + expect(t, !(&LineString{}).Intersects(rect)) + expect(t, !(&Polygon{}).Intersects(rect)) + + expect(t, (&Point{}).Distance(rect) != 0) + expect(t, (&Rect{}).Distance(rect) != 0) + expect(t, (&LineString{}).Distance(rect) != 0) + expect(t, (&Polygon{}).Distance(rect) != 0) + +} diff --git a/vendor/github.com/tidwall/geojson/spatial.go b/vendor/github.com/tidwall/geojson/spatial.go new file mode 100644 index 00000000..539e7447 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/spatial.go @@ -0,0 +1,89 @@ +package geojson + +import "github.com/tidwall/geojson/geometry" + +// Spatial ... +type Spatial interface { + WithinRect(rect geometry.Rect) bool + WithinPoint(point geometry.Point) bool + WithinLine(line *geometry.Line) bool + WithinPoly(poly *geometry.Poly) bool + IntersectsRect(rect geometry.Rect) bool + IntersectsPoint(point geometry.Point) bool + IntersectsLine(line *geometry.Line) bool + IntersectsPoly(poly *geometry.Poly) bool + DistanceRect(rect geometry.Rect) float64 + DistancePoint(point geometry.Point) float64 + DistanceLine(line *geometry.Line) float64 + DistancePoly(poly *geometry.Poly) float64 +} + +var _ = []Spatial{ + &Point{}, &LineString{}, &Polygon{}, &Feature{}, + &MultiPoint{}, &MultiLineString{}, &MultiPolygon{}, + &GeometryCollection{}, &FeatureCollection{}, &Rect{}, + EmptySpatial{}, +} + +// EmptySpatial ... +type EmptySpatial struct{} + +// WithinRect ... +func (s EmptySpatial) WithinRect(rect geometry.Rect) bool { + return false +} + +// WithinPoint ... +func (s EmptySpatial) WithinPoint(point geometry.Point) bool { + return false +} + +// WithinLine ... +func (s EmptySpatial) WithinLine(line *geometry.Line) bool { + return false +} + +// WithinPoly ... +func (s EmptySpatial) WithinPoly(poly *geometry.Poly) bool { + return false +} + +// IntersectsRect ... +func (s EmptySpatial) IntersectsRect(rect geometry.Rect) bool { + return false +} + +// IntersectsPoint ... +func (s EmptySpatial) IntersectsPoint(point geometry.Point) bool { + return false +} + +// IntersectsLine ... +func (s EmptySpatial) IntersectsLine(line *geometry.Line) bool { + return false +} + +// IntersectsPoly ... +func (s EmptySpatial) IntersectsPoly(poly *geometry.Poly) bool { + return false +} + +// DistanceRect ... +func (s EmptySpatial) DistanceRect(rect geometry.Rect) float64 { + return 0 +} + +// DistancePoint ... +func (s EmptySpatial) DistancePoint(point geometry.Point) float64 { + return 0 +} + +// DistanceLine ... +func (s EmptySpatial) DistanceLine(line *geometry.Line) float64 { + return 0 +} + +// DistancePoly ... +func (s EmptySpatial) DistancePoly(poly *geometry.Poly) float64 { + return 0 +} diff --git a/vendor/github.com/tidwall/geojson/test_files/.gitignore b/vendor/github.com/tidwall/geojson/test_files/.gitignore new file mode 100644 index 00000000..c3f5d8e0 --- /dev/null +++ b/vendor/github.com/tidwall/geojson/test_files/.gitignore @@ -0,0 +1 @@ +usa.geojson diff --git a/vendor/github.com/tidwall/geojson/test_files/boston_subset.geojson b/vendor/github.com/tidwall/geojson/test_files/boston_subset.geojson new file mode 100644 index 00000000..8742cbcc --- /dev/null +++ b/vendor/github.com/tidwall/geojson/test_files/boston_subset.geojson @@ -0,0 +1,2095 @@ +{"type":"FeatureCollection","bbox": [-71.485, 42.472, -71.45, 42.5],"features": [ +{"type":"Feature","properties":{"highway":"traffic_signals"},"geometry": {"type":"Point","coordinates": [-71.4747, 42.476152]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","highway":"turning_circle"},"geometry": {"type":"Point","coordinates": [-71.4686093, 42.4735655]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","highway":"turning_circle"},"geometry": {"type":"Point","coordinates": [-71.4551017, 42.4894111]}}, +{"type":"Feature","properties":{"ref":"41","highway":"motorway_junction"},"geometry": {"type":"Point","coordinates": [-71.472939, 42.499278]}}, +{"type":"Feature","properties":{"highway":"traffic_signals"},"geometry": {"type":"Point","coordinates": [-71.45376400000001, 42.474867]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","highway":"turning_circle"},"geometry": {"type":"Point","coordinates": [-71.470561, 42.4774347]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","highway":"turning_circle"},"geometry": {"type":"Point","coordinates": [-71.46855720000001, 42.4721979]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","highway":"turning_circle"},"geometry": {"type":"Point","coordinates": [-71.47519010000001, 42.4828729]}}, +{"type":"Feature","properties":{"place":"hamlet","gnis:id":"611516","gnis:County":"Middlesex","gnis:County_num":"017","is_in":"Middlesex,Massachusetts,Mass.,MA,USA","gnis:ST_alpha":"MA","ele":"70","import_uuid":"bb7269ee-502a-5391-8056-e3ce0e66489c","gnis:ST_num":"25","name":"West Acton","gnis:Class":"Populated Place"},"geometry": {"type":"Point","coordinates": [-71.4746914, 42.4761381]}}, +{"type":"Feature","properties":{"place":"hamlet","gnis:id":"611462","ele":"68","name":"Kelly's Corner","gnis:Class":"Populated Place"},"geometry": {"type":"Point","coordinates": [-71.4525539, 42.474929]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Windsor Avenue","gnis:county_name":"Middlesex","note":"branch library of town government","addr:city":"Acton","ele":"71","addr:housenumber":"21","addr:state":"MA","gnis:feature_id":"1972322","source":"USGS Geonames","name":"Citizens' Library","amenity":"library","attribution":"Office of Geographic and Environmental Information (MassGIS)","source_url":"http://mass.gov/mgis/libraries.htm","gnis:import_uuid":"57871b70-0100-4405-bb30-88b2e001a944"},"geometry": {"type":"Point","coordinates": [-71.47365000000001, 42.47539]}}, +{"type":"Feature","properties":{"created_by":"JOSM","region":"Metrowest","address":"15 Charter Road, Acton MA 01720","type":"School","name":"Library","office":"Merriam School","amenity":"library","attribution":"Office of Geographic and Environmental Information (MassGIS)","source_url":"http://mass.gov/mgis/libraries.htm"},"geometry": {"type":"Point","coordinates": [-71.4555629, 42.4777669]}}, +{"type":"Feature","properties":{"created_by":"JOSM","region":"Metrowest","address":"96 Hayward Road, Acton MA 01720","type":"School","name":"Library","office":"Acton-Boxborough Regional High School","amenity":"library","attribution":"Office of Geographic and Environmental Information (MassGIS)","source_url":"http://mass.gov/mgis/libraries.htm"},"geometry": {"type":"Point","coordinates": [-71.4581847, 42.4797935]}}, +{"type":"Feature","properties":{"created_by":"JOSM","region":"Metrowest","address":"16 Charter Road, Acton MA 00","type":"School","name":"Library","office":"R. J. Grey Jr. High School","amenity":"library","attribution":"Office of Geographic and Environmental Information (MassGIS)","source_url":"http://mass.gov/mgis/libraries.htm"},"geometry": {"type":"Point","coordinates": [-71.45775140000001, 42.4769103]}}, +{"type":"Feature","properties":{"gnis:state_id":"25","gnis:county_id":"017","religion":"christian","ele":"68","gnis:created":"08/27/2002","gnis:feature_id":"601563","name":"Mount Calvary Church","amenity":"place_of_worship"},"geometry": {"type":"Point","coordinates": [-71.4606187, 42.4750929]}}, +{"type":"Feature","properties":{"gnis:state_id":"25","natural":"peak","gnis:county_id":"017","ele":"85","gnis:created":"02/24/1974","gnis:feature_id":"611523","name":"Wright Hill"},"geometry": {"type":"Point","coordinates": [-71.4775447, 42.4781074]}}, +{"type":"Feature","properties":{"gnis:state_id":"25","gnis:county_id":"017","ele":"65","man_made":"tower","gnis:created":"08/01/1994","gnis:feature_id":"618930","name":"WHAB-FM (Acton)"},"geometry": {"type":"Point","coordinates": [-71.4572853, 42.4800929]}}, +{"type":"Feature","properties":{"gnis:county_name":"Middlesex","gnis:reviewed":"no","ele":"66","addr:state":"MA","gnis:feature_id":"1972880","source":"USGS Geonames","name":"Hosmer House Museum","tourism":"museum","gnis:import_uuid":"57871b70-0100-4405-bb30-88b2e001a944"},"geometry": {"type":"Point","coordinates": [-71.45145170000001, 42.4764819]}}, +{"type":"Feature","properties":{"name":"New London Style Pizza","amenity":"restaurant"},"geometry": {"type":"Point","coordinates": [-71.47309389999999, 42.4764004]}}, +{"type":"Feature","properties":{"cuisine":"pizza","name":"Sorrento's","amenity":"restaurant"},"geometry": {"type":"Point","coordinates": [-71.4539446, 42.4736212]}}, +{"type":"Feature","properties":{"name":"Starbuck's","amenity":"cafe"},"geometry": {"type":"Point","coordinates": [-71.451705, 42.474926]}}, +{"type":"Feature","properties":{"name":"West Acton Mobil","amenity":"fuel"},"geometry": {"type":"Point","coordinates": [-71.47249170000001, 42.4763037]}}, +{"type":"Feature","properties":{"shop":"architect","name":"Office of Michael Rosenfeld, Inc., Architects"},"geometry": {"type":"Point","coordinates": [-71.4720828, 42.4762369]}}, +{"type":"Feature","properties":{"shop":"department_store","name":"K-mart"},"geometry": {"type":"Point","coordinates": [-71.455827, 42.473812]}}, +{"type":"Feature","properties":{"shop":"travel_agency","name":"AAA Southern NE"},"geometry": {"type":"Point","coordinates": [-71.4518515, 42.4749463]}}, +{"type":"Feature","properties":{"description":"trail map and rules","board_type":"notice","information":"board","name":"Guggins Brook","tourism":"information"},"geometry": {"type":"Point","coordinates": [-71.4845151, 42.4778735]}}, +{"type":"Feature","properties":{"note":"semi-difficult stream crossing"},"geometry": {"type":"Point","coordinates": [-71.4838266, 42.4807977]}}, +{"type":"Feature","properties":{"railway":"level_crossing"},"geometry": {"type":"Point","coordinates": [-71.4773763, 42.4824917]}}, +{"type":"Feature","properties":{"railway":"level_crossing"},"geometry": {"type":"Point","coordinates": [-71.4731836, 42.4761161]}}, +{"type":"Feature","properties":{"railway":"level_crossing"},"geometry": {"type":"Point","coordinates": [-71.4735768, 42.476953]}}, +{"type":"Feature","properties":{"barrier":"gate","bicycle":"yes","foot":"yes"},"geometry": {"type":"Point","coordinates": [-71.4782799, 42.487686]}}, +{"type":"Feature","properties":{"highway":"turning_circle"},"geometry": {"type":"Point","coordinates": [-71.4850471, 42.4843495]}}, +{"type":"Feature","properties":{"highway":"turning_circle"},"geometry": {"type":"Point","coordinates": [-71.4836579, 42.487376]}}, +{"type":"Feature","properties":{"shop":"convenience"},"geometry": {"type":"Point","coordinates": [-71.47281030000001, 42.476327]}}, +{"type":"Feature","properties":{"shop":"car_repair","name":"Sal's"},"geometry": {"type":"Point","coordinates": [-71.47528010000001, 42.4730943]}}, +{"type":"Feature","properties":{"atm":"yes","name":"Bank North","amenity":"bank"},"geometry": {"type":"Point","coordinates": [-71.4520031, 42.4744767]}}, +{"type":"Feature","properties":{"operator":"Bank of America","amenity":"atm"},"geometry": {"type":"Point","coordinates": [-71.45308060000001, 42.4745696]}}, +{"type":"Feature","properties":{"name":"TD Bank","amenity":"bank"},"geometry": {"type":"Point","coordinates": [-71.4513413, 42.4743947]}}, +{"type":"Feature","properties":{"shop":"pet","name":"Pet Smart"},"geometry": {"type":"Point","coordinates": [-71.4505546, 42.4752874]}}, +{"type":"Feature","properties":{"name":"kiosk","tourism":"information"},"geometry": {"type":"Point","coordinates": [-71.4660184, 42.4974857]}}, +{"type":"Feature","properties":{"name":"Subway","amenity":"fast_food"},"geometry": {"type":"Point","coordinates": [-71.453844, 42.4739028]}}, +{"type":"Feature","properties":{"name":"Santander","amenity":"bank"},"geometry": {"type":"Point","coordinates": [-71.45182200000001, 42.475573]}}, +{"type":"Feature","properties":{"railway":"level_crossing"},"geometry": {"type":"Point","coordinates": [-71.4731367, 42.4761148]}}, +{"type":"Feature","properties":{"railway":"level_crossing"},"geometry": {"type":"Point","coordinates": [-71.4735269, 42.4769731]}}, +{"type":"Feature","properties":{"start_date":"1844","operator":"B&M","name":"West Acton (closed)","railway":"station","wikipedia":"en:West_Acton_(MBTA_station)","end_date":"1975","disused":"station"},"geometry": {"type":"Point","coordinates": [-71.4733725, 42.4765182]}}, +{"type":"Feature","properties":{"railway":"level_crossing"},"geometry": {"type":"Point","coordinates": [-71.47734869999999, 42.4825336]}}, +{"type":"Feature","properties":{"cuisine":"Frozen_Yogurt","addr:street":"Main Street","addr:city":"Acton","addr:housenumber":"255A","addr:state":"MA","name":"Orange Leaf","amenity":"restaurant","addr:postcode":"01720"},"geometry": {"type":"Point","coordinates": [-71.4538842, 42.473853]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","addr:city":"Acton","addr:housenumber":"255","addr:state":"MA","name":"Ratti Handa DMD","amenity":"dentist","addr:postcode":"01720"},"geometry": {"type":"Point","coordinates": [-71.4539022, 42.4737558]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.480682, 42.4772101],[-71.480687, 42.4771953],[-71.4807246, 42.4772021],[-71.4807212, 42.4772124],[-71.4807862, 42.4772241],[-71.4807754, 42.4772571],[-71.48079509999999, 42.4772607],[-71.4808001, 42.4772455],[-71.48088319999999, 42.4772605],[-71.4808598, 42.4773316],[-71.48078510000001, 42.4773182],[-71.4807881, 42.4773092],[-71.48056699999999, 42.4772693],[-71.4805918, 42.4771939],[-71.480682, 42.4772101]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46497789999999, 42.4854112],[-71.4649622, 42.485398],[-71.4649726, 42.4853912],[-71.4649431, 42.4853664],[-71.4650128, 42.485321],[-71.4650053, 42.4853147],[-71.4650224, 42.4853035],[-71.4650322, 42.4853117],[-71.4650484, 42.4853012],[-71.4650791, 42.4853271],[-71.46505139999999, 42.4853451],[-71.4651198, 42.4854027],[-71.4650496, 42.4854483],[-71.46499350000001, 42.4854011],[-71.46497789999999, 42.4854112]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4616379, 42.4725976],[-71.46163799999999, 42.4725807],[-71.4616551, 42.4725807],[-71.4616551, 42.4725941],[-71.4616958, 42.4725942],[-71.4616958, 42.4725757],[-71.4617166, 42.4725758],[-71.4617166, 42.4725964],[-71.4617855, 42.4725966],[-71.46178519999999, 42.4726671],[-71.46164419999999, 42.4726668],[-71.4616441, 42.4726805],[-71.4615451, 42.4726803],[-71.46154540000001, 42.4725975],[-71.4616379, 42.4725976]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4606601, 42.4796467],[-71.46064749999999, 42.4796067],[-71.4606363, 42.4796086],[-71.4606156, 42.4795428],[-71.4606907, 42.4795298],[-71.4606653, 42.4794488],[-71.4607705, 42.4794307],[-71.4608039, 42.4795372],[-71.4607907, 42.4795394],[-71.460803, 42.4795785],[-71.4607894, 42.4795808],[-71.460818, 42.4796719],[-71.46071739999999, 42.4796893],[-71.4607017, 42.4796396],[-71.4606601, 42.4796467]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46896340000001, 42.4920611],[-71.4689913, 42.4919871],[-71.4690027, 42.4919895],[-71.4690322, 42.4919111],[-71.46913429999999, 42.4919322],[-71.4690884, 42.4920542],[-71.4690992, 42.4920565],[-71.4690913, 42.4920773],[-71.46911040000001, 42.4920818],[-71.4690822, 42.4921474],[-71.4689966, 42.4921298],[-71.4690013, 42.4921174],[-71.4688725, 42.4920908],[-71.46888939999999, 42.4920459],[-71.46896340000001, 42.4920611]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4639088, 42.478457],[-71.4638978, 42.4784424],[-71.46389069999999, 42.4783234],[-71.4639809, 42.4783221],[-71.46398259999999, 42.4783846],[-71.464005, 42.4784128],[-71.46402, 42.4784061],[-71.4640917, 42.4784943],[-71.4640536, 42.4785113],[-71.46406, 42.4785192],[-71.464044, 42.4785263],[-71.464038, 42.4785189],[-71.4639998, 42.4785359],[-71.4639284, 42.4784482],[-71.4639088, 42.478457]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46899089999999, 42.4852621],[-71.46901579999999, 42.485221],[-71.468986, 42.4852111],[-71.4690081, 42.4851748],[-71.468988, 42.4851682],[-71.4689975, 42.4851526],[-71.4690167, 42.4851589],[-71.4690396, 42.4851212],[-71.4690761, 42.4851334],[-71.4690975, 42.4850981],[-71.4691467, 42.4851145],[-71.46901080000001, 42.4853383],[-71.4689269, 42.4853104],[-71.4689621, 42.4852525],[-71.46899089999999, 42.4852621]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4509204, 42.4871509],[-71.4508381, 42.4871489],[-71.45084249999999, 42.487049],[-71.450959, 42.4870484],[-71.45095860000001, 42.4870019],[-71.4511179, 42.487001],[-71.4511189, 42.4870944],[-71.4510964, 42.4870945],[-71.451097, 42.4871618],[-71.4510083, 42.4871623],[-71.4510081, 42.4871455],[-71.4509752, 42.4871457],[-71.450975, 42.4871302],[-71.45092080000001, 42.4871305],[-71.4509204, 42.4871509]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4809228, 42.4749169],[-71.48093710000001, 42.4748864],[-71.4809145, 42.4748806],[-71.48093489999999, 42.4748371],[-71.4809516, 42.4748414],[-71.48096839999999, 42.4748055],[-71.48107539999999, 42.474833],[-71.48104650000001, 42.4748946],[-71.4811129, 42.4749117],[-71.48108139999999, 42.4749788],[-71.4810578, 42.4749727],[-71.4810524, 42.4749841],[-71.48102470000001, 42.474977],[-71.4810389, 42.4749468],[-71.4809228, 42.4749169]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47222979999999, 42.4878784],[-71.4721716, 42.4878521],[-71.47223320000001, 42.4877776],[-71.4723195, 42.4878167],[-71.4723389, 42.4877933],[-71.4724331, 42.4878359],[-71.4724133, 42.4878598],[-71.4724435, 42.4878735],[-71.47237920000001, 42.4879626],[-71.4723233, 42.4879387],[-71.4723384, 42.4879204],[-71.4722651, 42.4878872],[-71.472279, 42.4878703],[-71.4722481, 42.4878563],[-71.47222979999999, 42.4878784]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47325290000001, 42.4788887],[-71.4732329, 42.4788572],[-71.4731467, 42.4788872],[-71.47309989999999, 42.4788134],[-71.47320759999999, 42.4787759],[-71.4732365, 42.4788215],[-71.47332179999999, 42.4787918],[-71.47334290000001, 42.4788251],[-71.4733939, 42.4788073],[-71.47343239999999, 42.478868],[-71.47337159999999, 42.4788891],[-71.4733794, 42.4789015],[-71.4733256, 42.4789202],[-71.473296, 42.4788737],[-71.47325290000001, 42.4788887]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47004459999999, 42.492571],[-71.470102, 42.4926189],[-71.4701877, 42.4925628],[-71.4701603, 42.4925399],[-71.47019640000001, 42.4925162],[-71.4702221, 42.4925377],[-71.4702626, 42.4925112],[-71.4703172, 42.4925567],[-71.4702413, 42.4926066],[-71.4702522, 42.4926157],[-71.4701601, 42.4926762],[-71.4701268, 42.4926483],[-71.4700685, 42.4926866],[-71.46998050000001, 42.4926131],[-71.47004459999999, 42.492571]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4619933, 42.4806546],[-71.46198320000001, 42.4806388],[-71.4619969, 42.480634],[-71.4620058, 42.4806477],[-71.4620693, 42.4806252],[-71.4620882, 42.4806545],[-71.4621365, 42.4806374],[-71.4621799, 42.4807046],[-71.4621909, 42.4807008],[-71.4622265, 42.4807559],[-71.4621643, 42.4807779],[-71.4621703, 42.4807872],[-71.46208489999999, 42.4808174],[-71.4619823, 42.4806585],[-71.4619933, 42.4806546]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"171","addr:state":"MA","source:datetime":"2014-11-08T14:01:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4556395, 42.4995002],[-71.45564039999999, 42.499387],[-71.455823, 42.4993878],[-71.4558222, 42.499486],[-71.4559028, 42.4994864],[-71.45590300000001, 42.4994691],[-71.45600140000001, 42.4994695],[-71.45600159999999, 42.4994502],[-71.45609159999999, 42.4994506],[-71.456091, 42.4995339],[-71.4560068, 42.4995336],[-71.45600659999999, 42.4995558],[-71.45581780000001, 42.499555],[-71.4558182, 42.4995009],[-71.4556395, 42.4995002]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47163380000001, 42.4783523],[-71.47158589999999, 42.4783115],[-71.4715983, 42.4783032],[-71.47157, 42.4782801],[-71.4716038, 42.4782574],[-71.471586, 42.478243],[-71.4716339, 42.4782107],[-71.4716914, 42.4782574],[-71.4717193, 42.4782385],[-71.4717696, 42.4782794],[-71.4717553, 42.4782891],[-71.4718045, 42.4783418],[-71.4717591, 42.4783656],[-71.4717067, 42.4783086],[-71.47163380000001, 42.4783523]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47804240000001, 42.4725068],[-71.4778902, 42.4724722],[-71.4779137, 42.4724154],[-71.4782175, 42.4724844],[-71.47820230000001, 42.472521],[-71.47821810000001, 42.4725246],[-71.4781948, 42.4725807],[-71.47818340000001, 42.4725781],[-71.47816659999999, 42.4726186],[-71.47812589999999, 42.4726094],[-71.4781091, 42.47265],[-71.4780652, 42.47264],[-71.4780743, 42.472618],[-71.478003, 42.4726018],[-71.47804240000001, 42.4725068]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4736652, 42.4889442],[-71.4735651, 42.4888748],[-71.4736077, 42.4888411],[-71.4736169, 42.4888474],[-71.4736453, 42.4888249],[-71.4737332, 42.4888858],[-71.4738181, 42.4888187],[-71.4738751, 42.4888581],[-71.47381, 42.4889096],[-71.4738213, 42.4889175],[-71.4737552, 42.4889698],[-71.47374139999999, 42.4889603],[-71.4737081, 42.4889866],[-71.4736566, 42.488951],[-71.4736652, 42.4889442]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4676668, 42.4952211],[-71.4676061, 42.4951933],[-71.467636, 42.4951576],[-71.4675671, 42.4951261],[-71.4676145, 42.4950694],[-71.4677533, 42.4951329],[-71.4677384, 42.4951508],[-71.46777040000001, 42.4951654],[-71.46777969999999, 42.4951542],[-71.4678653, 42.4951933],[-71.467812, 42.4952572],[-71.4677207, 42.4952154],[-71.4677315, 42.4952025],[-71.467696, 42.4951863],[-71.4676668, 42.4952211]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4536839, 42.4929882],[-71.45370269999999, 42.4929417],[-71.4537354, 42.492949],[-71.45376, 42.4928882],[-71.45387030000001, 42.4929126],[-71.4538525, 42.4929566],[-71.4539018, 42.4929675],[-71.45387770000001, 42.493027],[-71.4538242, 42.4930152],[-71.4537833, 42.493116],[-71.45370200000001, 42.493098],[-71.4537241, 42.4930433],[-71.45371230000001, 42.4930406],[-71.4537294, 42.4929983],[-71.4536839, 42.4929882]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.476009, 42.4746137],[-71.4760107, 42.4745948],[-71.4760734, 42.4745979],[-71.4760801, 42.4745246],[-71.47617219999999, 42.4745292],[-71.47616910000001, 42.4745632],[-71.47619349999999, 42.4745645],[-71.4761889, 42.4746148],[-71.4761153, 42.4746111],[-71.47611190000001, 42.4746482],[-71.4759981, 42.4746424],[-71.47599510000001, 42.4746757],[-71.475853, 42.4746685],[-71.4758588, 42.4746061],[-71.476009, 42.4746137]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4609584, 42.4776634],[-71.4609292, 42.4776456],[-71.460982, 42.4775982],[-71.461044, 42.477636],[-71.46108169999999, 42.4776021],[-71.4610509, 42.4775833],[-71.461083, 42.4775545],[-71.4611996, 42.4776256],[-71.4611124, 42.4777039],[-71.46109559999999, 42.4776936],[-71.46100730000001, 42.4777729],[-71.46093279999999, 42.4777274],[-71.4609576, 42.4777052],[-71.4609303, 42.4776885],[-71.4609584, 42.4776634]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.477717, 42.4762565],[-71.4777328, 42.4762022],[-71.4777768, 42.4762093],[-71.4777909, 42.476161],[-71.47791530000001, 42.4761809],[-71.4779071, 42.4762091],[-71.477969, 42.476219],[-71.4779554, 42.4762655],[-71.4778958, 42.4762559],[-71.47788799999999, 42.4762826],[-71.47784919999999, 42.4762764],[-71.47784489999999, 42.4762911],[-71.4777903, 42.4762824],[-71.47779420000001, 42.4762688],[-71.477717, 42.4762565]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4516351, 42.4898267],[-71.45162639999999, 42.4897828],[-71.4516139, 42.4897841],[-71.45160989999999, 42.4897639],[-71.4516225, 42.4897626],[-71.4516151, 42.4897254],[-71.4517238, 42.4897136],[-71.45174969999999, 42.4898446],[-71.4517702, 42.4898424],[-71.45177750000001, 42.4898792],[-71.45175380000001, 42.4898817],[-71.45176120000001, 42.4899174],[-71.4516756, 42.4899268],[-71.451655, 42.4898245],[-71.4516351, 42.4898267]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4719606, 42.4945521],[-71.4720264, 42.4944016],[-71.47210029999999, 42.4944193],[-71.4720945, 42.4944325],[-71.47212519999999, 42.4944399],[-71.47213720000001, 42.4944124],[-71.47219370000001, 42.494426],[-71.4721617, 42.4944991],[-71.4721478, 42.4944958],[-71.4721172, 42.4945659],[-71.472099, 42.4945615],[-71.47207659999999, 42.4946126],[-71.47197799999999, 42.494589],[-71.471991, 42.4945594],[-71.4719606, 42.4945521]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714173, 42.4929303],[-71.4714567, 42.4928986],[-71.47144419999999, 42.4928905],[-71.4714822, 42.4928585],[-71.4715499, 42.4929024],[-71.4715089, 42.492937],[-71.47149539999999, 42.4929282],[-71.4714726, 42.4929475],[-71.47150670000001, 42.4929696],[-71.4714223, 42.4930409],[-71.4713465, 42.4929917],[-71.47137379999999, 42.4929679],[-71.4713376, 42.4929441],[-71.47138, 42.492907],[-71.4714173, 42.4929303]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.464763, 42.4801973],[-71.46479890000001, 42.4801416],[-71.4647686, 42.4801309],[-71.4648299, 42.4800357],[-71.46492790000001, 42.4800702],[-71.46490180000001, 42.4801107],[-71.46493049999999, 42.4801209],[-71.46489649999999, 42.4801737],[-71.4649096, 42.4801783],[-71.46486950000001, 42.4802406],[-71.464828, 42.480226],[-71.4648064, 42.4802595],[-71.46471579999999, 42.4802275],[-71.4647404, 42.4801893],[-71.464763, 42.4801973]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4702266, 42.4938668],[-71.47028760000001, 42.4938142],[-71.47027799999999, 42.4938081],[-71.4703288, 42.4937626],[-71.4703476, 42.4937752],[-71.4703681, 42.4937591],[-71.4704076, 42.4937871],[-71.4703877, 42.493802],[-71.4704167, 42.4938214],[-71.4703014, 42.4939261],[-71.4702721, 42.4939076],[-71.4702084, 42.4939625],[-71.4701323, 42.4939142],[-71.4702039, 42.4938524],[-71.4702266, 42.4938668]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46185509999999, 42.481464],[-71.4617822, 42.481393],[-71.4618119, 42.4813763],[-71.4617993, 42.4813642],[-71.4618887, 42.4813206],[-71.4619177, 42.4813531],[-71.4619326, 42.4813458],[-71.4619629, 42.4813798],[-71.46197309999999, 42.4813748],[-71.4619854, 42.4813886],[-71.4619757, 42.4813933],[-71.4620068, 42.4814283],[-71.46191690000001, 42.4814721],[-71.46189320000001, 42.4814455],[-71.46185509999999, 42.481464]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46995099999999, 42.4905517],[-71.4700089, 42.4905851],[-71.47007960000001, 42.490527],[-71.4701383, 42.4905669],[-71.470102, 42.4905942],[-71.47009250000001, 42.4905873],[-71.47005830000001, 42.4906131],[-71.4700819, 42.4906303],[-71.4699876, 42.4907175],[-71.4699371, 42.4906876],[-71.4699459, 42.4906794],[-71.4699035, 42.4906542],[-71.4699571, 42.4906046],[-71.46991850000001, 42.4905817],[-71.46995099999999, 42.4905517]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47694869999999, 42.4877487],[-71.4769324, 42.4876663],[-71.4770463, 42.4876539],[-71.4770584, 42.4877154],[-71.477097, 42.4877112],[-71.4771058, 42.487756],[-71.4770183, 42.4877655],[-71.4770324, 42.4878372],[-71.4769744, 42.4878435],[-71.4769677, 42.4878098],[-71.4769393, 42.4878129],[-71.4769445, 42.4878392],[-71.476857, 42.4878486],[-71.47683960000001, 42.4877605],[-71.47694869999999, 42.4877487]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4617755, 42.4738527],[-71.4617588, 42.4737578],[-71.46178020000001, 42.4737557],[-71.4617769, 42.4737367],[-71.4618674, 42.473728],[-71.4618735, 42.4737624],[-71.4618871, 42.4737611],[-71.46190319999999, 42.4738527],[-71.461958, 42.4738474],[-71.4619694, 42.4739116],[-71.46178690000001, 42.4739292],[-71.4617768, 42.473872],[-71.461797, 42.47387],[-71.4617937, 42.4738509],[-71.4617755, 42.4738527]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4793712, 42.4809238],[-71.4794471, 42.4809233],[-71.4794474, 42.480943],[-71.4796269, 42.4809417],[-71.4796278, 42.4810053],[-71.47957630000001, 42.4810056],[-71.4795767, 42.4810372],[-71.479536, 42.4810375],[-71.4795364, 42.4810702],[-71.47933070000001, 42.4810717],[-71.4793301, 42.4810259],[-71.4793032, 42.4810261],[-71.4793021, 42.4809414],[-71.47937140000001, 42.4809409],[-71.4793712, 42.4809238]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46457890000001, 42.4894783],[-71.4646047, 42.4894044],[-71.4647312, 42.4894286],[-71.4647444, 42.4893908],[-71.46477779999999, 42.4893972],[-71.4647691, 42.4894219],[-71.4647939, 42.4894267],[-71.4647666, 42.489505],[-71.4647183, 42.4894957],[-71.46471529999999, 42.4895043],[-71.4646675, 42.4894951],[-71.46466409999999, 42.489505],[-71.4646262, 42.4894977],[-71.46462959999999, 42.489488],[-71.46457890000001, 42.4894783]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4841718, 42.4718631],[-71.484116, 42.4718424],[-71.48419060000001, 42.4718049],[-71.4842304, 42.4718196],[-71.48424300000001, 42.471801],[-71.4842531, 42.4718047],[-71.4843106, 42.4718116],[-71.48430190000001, 42.4718244],[-71.4843729, 42.4718507],[-71.484336, 42.4719053],[-71.484182, 42.4718481],[-71.4841718, 42.4718631]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4663103, 42.4784891],[-71.46629799999999, 42.4784542],[-71.4663161, 42.4784507],[-71.46630999999999, 42.4784333],[-71.4662625, 42.4784424],[-71.4662489, 42.4784036],[-71.4663049, 42.4783929],[-71.46628219999999, 42.4783281],[-71.4663635, 42.4783125],[-71.4663556, 42.4782897],[-71.46637509999999, 42.478286],[-71.4664566, 42.4785184],[-71.4663437, 42.4785401],[-71.4663248, 42.4784863],[-71.4663103, 42.4784891]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4732982, 42.4893679],[-71.4733171, 42.4893354],[-71.4733043, 42.4893313],[-71.473337, 42.4892752],[-71.47342759999999, 42.489304],[-71.4733919, 42.4893654],[-71.4733616, 42.4893558],[-71.47335099999999, 42.4893742],[-71.4733858, 42.4893853],[-71.4733572, 42.4894344],[-71.4733739, 42.4894397],[-71.47334600000001, 42.4894878],[-71.47320259999999, 42.4894422],[-71.473254, 42.4893538],[-71.4732982, 42.4893679]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4604945, 42.478691],[-71.4604959, 42.4786717],[-71.460582, 42.478675],[-71.4605863, 42.4786141],[-71.4606935, 42.4786183],[-71.460683, 42.478766],[-71.4606709, 42.4787655],[-71.4606663, 42.4788294],[-71.4605749, 42.4788259],[-71.4605761, 42.4788094],[-71.4605454, 42.4788082],[-71.46054719999999, 42.4787833],[-71.46050270000001, 42.4787816],[-71.4605091, 42.4786916],[-71.4604945, 42.478691]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46857679999999, 42.4934659],[-71.4686043, 42.4934381],[-71.4686274, 42.4934494],[-71.4686751, 42.4934028],[-71.46874699999999, 42.4934446],[-71.4686961, 42.4934901],[-71.4686504, 42.4934621],[-71.4685877, 42.4935255],[-71.4685746, 42.4935184],[-71.4685267, 42.4935668],[-71.46853609999999, 42.4935719],[-71.4685104, 42.4935979],[-71.46842049999999, 42.4935492],[-71.46852869999999, 42.4934398],[-71.46857679999999, 42.4934659]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695227, 42.4923843],[-71.46956280000001, 42.4923059],[-71.4695967, 42.4923154],[-71.46964730000001, 42.4922235],[-71.4697298, 42.4922527],[-71.4697008, 42.4922963],[-71.469666, 42.4922841],[-71.4696581, 42.4922931],[-71.4696746, 42.4922977],[-71.4696132, 42.492418],[-71.46960180000001, 42.4924148],[-71.4695842, 42.4924494],[-71.4695256, 42.492433],[-71.46954700000001, 42.4923911],[-71.4695227, 42.4923843]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4551318, 42.4723575],[-71.4552046, 42.4721993],[-71.4552228, 42.4722039],[-71.4552422, 42.4721617],[-71.4553422, 42.4721869],[-71.4553011, 42.4722764],[-71.45528899999999, 42.4722734],[-71.4552336, 42.4723937],[-71.4552214, 42.4723906],[-71.45519779999999, 42.4724421],[-71.4551862, 42.4724392],[-71.4551139, 42.4725965],[-71.45501520000001, 42.4725716],[-71.45511550000001, 42.4723534],[-71.4551318, 42.4723575]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752427, 42.475166],[-71.4752403, 42.4751955],[-71.47536909999999, 42.4752011],[-71.4753769, 42.4751037],[-71.4755454, 42.475111],[-71.4755419, 42.4751552],[-71.4754896, 42.4751529],[-71.4754848, 42.4752124],[-71.47539810000001, 42.4752086],[-71.4753939, 42.475261],[-71.4752687, 42.4752556],[-71.475266, 42.4752898],[-71.4750971, 42.4752824],[-71.47510680000001, 42.4751601],[-71.4752427, 42.475166]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"28","addr:state":"MA","source:datetime":"2014-11-08T14:01:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4533553, 42.4910172],[-71.453317, 42.4908753],[-71.453356, 42.4908696],[-71.4533467, 42.4908352],[-71.4533268, 42.4908382],[-71.45331109999999, 42.4907802],[-71.4533965, 42.4907676],[-71.45341190000001, 42.4908247],[-71.4533955, 42.4908271],[-71.45340539999999, 42.4908636],[-71.4534268, 42.4908605],[-71.45346790000001, 42.4910126],[-71.4534017, 42.4910224],[-71.4533986, 42.4910108],[-71.4533553, 42.4910172]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45233500000001, 42.4762884],[-71.4523205, 42.4762734],[-71.4523715, 42.4762464],[-71.4524404, 42.4763176],[-71.4524735, 42.4763001],[-71.45244700000001, 42.4762727],[-71.452496, 42.4762467],[-71.45255040000001, 42.476303],[-71.45247000000001, 42.4763457],[-71.4524555, 42.4763308],[-71.4524116, 42.476354],[-71.452404, 42.4763462],[-71.4523248, 42.4763882],[-71.4522644, 42.4763258],[-71.45233500000001, 42.4762884]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4668832, 42.4776838],[-71.46686339999999, 42.4776431],[-71.4668416, 42.4776489],[-71.4668255, 42.4776159],[-71.4668451, 42.4776106],[-71.4668271, 42.4775736],[-71.4669439, 42.4775425],[-71.4669944, 42.4776461],[-71.4670471, 42.4776321],[-71.4670682, 42.4776756],[-71.4670381, 42.4776836],[-71.46704939999999, 42.4777068],[-71.46693019999999, 42.4777386],[-71.4669013, 42.477679],[-71.4668832, 42.4776838]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47171729999999, 42.4826593],[-71.4717347, 42.482635],[-71.471796, 42.4826591],[-71.4718324, 42.4826081],[-71.4717478, 42.4825749],[-71.4718142, 42.4824822],[-71.47192149999999, 42.4825242],[-71.47188540000001, 42.4825746],[-71.47192579999999, 42.4825904],[-71.471857, 42.4826866],[-71.471829, 42.4826757],[-71.47178409999999, 42.4827385],[-71.47170370000001, 42.4827069],[-71.4717333, 42.4826656],[-71.47171729999999, 42.4826593]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689589, 42.4943823],[-71.46896510000001, 42.4943722],[-71.4689453, 42.4943656],[-71.468985, 42.4942999],[-71.46911710000001, 42.4943437],[-71.4691064, 42.4943613],[-71.46918359999999, 42.4943869],[-71.46915799999999, 42.4944291],[-71.4690793, 42.494403],[-71.4690741, 42.4944117],[-71.46904360000001, 42.4944016],[-71.469027, 42.4944291],[-71.4689638, 42.4944081],[-71.4689759, 42.494388],[-71.4689589, 42.4943823]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744711, 42.4725592],[-71.4744743, 42.4723989],[-71.4745745, 42.4724],[-71.4745727, 42.472492],[-71.47465149999999, 42.4724928],[-71.4746518, 42.4724764],[-71.4746846, 42.4724767],[-71.4746842, 42.4724932],[-71.4747411, 42.4724938],[-71.4747397, 42.4725631],[-71.4746177, 42.4725617],[-71.47461699999999, 42.4725984],[-71.474484, 42.4725969],[-71.4744848, 42.4725594],[-71.4744711, 42.4725592]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Mead Terrace","addr:city":"Acton","building":"yes","addr:housenumber":"5","addr:state":"MA","source:datetime":"2014-11-18T02:34:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763646, 42.476784],[-71.4763657, 42.4767607],[-71.4763136, 42.4767594],[-71.47631579999999, 42.4767134],[-71.47634669999999, 42.4767142],[-71.4763481, 42.4766856],[-71.4764248, 42.4766876],[-71.4764235, 42.4767137],[-71.4764679, 42.4767149],[-71.4764626, 42.4768236],[-71.47636369999999, 42.476821],[-71.47636489999999, 42.4767969],[-71.4763289, 42.476796],[-71.47632950000001, 42.4767831],[-71.4763646, 42.476784]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4591437, 42.4720572],[-71.4591451, 42.472023],[-71.4592646, 42.4720257],[-71.45926179999999, 42.4720935],[-71.45915669999999, 42.4720911],[-71.4591525, 42.4721926],[-71.4591765, 42.4721931],[-71.4591736, 42.4722622],[-71.45906549999999, 42.4722598],[-71.4590681, 42.4721963],[-71.4590355, 42.4721956],[-71.4590374, 42.4721509],[-71.4590144, 42.4721504],[-71.4590183, 42.4720543],[-71.4591437, 42.4720572]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4581383, 42.4749286],[-71.45811550000001, 42.4750454],[-71.45814559999999, 42.4750486],[-71.4581261, 42.4751484],[-71.4580512, 42.4751404],[-71.4580563, 42.4751139],[-71.4580409, 42.4751122],[-71.4580425, 42.4751037],[-71.4580304, 42.4751024],[-71.4580335, 42.4750862],[-71.4580058, 42.4750832],[-71.45803100000001, 42.4749538],[-71.4579889, 42.4749493],[-71.4579959, 42.4749133],[-71.4581383, 42.4749286]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4741255, 42.4916604],[-71.4741709, 42.4917085],[-71.47425269999999, 42.4916663],[-71.4743101, 42.4917272],[-71.4742402, 42.4917633],[-71.4741969, 42.4917174],[-71.4741823, 42.4917249],[-71.4742201, 42.4917651],[-71.4741642, 42.491794],[-71.4741308, 42.4917586],[-71.4740793, 42.4917852],[-71.4741006, 42.4918077],[-71.4740651, 42.4918261],[-71.4739798, 42.4917356],[-71.4741255, 42.4916604]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46927359999999, 42.4955644],[-71.46932700000001, 42.4955016],[-71.4694535, 42.4955594],[-71.469404, 42.4956187],[-71.4693894, 42.495612],[-71.46936530000001, 42.4956408],[-71.4693814, 42.4956481],[-71.469369, 42.4956639],[-71.4693949, 42.4956717],[-71.46937079999999, 42.4957659],[-71.4692869, 42.4957542],[-71.4693195, 42.4956266],[-71.4693065, 42.49562],[-71.469328, 42.4955897],[-71.46927359999999, 42.4955644]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47311379999999, 42.4777128],[-71.4731056, 42.4776977],[-71.4730905, 42.4777022],[-71.47307739999999, 42.4776782],[-71.47309919999999, 42.4776716],[-71.47308580000001, 42.477647],[-71.47318749999999, 42.4776166],[-71.4732499, 42.4777309],[-71.4732298, 42.4777369],[-71.4732575, 42.4777875],[-71.4731745, 42.4778124],[-71.47314369999999, 42.4777559],[-71.47315999999999, 42.477751],[-71.47313560000001, 42.4777063],[-71.47311379999999, 42.4777128]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759565, 42.4899946],[-71.4759638, 42.4899806],[-71.4759767, 42.4899843],[-71.47600009999999, 42.48994],[-71.47601280000001, 42.4899436],[-71.47602550000001, 42.4899196],[-71.47598139999999, 42.4899069],[-71.47600009999999, 42.4898714],[-71.4760946, 42.4898987],[-71.47606500000001, 42.4899548],[-71.4760929, 42.4899629],[-71.4760395, 42.4900642],[-71.475921, 42.49003],[-71.4759418, 42.4899904],[-71.4759565, 42.4899946]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46266540000001, 42.484233],[-71.462693, 42.4841765],[-71.4626693, 42.4841702],[-71.46268310000001, 42.484142],[-71.4626981, 42.484146],[-71.4627184, 42.4841045],[-71.46294380000001, 42.4841649],[-71.4629101, 42.4842337],[-71.4628774, 42.4842249],[-71.46282770000001, 42.4843265],[-71.4627418, 42.4843035],[-71.4627495, 42.4842879],[-71.46263999999999, 42.4842586],[-71.462654, 42.48423],[-71.46266540000001, 42.484233]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4808642, 42.4742094],[-71.48080040000001, 42.4741877],[-71.4808371, 42.4741285],[-71.48091580000001, 42.4741553],[-71.48088199999999, 42.4742097],[-71.48100820000001, 42.4742526],[-71.4810174, 42.4742378],[-71.4811028, 42.4742668],[-71.48108089999999, 42.474302],[-71.48109530000001, 42.4743069],[-71.4810789, 42.4743334],[-71.4810677, 42.4743296],[-71.4810394, 42.4743752],[-71.4808098, 42.4742971],[-71.4808642, 42.4742094]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"172","addr:state":"MA","source:datetime":"2014-11-08T14:01:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4562502, 42.4989058],[-71.45623759999999, 42.4988918],[-71.45624909999999, 42.4988861],[-71.45622160000001, 42.4988555],[-71.4563148, 42.4988095],[-71.4563603, 42.49886],[-71.456394, 42.4988434],[-71.4564184, 42.4988704],[-71.45638959999999, 42.4988847],[-71.4564348, 42.4989347],[-71.45632639999999, 42.4989882],[-71.4562828, 42.4989398],[-71.45629289999999, 42.4989348],[-71.4562617, 42.4989002],[-71.4562502, 42.4989058]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4776009, 42.4721212],[-71.4775975, 42.4721297],[-71.47753969999999, 42.4721174],[-71.47757230000001, 42.4720336],[-71.4775841, 42.4720362],[-71.4776026, 42.4719886],[-71.4777145, 42.4720125],[-71.477681, 42.4720986],[-71.4776991, 42.4721024],[-71.4776844, 42.4721402],[-71.4776689, 42.4721369],[-71.47766439999999, 42.4721484],[-71.47763279999999, 42.4721417],[-71.4776377, 42.4721291],[-71.4776009, 42.4721212]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4623618, 42.4721849],[-71.462372, 42.4721484],[-71.46232759999999, 42.4721417],[-71.4623477, 42.4720695],[-71.46243219999999, 42.4720824],[-71.46241689999999, 42.4721373],[-71.46247200000001, 42.4721457],[-71.4624635, 42.472192],[-71.4624166, 42.4721881],[-71.4623936, 42.4723044],[-71.4623017, 42.4722946],[-71.4623208, 42.472226],[-71.4623093, 42.4722243],[-71.46232190000001, 42.4721788],[-71.4623618, 42.4721849]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4656562, 42.4807668],[-71.4656675, 42.4807232],[-71.4655564, 42.4807074],[-71.4655841, 42.4806006],[-71.46566180000001, 42.4806117],[-71.4656488, 42.4806617],[-71.46575850000001, 42.4806773],[-71.46575180000001, 42.4807033],[-71.4658335, 42.4807149],[-71.4658135, 42.480792],[-71.4657258, 42.4807796],[-71.46572190000001, 42.4807944],[-71.4656829, 42.4807889],[-71.46568739999999, 42.4807712],[-71.4656562, 42.4807668]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47507090000001, 42.4741026],[-71.4750726, 42.474075],[-71.47513120000001, 42.474077],[-71.47512949999999, 42.4741042],[-71.47514630000001, 42.4741047],[-71.4751414, 42.4741826],[-71.47522650000001, 42.4741856],[-71.4752221, 42.4742558],[-71.4751382, 42.4742529],[-71.4751401, 42.4742232],[-71.4750549, 42.4742203],[-71.4750568, 42.4741901],[-71.4750428, 42.4741896],[-71.4750483, 42.4741019],[-71.47507090000001, 42.4741026]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4513797, 42.4863065],[-71.4513776, 42.4863179],[-71.4512471, 42.4863047],[-71.4512615, 42.4862268],[-71.45140050000001, 42.4862408],[-71.4514126, 42.486175],[-71.4515205, 42.4861859],[-71.451509, 42.4862481],[-71.45153980000001, 42.4862512],[-71.451527, 42.4863204],[-71.4514411, 42.4863117],[-71.4514368, 42.486335],[-71.4514015, 42.4863314],[-71.4514056, 42.4863091],[-71.4513797, 42.4863065]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4661344, 42.4861577],[-71.4661728, 42.4861147],[-71.4661445, 42.4861008],[-71.4661959, 42.4860432],[-71.46631669999999, 42.4861021],[-71.4663008, 42.48612],[-71.46633660000001, 42.4861375],[-71.4663018, 42.4861765],[-71.46627030000001, 42.4861611],[-71.466182, 42.4862602],[-71.4660842, 42.4862124],[-71.466117, 42.4861756],[-71.46610440000001, 42.4861695],[-71.4661208, 42.4861511],[-71.4661344, 42.4861577]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"5","addr:state":"MA","source:datetime":"2014-11-08T14:01:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45443280000001, 42.4956816],[-71.4544058, 42.4955032],[-71.454538, 42.4954922],[-71.45454479999999, 42.4955376],[-71.4545206, 42.4955396],[-71.4545358, 42.4956402],[-71.4545603, 42.4956381],[-71.4545557, 42.4956077],[-71.4545901, 42.4956049],[-71.45459870000001, 42.4956621],[-71.4545681, 42.4956646],[-71.4545741, 42.495704],[-71.4544595, 42.4957135],[-71.4544544, 42.4956798],[-71.45443280000001, 42.4956816]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45288909999999, 42.4909805],[-71.4528792, 42.4909704],[-71.4529283, 42.4909442],[-71.4529196, 42.4909352],[-71.45296190000001, 42.4909126],[-71.45299919999999, 42.4909508],[-71.45302270000001, 42.4909383],[-71.45306069999999, 42.4909773],[-71.4529721, 42.4910246],[-71.4529974, 42.4910506],[-71.4529287, 42.4910873],[-71.452901, 42.4910601],[-71.4528365, 42.4910959],[-71.4527844, 42.4910363],[-71.45288909999999, 42.4909805]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47308649999999, 42.4750749],[-71.4731254, 42.4750736],[-71.4731226, 42.4750282],[-71.4732173, 42.4750251],[-71.47321909999999, 42.4750538],[-71.4733274, 42.4750508],[-71.47332780000001, 42.4751409],[-71.47327610000001, 42.4751426],[-71.473277, 42.4751573],[-71.4732131, 42.4751594],[-71.4732125, 42.4751481],[-71.4731244, 42.4751494],[-71.473122, 42.4751083],[-71.4730886, 42.4751094],[-71.47308649999999, 42.4750749]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46175239999999, 42.4760333],[-71.4617525, 42.476021],[-71.4616032, 42.4760203],[-71.4616036, 42.4759724],[-71.46175359999999, 42.4759731],[-71.461754, 42.475929],[-71.4619386, 42.4759298],[-71.461938, 42.4760032],[-71.4620041, 42.4760035],[-71.4620037, 42.4760482],[-71.46193510000001, 42.4760479],[-71.461935, 42.4760599],[-71.4618354, 42.4760594],[-71.4618356, 42.4760337],[-71.46175239999999, 42.4760333]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.451053, 42.4916866],[-71.4510944, 42.4916915],[-71.45131910000001, 42.4916944],[-71.4513188, 42.4917072],[-71.4513745, 42.4917079],[-71.45137320000001, 42.4917651],[-71.45131569999999, 42.4917643],[-71.4513154, 42.4917743],[-71.4511012, 42.4917716],[-71.4511018, 42.4917494],[-71.45098969999999, 42.4917413],[-71.4509511, 42.4917305],[-71.4509832, 42.4916672],[-71.451053, 42.4916866]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4770946, 42.4760331],[-71.477215, 42.4760547],[-71.4771993, 42.4761024],[-71.4772273, 42.4761074],[-71.4772122, 42.4761535],[-71.47719910000001, 42.4761511],[-71.4771916, 42.4761738],[-71.4771602, 42.4761859],[-71.4770396, 42.4761653],[-71.4770455, 42.4761463],[-71.4770256, 42.4761429],[-71.4770525, 42.476065],[-71.4770826, 42.4760699],[-71.4770946, 42.4760331]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728628, 42.4923459],[-71.4728906, 42.4923764],[-71.4729449, 42.4923741],[-71.47296179999999, 42.4923221],[-71.47310539999999, 42.4923501],[-71.4730785, 42.4924258],[-71.472977, 42.492406],[-71.4729655, 42.4924382],[-71.4729109, 42.4924275],[-71.4729238, 42.4923911],[-71.4728964, 42.4923926],[-71.4728269, 42.4924241],[-71.4727912, 42.4923763],[-71.4728628, 42.4923459]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4796613, 42.4795278],[-71.47978500000001, 42.4795396],[-71.4797695, 42.4796286],[-71.4797506, 42.4796268],[-71.4797446, 42.4796614],[-71.4796207, 42.4796496],[-71.47962269999999, 42.4796384],[-71.4796035, 42.4796365],[-71.4796042, 42.4796316],[-71.47961170000001, 42.4796124],[-71.47962560000001, 42.4795962],[-71.4796303, 42.4795666],[-71.4796513, 42.4795682],[-71.4796613, 42.4795278]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4762583, 42.4759595],[-71.4762681, 42.4759582],[-71.4762776, 42.4759602],[-71.4762851, 42.475965],[-71.47628899999999, 42.4759713],[-71.4762885, 42.475976],[-71.4763301, 42.4759783],[-71.4763155, 42.4761247],[-71.4761924, 42.476118],[-71.47620670000001, 42.4759752],[-71.4762444, 42.4759772],[-71.4762453, 42.47597],[-71.4762502, 42.4759636],[-71.4762583, 42.4759595]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"196","addr:state":"MA","source:datetime":"2015-01-20T23:47:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46098859999999, 42.4992643],[-71.4610031, 42.4992509],[-71.46102, 42.4992452],[-71.46103840000001, 42.4992449],[-71.46107430000001, 42.4992257],[-71.4611451, 42.4992963],[-71.4608703, 42.499456],[-71.46079109999999, 42.4993814],[-71.460859, 42.4993419],[-71.4608405, 42.4993246],[-71.46088109999999, 42.499301],[-71.4609079, 42.4993261],[-71.46098499999999, 42.4992813],[-71.46098859999999, 42.4992643]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47035889999999, 42.4909563],[-71.47047689999999, 42.4908528],[-71.47056550000001, 42.4909082],[-71.4705246, 42.4909441],[-71.47056600000001, 42.49097],[-71.4705258, 42.4910053],[-71.4704838, 42.4909791],[-71.470428, 42.491028],[-71.4704413, 42.4910363],[-71.4704138, 42.4910605],[-71.4703384, 42.4910134],[-71.47038499999999, 42.4909726],[-71.47035889999999, 42.4909563]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763935, 42.4743294],[-71.4763967, 42.4743023],[-71.4764129, 42.4743033],[-71.4764176, 42.474263],[-71.47647430000001, 42.4742666],[-71.4764718, 42.4742879],[-71.4765271, 42.4742915],[-71.4765154, 42.4743898],[-71.4763849, 42.4743813],[-71.476388, 42.4743546],[-71.4763743, 42.4743537],[-71.4763773, 42.4743284],[-71.4763935, 42.4743294]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744287, 42.4745881],[-71.4744304, 42.4745517],[-71.47438630000001, 42.4745505],[-71.4743891, 42.4744935],[-71.4744946, 42.4744963],[-71.4744919, 42.4745506],[-71.4745415, 42.4745519],[-71.47453950000001, 42.4745937],[-71.47455600000001, 42.4745941],[-71.4745532, 42.4746512],[-71.47436999999999, 42.4746463],[-71.47437290000001, 42.4745866],[-71.4744287, 42.4745881]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4535814, 42.4730653],[-71.4535576, 42.4730476],[-71.4535343, 42.473065],[-71.4535423, 42.4730709],[-71.4535322, 42.4730784],[-71.4534706, 42.4730328],[-71.4534798, 42.473026],[-71.4533928, 42.4729618],[-71.45350259999999, 42.4728802],[-71.45354810000001, 42.4729138],[-71.4535145, 42.4729388],[-71.4536334, 42.4730267],[-71.4535814, 42.4730653]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46979589999999, 42.4913539],[-71.4698368, 42.4913085],[-71.4698877, 42.4913336],[-71.46991439999999, 42.4913312],[-71.46993569999999, 42.4913438],[-71.4698944, 42.4913804],[-71.46987420000001, 42.4913692],[-71.4698552, 42.4913902],[-71.46990750000001, 42.4914172],[-71.4698494, 42.4914745],[-71.46974640000001, 42.4914237],[-71.4698052, 42.4913584],[-71.46979589999999, 42.4913539]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48342510000001, 42.4720758],[-71.48347029999999, 42.472012],[-71.4835458, 42.4720413],[-71.483535, 42.4720566],[-71.4835791, 42.4720737],[-71.4835976, 42.4720476],[-71.4837021, 42.4720881],[-71.48365130000001, 42.4721599],[-71.48358880000001, 42.4721356],[-71.4835685, 42.4721643],[-71.4835181, 42.4721448],[-71.48353640000001, 42.472119],[-71.48342510000001, 42.4720758]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45044350000001, 42.485627],[-71.45042460000001, 42.485547],[-71.4504402, 42.485545],[-71.4504333, 42.4855158],[-71.4505135, 42.4855054],[-71.4505188, 42.4855279],[-71.45061080000001, 42.485516],[-71.4506176, 42.4855451],[-71.45071590000001, 42.4855324],[-71.45074169999999, 42.485614],[-71.4506349, 42.4856319],[-71.4506282, 42.4856032],[-71.45044350000001, 42.485627]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4646796, 42.4790995],[-71.464624, 42.479068],[-71.46456910000001, 42.4791211],[-71.464517, 42.4790915],[-71.46463919999999, 42.4789733],[-71.4646965, 42.4790058],[-71.4647274, 42.478976],[-71.4648231, 42.4790302],[-71.46481470000001, 42.4790383],[-71.464885, 42.4790781],[-71.4648258, 42.4791354],[-71.4647102, 42.4790699],[-71.4646796, 42.4790995]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4506312, 42.4883675],[-71.4505673, 42.4883542],[-71.45061339999999, 42.4882327],[-71.45067969999999, 42.4882465],[-71.45067539999999, 42.4882578],[-71.4507186, 42.4882668],[-71.4507155, 42.4882751],[-71.45074820000001, 42.4882819],[-71.4507613, 42.4882475],[-71.4508599, 42.4882681],[-71.45081380000001, 42.4883896],[-71.4506368, 42.4883528],[-71.4506312, 42.4883675]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4730011, 42.4918707],[-71.4730239, 42.4918575],[-71.47298619999999, 42.4918217],[-71.4730879, 42.491763],[-71.4730699, 42.491746],[-71.47313370000001, 42.4917091],[-71.4731779, 42.491751],[-71.47311019999999, 42.4917901],[-71.4731519, 42.4918297],[-71.472992, 42.491922],[-71.4729401, 42.4918727],[-71.4729794, 42.49185],[-71.4730011, 42.4918707]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47230570000001, 42.4930289],[-71.47230639999999, 42.4929912],[-71.4722512, 42.4929906],[-71.4722527, 42.4929166],[-71.4723535, 42.4929177],[-71.4723528, 42.4929526],[-71.4723947, 42.4929531],[-71.47239329999999, 42.4930253],[-71.4723572, 42.4930249],[-71.4723558, 42.4930993],[-71.4722501, 42.4930981],[-71.4722515, 42.4930283],[-71.47230570000001, 42.4930289]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46072239999999, 42.4824057],[-71.4607372, 42.4823831],[-71.46068289999999, 42.4823637],[-71.46073370000001, 42.4822856],[-71.460885, 42.4823395],[-71.4608745, 42.4823558],[-71.4609528, 42.4823837],[-71.46088690000001, 42.4824851],[-71.4608626, 42.4824764],[-71.4608444, 42.4825044],[-71.4607681, 42.4824771],[-71.4607973, 42.4824324],[-71.46072239999999, 42.4824057]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4719477, 42.4758914],[-71.47195550000001, 42.475806],[-71.4720528, 42.4758109],[-71.4720519, 42.4758207],[-71.47208329999999, 42.4758223],[-71.4720799, 42.4758596],[-71.4722106, 42.4758662],[-71.4722068, 42.4759075],[-71.4721577, 42.475905],[-71.47215509999999, 42.4759332],[-71.4720703, 42.4759289],[-71.4720731, 42.4758977],[-71.4719477, 42.4758914]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4801097, 42.480957],[-71.48011579999999, 42.480865],[-71.48009879999999, 42.4808643],[-71.48010309999999, 42.4807992],[-71.4802909, 42.480806],[-71.4802892, 42.4808321],[-71.48024770000001, 42.4808306],[-71.4802358, 42.4810089],[-71.4801854, 42.4810071],[-71.48018380000001, 42.4810309],[-71.4800822, 42.4810272],[-71.4800869, 42.4809562],[-71.4801097, 42.480957]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4658688, 42.4905858],[-71.4659395, 42.4904775],[-71.46597730000001, 42.490491],[-71.4659928, 42.4904672],[-71.4660635, 42.4904925],[-71.4660258, 42.4905503],[-71.4660056, 42.4905431],[-71.4659158, 42.4906806],[-71.46584009999999, 42.4906535],[-71.4658472, 42.4906426],[-71.4658122, 42.4906301],[-71.4658464, 42.4905777],[-71.4658688, 42.4905858]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47109519999999, 42.4833957],[-71.4710668, 42.4833835],[-71.4710597, 42.4833934],[-71.4709096, 42.4833473],[-71.470941, 42.4832912],[-71.47112420000001, 42.4833474],[-71.4711351, 42.483328],[-71.4712263, 42.4833642],[-71.4711739, 42.4834252],[-71.47113950000001, 42.4834085],[-71.4711554, 42.4833906],[-71.4711164, 42.4833716],[-71.47109519999999, 42.4833957]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4750445, 42.4721409],[-71.47504600000001, 42.4721148],[-71.4750244, 42.4721141],[-71.47502609999999, 42.4720848],[-71.4750442, 42.4720854],[-71.47504549999999, 42.4720628],[-71.47512829999999, 42.4720655],[-71.4751239, 42.4721431],[-71.47513840000001, 42.4721435],[-71.4751345, 42.4722108],[-71.4750177, 42.4722071],[-71.4750215, 42.4721402],[-71.4750445, 42.4721409]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4746232, 42.4776421],[-71.4746188, 42.4776106],[-71.4746811, 42.4776059],[-71.47467810000001, 42.4775846],[-71.47477019999999, 42.4775776],[-71.4747818, 42.4776613],[-71.4747972, 42.4776602],[-71.4748017, 42.4776929],[-71.4746459, 42.4777047],[-71.4746411, 42.4776705],[-71.47469100000001, 42.4776667],[-71.47468689999999, 42.4776373],[-71.4746232, 42.4776421]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4604361, 42.4884602],[-71.4603597, 42.4883589],[-71.460489, 42.4883055],[-71.4605092, 42.4883324],[-71.46052039999999, 42.4883278],[-71.46056400000001, 42.4883857],[-71.46051989999999, 42.4884039],[-71.4605346, 42.4884234],[-71.4604858, 42.4884436],[-71.46049189999999, 42.4884517],[-71.4604705, 42.4884605],[-71.4604621, 42.4884494],[-71.4604361, 42.4884602]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Bulette Road","addr:city":"Acton","building":"yes","addr:housenumber":"22","addr:state":"MA","source:datetime":"2015-01-20T23:47:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4690244, 42.4984081],[-71.46897869999999, 42.4983723],[-71.4690227, 42.4983415],[-71.4690377, 42.4983532],[-71.4691115, 42.4983016],[-71.4691751, 42.4983514],[-71.4691169, 42.4983921],[-71.4691761, 42.4984385],[-71.46910699999999, 42.4984868],[-71.4690451, 42.4984383],[-71.469064, 42.4984251],[-71.46903380000001, 42.4984015],[-71.4690244, 42.4984081]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4611359, 42.476847],[-71.4611439, 42.4767812],[-71.46120929999999, 42.4767856],[-71.46121050000001, 42.4767762],[-71.4613682, 42.4767867],[-71.4613659, 42.4768056],[-71.4614392, 42.4768104],[-71.4614323, 42.4768678],[-71.4613562, 42.4768627],[-71.46135460000001, 42.4768762],[-71.4611915, 42.4768653],[-71.4611932, 42.4768508],[-71.4611359, 42.476847]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46723369999999, 42.4786584],[-71.4672279, 42.4785402],[-71.46735510000001, 42.4785367],[-71.4673569, 42.4785738],[-71.4673335, 42.4785745],[-71.4673354, 42.4786125],[-71.4673524, 42.4786121],[-71.4673543, 42.478651],[-71.4673987, 42.4786498],[-71.46740079999999, 42.4786917],[-71.4672502, 42.4786957],[-71.46724829999999, 42.478658],[-71.46723369999999, 42.4786584]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4507409, 42.4839038],[-71.450728, 42.4838753],[-71.4508151, 42.4838537],[-71.4508283, 42.4838827],[-71.4508924, 42.4838668],[-71.4509221, 42.4839324],[-71.4508148, 42.4839591],[-71.4508093, 42.4839468],[-71.45078049999999, 42.4839539],[-71.4507917, 42.4839787],[-71.45065889999999, 42.4840117],[-71.4506233, 42.483933],[-71.4507409, 42.4839038]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47332900000001, 42.477948],[-71.4733356, 42.4779609],[-71.4734043, 42.4779415],[-71.4734369, 42.4780049],[-71.47340629999999, 42.4780136],[-71.4734022, 42.4780057],[-71.4733713, 42.4780144],[-71.4734097, 42.4780891],[-71.4733002, 42.47812],[-71.4732691, 42.4780595],[-71.4733164, 42.4780462],[-71.4732739, 42.4779635],[-71.47332900000001, 42.477948]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47852450000001, 42.4762444],[-71.4785184, 42.4762578],[-71.47857689999999, 42.4762724],[-71.4785692, 42.4762894],[-71.4786002, 42.4762971],[-71.47859, 42.4763197],[-71.4786302, 42.4763298],[-71.4785969, 42.4764029],[-71.4784633, 42.4763695],[-71.4784709, 42.4763527],[-71.4783872, 42.4763319],[-71.478437, 42.4762226],[-71.47852450000001, 42.4762444]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45293789999999, 42.4870947],[-71.4528688, 42.4871558],[-71.4529074, 42.4871797],[-71.4528603, 42.4872214],[-71.4528724, 42.4872289],[-71.4528366, 42.4872606],[-71.45279909999999, 42.4872374],[-71.4528208, 42.4872183],[-71.4527922, 42.4872006],[-71.4528055, 42.4871888],[-71.4527428, 42.48715],[-71.45285990000001, 42.4870464],[-71.45293789999999, 42.4870947]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4675392, 42.4868034],[-71.4674795, 42.4867475],[-71.4676314, 42.4866588],[-71.4676738, 42.4866985],[-71.46765379999999, 42.4867103],[-71.4676877, 42.486742],[-71.46770840000001, 42.4867299],[-71.4677473, 42.4867664],[-71.46772489999999, 42.4867795],[-71.4677585, 42.486811],[-71.4676845, 42.4868542],[-71.4675953, 42.4867706],[-71.4675392, 42.4868034]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4727003, 42.4758462],[-71.47269799999999, 42.4757881],[-71.4727524, 42.4757869],[-71.4727537, 42.4758195],[-71.4728009, 42.4758185],[-71.4728046, 42.4759118],[-71.4727864, 42.4759122],[-71.4727875, 42.4759411],[-71.47264060000001, 42.4759443],[-71.4726376, 42.4758677],[-71.4727183, 42.475866],[-71.4727175, 42.4758458],[-71.4727003, 42.4758462]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4681071, 42.4853194],[-71.46812629999999, 42.4853283],[-71.4682011, 42.4852402],[-71.46828240000001, 42.485278],[-71.4682102, 42.4853632],[-71.4681762, 42.4853474],[-71.4681556, 42.4853716],[-71.4681796, 42.4853827],[-71.46813210000001, 42.4854387],[-71.4680604, 42.4854053],[-71.468104, 42.485354],[-71.4680852, 42.4853452],[-71.4681071, 42.4853194]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4693345, 42.4830811],[-71.4693644, 42.4830327],[-71.4693965, 42.4830436],[-71.4694338, 42.4829832],[-71.4695014, 42.483006],[-71.4694836, 42.4830348],[-71.4695218, 42.4830477],[-71.46947280000001, 42.483127],[-71.46944240000001, 42.4831167],[-71.4694226, 42.4831489],[-71.4693398, 42.4831208],[-71.46935910000001, 42.4830894],[-71.4693345, 42.4830811]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4683722, 42.4850198],[-71.4684901, 42.4848764],[-71.4685552, 42.4849057],[-71.4685298, 42.4849367],[-71.46855410000001, 42.4849477],[-71.4684863, 42.4850301],[-71.4684669, 42.4850214],[-71.46844230000001, 42.4850512],[-71.4685259, 42.4850889],[-71.4684781, 42.485147],[-71.46825509999999, 42.4850465],[-71.4683028, 42.4849885],[-71.4683722, 42.4850198]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4669995, 42.4874755],[-71.4670285, 42.4874318],[-71.4670526, 42.4874406],[-71.46707139999999, 42.4874121],[-71.46713320000001, 42.4874346],[-71.4671135, 42.4874644],[-71.4671263, 42.4874691],[-71.4670503, 42.4875838],[-71.4669528, 42.4875485],[-71.4669918, 42.4874896],[-71.466966, 42.4874803],[-71.4669751, 42.4874666],[-71.4669995, 42.4874755]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4620408, 42.4768087],[-71.46201720000001, 42.4767745],[-71.4620781, 42.4767516],[-71.46210120000001, 42.4767851],[-71.46215290000001, 42.4767656],[-71.4621231, 42.4767223],[-71.46222469999999, 42.476684],[-71.4623096, 42.4768075],[-71.4621606, 42.4768637],[-71.4621518, 42.4768509],[-71.46207010000001, 42.4768818],[-71.46202409999999, 42.476815],[-71.4620408, 42.4768087]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47127709999999, 42.4786907],[-71.4713069, 42.4786635],[-71.4712909, 42.4786539],[-71.4713757, 42.4785768],[-71.471439, 42.4786149],[-71.4714129, 42.4786387],[-71.47145039999999, 42.4786613],[-71.47143010000001, 42.4786797],[-71.4714405, 42.478686],[-71.47133770000001, 42.4787796],[-71.4712134, 42.4787047],[-71.4712481, 42.4786732],[-71.47127709999999, 42.4786907]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692637, 42.4734172],[-71.46930829999999, 42.4733657],[-71.4693793, 42.4733995],[-71.4693394, 42.4734458],[-71.46936599999999, 42.4734582],[-71.4693419, 42.4734865],[-71.4693049, 42.4734693],[-71.46927549999999, 42.473504],[-71.4693063, 42.4735184],[-71.46928, 42.4735494],[-71.4691689, 42.4734977],[-71.4692448, 42.4734084],[-71.4692637, 42.4734172]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:10Z"},"geometry": {"type":"LineString","coordinates": [[-71.462524, 42.4718488],[-71.46243010000001, 42.4718293]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47469049999999, 42.4718222],[-71.4746856, 42.4719431],[-71.4747627, 42.4719448],[-71.47476020000001, 42.4720073],[-71.474661, 42.4720051],[-71.4746624, 42.471972],[-71.47461269999999, 42.4719709],[-71.4746141, 42.471936],[-71.4745829, 42.4719353],[-71.4745843, 42.4719011],[-71.47452749999999, 42.4718999],[-71.4745307, 42.4718187],[-71.47469049999999, 42.4718222]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4518485, 42.4862361],[-71.45189240000001, 42.4862424],[-71.451898, 42.4862212],[-71.45203119999999, 42.4862404],[-71.4520382, 42.4862138],[-71.45211430000001, 42.4862248],[-71.4520978, 42.4862873],[-71.4520174, 42.4862756],[-71.4520117, 42.4862975],[-71.45199650000001, 42.4862953],[-71.4519942, 42.4863042],[-71.45183659999999, 42.4862814],[-71.4518485, 42.4862361]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687086, 42.494245],[-71.468659, 42.4942197],[-71.468667, 42.4942111],[-71.46862, 42.4941872],[-71.4685951, 42.4942139],[-71.4685509, 42.4941914],[-71.4686373, 42.4940985],[-71.4687869, 42.4941747],[-71.46875559999999, 42.4942084],[-71.46878220000001, 42.4942219],[-71.468751, 42.4942554],[-71.4687156, 42.4942374],[-71.4687086, 42.494245]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.475719, 42.4847513],[-71.4757151, 42.4847182],[-71.4757553, 42.4847157],[-71.47573610000001, 42.48455],[-71.4758278, 42.4845442],[-71.4758329, 42.4845881],[-71.4758112, 42.4845895],[-71.4758149, 42.4846218],[-71.4758301, 42.4846208],[-71.47584569999999, 42.4847557],[-71.4757408, 42.4847623],[-71.47573939999999, 42.48475],[-71.475719, 42.4847513]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4784252, 42.4884494],[-71.47842850000001, 42.4884093],[-71.4784014, 42.4884081],[-71.4784039, 42.4883789],[-71.4785426, 42.4883852],[-71.4785378, 42.4884439],[-71.47851660000001, 42.4884429],[-71.478514, 42.4884745],[-71.47846629999999, 42.4884723],[-71.4784609, 42.4885376],[-71.4783663, 42.4885333],[-71.4783735, 42.488447],[-71.4784252, 42.4884494]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4830779, 42.4729955],[-71.4830608, 42.4728955],[-71.48316, 42.4728862],[-71.4831664, 42.4729239],[-71.48319189999999, 42.4729215],[-71.48319480000001, 42.4729386],[-71.48317280000001, 42.4729407],[-71.4831784, 42.4729734],[-71.48323379999999, 42.4729682],[-71.4832413, 42.4730122],[-71.4831042, 42.473025],[-71.4830989, 42.4729936],[-71.4830779, 42.4729955]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:07Z"},"geometry": {"type":"LineString","coordinates": [[-71.4508798, 42.4718457],[-71.45092820000001, 42.4718589],[-71.4509008, 42.471914],[-71.45085, 42.4719001],[-71.4508302, 42.4719398],[-71.4508422, 42.471943],[-71.45082979999999, 42.4719679],[-71.4507335, 42.4719416],[-71.4507454, 42.4719178],[-71.4507339, 42.4719147]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4785816, 42.4723119],[-71.4786029, 42.4722619],[-71.4786982, 42.4722842],[-71.4786897, 42.4723042],[-71.4787592, 42.4723205],[-71.4787101, 42.4724359],[-71.4786367, 42.4724187],[-71.47866260000001, 42.4723579],[-71.47859459999999, 42.472342],[-71.47855439999999, 42.4724364],[-71.4784185, 42.4724046],[-71.4784691, 42.4722857],[-71.4785816, 42.4723119]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47073349999999, 42.483116],[-71.47068609999999, 42.4830864],[-71.4707147, 42.4830613],[-71.4706673, 42.4830317],[-71.470704, 42.4829995],[-71.47068640000001, 42.4829885],[-71.4707496, 42.4829331],[-71.4708244, 42.4829798],[-71.4708067, 42.4829953],[-71.4709026, 42.4830552],[-71.47082229999999, 42.4831257],[-71.47076389999999, 42.4830893],[-71.47073349999999, 42.483116]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738524, 42.4728375],[-71.4738519, 42.4728251],[-71.4739108, 42.4728238],[-71.47391210000001, 42.4728562],[-71.4739749, 42.4728549],[-71.4739774, 42.4729173],[-71.47390660000001, 42.4729188],[-71.4739062, 42.47291],[-71.47382349999999, 42.4729117],[-71.4738243, 42.4729324],[-71.47380130000001, 42.4729329],[-71.4737976, 42.4728386],[-71.4738524, 42.4728375]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4525987, 42.4861666],[-71.45260039999999, 42.4861322],[-71.45269039999999, 42.4861347],[-71.45268900000001, 42.4861626],[-71.4527666, 42.4861648],[-71.45276459999999, 42.4862042],[-71.4527396, 42.4862035],[-71.4527372, 42.48625],[-71.4526839, 42.4862485],[-71.4526834, 42.4862594],[-71.4525795, 42.4862565],[-71.4525841, 42.4861662],[-71.4525987, 42.4861666]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4516639, 42.4940439],[-71.45165299999999, 42.4939712],[-71.4517585, 42.4939627],[-71.4517565, 42.4939485],[-71.4517893, 42.4939459],[-71.4517913, 42.4939595],[-71.4518885, 42.4939517],[-71.45189430000001, 42.4939885],[-71.4519306, 42.4939856],[-71.45193709999999, 42.4940323],[-71.45175209999999, 42.4940471],[-71.4517508, 42.4940369],[-71.4516639, 42.4940439]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"9","source:datetime":"2014-04-11T16:40:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48237020000001, 42.4747011],[-71.4822775, 42.4746638],[-71.4822829, 42.4746564],[-71.4822151, 42.4746291],[-71.48228159999999, 42.4745387],[-71.48236609999999, 42.4745727],[-71.48235560000001, 42.474587],[-71.4824122, 42.4746098],[-71.48241849999999, 42.4746012],[-71.4825419, 42.474651],[-71.4824809, 42.4747338],[-71.482377, 42.4746919],[-71.48237020000001, 42.4747011]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4532566, 42.4927147],[-71.4532457, 42.492617],[-71.4532626, 42.4926159],[-71.45325130000001, 42.4925147],[-71.4533363, 42.4925094],[-71.4533416, 42.4925568],[-71.4534062, 42.4925529],[-71.453413, 42.4926128],[-71.45337019999999, 42.4926155],[-71.45338700000001, 42.4927648],[-71.453292, 42.4927706],[-71.45328550000001, 42.4927129],[-71.4532566, 42.4927147]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47102839999999, 42.4758641],[-71.4710281, 42.4758182],[-71.4710834, 42.475818],[-71.4710832, 42.4757947],[-71.4711882, 42.4757943],[-71.4711884, 42.4758268],[-71.47125579999999, 42.4758265],[-71.4712562, 42.4758719],[-71.47120080000001, 42.4758722],[-71.4712011, 42.4759123],[-71.4710747, 42.4759129],[-71.4710743, 42.4758639],[-71.47102839999999, 42.4758641]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4733629, 42.4738435],[-71.4733629, 42.4738332],[-71.47342740000001, 42.4738333],[-71.4734273, 42.4738494],[-71.4734763, 42.4738495],[-71.4734761, 42.4738914],[-71.4734253, 42.4738913],[-71.47342519999999, 42.4739089],[-71.47334960000001, 42.4739088],[-71.47334960000001, 42.473897],[-71.4733212, 42.473897],[-71.4733214, 42.4738435],[-71.4733629, 42.4738435]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4664279, 42.4863562],[-71.4664116, 42.4863454],[-71.46638609999999, 42.4863666],[-71.4663396, 42.4863359],[-71.46636820000001, 42.4863122],[-71.4663575, 42.4863051],[-71.4664654, 42.4862157],[-71.4665344, 42.4862611],[-71.4665222, 42.4862695],[-71.46661450000001, 42.4863427],[-71.4665303, 42.4864126],[-71.4664355, 42.4863499],[-71.4664279, 42.4863562]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4684209, 42.4879322],[-71.4683418, 42.4879092],[-71.4683716, 42.4878531],[-71.4683867, 42.4878575],[-71.4684017, 42.4878292],[-71.4683462, 42.487813],[-71.46839009999999, 42.4877302],[-71.4685221, 42.4877686],[-71.4684896, 42.4878298],[-71.468506, 42.4878346],[-71.46847990000001, 42.4878838],[-71.468451, 42.4878754],[-71.4684209, 42.4879322]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45166209999999, 42.4859308],[-71.4516723, 42.4859072],[-71.4516856, 42.4859103],[-71.4517005, 42.4858756],[-71.4516818, 42.4858712],[-71.4516909, 42.4858497],[-71.4517091, 42.485854],[-71.4517223, 42.4858232],[-71.4518205, 42.4858463],[-71.4517556, 42.4859975],[-71.4516101, 42.4859634],[-71.45162759999999, 42.4859227],[-71.45166209999999, 42.4859308]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.458794, 42.4871497],[-71.45875220000001, 42.4871454],[-71.45875839999999, 42.4871119],[-71.4586372, 42.4870996],[-71.4586515, 42.4870223],[-71.45889080000001, 42.4870467],[-71.45889270000001, 42.4870366],[-71.4589802, 42.4870455],[-71.4589574, 42.4871678],[-71.4589034, 42.4871623],[-71.45890919999999, 42.4871312],[-71.4587995, 42.48712],[-71.458794, 42.4871497]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4756735, 42.4893945],[-71.47570450000001, 42.4893472],[-71.4757467, 42.4893624],[-71.4757206, 42.4894022],[-71.4757704, 42.4894201],[-71.4757379, 42.4894695],[-71.475762, 42.4894782],[-71.4757352, 42.4895189],[-71.4756198, 42.4894774],[-71.4756302, 42.4894615],[-71.4754549, 42.4893984],[-71.4754988, 42.4893316],[-71.4756735, 42.4893945]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4754094, 42.4727933],[-71.4754202, 42.4727693],[-71.4754709, 42.4727818],[-71.475461, 42.4728037],[-71.4754894, 42.4728107],[-71.4754834, 42.472824],[-71.4754968, 42.4728273],[-71.4754858, 42.4728518],[-71.47546010000001, 42.4728455],[-71.4754418, 42.472886],[-71.47534810000001, 42.4728628],[-71.47538249999999, 42.4727867],[-71.4754094, 42.4727933]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4803385, 42.4740424],[-71.4803715, 42.4739815],[-71.4804335, 42.4739999],[-71.4804378, 42.473992],[-71.4805179, 42.4740158],[-71.4805033, 42.4740427],[-71.48052250000001, 42.4740484],[-71.48050840000001, 42.4740743],[-71.48049330000001, 42.4740698],[-71.48047769999999, 42.4740987],[-71.4803896, 42.4740725],[-71.4803965, 42.4740597],[-71.4803385, 42.4740424]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4562775, 42.4898921],[-71.4563246, 42.4897984],[-71.45640760000001, 42.4898213],[-71.4563606, 42.4899148],[-71.45638390000001, 42.4899212],[-71.4563983, 42.4899427],[-71.4563902, 42.4899613],[-71.45636140000001, 42.4899716],[-71.456337, 42.4899654],[-71.4563143, 42.4900127],[-71.4562093, 42.4899846],[-71.4562585, 42.4898868],[-71.4562775, 42.4898921]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4719379, 42.4775562],[-71.4719273, 42.4775623],[-71.4719697, 42.4776023],[-71.4719513, 42.477613],[-71.47196289999999, 42.4776239],[-71.47186499999999, 42.4776807],[-71.4717986, 42.477618],[-71.471819, 42.4776062],[-71.4717923, 42.477581],[-71.4718127, 42.4775691],[-71.47177929999999, 42.4775376],[-71.4718653, 42.4774877],[-71.4719379, 42.4775562]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47142770000001, 42.4891995],[-71.4714913, 42.4892418],[-71.4715412, 42.4892006],[-71.4716037, 42.4892422],[-71.4715518, 42.4892849],[-71.4715661, 42.4892944],[-71.4715365, 42.4893187],[-71.47155600000001, 42.4893317],[-71.47147560000001, 42.489398],[-71.471397, 42.4893457],[-71.47146050000001, 42.4892934],[-71.4713793, 42.4892393],[-71.47142770000001, 42.4891995]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:48:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46669970000001, 42.477382],[-71.4666989, 42.4773628],[-71.4666479, 42.4773639],[-71.4666426, 42.4772948],[-71.4667846, 42.4772887],[-71.466784, 42.4772804],[-71.4668508, 42.4772775],[-71.466858, 42.4773701],[-71.4669165, 42.4773551],[-71.46693740000001, 42.4774106],[-71.46681409999999, 42.4774391],[-71.46678660000001, 42.4773795],[-71.46669970000001, 42.477382]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45280080000001, 42.4967048],[-71.45278399999999, 42.4966849],[-71.4528067, 42.4966745],[-71.4527977, 42.4966637],[-71.45291349999999, 42.4966103],[-71.4529752, 42.4966836],[-71.4529462, 42.496697],[-71.45300039999999, 42.4967558],[-71.45281660000001, 42.4968485],[-71.45277369999999, 42.496802],[-71.4528722, 42.4967523],[-71.45282349999999, 42.4966943],[-71.45280080000001, 42.4967048]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.469128, 42.4725966],[-71.4691117, 42.4725534],[-71.46913979999999, 42.4725476],[-71.4691131, 42.4724769],[-71.4691547, 42.4724683],[-71.4691673, 42.4725017],[-71.4692175, 42.4724914],[-71.46923409999999, 42.4725352],[-71.4692761, 42.4725265],[-71.4693175, 42.4726362],[-71.4692203, 42.4726563],[-71.4691928, 42.4725832],[-71.469128, 42.4725966]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4709084, 42.4905167],[-71.4709515, 42.4904915],[-71.4709666, 42.4905056],[-71.47103509999999, 42.4904655],[-71.4710875, 42.4905145],[-71.47102, 42.490554],[-71.4710111, 42.4905457],[-71.47096980000001, 42.4905699],[-71.4709376, 42.4905398],[-71.4708406, 42.4905965],[-71.4707812, 42.4905409],[-71.4708754, 42.4904858],[-71.4709084, 42.4905167]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706113, 42.4907752],[-71.4705481, 42.4907403],[-71.4705668, 42.4907218],[-71.4705463, 42.4907104],[-71.4706573, 42.4906003],[-71.4707679, 42.4906614],[-71.4707329, 42.4906961],[-71.470709, 42.4906828],[-71.47067370000001, 42.4907178],[-71.47074069999999, 42.4907548],[-71.4706991, 42.4907962],[-71.4706291, 42.4907575],[-71.4706113, 42.4907752]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46961349999999, 42.4718688],[-71.4696033, 42.4718335],[-71.46958429999999, 42.4718365],[-71.4696225, 42.4718019],[-71.4697043, 42.4718289],[-71.4697154, 42.4718272],[-71.4697338, 42.471891],[-71.46965899999999, 42.4719029],[-71.4696476, 42.4718634],[-71.46961349999999, 42.4718688]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46977939999999, 42.4840424],[-71.46966620000001, 42.4840048],[-71.4696794, 42.483983],[-71.46962379999999, 42.4839645],[-71.4696735, 42.4838826],[-71.4697481, 42.4839075],[-71.4697579, 42.4838912],[-71.4698585, 42.4839247],[-71.4698473, 42.4839432],[-71.46992400000001, 42.4839687],[-71.4698746, 42.48405],[-71.4697915, 42.4840224],[-71.46977939999999, 42.4840424]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4735137, 42.4862748],[-71.47350830000001, 42.486247],[-71.47348340000001, 42.4862497],[-71.4734704, 42.4861837],[-71.47362769999999, 42.4861667],[-71.47363350000001, 42.4861964],[-71.4736702, 42.4861924],[-71.4736774, 42.4862289],[-71.4736501, 42.4862319],[-71.4736661, 42.4863135],[-71.4735544, 42.4863255],[-71.4735438, 42.4862715],[-71.4735137, 42.4862748]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4760698, 42.4757095],[-71.4760361, 42.475681],[-71.4760481, 42.4756732],[-71.47600989999999, 42.4756409],[-71.4760294, 42.4756283],[-71.47595579999999, 42.4755661],[-71.47598170000001, 42.4755492],[-71.47601330000001, 42.475576],[-71.4760617, 42.4755446],[-71.4761979, 42.4756596],[-71.4761077, 42.4757181],[-71.4760855, 42.4756993],[-71.4760698, 42.4757095]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692548, 42.4806176],[-71.46937029999999, 42.4806927],[-71.4693972, 42.48067],[-71.469375, 42.4806555],[-71.46945719999999, 42.4805862],[-71.4695292, 42.480633],[-71.4694484, 42.4807011],[-71.46943570000001, 42.4806928],[-71.46936580000001, 42.4807518],[-71.46934589999999, 42.4807389],[-71.469311, 42.4807683],[-71.4691783, 42.4806821],[-71.4692548, 42.4806176]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4673343, 42.4881871],[-71.4672214, 42.4881434],[-71.4672711, 42.488073],[-71.4673801, 42.4881152],[-71.4673595, 42.4881444],[-71.4674021, 42.4881608],[-71.46741040000001, 42.4881491],[-71.467483, 42.4881772],[-71.4674406, 42.4882371],[-71.4673669, 42.4882086],[-71.4673786, 42.488192],[-71.4673411, 42.4881775],[-71.4673343, 42.4881871]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4729844, 42.4881892],[-71.47287609999999, 42.4881536],[-71.472917, 42.4880854],[-71.4729779, 42.4881054],[-71.47299099999999, 42.4880836],[-71.4730373, 42.4880988],[-71.47304750000001, 42.4880819],[-71.4730702, 42.4880894],[-71.4730656, 42.4880971],[-71.4731657, 42.48813],[-71.47312220000001, 42.4881894],[-71.4730158, 42.4881472],[-71.4729844, 42.4881892]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45158840000001, 42.4890372],[-71.4516139, 42.4889358],[-71.4517129, 42.4889495],[-71.45170299999999, 42.488989],[-71.4517567, 42.4889965],[-71.45174729999999, 42.4890335],[-71.451694, 42.4890261],[-71.45166140000001, 42.4891557],[-71.4515689, 42.4891428],[-71.4515906, 42.4890568],[-71.4515699, 42.4890539],[-71.4515746, 42.4890353],[-71.45158840000001, 42.4890372]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4621819, 42.4780493],[-71.46219139999999, 42.4780556],[-71.4621769, 42.4780677],[-71.4622316, 42.4781437],[-71.46214209999999, 42.4781823],[-71.4620944, 42.4781218],[-71.4621109, 42.4781147],[-71.4619809, 42.4780329],[-71.46205980000001, 42.477971],[-71.4621349, 42.4780206],[-71.46216579999999, 42.4779949],[-71.46221130000001, 42.4780249],[-71.4621819, 42.4780493]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"7","addr:state":"MA","source:datetime":"2014-11-08T14:01:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.454215, 42.4951502],[-71.45418960000001, 42.4950189],[-71.4542764, 42.4950097],[-71.4542871, 42.4950649],[-71.4543432, 42.495059],[-71.45435260000001, 42.4951077],[-71.45437339999999, 42.4951055],[-71.45438, 42.4951397],[-71.4543559, 42.4951423],[-71.4543743, 42.4952372],[-71.4542962, 42.4952455],[-71.45427650000001, 42.4951437],[-71.454215, 42.4951502]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4825719, 42.4721863],[-71.4825579, 42.4721538],[-71.4825974, 42.4721445],[-71.4825694, 42.4720797],[-71.48268040000001, 42.4720534],[-71.48270460000001, 42.4721096],[-71.4827219, 42.4721055],[-71.4827384, 42.4721438],[-71.482754, 42.4721401],[-71.482782, 42.4722048],[-71.4826417, 42.472238],[-71.482615, 42.4721761],[-71.4825719, 42.4721863]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45874809999999, 42.4846089],[-71.45876680000001, 42.4845726],[-71.4588176, 42.484587],[-71.45885490000001, 42.4845149],[-71.4589502, 42.4845419],[-71.4588964, 42.484646],[-71.4588797, 42.4846413],[-71.45887, 42.4846601],[-71.45892689999999, 42.4846762],[-71.4588966, 42.484735],[-71.45877400000001, 42.4847003],[-71.45881199999999, 42.4846269],[-71.45874809999999, 42.4846089]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46173279999999, 42.4766182],[-71.4617252, 42.4765991],[-71.4617463, 42.4765945],[-71.46172989999999, 42.4765532],[-71.4617478, 42.4765494],[-71.4617262, 42.4764947],[-71.4618104, 42.4764765],[-71.4618542, 42.4765874],[-71.4619065, 42.4765761],[-71.46192259999999, 42.47662],[-71.4617674, 42.4766538],[-71.4617518, 42.4766141],[-71.46173279999999, 42.4766182]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692412, 42.4847371],[-71.46922410000001, 42.4847299],[-71.4692419, 42.4847068],[-71.4692592, 42.4847141],[-71.4692873, 42.4846775],[-71.4693754, 42.4847146],[-71.4692896, 42.4848261],[-71.4692769, 42.4848207],[-71.4691984, 42.4849228],[-71.46912690000001, 42.4848927],[-71.4691894, 42.4848232],[-71.4691785, 42.4848186],[-71.4692412, 42.4847371]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763875, 42.4848915],[-71.4763839, 42.4848611],[-71.476331, 42.4848646],[-71.4763258, 42.4848218],[-71.4764794, 42.4848117],[-71.4764879, 42.4848828],[-71.4764741, 42.4848837],[-71.4764763, 42.4849018],[-71.4764916, 42.4849007],[-71.4765008, 42.4849774],[-71.476342, 42.4849879],[-71.4763308, 42.4848952],[-71.4763875, 42.4848915]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46557180000001, 42.4910212],[-71.4655885, 42.4909954],[-71.4656531, 42.4910183],[-71.4656303, 42.4910536],[-71.46566869999999, 42.4910672],[-71.46563209999999, 42.4911239],[-71.4656433, 42.4911279],[-71.4656028, 42.4911905],[-71.4654953, 42.4911525],[-71.4655455, 42.4910747],[-71.4654853, 42.4910534],[-71.4655183, 42.4910022],[-71.46557180000001, 42.4910212]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4603585, 42.4813099],[-71.4604688, 42.4813647],[-71.4604781, 42.4813544],[-71.46051989999999, 42.4813752],[-71.46055269999999, 42.481339],[-71.4606108, 42.4813679],[-71.4605792, 42.4814027],[-71.4605931, 42.4814095],[-71.46051180000001, 42.4814991],[-71.4604227, 42.4814548],[-71.46044190000001, 42.4814336],[-71.460307, 42.4813666],[-71.4603585, 42.4813099]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47626, 42.4896255],[-71.47624020000001, 42.4896547],[-71.47628400000001, 42.4896709],[-71.4762652, 42.4896987],[-71.4762492, 42.4896928],[-71.47618989999999, 42.4897805],[-71.47609919999999, 42.4897458],[-71.4760927, 42.4897357],[-71.4760938, 42.4897246],[-71.476102, 42.4897152],[-71.4761146, 42.4897102],[-71.4761767, 42.4895946],[-71.47626, 42.4896255]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4517522, 42.4845265],[-71.4517245, 42.4844906],[-71.4518056, 42.4844564],[-71.4518221, 42.4844777],[-71.45185789999999, 42.4844626],[-71.45189999999999, 42.4845172],[-71.451857, 42.4845354],[-71.4518721, 42.484555],[-71.4519111, 42.4845385],[-71.4519353, 42.4845699],[-71.4518017, 42.4846263],[-71.45173339999999, 42.4845344],[-71.4517522, 42.4845265]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4584852, 42.4831817],[-71.45848100000001, 42.4831625],[-71.45849680000001, 42.4831606],[-71.4584943, 42.4831492],[-71.458446, 42.483155],[-71.4584382, 42.48312],[-71.4585822, 42.4831025],[-71.458591, 42.4831424],[-71.4586058, 42.4831406],[-71.4586285, 42.483243],[-71.45852189999999, 42.483256],[-71.45850489999999, 42.4831793],[-71.4584852, 42.4831817]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46831539999999, 42.4902795],[-71.4683615, 42.4902454],[-71.4683259, 42.4902191],[-71.4683675, 42.4901883],[-71.4684053, 42.4902163],[-71.4684487, 42.4901841],[-71.46852920000001, 42.4902436],[-71.4684268, 42.4903195],[-71.4683977, 42.490298],[-71.4683064, 42.4903656],[-71.4682298, 42.4903089],[-71.4682925, 42.4902625],[-71.46831539999999, 42.4902795]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738533, 42.4750586],[-71.47395229999999, 42.475064],[-71.47393940000001, 42.4751938],[-71.4738582, 42.4751894],[-71.4738625, 42.4751462],[-71.4737544, 42.4751403],[-71.4737561, 42.4751232],[-71.47373349999999, 42.475122],[-71.4737384, 42.4750726],[-71.4737653, 42.4750741],[-71.4737695, 42.4750313],[-71.47385559999999, 42.475036],[-71.4738533, 42.4750586]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46324610000001, 42.4806639],[-71.46321759999999, 42.4806534],[-71.4632244, 42.4806434],[-71.4631466, 42.4806147],[-71.46312020000001, 42.4806539],[-71.46305, 42.4806281],[-71.4631263, 42.4805147],[-71.4632806, 42.4805716],[-71.46327290000001, 42.480583],[-71.46334950000001, 42.4806113],[-71.4633068, 42.4806748],[-71.46325229999999, 42.4806547],[-71.46324610000001, 42.4806639]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4748191, 42.4756687],[-71.4748206, 42.4756508],[-71.4748467, 42.475652],[-71.4748452, 42.4756698],[-71.4749017, 42.4756724],[-71.47490019999999, 42.4756909],[-71.4749628, 42.4756937],[-71.4749602, 42.4757254],[-71.4749056, 42.475723],[-71.4749046, 42.4757349],[-71.4747972, 42.47573],[-71.4748024, 42.475668],[-71.4748191, 42.4756687]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4639409, 42.4871641],[-71.4639459, 42.4871016],[-71.46396780000001, 42.4871025],[-71.4639739, 42.4870264],[-71.4640065, 42.4870279],[-71.4640028, 42.4870734],[-71.4640453, 42.4870753],[-71.46403789999999, 42.487168],[-71.4640033, 42.4871665],[-71.4639998, 42.4872108],[-71.4639622, 42.4872092],[-71.4639658, 42.4871651],[-71.4639409, 42.4871641]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4633893, 42.4762735],[-71.46332750000001, 42.4762247],[-71.46336359999999, 42.4761996],[-71.4633348, 42.4761768],[-71.4634125, 42.4761229],[-71.4634483, 42.4761512],[-71.4634717, 42.476135],[-71.46357329999999, 42.4762153],[-71.4635517, 42.4762302],[-71.46358909999999, 42.4762597],[-71.4635047, 42.4763182],[-71.4634207, 42.4762517],[-71.4633893, 42.4762735]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45885560000001, 42.4835331],[-71.45883120000001, 42.483496],[-71.45881369999999, 42.4835023],[-71.4587849, 42.4834583],[-71.4588244, 42.4834441],[-71.4587788, 42.4833745],[-71.4588467, 42.4833501],[-71.4588954, 42.4834244],[-71.45891829999999, 42.4834162],[-71.4590039, 42.4835468],[-71.4589146, 42.4835789],[-71.4588791, 42.4835247],[-71.45885560000001, 42.4835331]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47042740000001, 42.4995446],[-71.47044270000001, 42.4994875],[-71.47050350000001, 42.4994964],[-71.4705189, 42.4994389],[-71.4705067, 42.4994371],[-71.4705242, 42.4993718],[-71.4706377, 42.4993885],[-71.4705886, 42.4995724],[-71.47057650000001, 42.4995706],[-71.4705668, 42.4996066],[-71.4704768, 42.4995935],[-71.4704875, 42.4995534],[-71.47042740000001, 42.4995446]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4667537, 42.4868684],[-71.4667934, 42.4868014],[-71.46688140000001, 42.4868299],[-71.46685600000001, 42.486873],[-71.4668688, 42.4868771],[-71.46683179999999, 42.4869397],[-71.46684879999999, 42.4869452],[-71.466801, 42.4870261],[-71.4666869, 42.4869891],[-71.4667312, 42.4869141],[-71.4666782, 42.4868969],[-71.46670450000001, 42.4868525],[-71.4667537, 42.4868684]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4653543, 42.4913419],[-71.4653818, 42.4912972],[-71.46535179999999, 42.4912871],[-71.4653891, 42.4912266],[-71.465481, 42.4912577],[-71.46545949999999, 42.4912924],[-71.4654408, 42.491286],[-71.4654171, 42.4913245],[-71.4654587, 42.4913386],[-71.4654139, 42.4914113],[-71.46527519999999, 42.4913644],[-71.4653003, 42.4913237],[-71.4653543, 42.4913419]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4609526, 42.4725841],[-71.4609832, 42.4725064],[-71.4610325, 42.472517],[-71.4610368, 42.4725061],[-71.46106690000001, 42.4725126],[-71.46106260000001, 42.4725235],[-71.4611408, 42.4725404],[-71.4611098, 42.4726191],[-71.461071, 42.4726108],[-71.4610622, 42.4726332],[-71.4609859, 42.4726168],[-71.4609952, 42.4725933],[-71.4609526, 42.4725841]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4666831, 42.4894096],[-71.46664250000001, 42.4893929],[-71.46667739999999, 42.4893465],[-71.4667886, 42.4893922],[-71.4667646, 42.4894243],[-71.4667834, 42.489432],[-71.4667582, 42.4894656],[-71.4667867, 42.4894773],[-71.46675930000001, 42.4895137],[-71.4667355, 42.489504],[-71.46670829999999, 42.4895403],[-71.46661400000001, 42.4895016],[-71.4666831, 42.4894096]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47197389999999, 42.4741774],[-71.47196409999999, 42.4741309],[-71.4720537, 42.4741206],[-71.47206199999999, 42.4741598],[-71.47211969999999, 42.4741531],[-71.4721263, 42.4741843],[-71.4720866, 42.4741889],[-71.4721012, 42.4742581],[-71.47199809999999, 42.47427],[-71.4719873, 42.474219],[-71.4720118, 42.4742161],[-71.47200290000001, 42.4741741],[-71.47197389999999, 42.4741774]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"27","addr:state":"MA","source:datetime":"2014-11-08T14:01:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4542756, 42.4910753],[-71.45426930000001, 42.4911294],[-71.4542884, 42.4911306],[-71.4542838, 42.49117],[-71.45426759999999, 42.491169],[-71.4542658, 42.4911843],[-71.4543346, 42.4911887],[-71.4543291, 42.4912354],[-71.4542653, 42.4912313],[-71.4542642, 42.4912407],[-71.454154, 42.4912337],[-71.4541732, 42.4910687],[-71.4542756, 42.4910753]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4664178, 42.4907576],[-71.4664471, 42.4907288],[-71.466492, 42.4907539],[-71.46647470000001, 42.4907709],[-71.466488, 42.4907783],[-71.46645890000001, 42.4908069],[-71.4664868, 42.4908225],[-71.4664046, 42.4909139],[-71.46633919999999, 42.4908817],[-71.46637939999999, 42.4908369],[-71.4663364, 42.4908157],[-71.4663975, 42.4907478],[-71.4664178, 42.4907576]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706832, 42.4870945],[-71.4706224, 42.4870648],[-71.47065809999999, 42.4870249],[-71.47062990000001, 42.487011],[-71.4706532, 42.486985],[-71.4706784, 42.4869974],[-71.4707155, 42.4869559],[-71.4707957, 42.4869952],[-71.4707588, 42.4870365],[-71.47078620000001, 42.4870499],[-71.47073779999999, 42.487104],[-71.4706939, 42.4870825],[-71.4706832, 42.4870945]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4626056, 42.4760285],[-71.4626157, 42.4760043],[-71.4627569, 42.4760368],[-71.462717, 42.4761318],[-71.4627078, 42.4761376],[-71.46269650000001, 42.4761406],[-71.4626768, 42.4761386],[-71.4626621, 42.4761287],[-71.4626576, 42.4761172],[-71.46266439999999, 42.4761038],[-71.46246600000001, 42.4760629],[-71.46248629999999, 42.4759993],[-71.4626056, 42.4760285]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4647832, 42.4902981],[-71.4647854, 42.4901993],[-71.46488340000001, 42.4902006],[-71.46488309999999, 42.4902121],[-71.4649466, 42.4902129],[-71.464947, 42.4901964],[-71.46506549999999, 42.4901979],[-71.46506309999999, 42.4903036],[-71.46496310000001, 42.4903023],[-71.4649624, 42.4903323],[-71.4649045, 42.4903316],[-71.4649052, 42.4902996],[-71.4647832, 42.4902981]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706874, 42.4762506],[-71.47068950000001, 42.476189],[-71.4708177, 42.4761914],[-71.4708189, 42.4761568],[-71.4708795, 42.4761579],[-71.4708789, 42.4761767],[-71.4709395, 42.4761778],[-71.4709367, 42.4762609],[-71.4707804, 42.4762581],[-71.47077969999999, 42.4762793],[-71.4707221, 42.4762783],[-71.47072300000001, 42.4762512],[-71.4706874, 42.4762506]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4720907, 42.4914752],[-71.4720904, 42.4915141],[-71.4722168, 42.4915147],[-71.4722161, 42.491589],[-71.4721497, 42.4915887],[-71.47214940000001, 42.4916232],[-71.4720744, 42.4916228],[-71.47207469999999, 42.4915879],[-71.47203349999999, 42.4915877],[-71.4720332, 42.4916252],[-71.4719813, 42.491625],[-71.4719825, 42.4914747],[-71.4720907, 42.4914752]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4547561, 42.4731998],[-71.45485669999999, 42.4732228],[-71.45481239999999, 42.473329],[-71.45472030000001, 42.4733079],[-71.45475450000001, 42.4733723],[-71.4546859, 42.4733917],[-71.4546938, 42.4734071],[-71.4546194, 42.4734281],[-71.45458189999999, 42.4733555],[-71.4546558, 42.4733345],[-71.45464819999999, 42.47332],[-71.4547214, 42.4732992],[-71.4547561, 42.4731998]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4518169, 42.4902361],[-71.4518252, 42.4901171],[-71.45192419999999, 42.4901209],[-71.4519172, 42.4902211],[-71.4519514, 42.4902224],[-71.4519488, 42.4902597],[-71.4519367, 42.4902593],[-71.45193310000001, 42.4903112],[-71.45190460000001, 42.4903101],[-71.4519023, 42.4903437],[-71.45178249999999, 42.4903392],[-71.4517898, 42.4902351],[-71.4518169, 42.4902361]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4532117, 42.4923148],[-71.453211, 42.4922964],[-71.45322849999999, 42.492296],[-71.4532269, 42.4922538],[-71.45323860000001, 42.4922535],[-71.4532347, 42.4921539],[-71.4533737, 42.492151],[-71.4533759, 42.4922089],[-71.4533291, 42.4922099],[-71.4533349, 42.4923576],[-71.45323399999999, 42.4923598],[-71.4532322, 42.4923143],[-71.4532117, 42.4923148]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45992099999999, 42.4766094],[-71.45986569999999, 42.4765713],[-71.4599126, 42.476534],[-71.4599041, 42.4765281],[-71.4599406, 42.4764992],[-71.4599497, 42.4765054],[-71.459963, 42.4764948],[-71.45991789999999, 42.4764637],[-71.4599522, 42.4764365],[-71.4600901, 42.4765316],[-71.45999019999999, 42.476611],[-71.4599521, 42.4765847],[-71.45992099999999, 42.4766094]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4739232, 42.4735718],[-71.4739233, 42.4735612],[-71.4740268, 42.4735616],[-71.4740267, 42.4735737],[-71.4740735, 42.4735739],[-71.47407320000001, 42.4736252],[-71.47402390000001, 42.473625],[-71.4740236, 42.4736674],[-71.47397119999999, 42.4736672],[-71.4739714, 42.4736504],[-71.4738559, 42.4736499],[-71.4738565, 42.4735715],[-71.4739232, 42.4735718]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4632342, 42.4780959],[-71.4632136, 42.4779639],[-71.4633077, 42.4779558],[-71.46331309999999, 42.4779905],[-71.46335500000001, 42.4779869],[-71.4633618, 42.4780302],[-71.4633204, 42.4780337],[-71.46332889999999, 42.4780877],[-71.46329040000001, 42.4780909],[-71.4632921, 42.4781017],[-71.46327770000001, 42.478103],[-71.46327599999999, 42.4780923],[-71.4632342, 42.4780959]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618253, 42.479375],[-71.4618133, 42.479344],[-71.4617942, 42.4793481],[-71.4617709, 42.479288],[-71.46182229999999, 42.4792771],[-71.4618042, 42.4792303],[-71.46189320000001, 42.4792114],[-71.461933, 42.4793141],[-71.4619544, 42.4793095],[-71.4619926, 42.4794082],[-71.461888, 42.4794304],[-71.4618634, 42.4793669],[-71.4618253, 42.479375]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4613512, 42.4796699],[-71.4612985, 42.4795514],[-71.4613624, 42.4795358],[-71.46135529999999, 42.4795197],[-71.4613796, 42.4795138],[-71.461386, 42.4795282],[-71.46140870000001, 42.4795227],[-71.4614622, 42.4796431],[-71.4614909, 42.4796361],[-71.461517, 42.479695],[-71.46139340000001, 42.4797251],[-71.4613672, 42.479666],[-71.4613512, 42.4796699]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"238","addr:state":"MA","source:datetime":"2015-01-20T23:47:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4654917, 42.4986413],[-71.4654917, 42.4986531],[-71.46539869999999, 42.4986532],[-71.46539850000001, 42.4985618],[-71.4656851, 42.4985615],[-71.4656851, 42.4985979],[-71.46573909999999, 42.4985979],[-71.46573909999999, 42.498637],[-71.46569599999999, 42.498637],[-71.4656961, 42.4986585],[-71.4655394, 42.4986586],[-71.4655394, 42.4986413],[-71.4654917, 42.4986413]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46311900000001, 42.4765202],[-71.4631668, 42.4764654],[-71.4632239, 42.4764927],[-71.4632388, 42.4764756],[-71.4633449, 42.4765262],[-71.4632807, 42.4765999],[-71.46326120000001, 42.4765906],[-71.46320590000001, 42.4766541],[-71.4631899, 42.4766464],[-71.463161, 42.4766795],[-71.4630945, 42.4766478],[-71.4631802, 42.4765495],[-71.46311900000001, 42.4765202]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"12","addr:state":"MA","source:datetime":"2014-11-08T14:01:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4531949, 42.493916],[-71.4532456, 42.4938088],[-71.45333100000001, 42.4938309],[-71.4533016, 42.4938931],[-71.4533155, 42.4938968],[-71.45329460000001, 42.493941],[-71.45331710000001, 42.4939468],[-71.4533077, 42.4939666],[-71.453288, 42.4939615],[-71.45326679999999, 42.4940063],[-71.4531278, 42.4939703],[-71.453158, 42.4939065],[-71.4531949, 42.493916]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47971029999999, 42.4798321],[-71.4797111, 42.4797946],[-71.47969089999999, 42.4797944],[-71.4796917, 42.4797565],[-71.479857, 42.4797586],[-71.47985509999999, 42.479842],[-71.4798267, 42.4798417],[-71.4798244, 42.479944],[-71.47973260000001, 42.4799429],[-71.47973399999999, 42.4798788],[-71.4796953, 42.4798783],[-71.4796963, 42.479832],[-71.47971029999999, 42.4798321]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474692, 42.4880261],[-71.47473309999999, 42.4880023],[-71.47471059999999, 42.4879809],[-71.4747615, 42.4879514],[-71.4747928, 42.487981],[-71.47481999999999, 42.4879652],[-71.4748726, 42.488015],[-71.4746902, 42.4881206],[-71.4746267, 42.4880605],[-71.474661, 42.4880407],[-71.4746511, 42.4880313],[-71.4746799, 42.4880146],[-71.474692, 42.4880261]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4655643, 42.4814791],[-71.4655606, 42.4814935],[-71.4656524, 42.4815063],[-71.4656494, 42.4815183],[-71.46566970000001, 42.4815211],[-71.4656562, 42.4815743],[-71.46562710000001, 42.4815703],[-71.4655838, 42.481741],[-71.4654852, 42.4817273],[-71.4655114, 42.481624],[-71.4654707, 42.4816184],[-71.46550809999999, 42.4814713],[-71.4655643, 42.4814791]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46786280000001, 42.4886066],[-71.4678768, 42.4885855],[-71.4678419, 42.4885728],[-71.4678918, 42.4884976],[-71.4679781, 42.4885291],[-71.4679306, 42.4886005],[-71.4679539, 42.4886089],[-71.46793359999999, 42.4886395],[-71.4679055, 42.4886293],[-71.4678838, 42.4886621],[-71.4678042, 42.4886331],[-71.4678298, 42.4885946],[-71.46786280000001, 42.4886066]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47946659999999, 42.4718745],[-71.4795755, 42.4718462],[-71.47954129999999, 42.4718548],[-71.4795579, 42.471891],[-71.4794936, 42.4719072],[-71.4794773, 42.4718718],[-71.47946659999999, 42.4718745]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713155, 42.4730356],[-71.4713096, 42.4730243],[-71.4713689, 42.4730075],[-71.4713747, 42.4730188],[-71.471412, 42.4730082],[-71.47143920000001, 42.4730609],[-71.4714039, 42.4730709],[-71.47141139999999, 42.4730855],[-71.4713514, 42.4731025],[-71.4713438, 42.4730879],[-71.47126160000001, 42.4731112],[-71.4712344, 42.4730585],[-71.4713155, 42.4730356]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46325450000001, 42.4840017],[-71.46326980000001, 42.4839781],[-71.46322670000001, 42.4839629],[-71.46326550000001, 42.483903],[-71.4633889, 42.4839467],[-71.46335259999999, 42.4840029],[-71.4633836, 42.4840139],[-71.46331840000001, 42.4841146],[-71.4632031, 42.4840737],[-71.46323, 42.484032],[-71.4632178, 42.4840277],[-71.4632383, 42.483996],[-71.46325450000001, 42.4840017]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4575148, 42.4873548],[-71.4575494, 42.4873249],[-71.4575799, 42.4873442],[-71.4576667, 42.4872691],[-71.4577481, 42.4873206],[-71.45765609999999, 42.4874002],[-71.4576417, 42.4873911],[-71.4576019, 42.4874255],[-71.45761090000001, 42.4874312],[-71.4575483, 42.4874854],[-71.4574781, 42.487441],[-71.4575511, 42.4873778],[-71.4575148, 42.4873548]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4588821, 42.4825001],[-71.45887569999999, 42.4824891],[-71.45882949999999, 42.4825041],[-71.45879549999999, 42.4824465],[-71.458956, 42.4823946],[-71.45898579999999, 42.4824453],[-71.4590135, 42.4824363],[-71.4590693, 42.4825308],[-71.45908919999999, 42.4825244],[-71.4591011, 42.4825446],[-71.4589972, 42.4825782],[-71.45894010000001, 42.4824814],[-71.4588821, 42.4825001]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4624452, 42.4816027],[-71.4624529, 42.4815872],[-71.4625786, 42.4816212],[-71.462565, 42.4816487],[-71.4625789, 42.4816524],[-71.46257420000001, 42.481662],[-71.4625637, 42.4816591],[-71.4625461, 42.4816948],[-71.46241879999999, 42.4816604],[-71.46239989999999, 42.4816987],[-71.4623508, 42.4816854],[-71.46239799999999, 42.4815899],[-71.4624452, 42.4816027]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4710321, 42.4732261],[-71.47101429999999, 42.4732018],[-71.4710305, 42.4731953],[-71.47101429999999, 42.4731731],[-71.4711302, 42.4731264],[-71.47116269999999, 42.4731706],[-71.4711777, 42.4731646],[-71.4711901, 42.4731815],[-71.47116750000001, 42.4731906],[-71.471181, 42.4732091],[-71.4711128, 42.4732365],[-71.47108849999999, 42.4732034],[-71.4710321, 42.4732261]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47040320000001, 42.4721142],[-71.4704134, 42.4721742],[-71.4704648, 42.4721694],[-71.47047329999999, 42.4722192],[-71.4702904, 42.4722362],[-71.4702717, 42.4721264],[-71.4703083, 42.472123],[-71.4703054, 42.472106],[-71.47023710000001, 42.4721124],[-71.47022509999999, 42.4720419],[-71.4703344, 42.4720317],[-71.4703493, 42.4721192],[-71.47040320000001, 42.4721142]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4590061, 42.4718628],[-71.4589941, 42.4718212],[-71.4590089, 42.4718189],[-71.4591573, 42.4718827],[-71.4590333, 42.4719022],[-71.4590214, 42.4718604],[-71.4590061, 42.4718628]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4545486, 42.4907425],[-71.4545, 42.4906941],[-71.45461229999999, 42.4906323],[-71.4545968, 42.4906169],[-71.4547093, 42.4905549],[-71.4547742, 42.4906195],[-71.4547482, 42.4906338],[-71.4547692, 42.4906546],[-71.4546921, 42.490697],[-71.454713, 42.4907179],[-71.45465849999999, 42.4907479],[-71.45461589999999, 42.4907054],[-71.4545486, 42.4907425]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4668355, 42.4799896],[-71.4668528, 42.479955],[-71.46693639999999, 42.479978],[-71.466919, 42.4800127],[-71.4669414, 42.4800188],[-71.4669196, 42.4800623],[-71.46686649999999, 42.4800477],[-71.4668131, 42.4801543],[-71.4667223, 42.4801293],[-71.46674179999999, 42.4800905],[-71.4667266, 42.4800863],[-71.4667824, 42.479975],[-71.4668355, 42.4799896]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47011999999999, 42.4804649],[-71.4700405, 42.4804888],[-71.4700627, 42.4805293],[-71.4700443, 42.4805348],[-71.4700543, 42.4805529],[-71.4699614, 42.4805809],[-71.4699267, 42.4805176],[-71.4699782, 42.480502],[-71.4699315, 42.480417],[-71.4699682, 42.480406],[-71.4699406, 42.4803557],[-71.470043, 42.4803249],[-71.47011999999999, 42.4804649]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46575780000001, 42.4861661],[-71.46576159999999, 42.4861517],[-71.4658026, 42.4861577],[-71.4658055, 42.4861468],[-71.46585930000001, 42.4861546],[-71.46585589999999, 42.4861672],[-71.4659018, 42.4861739],[-71.46587940000001, 42.4862578],[-71.46573720000001, 42.4862371],[-71.4657397, 42.4862278],[-71.46564530000001, 42.4862141],[-71.4656619, 42.4861521],[-71.46575780000001, 42.4861661]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4774895, 42.4850849],[-71.4773438, 42.4850984],[-71.47734730000001, 42.4851195],[-71.47723550000001, 42.4851298],[-71.4772423, 42.4851706],[-71.4771394, 42.4851801],[-71.47712989999999, 42.4851234],[-71.477242, 42.4851131],[-71.4772373, 42.4850856],[-71.4773417, 42.485076],[-71.4773362, 42.4850434],[-71.4774803, 42.4850302],[-71.4774895, 42.4850849]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4729687, 42.4783828],[-71.4729541, 42.4783578],[-71.4730199, 42.4783368],[-71.4730122, 42.4783235],[-71.4731196, 42.4782892],[-71.4731776, 42.4783885],[-71.4731371, 42.4784015],[-71.4731477, 42.4784197],[-71.47310179999999, 42.4784344],[-71.47309559999999, 42.4784238],[-71.4729128, 42.4784822],[-71.4728727, 42.4784135],[-71.4729687, 42.4783828]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4676185, 42.4889735],[-71.46764349999999, 42.4889342],[-71.4677224, 42.4889617],[-71.4676979, 42.4890002],[-71.4676724, 42.4889913],[-71.4676225, 42.4890697],[-71.46765619999999, 42.4890814],[-71.46760449999999, 42.4891627],[-71.4675246, 42.4891349],[-71.46757529999999, 42.489055],[-71.4675393, 42.4890425],[-71.4675895, 42.4889634],[-71.4676185, 42.4889735]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752838, 42.4876324],[-71.4752491, 42.4876673],[-71.4752156, 42.4876491],[-71.4751569, 42.4877082],[-71.47508089999999, 42.4876668],[-71.4751413, 42.487606],[-71.4751653, 42.4876191],[-71.47518909999999, 42.4875951],[-71.4751629, 42.4875809],[-71.4752246, 42.4875189],[-71.47530709999999, 42.4875638],[-71.47525469999999, 42.4876166],[-71.4752838, 42.4876324]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46936890000001, 42.4964538],[-71.4693695, 42.4964661],[-71.469319, 42.4964675],[-71.4693183, 42.4964542],[-71.4692417, 42.4964563],[-71.4692393, 42.4964093],[-71.4693283, 42.4964068],[-71.4693243, 42.4963287],[-71.46946629999999, 42.4963248],[-71.46947919999999, 42.4965759],[-71.4694075, 42.4965779],[-71.4694011, 42.4964529],[-71.46936890000001, 42.4964538]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4531757, 42.4727341],[-71.45310379999999, 42.4726246],[-71.45322419999999, 42.4725813],[-71.4532433, 42.4726104],[-71.4532141, 42.4726209],[-71.4532361, 42.4726544],[-71.45327039999999, 42.472642],[-71.4532932, 42.4726768],[-71.4532658, 42.4726866],[-71.45330509999999, 42.4727529],[-71.4532286, 42.4727781],[-71.4531853, 42.4727294],[-71.4531757, 42.4727341]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738076, 42.4740849],[-71.4738091, 42.4740091],[-71.4738893, 42.47401],[-71.4738894, 42.474001],[-71.47395090000001, 42.4740017],[-71.4739508, 42.4740112],[-71.474002, 42.4740119],[-71.4740005, 42.474087],[-71.4739464, 42.4740864],[-71.4739462, 42.4740963],[-71.4738971, 42.4740958],[-71.4738973, 42.4740859],[-71.4738076, 42.4740849]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4693643, 42.4889559],[-71.4693977, 42.4889067],[-71.4694105, 42.4889114],[-71.4694519, 42.4888504],[-71.4695347, 42.4888812],[-71.4694909, 42.4889499],[-71.4695978, 42.4889878],[-71.469568, 42.4890317],[-71.46948260000001, 42.489],[-71.4694574, 42.4890371],[-71.46939279999999, 42.4890131],[-71.4694181, 42.4889758],[-71.4693643, 42.4889559]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45181220000001, 42.4905955],[-71.4517817, 42.4906749],[-71.45163959999999, 42.490645],[-71.4516433, 42.4906353],[-71.4515955, 42.4906252],[-71.45163290000001, 42.4905279],[-71.4516929, 42.4905406],[-71.4516828, 42.4905669],[-71.4517309, 42.490577],[-71.4517354, 42.4905653],[-71.4517745, 42.4905736],[-71.4517695, 42.4905865],[-71.45181220000001, 42.4905955]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46705249999999, 42.4859076],[-71.4669722, 42.4858683],[-71.4670182, 42.4858169],[-71.4670345, 42.4858249],[-71.4670759, 42.4857785],[-71.4671335, 42.4858067],[-71.4671491, 42.4857893],[-71.46719710000001, 42.4858128],[-71.46718559999999, 42.4858257],[-71.4672444, 42.4858545],[-71.46717700000001, 42.48593],[-71.4670765, 42.4858808],[-71.46705249999999, 42.4859076]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45149290000001, 42.4930525],[-71.4514712, 42.493018],[-71.45155099999999, 42.4929905],[-71.4516106, 42.4930852],[-71.4515363, 42.4931108],[-71.4515512, 42.4931346],[-71.451508, 42.4931494],[-71.4515026, 42.4931408],[-71.4513477, 42.4931942],[-71.4513256, 42.4931591],[-71.4513694, 42.493144],[-71.4513441, 42.4931038],[-71.45149290000001, 42.4930525]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46991869999999, 42.4952681],[-71.4699439, 42.4952081],[-71.4700716, 42.4952375],[-71.4700491, 42.495291],[-71.47003340000001, 42.4952873],[-71.4699959, 42.4953764],[-71.4700352, 42.4953855],[-71.4700193, 42.4954234],[-71.4699203, 42.4954006],[-71.4699354, 42.4953646],[-71.4699013, 42.4953567],[-71.4699368, 42.4952723],[-71.46991869999999, 42.4952681]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714116, 42.4845423],[-71.47131640000001, 42.4845077],[-71.4713443, 42.4844657],[-71.47118260000001, 42.4844069],[-71.471233, 42.4843309],[-71.47138099999999, 42.4843847],[-71.4713481, 42.4844343],[-71.4714261, 42.4844626],[-71.4714441, 42.4844354],[-71.47151599999999, 42.4844616],[-71.4714603, 42.4845456],[-71.47141929999999, 42.4845307],[-71.4714116, 42.4845423]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4720138, 42.4888156],[-71.47200460000001, 42.4887565],[-71.4720956, 42.4887487],[-71.4721029, 42.4887955],[-71.47219749999999, 42.4887874],[-71.4722075, 42.4888515],[-71.47207710000001, 42.4888626],[-71.4720839, 42.4889065],[-71.47195309999999, 42.4889177],[-71.4719418, 42.4888453],[-71.47205700000001, 42.4888355],[-71.47205339999999, 42.4888123],[-71.4720138, 42.4888156]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4787449, 42.4775101],[-71.4787257, 42.4774746],[-71.47870279999999, 42.4774814],[-71.4786806, 42.4774403],[-71.47864370000001, 42.4774512],[-71.4786184, 42.4774044],[-71.4787766, 42.4773575],[-71.4788406, 42.4774758],[-71.4788539, 42.4774719],[-71.47888829999999, 42.4775354],[-71.47880480000001, 42.4775602],[-71.47877320000001, 42.4775017],[-71.4787449, 42.4775101]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752957, 42.4903763],[-71.47526910000001, 42.490424],[-71.4753072, 42.4904357],[-71.4752584, 42.4905232],[-71.4751674, 42.4904954],[-71.4751807, 42.4904715],[-71.4751414, 42.4904594],[-71.47516779999999, 42.4904121],[-71.47513429999999, 42.4904019],[-71.47515420000001, 42.4903661],[-71.4751335, 42.4903597],[-71.4751492, 42.4903316],[-71.4752957, 42.4903763]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4736812, 42.4753829],[-71.4738357, 42.4753878],[-71.4738335, 42.4754265],[-71.4737502, 42.4754239],[-71.4737489, 42.4754453],[-71.4738129, 42.4754473],[-71.4738105, 42.4754877],[-71.473764, 42.4754862],[-71.47376269999999, 42.4755085],[-71.4737243, 42.4755072],[-71.4737255, 42.4754867],[-71.4736753, 42.4754851],[-71.4736812, 42.4753829]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4568527, 42.474976],[-71.4568593, 42.4749436],[-71.4567789, 42.4749347],[-71.45679250000001, 42.4748678],[-71.4568417, 42.4748732],[-71.4568371, 42.4748954],[-71.4569984, 42.4749136],[-71.4569819, 42.4749947],[-71.4569327, 42.4749892],[-71.45693079999999, 42.4749986],[-71.4568891, 42.4749939],[-71.4568919, 42.4749803],[-71.4568527, 42.474976]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4649738, 42.4839276],[-71.4650353, 42.4839347],[-71.46503939999999, 42.4839152],[-71.465097, 42.4839219],[-71.4650934, 42.483939],[-71.4651248, 42.4839427],[-71.46512989999999, 42.4839185],[-71.4652237, 42.4839294],[-71.4652024, 42.4840298],[-71.46512060000001, 42.4840203],[-71.4651181, 42.4840321],[-71.4649556, 42.4840133],[-71.4649738, 42.4839276]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46624490000001, 42.4781304],[-71.4661217, 42.4780225],[-71.46620369999999, 42.4779712],[-71.46631910000001, 42.4780723],[-71.46634779999999, 42.4780543],[-71.4663707, 42.4780744],[-71.4664252, 42.4780403],[-71.4664837, 42.4780914],[-71.4663539, 42.4781726],[-71.4663372, 42.4781579],[-71.4662926, 42.4781858],[-71.46623580000001, 42.4781361],[-71.46624490000001, 42.4781304]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4548209, 42.4902817],[-71.45481220000001, 42.490274],[-71.45484980000001, 42.4902506],[-71.4548396, 42.4902416],[-71.45495940000001, 42.490167],[-71.4550325, 42.4902314],[-71.45493310000001, 42.4902933],[-71.4549679, 42.4903239],[-71.454909, 42.4903606],[-71.45488020000001, 42.4903353],[-71.4548095, 42.4903792],[-71.4547493, 42.4903263],[-71.4548209, 42.4902817]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4672449, 42.4762928],[-71.4672144, 42.4762362],[-71.4673398, 42.4761992],[-71.4673305, 42.4761819],[-71.46735750000001, 42.476174],[-71.4673677, 42.476193],[-71.4674318, 42.4761741],[-71.467483, 42.4762692],[-71.4674237, 42.4762867],[-71.467418, 42.476276],[-71.46730599999999, 42.4763091],[-71.4672901, 42.4762794],[-71.4672449, 42.4762928]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47432670000001, 42.4749121],[-71.47432809999999, 42.4748081],[-71.4744041, 42.4748086],[-71.4744043, 42.4747925],[-71.4744726, 42.474793],[-71.4744725, 42.4748053],[-71.4746014, 42.4748062],[-71.47460049999999, 42.4748697],[-71.4744166, 42.4748684],[-71.47441499999999, 42.4749863],[-71.4742851, 42.4749854],[-71.4742861, 42.4749118],[-71.47432670000001, 42.4749121]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4535808, 42.4936005],[-71.4535668, 42.4935461],[-71.45361219999999, 42.4935397],[-71.45360290000001, 42.4935038],[-71.4535793, 42.4935071],[-71.4535656, 42.4934538],[-71.4536715, 42.4934389],[-71.45369959999999, 42.4935477],[-71.4537476, 42.493541],[-71.4537675, 42.4936183],[-71.453636, 42.4936369],[-71.453625, 42.4935942],[-71.4535808, 42.4936005]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46863999999999, 42.4725671],[-71.46864859999999, 42.472537],[-71.4686686, 42.4725196],[-71.46869270000001, 42.4725223],[-71.4687067, 42.472543],[-71.46873290000001, 42.4725465],[-71.4687057, 42.4726584],[-71.4686482, 42.4726508],[-71.468654, 42.4726309],[-71.4685052, 42.4726075],[-71.4685221, 42.4725485],[-71.46863999999999, 42.4725671]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4614454, 42.4806163],[-71.461468, 42.4806554],[-71.4614754, 42.4806517],[-71.4614842, 42.480651],[-71.46149250000001, 42.4806535],[-71.46149819999999, 42.4806586],[-71.4614988, 42.4806694],[-71.4614897, 42.4806768],[-71.46152789999999, 42.4807398],[-71.4614356, 42.4807713],[-71.4613615, 42.4806428],[-71.4614454, 42.4806163]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4743849, 42.4762406],[-71.4744031, 42.4763617],[-71.4743843, 42.4763632],[-71.47439, 42.4764009],[-71.4743452, 42.4764046],[-71.4743539, 42.4764625],[-71.4743981, 42.4764503],[-71.4744328, 42.4765095],[-71.4743623, 42.4765321],[-71.474322, 42.4764704],[-71.47428410000001, 42.4762386],[-71.4743849, 42.4762406]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4621466, 42.4868262],[-71.4621537, 42.486834],[-71.46215650000001, 42.4868493],[-71.46214670000001, 42.4868683],[-71.4621842, 42.4868808],[-71.46211820000001, 42.487011],[-71.462011, 42.4869806],[-71.4620883, 42.4868312],[-71.46210000000001, 42.4868223],[-71.46211580000001, 42.4868181],[-71.46213640000001, 42.4868207],[-71.4621466, 42.4868262]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47835310000001, 42.4874064],[-71.4783353, 42.4874105],[-71.4783765, 42.4875084],[-71.47827890000001, 42.4875308],[-71.4782451, 42.4874503],[-71.4782934, 42.4874392],[-71.4782866, 42.487423],[-71.47826739999999, 42.4874274],[-71.47825, 42.487386],[-71.4783362, 42.4873662],[-71.47835310000001, 42.4874064]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.479332, 42.4723963],[-71.47935080000001, 42.4724033],[-71.47933930000001, 42.4724205],[-71.4794059, 42.472445],[-71.4793609, 42.4725119],[-71.4791726, 42.4724425],[-71.4792109, 42.4723856],[-71.4792575, 42.4724028],[-71.47926339999999, 42.4723941],[-71.4793196, 42.4724148],[-71.479332, 42.4723963]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Massachusetts Avenue","addr:city":"Acton","building":"yes","addr:housenumber":"590","addr:state":"MA","source:datetime":"2015-01-23T23:15:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4742737, 42.475879],[-71.47436709999999, 42.4758799],[-71.4743643, 42.4760425],[-71.47424839999999, 42.4760414],[-71.4742481, 42.4760578],[-71.474161, 42.476057],[-71.4741614, 42.4760371],[-71.474053, 42.4760361],[-71.4740556, 42.4758874],[-71.4742735, 42.4758895],[-71.4742737, 42.475879]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4773738, 42.4873135],[-71.47727310000001, 42.4873308],[-71.47727860000001, 42.4873483],[-71.477153, 42.4873699],[-71.47712970000001, 42.4872956],[-71.4772226, 42.4872796],[-71.47721249999999, 42.4872474],[-71.4772655, 42.4872383],[-71.47727620000001, 42.4872722],[-71.4773565, 42.4872584],[-71.4773738, 42.4873135]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47201440000001, 42.4875368],[-71.4719539, 42.4876191],[-71.47196599999999, 42.487624],[-71.4719085, 42.4877023],[-71.47183920000001, 42.4876743],[-71.4718551, 42.4876526],[-71.4716902, 42.4875862],[-71.4717321, 42.4875291],[-71.4718967, 42.4875954],[-71.4719569, 42.4875136],[-71.47201440000001, 42.4875368]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"9","addr:state":"MA","source:datetime":"2014-11-08T14:01:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543094, 42.4947796],[-71.4542213, 42.4947762],[-71.4542239, 42.494739],[-71.45420900000001, 42.4947384],[-71.4542118, 42.4946984],[-71.4541945, 42.4946978],[-71.45419630000001, 42.494672],[-71.454216, 42.4946727],[-71.4542178, 42.4946465],[-71.45431600000001, 42.4946503],[-71.4543094, 42.4947796]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4649505, 42.4784599],[-71.4649033, 42.4784826],[-71.46497650000001, 42.4785659],[-71.4648928, 42.4786062],[-71.4648207, 42.4785242],[-71.46483379999999, 42.4785179],[-71.4647484, 42.4784306],[-71.4648129, 42.4783959],[-71.4648602, 42.4784442],[-71.4649094, 42.4784179],[-71.4649505, 42.4784599]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4611123, 42.4786982],[-71.4611366, 42.4786978],[-71.4611352, 42.4786534],[-71.46123559999999, 42.4786516],[-71.46124229999999, 42.4788634],[-71.4611529, 42.4788649],[-71.4611496, 42.4787612],[-71.46113579999999, 42.4787615],[-71.46113440000001, 42.4787146],[-71.4611129, 42.478715],[-71.4611123, 42.4786982]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46345669999999, 42.4830929],[-71.4635136, 42.483147],[-71.4634406, 42.483189],[-71.4634097, 42.4831595],[-71.46325299999999, 42.4832495],[-71.4631784, 42.4831784],[-71.4632519, 42.4831362],[-71.4632745, 42.4831576],[-71.463323, 42.4831298],[-71.4633491, 42.4831547],[-71.46345669999999, 42.4830929]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4663492, 42.4858233],[-71.46636839999999, 42.4858275],[-71.4663369, 42.485907],[-71.4662476, 42.4858876],[-71.4662786, 42.4858093],[-71.46624129999999, 42.4858013],[-71.4663005, 42.4856519],[-71.4664448, 42.4856832],[-71.4664294, 42.4857222],[-71.4663925, 42.4857142],[-71.4663492, 42.4858233]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4673404, 42.4789741],[-71.46738569999999, 42.4789818],[-71.4673734, 42.4790212],[-71.4673154, 42.4790113],[-71.4672814, 42.4791204],[-71.467162, 42.4791],[-71.4671962, 42.4789902],[-71.4672164, 42.4789936],[-71.46723609999999, 42.4789303],[-71.467348, 42.4789494],[-71.4673404, 42.4789741]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:47:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4525513, 42.4730914],[-71.4526852, 42.4730913],[-71.4526852, 42.4731581],[-71.4526089, 42.4731581],[-71.4526089, 42.473192],[-71.4525368, 42.473192],[-71.4525368, 42.4731589],[-71.4524048, 42.4731589],[-71.4524047, 42.4730695],[-71.4525513, 42.4730695],[-71.4525513, 42.4730914]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"25","addr:state":"MA","source:datetime":"2014-11-08T14:01:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4544503, 42.4915841],[-71.4544097, 42.4915971],[-71.454458, 42.4916775],[-71.45436789999999, 42.4917086],[-71.4543109, 42.4916183],[-71.45432599999999, 42.491613],[-71.4542711, 42.4915189],[-71.45435809999999, 42.4914911],[-71.4543956, 42.4915553],[-71.45442749999999, 42.4915451],[-71.4544503, 42.4915841]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4508338, 42.4722222],[-71.45090329999999, 42.4722377],[-71.4508848, 42.4722829],[-71.4507121, 42.4722444],[-71.4507505, 42.47215],[-71.45073859999999, 42.4721473],[-71.4507589, 42.4720975],[-71.4508101, 42.4721089],[-71.45081860000001, 42.4720878],[-71.45088269999999, 42.4721021],[-71.4508338, 42.4722222]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4674146, 42.488465],[-71.4674339, 42.4884741],[-71.467367, 42.4885519],[-71.46728160000001, 42.4885117],[-71.4673627, 42.4884173],[-71.4673334, 42.4884035],[-71.46737709999999, 42.4883526],[-71.4674627, 42.4883929],[-71.4674479, 42.4884101],[-71.46745780000001, 42.4884147],[-71.4674146, 42.488465]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4529332, 42.4931601],[-71.4529984, 42.4931176],[-71.4530369, 42.49315],[-71.45305620000001, 42.4931374],[-71.4531447, 42.4932119],[-71.4530637, 42.4932647],[-71.453012, 42.4932212],[-71.45295779999999, 42.4932565],[-71.4528735, 42.4931857],[-71.45292430000001, 42.4931527],[-71.4529332, 42.4931601]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.452625, 42.4855913],[-71.4525766, 42.4856068],[-71.45262719999999, 42.4856936],[-71.45252600000001, 42.4857259],[-71.45247139999999, 42.4856322],[-71.4524933, 42.4856252],[-71.4524395, 42.4855328],[-71.4525228, 42.4855062],[-71.4525553, 42.485562],[-71.45259969999999, 42.4855478],[-71.452625, 42.4855913]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4589884, 42.4888828],[-71.4590656, 42.4888565],[-71.45910139999999, 42.4889141],[-71.4591207, 42.4889075],[-71.4591832, 42.489008],[-71.4590875, 42.4890406],[-71.4590444, 42.4889712],[-71.4590029, 42.4889854],[-71.45898649999999, 42.488959],[-71.45902719999999, 42.4889451],[-71.4589884, 42.4888828]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696553, 42.4795767],[-71.4696593, 42.4795935],[-71.46957759999999, 42.4796043],[-71.4695739, 42.479589],[-71.46949549999999, 42.479647],[-71.4694235, 42.4795938],[-71.4694808, 42.4795408],[-71.4695402, 42.4795208],[-71.4696701, 42.4794958],[-71.4696909, 42.4795706],[-71.4696553, 42.4795767]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46110659999999, 42.4802759],[-71.4611824, 42.480245],[-71.4612411, 42.4803237],[-71.4612604, 42.4803158],[-71.4613385, 42.4804207],[-71.4612424, 42.4804599],[-71.46117769999999, 42.480373],[-71.4611395, 42.4803886],[-71.46110640000001, 42.4803442],[-71.4611455, 42.4803283],[-71.46110659999999, 42.4802759]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4701922, 42.4869723],[-71.4702066, 42.4869808],[-71.470089, 42.4871014],[-71.46999219999999, 42.487044],[-71.47007410000001, 42.4869683],[-71.47006089999999, 42.4869605],[-71.47008150000001, 42.4869414],[-71.47009490000001, 42.4869493],[-71.47013560000001, 42.4869117],[-71.47020999999999, 42.4869558],[-71.4701922, 42.4869723]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46985359999999, 42.4834712],[-71.4698679, 42.4834626],[-71.469781, 42.483383],[-71.46986510000001, 42.4833327],[-71.4700978, 42.4835315],[-71.4700296, 42.483578],[-71.46993139999999, 42.4835011],[-71.46992179999999, 42.4835079],[-71.4698918, 42.4834845],[-71.4698808, 42.4834922],[-71.46985359999999, 42.4834712]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4613746, 42.4783811],[-71.4613937, 42.4783872],[-71.4613628, 42.4784401],[-71.4613267, 42.4784286],[-71.4612911, 42.4784896],[-71.4611907, 42.4784574],[-71.46124469999999, 42.4783648],[-71.46126, 42.4783697],[-71.4613141, 42.478277],[-71.4614163, 42.4783097],[-71.4613746, 42.4783811]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"31","addr:state":"MA","source:datetime":"2014-11-08T13:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4580394, 42.4945856],[-71.4580124, 42.4945786],[-71.4579968, 42.4946118],[-71.4580287, 42.4946198],[-71.45798120000001, 42.4947245],[-71.45788570000001, 42.4946968],[-71.45793500000001, 42.4945917],[-71.45794600000001, 42.4945946],[-71.4579864, 42.4945085],[-71.458066, 42.494529],[-71.4580394, 42.4945856]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47475559999999, 42.4775056],[-71.4747029, 42.4775115],[-71.4747098, 42.477545],[-71.47461060000001, 42.477556],[-71.4745947, 42.4774782],[-71.4746366, 42.4774735],[-71.4746245, 42.4774142],[-71.4747036, 42.4774054],[-71.4747078, 42.4774261],[-71.47473859999999, 42.4774227],[-71.47475559999999, 42.4775056]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46582669999999, 42.4876008],[-71.46586739999999, 42.4876121],[-71.4658247, 42.4876964],[-71.4656816, 42.4876568],[-71.4656923, 42.4876356],[-71.465619, 42.4876153],[-71.4656513, 42.4875515],[-71.4657581, 42.487581],[-71.4657718, 42.487554],[-71.4658407, 42.4875731],[-71.46582669999999, 42.4876008]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46563070000001, 42.4868438],[-71.46556289999999, 42.4867757],[-71.4656366, 42.4867356],[-71.46562539999999, 42.4867243],[-71.4657129, 42.4866766],[-71.4656883, 42.4866519],[-71.46573650000001, 42.4866256],[-71.46582789999999, 42.4867175],[-71.46570060000001, 42.4867869],[-71.4657127, 42.4867991],[-71.46563070000001, 42.4868438]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618632, 42.4775051],[-71.46196860000001, 42.4774316],[-71.46203970000001, 42.4774874],[-71.46190439999999, 42.4775818],[-71.4619387, 42.4776088],[-71.4618834, 42.4776474],[-71.4618499, 42.4776211],[-71.4618321, 42.4776336],[-71.4617686, 42.4775838],[-71.4618718, 42.4775118],[-71.4618632, 42.4775051]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4775008, 42.4865429],[-71.47756819999999, 42.4865415],[-71.477569, 42.4865625],[-71.4776028, 42.4865618],[-71.4776066, 42.4866603],[-71.4775126, 42.4866623],[-71.4775118, 42.486642],[-71.4774532, 42.4866432],[-71.4774515, 42.4865986],[-71.4775029, 42.4865975],[-71.4775008, 42.4865429]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4635378, 42.4793113],[-71.46354770000001, 42.4793047],[-71.4635596, 42.4793146],[-71.46354890000001, 42.4793218],[-71.4635906, 42.4793563],[-71.4635095, 42.47941],[-71.4633456, 42.4792745],[-71.46340840000001, 42.4792329],[-71.4634786, 42.4792909],[-71.4634978, 42.4792782],[-71.4635378, 42.4793113]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.470788, 42.4861451],[-71.4708012, 42.4861508],[-71.47076680000001, 42.4861943],[-71.47065790000001, 42.4861472],[-71.4707473, 42.4860341],[-71.47071270000001, 42.4860191],[-71.470742, 42.4859821],[-71.4708273, 42.486019],[-71.4707959, 42.4860588],[-71.4708408, 42.4860783],[-71.470788, 42.4861451]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4748043, 42.4777765],[-71.4748259, 42.477774],[-71.4748293, 42.4777903],[-71.4748116, 42.4777924],[-71.4748297, 42.4778776],[-71.47475489999999, 42.4778863],[-71.47474459999999, 42.4778375],[-71.4747284, 42.4778394],[-71.4747102, 42.4777539],[-71.4747974, 42.4777438],[-71.4748043, 42.4777765]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:50Z"},"geometry": {"type":"LineString","coordinates": [[-71.4504139, 42.4840991],[-71.4504139, 42.4840991]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47337469999999, 42.4736871],[-71.4734926, 42.4736855],[-71.4734943, 42.473757],[-71.4734243, 42.4737579],[-71.47342449999999, 42.473768],[-71.4733602, 42.4737688],[-71.4733598, 42.4737504],[-71.47320190000001, 42.4737525],[-71.47320070000001, 42.4736996],[-71.473375, 42.4736973],[-71.47337469999999, 42.4736871]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4673659, 42.4949206],[-71.4673068, 42.4949849],[-71.46728280000001, 42.4949728],[-71.4672723, 42.4949842],[-71.46724519999999, 42.4949705],[-71.4672247, 42.4949928],[-71.4671701, 42.4949653],[-71.4672025, 42.4949301],[-71.4671829, 42.4949203],[-71.4672407, 42.4948575],[-71.4673659, 42.4949206]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4676408, 42.4878205],[-71.4676614, 42.487793],[-71.46778860000001, 42.487845],[-71.4677449, 42.4879036],[-71.4676748, 42.4878749],[-71.4676831, 42.4878639],[-71.4676285, 42.4878416],[-71.46759849999999, 42.4878817],[-71.4674993, 42.4878411],[-71.4675443, 42.487781],[-71.4676408, 42.4878205]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4748804, 42.487331],[-71.4748276, 42.4873009],[-71.47484559999999, 42.4872836],[-71.4748208, 42.4872694],[-71.47487409999999, 42.4872181],[-71.47483819999999, 42.4871976],[-71.4748647, 42.4871722],[-71.4749685, 42.4872315],[-71.474949, 42.4872502],[-71.4749587, 42.4872557],[-71.4748804, 42.487331]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4701231, 42.4841137],[-71.4702085, 42.4840614],[-71.47029670000001, 42.4841402],[-71.4702834, 42.4841483],[-71.470315, 42.4841766],[-71.47024279999999, 42.4842208],[-71.47020310000001, 42.4841853],[-71.4701663, 42.4842079],[-71.47013080000001, 42.4841761],[-71.4701677, 42.4841535],[-71.4701231, 42.4841137]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4745666, 42.4730817],[-71.474627, 42.4730798],[-71.4746301, 42.4731581],[-71.4747247, 42.4731555],[-71.4747272, 42.4732191],[-71.4746143, 42.4732215],[-71.4746119, 42.4731609],[-71.4745411, 42.4731624],[-71.4745404, 42.4731447],[-71.4745691, 42.4731441],[-71.4745666, 42.4730817]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4534206, 42.4903286],[-71.4534894, 42.4903208],[-71.4534996, 42.4903695],[-71.45358709999999, 42.4903595],[-71.4536028, 42.4904345],[-71.4532943, 42.4904697],[-71.4532648, 42.4903285],[-71.4533382, 42.4903201],[-71.45334819999999, 42.4903682],[-71.4534269, 42.4903592],[-71.4534206, 42.4903286]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46226609999999, 42.48011],[-71.4623374, 42.4800758],[-71.46236450000001, 42.4801068],[-71.4623788, 42.4801],[-71.4624537, 42.4801856],[-71.4623697, 42.4802259],[-71.4623239, 42.4801736],[-71.4622861, 42.4801917],[-71.4622519, 42.4801525],[-71.46228809999999, 42.4801352],[-71.46226609999999, 42.48011]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4750987, 42.4870661],[-71.47510939999999, 42.4870713],[-71.4750489, 42.4871391],[-71.47497509999999, 42.4871031],[-71.4750342, 42.4870368],[-71.4750056, 42.4870228],[-71.4750275, 42.4869982],[-71.47505510000001, 42.4870117],[-71.4750858, 42.4869772],[-71.47515, 42.4870085],[-71.4750987, 42.4870661]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696503, 42.4947127],[-71.4695687, 42.4946745],[-71.46958669999999, 42.4946535],[-71.4694308, 42.4945804],[-71.4694798, 42.4945233],[-71.46946629999999, 42.4945169],[-71.46949050000001, 42.4944887],[-71.46962329999999, 42.494551],[-71.469605, 42.4945723],[-71.4697231, 42.4946277],[-71.4696503, 42.4947127]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4810717, 42.4738547],[-71.4811206, 42.4737586],[-71.48121879999999, 42.4737836],[-71.4811728, 42.4738856],[-71.4812247, 42.4739015],[-71.4812015, 42.4739429],[-71.4810853, 42.4739073],[-71.4810707, 42.4739334],[-71.4809848, 42.4739071],[-71.4810226, 42.4738396],[-71.4810717, 42.4738547]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4685097, 42.4732584],[-71.4684996, 42.4732623],[-71.4685183, 42.473289],[-71.46838940000001, 42.4733385],[-71.46834870000001, 42.4732806],[-71.4684132, 42.4732558],[-71.4683914, 42.4732247],[-71.46845519999999, 42.4732002],[-71.4684743, 42.4732274],[-71.4684851, 42.4732233],[-71.4685097, 42.4732584]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.476377, 42.4720603],[-71.4762975, 42.4720207],[-71.4763126, 42.4720042],[-71.4762887, 42.4719923],[-71.4763201, 42.4719578],[-71.47630289999999, 42.4719492],[-71.4763548, 42.4718922],[-71.4764653, 42.4719473],[-71.4764338, 42.471982],[-71.4764438, 42.471987],[-71.476377, 42.4720603]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4758226, 42.4905035],[-71.4757834, 42.4904958],[-71.47576770000001, 42.4905402],[-71.4757262, 42.4905321],[-71.4757209, 42.4905471],[-71.4756661, 42.4905364],[-71.47570020000001, 42.4904404],[-71.4757742, 42.4904548],[-71.4757772, 42.4904464],[-71.4758386, 42.4904583],[-71.4758226, 42.4905035]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474574, 42.4764374],[-71.47456560000001, 42.4763915],[-71.47449880000001, 42.4763982],[-71.47450480000001, 42.4764307],[-71.47442359999999, 42.4764389],[-71.47441499999999, 42.4763921],[-71.4744839, 42.4763851],[-71.4744757, 42.4763405],[-71.4746312, 42.4763248],[-71.4746505, 42.4764296],[-71.474574, 42.4764374]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4756256, 42.4897487],[-71.4755241, 42.489728],[-71.47555819999999, 42.4896364],[-71.47554719999999, 42.4896342],[-71.4755554, 42.489612],[-71.47552570000001, 42.489606],[-71.4755473, 42.489548],[-71.4756343, 42.4895657],[-71.47560319999999, 42.489649],[-71.47565849999999, 42.4896603],[-71.4756256, 42.4897487]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4521324, 42.4961633],[-71.4521987, 42.4961275],[-71.4523081, 42.4962387],[-71.4523301, 42.4962268],[-71.4524033, 42.4963012],[-71.4523167, 42.4963479],[-71.4522405, 42.4962704],[-71.4521936, 42.4962957],[-71.4521477, 42.4962491],[-71.4521929, 42.4962248],[-71.4521324, 42.4961633]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4642011, 42.4773013],[-71.4642525, 42.4772963],[-71.46426049999999, 42.477341],[-71.46421340000001, 42.4773456],[-71.4642274, 42.4774239],[-71.4641716, 42.4774294],[-71.4641628, 42.4773802],[-71.4641101, 42.4773854],[-71.4640903, 42.4772744],[-71.4641945, 42.4772642],[-71.4642011, 42.4773013]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46977320000001, 42.4883596],[-71.46985239999999, 42.4883874],[-71.4697941, 42.4884976],[-71.4698195, 42.488505],[-71.46979210000001, 42.4885566],[-71.46966209999999, 42.4885188],[-71.46972390000001, 42.4884293],[-71.469763, 42.4884441],[-71.46977579999999, 42.4884256],[-71.4697341, 42.4884098],[-71.46977320000001, 42.4883596]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757041, 42.4742045],[-71.4757599, 42.4742038],[-71.4757608, 42.4742444],[-71.4757129, 42.4742449],[-71.4757139, 42.4742897],[-71.4755627, 42.4742915],[-71.47556040000001, 42.4741867],[-71.47565969999999, 42.4741855],[-71.47565899999999, 42.4741565],[-71.4757031, 42.474156],[-71.4757041, 42.4742045]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46553160000001, 42.4902866],[-71.46547839999999, 42.4902883],[-71.46548079999999, 42.490331],[-71.46543610000001, 42.4903324],[-71.46543680000001, 42.490345],[-71.46538769999999, 42.4903465],[-71.46538529999999, 42.4903055],[-71.4652995, 42.4903082],[-71.465294, 42.4902106],[-71.4655269, 42.4902033],[-71.46553160000001, 42.4902866]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4766135, 42.4837867],[-71.47668640000001, 42.4837785],[-71.4767069, 42.4838775],[-71.4766611, 42.4838826],[-71.47666390000001, 42.4838958],[-71.4766314, 42.4838995],[-71.4766292, 42.4838886],[-71.4765858, 42.4838935],[-71.4765716, 42.4838252],[-71.47662029999999, 42.4838197],[-71.4766135, 42.4837867]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46706810000001, 42.4795354],[-71.4671185, 42.4795478],[-71.46709989999999, 42.479589],[-71.4670622, 42.4795797],[-71.4670158, 42.4796824],[-71.4669075, 42.4796556],[-71.46695130000001, 42.4795586],[-71.4669176, 42.4795502],[-71.4669435, 42.4794928],[-71.46707290000001, 42.4795248],[-71.46706810000001, 42.4795354]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618461, 42.4730228],[-71.46191, 42.473022],[-71.4619107, 42.4730534],[-71.46197359999999, 42.4730527],[-71.4619754, 42.4731322],[-71.4618882, 42.4731332],[-71.46188789999999, 42.4731212],[-71.4617537, 42.4731228],[-71.4617521, 42.473049],[-71.4618467, 42.4730478],[-71.4618461, 42.4730228]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4648, 42.4885793],[-71.4647867, 42.4885711],[-71.4646492, 42.4886926],[-71.4645524, 42.4886326],[-71.46459489999999, 42.4885951],[-71.4645783, 42.4885848],[-71.4646832, 42.4884921],[-71.46470530000001, 42.4885058],[-71.4647609, 42.4884568],[-71.46486539999999, 42.4885215],[-71.4648, 42.4885793]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47634410000001, 42.4881018],[-71.4764083, 42.4881094],[-71.4763806, 42.4882382],[-71.4763222, 42.4882313],[-71.4763066, 42.4883034],[-71.47622320000001, 42.4882935],[-71.4762444, 42.4881954],[-71.47629550000001, 42.4882015],[-71.4763102, 42.4881332],[-71.4763367, 42.4881363],[-71.47634410000001, 42.4881018]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46768830000001, 42.4902184],[-71.4677054, 42.4901907],[-71.46775839999999, 42.4902085],[-71.4677403, 42.4902379],[-71.46783619999999, 42.4902702],[-71.4678002, 42.4903287],[-71.4677186, 42.4903012],[-71.4677063, 42.4903212],[-71.46754679999999, 42.4902675],[-71.46759609999999, 42.4901874],[-71.46768830000001, 42.4902184]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46238940000001, 42.4772592],[-71.46232209999999, 42.4772976],[-71.46235350000001, 42.4773277],[-71.4623052, 42.4773552],[-71.4622756, 42.4773267],[-71.46216920000001, 42.4773872],[-71.4621027, 42.4773231],[-71.46221490000001, 42.4772593],[-71.4622258, 42.4772699],[-71.4623356, 42.4772074],[-71.46238940000001, 42.4772592]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.460134, 42.489344],[-71.46030210000001, 42.4893419],[-71.4603036, 42.4894054],[-71.4601821, 42.4894069],[-71.46018290000001, 42.4894394],[-71.4600868, 42.4894406],[-71.4600863, 42.4894177],[-71.4599922, 42.4894189],[-71.45999019999999, 42.4893319],[-71.4601337, 42.4893301],[-71.460134, 42.489344]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46609220000001, 42.4765542],[-71.4659657, 42.4766021],[-71.465971, 42.4766097],[-71.4658405, 42.4766591],[-71.46579130000001, 42.4765878],[-71.4658706, 42.4765577],[-71.4658458, 42.4765217],[-71.4658821, 42.476508],[-71.46590449999999, 42.4765405],[-71.4660458, 42.476487],[-71.46609220000001, 42.4765542]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4664398, 42.4899374],[-71.46648020000001, 42.4899534],[-71.46643, 42.4900226],[-71.4663096, 42.4899748],[-71.4663577, 42.4899084],[-71.46632219999999, 42.4898943],[-71.4663439, 42.4898643],[-71.4663724, 42.4898756],[-71.4663839, 42.4898598],[-71.466471, 42.4898944],[-71.4664398, 42.4899374]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4615038, 42.4900931],[-71.4615818, 42.4900674],[-71.4616288, 42.4901455],[-71.46155229999999, 42.4901707],[-71.46154660000001, 42.4901611],[-71.4613844, 42.4902145],[-71.4613301, 42.4901241],[-71.46144080000001, 42.4900876],[-71.4614644, 42.4901269],[-71.4615143, 42.4901105],[-71.4615038, 42.4900931]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45395139999999, 42.471827],[-71.45393869999999, 42.4718455],[-71.4540083, 42.4718713],[-71.4539724, 42.4719239],[-71.4539233, 42.4719058],[-71.4539329, 42.4718918],[-71.45389470000001, 42.4718775],[-71.4538876, 42.4718878],[-71.453783, 42.4718486],[-71.45395139999999, 42.471827]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46926000000001, 42.4884356],[-71.4692766, 42.4884416],[-71.4692534, 42.4884761],[-71.46923959999999, 42.4884711],[-71.4692154, 42.4885073],[-71.46911799999999, 42.4884714],[-71.4691887, 42.488366],[-71.4692503, 42.4883886],[-71.46924009999999, 42.4884037],[-71.4692732, 42.4884159],[-71.46926000000001, 42.4884356]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4625217, 42.4864413],[-71.4626066, 42.486502],[-71.4625025, 42.4865817],[-71.46249210000001, 42.4865742],[-71.4623848, 42.4866621],[-71.46231229999999, 42.4866137],[-71.4623816, 42.4865569],[-71.4623405, 42.4865294],[-71.4623833, 42.4864944],[-71.46241910000001, 42.4865199],[-71.4625217, 42.4864413]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46334179999999, 42.489261],[-71.4635159, 42.4892573],[-71.46352, 42.4893634],[-71.4634762, 42.4893643],[-71.4634768, 42.4893795],[-71.4634197, 42.4893807],[-71.4634191, 42.4893645],[-71.4632845, 42.4893674],[-71.4632812, 42.4892829],[-71.4633426, 42.4892816],[-71.46334179999999, 42.489261]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4536478, 42.4892919],[-71.4535297, 42.4893467],[-71.4535425, 42.4893618],[-71.45342359999999, 42.489417],[-71.45336639999999, 42.4893494],[-71.4534755, 42.4892988],[-71.4534476, 42.4892659],[-71.45348850000001, 42.4892469],[-71.4535145, 42.4892777],[-71.4536015, 42.4892373],[-71.4536478, 42.4892919]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689496, 42.4826076],[-71.4689269, 42.4826188],[-71.46892819999999, 42.4826293],[-71.46889640000001, 42.4826481],[-71.4688783, 42.4826418],[-71.4688082, 42.4826789],[-71.4687349, 42.4826026],[-71.4689626, 42.4824895],[-71.4690391, 42.482574],[-71.4689563, 42.482615],[-71.4689496, 42.4826076]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4776684, 42.4896142],[-71.4776999, 42.4896182],[-71.47767930000001, 42.4897075],[-71.4775834, 42.4896954],[-71.4776033, 42.4896093],[-71.47753590000001, 42.4896008],[-71.4775461, 42.4895564],[-71.4775751, 42.4895601],[-71.4775865, 42.4895106],[-71.47768929999999, 42.4895236],[-71.4776684, 42.4896142]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45868350000001, 42.4850467],[-71.4587808, 42.4850671],[-71.458741, 42.4851715],[-71.45870360000001, 42.4851636],[-71.4586918, 42.4851946],[-71.45862529999999, 42.4851807],[-71.4586493, 42.4851177],[-71.4585854, 42.4851044],[-71.4586176, 42.4850199],[-71.4586881, 42.4850347],[-71.45868350000001, 42.4850467]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47691039999999, 42.4908949],[-71.47705000000001, 42.4909507],[-71.47696240000001, 42.4910707],[-71.4769472, 42.4910646],[-71.47690040000001, 42.4911289],[-71.4768049, 42.4910907],[-71.4768531, 42.4910246],[-71.4768653, 42.4910295],[-71.476918, 42.4909572],[-71.47687689999999, 42.4909408],[-71.47691039999999, 42.4908949]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"198","addr:state":"MA","source:datetime":"2015-01-20T23:47:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46151709999999, 42.4989387],[-71.46153219999999, 42.498962],[-71.46144390000001, 42.4989933],[-71.46137589999999, 42.498888],[-71.4614983, 42.4988447],[-71.46149269999999, 42.4988361],[-71.461546, 42.4988173],[-71.4615534, 42.4988288],[-71.4615956, 42.4988138],[-71.4616467, 42.4988928],[-71.46151709999999, 42.4989387]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4638697, 42.4789842],[-71.4639229, 42.4789527],[-71.4639664, 42.4789929],[-71.4639167, 42.4790224],[-71.46401059999999, 42.4791093],[-71.4639296, 42.4791403],[-71.4638205, 42.4790331],[-71.4638047, 42.4790443],[-71.4637195, 42.4789654],[-71.4637987, 42.4789185],[-71.4638697, 42.4789842]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45317470000001, 42.4866559],[-71.4532251, 42.4866687],[-71.4532012, 42.4867202],[-71.45315410000001, 42.4867082],[-71.4531265, 42.4867677],[-71.4530386, 42.4867454],[-71.4530834, 42.4866489],[-71.4530673, 42.4866448],[-71.45311150000001, 42.4865494],[-71.4532122, 42.486575],[-71.45317470000001, 42.4866559]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45253580000001, 42.4953833],[-71.45257549999999, 42.4953872],[-71.4525683, 42.4954278],[-71.4525292, 42.4954239],[-71.45252499999999, 42.4954474],[-71.4523061, 42.4954261],[-71.4523224, 42.495334],[-71.4524914, 42.4953505],[-71.452488, 42.4953696],[-71.4525373, 42.4953744],[-71.45253580000001, 42.4953833]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45769679999999, 42.4749091],[-71.4577737, 42.4749163],[-71.45776309999999, 42.474978],[-71.4576478, 42.4749672],[-71.4576457, 42.4749797],[-71.45754460000001, 42.4749702],[-71.4575597, 42.4748817],[-71.4576577, 42.4748909],[-71.45765350000001, 42.4749158],[-71.45769490000001, 42.4749196],[-71.45769679999999, 42.4749091]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45826820000001, 42.4869796],[-71.4582182, 42.4870123],[-71.45823369999999, 42.4870253],[-71.4581203, 42.4870994],[-71.4581089, 42.4870898],[-71.45804510000001, 42.4871315],[-71.4579822, 42.4870788],[-71.4581609, 42.4869621],[-71.4581773, 42.4869758],[-71.45822579999999, 42.4869441],[-71.45826820000001, 42.4869796]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"20","addr:state":"MA","source:datetime":"2014-11-08T14:01:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45374510000001, 42.492578],[-71.4537959, 42.492573],[-71.45377619999999, 42.4924635],[-71.4538799, 42.4924533],[-71.45391909999999, 42.4926713],[-71.45389729999999, 42.4926734],[-71.45389539999999, 42.4926625],[-71.4538239, 42.4926695],[-71.4538124, 42.4926057],[-71.45375110000001, 42.4926117],[-71.45374510000001, 42.492578]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706908, 42.4899063],[-71.4707714, 42.4898257],[-71.4709139, 42.4899039],[-71.4708777, 42.4899401],[-71.4708503, 42.4899252],[-71.47080649999999, 42.4899689],[-71.47076250000001, 42.4899448],[-71.4707475, 42.4899598],[-71.4707246, 42.4899473],[-71.4707391, 42.4899328],[-71.4706908, 42.4899063]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4682584, 42.4882441],[-71.4683781, 42.4882909],[-71.46833959999999, 42.4883449],[-71.4682973, 42.4883284],[-71.4682272, 42.4884268],[-71.4681076, 42.4883801],[-71.4681482, 42.4883231],[-71.4681698, 42.4883316],[-71.4682021, 42.4882861],[-71.46822280000001, 42.4882941],[-71.4682584, 42.4882441]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"234","addr:state":"MA","source:datetime":"2015-01-20T23:47:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46353209999999, 42.4990484],[-71.4636369, 42.4990724],[-71.4635945, 42.4991741],[-71.4636326, 42.4991829],[-71.46359990000001, 42.4992613],[-71.4633944, 42.4992143],[-71.46341049999999, 42.4991757],[-71.46342970000001, 42.4991801],[-71.46344740000001, 42.4991376],[-71.46349069999999, 42.4991475],[-71.46353209999999, 42.4990484]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4731774, 42.4747342],[-71.4734153, 42.4747365],[-71.4734139, 42.4748178],[-71.47334549999999, 42.4748171],[-71.4733452, 42.4748341],[-71.473281, 42.4748334],[-71.4732814, 42.4748124],[-71.473192, 42.4748115],[-71.47319299999999, 42.4747534],[-71.4731771, 42.4747532],[-71.4731774, 42.4747342]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4525775, 42.4867727],[-71.4526259, 42.4867832],[-71.4526128, 42.4868161],[-71.45256790000001, 42.4868063],[-71.4525381, 42.4868809],[-71.4524279, 42.4868568],[-71.45245130000001, 42.4867982],[-71.45238740000001, 42.4867842],[-71.4524127, 42.4867208],[-71.4525833, 42.4867581],[-71.4525775, 42.4867727]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4510946, 42.4844699],[-71.45105409999999, 42.4844484],[-71.4510281, 42.4844751],[-71.4509917, 42.4844557],[-71.4509401, 42.4845093],[-71.4508476, 42.4844601],[-71.4509303, 42.4843745],[-71.4509417, 42.4843806],[-71.4509712, 42.4843502],[-71.4511292, 42.4844343],[-71.4510946, 42.4844699]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"16","source:datetime":"2014-04-11T16:40:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4812718, 42.4742548],[-71.4813675, 42.4742836],[-71.4813205, 42.4743692],[-71.4812991, 42.4743627],[-71.48125039999999, 42.4744514],[-71.4811584, 42.4744238],[-71.4811922, 42.4743622],[-71.48122239999999, 42.4743713],[-71.4812392, 42.4743407],[-71.48122669999999, 42.4743369],[-71.4812718, 42.4742548]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4509884, 42.4891695],[-71.45100360000001, 42.4891698],[-71.4510002, 42.4892758],[-71.4508889, 42.4892739],[-71.4508901, 42.4892364],[-71.4508404, 42.4892355],[-71.4508416, 42.4891967],[-71.450891, 42.4891976],[-71.4508933, 42.489126],[-71.45098969999999, 42.4891277],[-71.4509884, 42.4891695]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46383539999999, 42.471889],[-71.4638917, 42.4719163],[-71.4638602, 42.4719519],[-71.46381119999999, 42.4719281],[-71.4637615, 42.4719841],[-71.46367410000001, 42.4719417],[-71.4637273, 42.4718816],[-71.46374299999999, 42.4718892],[-71.46377990000001, 42.4718476],[-71.46384430000001, 42.4718789],[-71.46383539999999, 42.471889]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689206, 42.4889524],[-71.468867, 42.4889335],[-71.46887409999999, 42.4889226],[-71.46883219999999, 42.4889078],[-71.4688512, 42.4888784],[-71.46880969999999, 42.4888637],[-71.4688347, 42.4888249],[-71.4688977, 42.4888471],[-71.4689279, 42.4888002],[-71.4690018, 42.4888263],[-71.4689206, 42.4889524]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47690160000001, 42.4880906],[-71.4769517, 42.488093],[-71.4769472, 42.4881456],[-71.4768809, 42.4881425],[-71.4768795, 42.4881595],[-71.4768059, 42.488156],[-71.4768088, 42.4881226],[-71.4767885, 42.4881216],[-71.47680130000001, 42.4879737],[-71.4769113, 42.487979],[-71.47690160000001, 42.4880906]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4508694, 42.4899326],[-71.45094589999999, 42.4899272],[-71.45095790000001, 42.4900206],[-71.4509786, 42.4900192],[-71.4509925, 42.4901276],[-71.4508804, 42.4901355],[-71.4508675, 42.4900352],[-71.45082429999999, 42.4900382],[-71.45081829999999, 42.4899909],[-71.4508764, 42.4899868],[-71.4508694, 42.4899326]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47216589999999, 42.4973304],[-71.47217499999999, 42.497337],[-71.4720793, 42.4974093],[-71.4719904, 42.4973449],[-71.4720749, 42.4972811],[-71.4720552, 42.4972668],[-71.4722041, 42.4971543],[-71.47228490000001, 42.4972128],[-71.4721756, 42.4972954],[-71.4721943, 42.4973089],[-71.47216589999999, 42.4973304]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47794279999999, 42.4721367],[-71.47796990000001, 42.4721435],[-71.47794279999999, 42.4722029],[-71.4778286, 42.4721742],[-71.4778427, 42.4721435],[-71.4778093, 42.4721351],[-71.4778329, 42.4720835],[-71.4778635, 42.4720911],[-71.477879, 42.4720574],[-71.4779688, 42.47208],[-71.47794279999999, 42.4721367]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46883390000001, 42.471835],[-71.46880299999999, 42.4718968],[-71.46878510000001, 42.4718919],[-71.46875350000001, 42.471955],[-71.4686066, 42.4719147],[-71.46861079999999, 42.4719063],[-71.4685525, 42.4718903],[-71.4685832, 42.4718289],[-71.46873720000001, 42.4718719],[-71.46876469999999, 42.4718168],[-71.46883390000001, 42.471835]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757911, 42.4757424],[-71.4758484, 42.4757088],[-71.4759669, 42.4758196],[-71.475911, 42.4758524],[-71.4759011, 42.4758431],[-71.4758794, 42.4758558],[-71.47585429999999, 42.4758324],[-71.47583640000001, 42.4758429],[-71.4757962, 42.4758053],[-71.4758344, 42.475783],[-71.4757911, 42.4757424]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45075249999999, 42.4860772],[-71.4508026, 42.4860752],[-71.450805, 42.4861089],[-71.4508924, 42.4861054],[-71.45089919999999, 42.4861989],[-71.4508139, 42.4862023],[-71.4508132, 42.4861929],[-71.4506698, 42.4861986],[-71.4506635, 42.4861129],[-71.45075490000001, 42.4861093],[-71.45075249999999, 42.4860772]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4827789, 42.473665],[-71.4827714, 42.4736733],[-71.4828471, 42.4737111],[-71.48278929999999, 42.4737744],[-71.4827129, 42.4737362],[-71.4827214, 42.4737269],[-71.4826286, 42.4736805],[-71.48261770000001, 42.4736924],[-71.482597, 42.473682],[-71.4826647, 42.4736079],[-71.4827789, 42.473665]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4633051, 42.4748745],[-71.463362, 42.4748729],[-71.46336580000001, 42.4749507],[-71.4632242, 42.4749545],[-71.4632264, 42.4749981],[-71.46315850000001, 42.4749999],[-71.4631571, 42.4749713],[-71.4631279, 42.4749721],[-71.46312260000001, 42.4748648],[-71.4633044, 42.4748599],[-71.4633051, 42.4748745]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45852979999999, 42.4747457],[-71.45864229999999, 42.4747567],[-71.45862750000001, 42.4748397],[-71.4584852, 42.4748258],[-71.45848839999999, 42.4748082],[-71.4584222, 42.4748017],[-71.4584345, 42.4747331],[-71.458446, 42.4747342],[-71.4584511, 42.4747059],[-71.4585355, 42.4747141],[-71.45852979999999, 42.4747457]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46604019999999, 42.4880925],[-71.4662161, 42.4881393],[-71.4661897, 42.4881936],[-71.46612709999999, 42.488177],[-71.46609840000001, 42.488236],[-71.46598539999999, 42.488206],[-71.4660047, 42.4881612],[-71.4659383, 42.4881435],[-71.46596599999999, 42.4880863],[-71.4660344, 42.4881045],[-71.46604019999999, 42.4880925]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715109, 42.4758949],[-71.47144950000001, 42.4758966],[-71.4714505, 42.4759169],[-71.47137309999999, 42.475919],[-71.47136949999999, 42.4758475],[-71.47142409999999, 42.475846],[-71.47141999999999, 42.4757656],[-71.47148559999999, 42.4757638],[-71.47148970000001, 42.4758449],[-71.4715084, 42.4758443],[-71.4715109, 42.4758949]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4519753, 42.4730278],[-71.4521409, 42.473057],[-71.4521089, 42.4731569],[-71.45209149999999, 42.4731539],[-71.45207720000001, 42.4731985],[-71.4520275, 42.4731898],[-71.4520409, 42.4731478],[-71.4518893, 42.4731211],[-71.4519134, 42.473046],[-71.4519665, 42.4730553],[-71.4519753, 42.4730278]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4704947, 42.486311],[-71.47059710000001, 42.4863473],[-71.4705409, 42.4864343],[-71.4704684, 42.4864086],[-71.47040610000001, 42.4865049],[-71.470314, 42.4864723],[-71.47035459999999, 42.4864095],[-71.4703744, 42.4864165],[-71.47039909999999, 42.4863784],[-71.4704414, 42.4863934],[-71.4704947, 42.486311]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.465301, 42.4879656],[-71.46534130000001, 42.487893],[-71.465519, 42.4879469],[-71.46551460000001, 42.487955],[-71.46558520000001, 42.4879764],[-71.4655436, 42.4880514],[-71.4654721, 42.4880297],[-71.46546069999999, 42.4880503],[-71.4654019, 42.4880324],[-71.46541910000001, 42.4880014],[-71.465301, 42.4879656]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4746829, 42.4728319],[-71.47473460000001, 42.4728316],[-71.4747354, 42.4729128],[-71.47469719999999, 42.472913],[-71.4746975, 42.4729418],[-71.4745884, 42.4729424],[-71.4745874, 42.4728375],[-71.4745183, 42.4728379],[-71.4745179, 42.4728037],[-71.47468259999999, 42.4728028],[-71.4746829, 42.4728319]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4673803, 42.4907005],[-71.4674179, 42.4907138],[-71.46739410000001, 42.490751],[-71.46719659999999, 42.4906815],[-71.4672341, 42.4906231],[-71.467218, 42.4906175],[-71.4672758, 42.4905273],[-71.4673471, 42.4905524],[-71.4672915, 42.4906388],[-71.4673963, 42.4906757],[-71.4673803, 42.4907005]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45238070000001, 42.4939566],[-71.4524799, 42.4939111],[-71.4526259, 42.4940856],[-71.45260519999999, 42.494095],[-71.452612, 42.4941032],[-71.4525404, 42.494136],[-71.4525079, 42.4940972],[-71.452484, 42.4941081],[-71.45239580000001, 42.4940026],[-71.4524127, 42.4939949],[-71.45238070000001, 42.4939566]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4682691, 42.4736373],[-71.4682566, 42.4736416],[-71.4683227, 42.4737464],[-71.4682291, 42.4737787],[-71.4682222, 42.4737679],[-71.4681903, 42.4737789],[-71.4681314, 42.4736855],[-71.4681599, 42.4736757],[-71.4681269, 42.4736233],[-71.4682364, 42.4735855],[-71.4682691, 42.4736373]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4779724, 42.4882858],[-71.4779341, 42.4882837],[-71.477932, 42.4883039],[-71.4779733, 42.4883062],[-71.4779632, 42.4884056],[-71.47786859999999, 42.4884004],[-71.4778809, 42.4882788],[-71.47783699999999, 42.4882764],[-71.47784540000001, 42.4881936],[-71.477981, 42.4882012],[-71.4779724, 42.4882858]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706676, 42.4935806],[-71.47067699999999, 42.4935875],[-71.4705735, 42.4936652],[-71.47049060000001, 42.4936047],[-71.4705917, 42.4935289],[-71.4705702, 42.4935131],[-71.4706067, 42.4934857],[-71.4706355, 42.4935068],[-71.4706673, 42.493483],[-71.4707334, 42.4935313],[-71.4706676, 42.4935806]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713435, 42.4922811],[-71.4713814, 42.4922976],[-71.47131520000001, 42.4923809],[-71.4712664, 42.4923596],[-71.4712416, 42.4923907],[-71.471174, 42.4923612],[-71.4712117, 42.4923138],[-71.4711939, 42.492306],[-71.4712632, 42.4922187],[-71.47135969999999, 42.4922607],[-71.4713435, 42.4922811]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4782479, 42.4891878],[-71.4783521, 42.4892051],[-71.4783158, 42.489325],[-71.4782627, 42.4893162],[-71.4782412, 42.4893875],[-71.4781483, 42.4893721],[-71.4781638, 42.4893208],[-71.47818650000001, 42.4893246],[-71.4781917, 42.4893076],[-71.47821070000001, 42.4893107],[-71.4782479, 42.4891878]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46591549999999, 42.494274],[-71.46598040000001, 42.494206],[-71.46606629999999, 42.4942509],[-71.4660133, 42.4943065],[-71.4660715, 42.494337],[-71.4660374, 42.4943727],[-71.46598400000001, 42.4943448],[-71.4659723, 42.494357],[-71.46585020000001, 42.4942933],[-71.4658841, 42.4942577],[-71.46591549999999, 42.494274]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692761, 42.4834931],[-71.46934109999999, 42.4835102],[-71.46926929999999, 42.483637],[-71.469246, 42.4836282],[-71.4692041, 42.4837155],[-71.469071, 42.4836804],[-71.4691167, 42.4835853],[-71.46913739999999, 42.4835908],[-71.4691849, 42.483492],[-71.4692663, 42.4835134],[-71.4692761, 42.4834931]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4575943, 42.4878311],[-71.4576805, 42.4877822],[-71.4578202, 42.4879179],[-71.45769629999999, 42.4879928],[-71.45766, 42.4879586],[-71.45770400000001, 42.487933],[-71.4576648, 42.4878961],[-71.45765160000001, 42.4879038],[-71.4576168, 42.487871],[-71.45762929999999, 42.4878637],[-71.4575943, 42.4878311]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4510119, 42.488766],[-71.45111199999999, 42.4887822],[-71.4510738, 42.4889121],[-71.45105839999999, 42.4889096],[-71.45103779999999, 42.4889796],[-71.45096220000001, 42.4889674],[-71.4509805, 42.488905],[-71.4509195, 42.4888952],[-71.4509309, 42.4888567],[-71.4509827, 42.4888651],[-71.4510119, 42.488766]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4625625, 42.4789677],[-71.46252010000001, 42.4789766],[-71.46253950000001, 42.4790274],[-71.4624396, 42.4790483],[-71.4624025, 42.478951],[-71.4624152, 42.4789484],[-71.4624002, 42.4789092],[-71.462484, 42.4788917],[-71.4624941, 42.4789182],[-71.4625399, 42.4789086],[-71.4625625, 42.4789677]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.452772, 42.4882121],[-71.45300039999999, 42.4882096],[-71.45300159999999, 42.4882656],[-71.4529254, 42.4882664],[-71.45292739999999, 42.4883641],[-71.45278450000001, 42.4883657],[-71.45278329999999, 42.4883065],[-71.4527253, 42.4883072],[-71.4527247, 42.4882745],[-71.4527732, 42.488274],[-71.452772, 42.4882121]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4628797, 42.4766569],[-71.4628543, 42.4766673],[-71.4629126, 42.4767345],[-71.4628024, 42.4767868],[-71.4627871, 42.4767692],[-71.46266559999999, 42.4768269],[-71.4626105, 42.4767633],[-71.46283149999999, 42.4766773],[-71.4628117, 42.4766494],[-71.4628608, 42.4766303],[-71.4628797, 42.4766569]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46634090000001, 42.4820264],[-71.4662982, 42.4820956],[-71.466199, 42.4820621],[-71.4662701, 42.4819468],[-71.4663676, 42.4819798],[-71.46636220000001, 42.4819885],[-71.4664054, 42.4820031],[-71.46638849999999, 42.4820306],[-71.46635860000001, 42.4820204],[-71.4663525, 42.4820303],[-71.46634090000001, 42.4820264]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4601748, 42.4759705],[-71.4601998, 42.4759345],[-71.4603135, 42.4759779],[-71.46030709999999, 42.4759872],[-71.4603544, 42.4760052],[-71.46031259999999, 42.4760653],[-71.46015420000001, 42.4760049],[-71.46014700000001, 42.4760152],[-71.4601042, 42.4759989],[-71.4601346, 42.4759552],[-71.4601748, 42.4759705]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4615771, 42.4784339],[-71.46164090000001, 42.4783704],[-71.4617598, 42.4784359],[-71.4617499, 42.4784457],[-71.4618574, 42.4785049],[-71.4618021, 42.4785599],[-71.46168249999999, 42.478494],[-71.4616564, 42.4785198],[-71.4616036, 42.4784907],[-71.4616309, 42.4784635],[-71.4615771, 42.4784339]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4769391, 42.4836678],[-71.4770553, 42.4836524],[-71.4770664, 42.4836984],[-71.4770403, 42.4837019],[-71.477068, 42.4838168],[-71.47697839999999, 42.4838287],[-71.4769707, 42.4837969],[-71.47694300000001, 42.4838006],[-71.4769379, 42.4837796],[-71.4769653, 42.483776],[-71.4769391, 42.4836678]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4661303, 42.4903673],[-71.46616830000001, 42.4903819],[-71.4661337, 42.4904313],[-71.46601339999999, 42.4903851],[-71.46604720000001, 42.4903367],[-71.46601250000001, 42.4903234],[-71.4660375, 42.4902878],[-71.4660731, 42.4903014],[-71.4660938, 42.4902718],[-71.4661753, 42.4903031],[-71.4661303, 42.4903673]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4516508, 42.4894623],[-71.45169490000001, 42.4894577],[-71.451705, 42.4895113],[-71.45165419999999, 42.4895165],[-71.4516579, 42.4895361],[-71.4515727, 42.4895449],[-71.4515531, 42.4894407],[-71.45153449999999, 42.4894426],[-71.4515147, 42.4893375],[-71.4516252, 42.4893261],[-71.4516508, 42.4894623]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4602091, 42.484267],[-71.46022429999999, 42.4842356],[-71.46027890000001, 42.4842501],[-71.46027239999999, 42.4842635],[-71.46035569999999, 42.4842856],[-71.4603106, 42.4843787],[-71.4601874, 42.4843459],[-71.46018359999999, 42.4843537],[-71.4600442, 42.4843167],[-71.46008430000001, 42.4842339],[-71.4602091, 42.484267]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4719927, 42.4904531],[-71.47212570000001, 42.4904885],[-71.47208070000001, 42.4905813],[-71.472047, 42.4905723],[-71.4720236, 42.4906204],[-71.47197610000001, 42.4906078],[-71.4719995, 42.4905596],[-71.4719703, 42.4905518],[-71.4720023, 42.490486],[-71.47197970000001, 42.4904799],[-71.4719927, 42.4904531]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4628364, 42.4804076],[-71.4629108, 42.480444],[-71.462863, 42.4804976],[-71.46281070000001, 42.480472],[-71.46278359999999, 42.4805023],[-71.4627246, 42.4804735],[-71.46275079999999, 42.4804442],[-71.46263310000001, 42.4803865],[-71.4626943, 42.480318],[-71.4628488, 42.4803937],[-71.4628364, 42.4804076]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47675889999999, 42.487321],[-71.4768072, 42.4873079],[-71.47681799999999, 42.4873297],[-71.4768674, 42.4873163],[-71.4769024, 42.4873871],[-71.4767745, 42.4874218],[-71.47675150000001, 42.4873753],[-71.47669689999999, 42.4873901],[-71.4766827, 42.4873613],[-71.4767674, 42.4873383],[-71.47675889999999, 42.487321]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4640563, 42.4900848],[-71.46414969999999, 42.4900906],[-71.4641416, 42.4901612],[-71.46392299999999, 42.4901475],[-71.4639189, 42.490183],[-71.46386800000001, 42.4901798],[-71.46387919999999, 42.4900823],[-71.4639308, 42.4900856],[-71.46393310000001, 42.4900652],[-71.4640577, 42.490073],[-71.4640563, 42.4900848]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"43","addr:state":"MA","source:datetime":"2014-11-08T13:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45933410000001, 42.4932837],[-71.45937069999999, 42.4932925],[-71.4593515, 42.4933365],[-71.4593225, 42.4933296],[-71.459288, 42.493409],[-71.4591878, 42.4933851],[-71.4592315, 42.4932849],[-71.45924429999999, 42.4932879],[-71.4592674, 42.4932348],[-71.4593471, 42.4932539],[-71.45933410000001, 42.4932837]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4740836, 42.4734532],[-71.474031, 42.4734537],[-71.47403060000001, 42.4734296],[-71.4739881, 42.47343],[-71.4739885, 42.4734541],[-71.47384409999999, 42.4734554],[-71.47384270000001, 42.4733704],[-71.4739792, 42.4733692],[-71.4739794, 42.47338],[-71.4740824, 42.4733791],[-71.4740836, 42.4734532]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696519, 42.4723149],[-71.469795, 42.4722884],[-71.46977579999999, 42.4722316],[-71.46988519999999, 42.4722113],[-71.4699401, 42.472374],[-71.4698308, 42.4723942],[-71.4698209, 42.4723651],[-71.4696931, 42.4723888],[-71.46967600000001, 42.4723382],[-71.4696608, 42.4723411],[-71.4696519, 42.4723149]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4504039, 42.487444],[-71.45054810000001, 42.4874525],[-71.4505388, 42.487539],[-71.45041759999999, 42.4875318],[-71.45041430000001, 42.4875622],[-71.4504025, 42.4874571],[-71.4504039, 42.487444]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4629784, 42.4731128],[-71.46304000000001, 42.473113],[-71.4630396, 42.4731874],[-71.4629078, 42.473187],[-71.4629077, 42.4731967],[-71.4627796, 42.4731963],[-71.4627801, 42.4731138],[-71.46290860000001, 42.4731142],[-71.46290879999999, 42.4730826],[-71.4629786, 42.4730828],[-71.4629784, 42.4731128]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4690215, 42.4728907],[-71.4690497, 42.4728505],[-71.46901339999999, 42.4728366],[-71.4690506, 42.4727834],[-71.46917209999999, 42.47283],[-71.46908019999999, 42.4729611],[-71.46900309999999, 42.4729315],[-71.4689872, 42.4729542],[-71.46893009999999, 42.4729323],[-71.4689724, 42.4728718],[-71.4690215, 42.4728907]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4685842, 42.48104],[-71.4686256, 42.4810004],[-71.4687117, 42.4810496],[-71.4687471, 42.4810227],[-71.4688149, 42.4810667],[-71.4687587, 42.4811206],[-71.46870079999999, 42.4810875],[-71.4686753, 42.4811121],[-71.4686193, 42.48108],[-71.4686323, 42.4810675],[-71.4685842, 42.48104]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697369, 42.4886465],[-71.4697051, 42.4886935],[-71.46972239999999, 42.4886999],[-71.46969420000001, 42.4887416],[-71.4697061, 42.488746],[-71.46968630000001, 42.4887752],[-71.4696283, 42.4887537],[-71.4696141, 42.4887748],[-71.46951730000001, 42.4887388],[-71.46961140000001, 42.4885999],[-71.4697369, 42.4886465]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4711234, 42.4823129],[-71.47113160000001, 42.4823008],[-71.4712633, 42.4823469],[-71.47119669999999, 42.4824452],[-71.4711384, 42.4824236],[-71.4711547, 42.4823996],[-71.47104469999999, 42.4823587],[-71.4710365, 42.4823708],[-71.47100620000001, 42.4823595],[-71.47105500000001, 42.4822875],[-71.4711234, 42.4823129]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4670948, 42.4897922],[-71.4671427, 42.4898102],[-71.46711380000001, 42.4898523],[-71.467097, 42.4898459],[-71.4670665, 42.4898903],[-71.46698480000001, 42.4898595],[-71.4670296, 42.4897942],[-71.46700079999999, 42.4897834],[-71.4670543, 42.4897055],[-71.4671338, 42.4897354],[-71.4670948, 42.4897922]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687576, 42.4830146],[-71.4687761, 42.4830222],[-71.4686571, 42.4831819],[-71.4685613, 42.4831428],[-71.46860700000001, 42.4830814],[-71.4685964, 42.4830771],[-71.4686703, 42.4829778],[-71.4686816, 42.4829824],[-71.4687086, 42.482946],[-71.4687854, 42.4829774],[-71.4687576, 42.4830146]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:46:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4633264, 42.4859007],[-71.46331429999999, 42.4858945],[-71.4632422, 42.4859715],[-71.46321469999999, 42.4859574],[-71.463148, 42.4860287],[-71.4630927, 42.4860003],[-71.4632343, 42.4858489],[-71.4632086, 42.4858354],[-71.4633001, 42.485749],[-71.46340910000001, 42.4858123],[-71.4633264, 42.4859007]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47426900000001, 42.4737366],[-71.47438820000001, 42.473736],[-71.4743886, 42.4737808],[-71.4746083, 42.4737796],[-71.4746091, 42.47386],[-71.4744487, 42.4738608],[-71.47444849999999, 42.4738422],[-71.47436, 42.4738427],[-71.4743596, 42.4738052],[-71.47426969999999, 42.4738057],[-71.47426900000001, 42.4737366]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4521165, 42.4905446],[-71.4521702, 42.4905485],[-71.4521774, 42.4904937],[-71.45223369999999, 42.4904978],[-71.4522158, 42.4906332],[-71.4520208, 42.4906191],[-71.45203170000001, 42.4905368],[-71.45207980000001, 42.4905402],[-71.45208119999999, 42.4905295],[-71.4521182, 42.4905322],[-71.4521165, 42.4905446]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4782522, 42.4749552],[-71.47813429999999, 42.4749744],[-71.4781243, 42.474941],[-71.4780909, 42.4749464],[-71.4780973, 42.474968],[-71.47804410000001, 42.4749767],[-71.4780178, 42.4748882],[-71.4781029, 42.4748743],[-71.4781115, 42.4749033],[-71.47823099999999, 42.4748838],[-71.4782522, 42.4749552]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4635072, 42.4811782],[-71.46353019999999, 42.4811902],[-71.46348, 42.4812426],[-71.46336580000001, 42.4811828],[-71.4633954, 42.4811519],[-71.46335259999999, 42.4811295],[-71.46338179999999, 42.481099],[-71.4634243, 42.4811213],[-71.4634915, 42.481051],[-71.463583, 42.4810989],[-71.4635072, 42.4811782]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4729895, 42.4899792],[-71.4730316, 42.4899858],[-71.47302310000001, 42.4900153],[-71.47299599999999, 42.490011],[-71.4729853, 42.4900484],[-71.4728988, 42.4900347],[-71.4729171, 42.4899713],[-71.47288829999999, 42.4899667],[-71.47291439999999, 42.4898759],[-71.47301469999999, 42.4898917],[-71.4729895, 42.4899792]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45225840000001, 42.4873628],[-71.45222579999999, 42.4874163],[-71.4522776, 42.4874336],[-71.4522499, 42.4874791],[-71.4522351, 42.4874742],[-71.4522205, 42.487498],[-71.4521065, 42.4874599],[-71.4521271, 42.4874261],[-71.4521127, 42.4874213],[-71.452167, 42.4873323],[-71.45225840000001, 42.4873628]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45105650000001, 42.4725069],[-71.4511117, 42.4725083],[-71.4511062, 42.4726301],[-71.4510093, 42.4726277],[-71.4510097, 42.4726175],[-71.45092030000001, 42.4726152],[-71.45092529999999, 42.472504],[-71.4510162, 42.4725062],[-71.4510171, 42.4724843],[-71.4510575, 42.4724853],[-71.45105650000001, 42.4725069]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"14","source:datetime":"2014-04-11T16:40:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48141320000001, 42.474747],[-71.481405, 42.4747527],[-71.4814415, 42.4747812],[-71.4813705, 42.4748311],[-71.4813595, 42.4748225],[-71.48134020000001, 42.4748361],[-71.481301, 42.4748055],[-71.48131739999999, 42.474794],[-71.4812233, 42.4747205],[-71.4813054, 42.4746629],[-71.48141320000001, 42.474747]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4625771, 42.474418],[-71.4627143, 42.4744341],[-71.4626969, 42.4745154],[-71.4626039, 42.4745045],[-71.46260100000001, 42.4745178],[-71.4625455, 42.4745113],[-71.462548, 42.4744995],[-71.4624838, 42.474492],[-71.46250980000001, 42.4743706],[-71.46258539999999, 42.4743794],[-71.4625771, 42.474418]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4631444, 42.4789506],[-71.4631657, 42.478949],[-71.4631804, 42.4790546],[-71.4631432, 42.4790574],[-71.46314460000001, 42.4790679],[-71.4631261, 42.4790693],[-71.4631249, 42.4790605],[-71.4630727, 42.4790645],[-71.4630531, 42.4789241],[-71.46313979999999, 42.4789175],[-71.4631444, 42.4789506]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759287, 42.4891487],[-71.47594700000001, 42.4891883],[-71.4758837, 42.4892724],[-71.4757922, 42.4892347],[-71.47582, 42.4891978],[-71.4757632, 42.4891744],[-71.47583, 42.4890856],[-71.47585770000001, 42.489097],[-71.4758807, 42.4890664],[-71.4759646, 42.489101],[-71.4759287, 42.4891487]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4762903, 42.4894978],[-71.4763293, 42.4894324],[-71.47642260000001, 42.4894628],[-71.4764607, 42.4893989],[-71.4765775, 42.4894371],[-71.4764996, 42.4895677],[-71.476381, 42.489529],[-71.4763662, 42.4895539],[-71.4763066, 42.4895344],[-71.4763222, 42.4895083],[-71.4762903, 42.4894978]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46306679999999, 42.4909405],[-71.4631347, 42.4908775],[-71.4631874, 42.4909086],[-71.4632168, 42.4908814],[-71.46342850000001, 42.4910064],[-71.463353, 42.4910764],[-71.4632361, 42.4910074],[-71.4631948, 42.4910457],[-71.4630895, 42.4909836],[-71.463109, 42.4909655],[-71.46306679999999, 42.4909405]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4661534, 42.4915827],[-71.4661165, 42.4916359],[-71.4661411, 42.4916452],[-71.4661157, 42.4916818],[-71.466086, 42.4916706],[-71.4660665, 42.4916987],[-71.46598640000001, 42.4916683],[-71.46600669999999, 42.4916389],[-71.46599380000001, 42.491634],[-71.46605510000001, 42.4915454],[-71.4661534, 42.4915827]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45264349999999, 42.4926981],[-71.45275580000001, 42.4926758],[-71.4528002, 42.4927984],[-71.4527877, 42.4928009],[-71.4528098, 42.4928621],[-71.4527148, 42.4928809],[-71.45269279999999, 42.49282],[-71.45265089999999, 42.4928283],[-71.4526369, 42.4927897],[-71.452674, 42.4927824],[-71.45264349999999, 42.4926981]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4638696, 42.4816074],[-71.4639031, 42.4816211],[-71.4638726, 42.4816619],[-71.4638348, 42.4816465],[-71.4638009, 42.4816918],[-71.4637187, 42.4816581],[-71.4637649, 42.4815962],[-71.463753, 42.4815913],[-71.463846, 42.4814667],[-71.4639445, 42.481507],[-71.4638696, 42.4816074]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4727833, 42.4937883],[-71.4728693, 42.493767],[-71.4729006, 42.4938359],[-71.4727784, 42.4938662],[-71.47277200000001, 42.4938521],[-71.4726843, 42.4938738],[-71.4726091, 42.4937078],[-71.4726721, 42.4936921],[-71.4726817, 42.4937133],[-71.4727424, 42.4936982],[-71.4727833, 42.4937883]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738648, 42.4744921],[-71.47394370000001, 42.4744944],[-71.4739414, 42.4745388],[-71.4738675, 42.4745366],[-71.473867, 42.4745452],[-71.47375150000001, 42.4745418],[-71.47375529999999, 42.4744712],[-71.4738078, 42.4744727],[-71.4738084, 42.474462],[-71.4738663, 42.4744637],[-71.4738648, 42.4744921]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47895750000001, 42.4805912],[-71.47897159999999, 42.480602],[-71.47892779999999, 42.4806332],[-71.4788787, 42.4805953],[-71.4789354, 42.480555],[-71.4788568, 42.4804943],[-71.4789566, 42.4804234],[-71.4790265, 42.4804774],[-71.47895629999999, 42.4805272],[-71.479, 42.4805609],[-71.47895750000001, 42.4805912]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47646279999999, 42.4755216],[-71.4763851, 42.4755605],[-71.47639839999999, 42.4755751],[-71.4763118, 42.4756186],[-71.4762526, 42.4755539],[-71.4763007, 42.4755298],[-71.4762585, 42.4754837],[-71.47630169999999, 42.475462],[-71.4763263, 42.4754888],[-71.4763992, 42.4754522],[-71.47646279999999, 42.4755216]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4603017, 42.4720558],[-71.46045530000001, 42.4720492],[-71.4604575, 42.4720776],[-71.46053499999999, 42.4720744],[-71.46053879999999, 42.4721235],[-71.46030810000001, 42.4721333],[-71.4603073, 42.472123],[-71.4602691, 42.4721247],[-71.46026740000001, 42.4721031],[-71.46030519999999, 42.4721015],[-71.4603017, 42.4720558]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47387139999999, 42.4724218],[-71.4739197, 42.4724204],[-71.47392360000001, 42.4724942],[-71.4738948, 42.4724951],[-71.4738984, 42.4725644],[-71.4738134, 42.4725668],[-71.47380939999999, 42.4724914],[-71.47377779999999, 42.4724923],[-71.47377349999999, 42.4724098],[-71.47387070000001, 42.472407],[-71.47387139999999, 42.4724218]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4598299, 42.487541],[-71.4598713, 42.4875271],[-71.4599152, 42.4875986],[-71.46003469999999, 42.4875584],[-71.4600757, 42.4876251],[-71.4598963, 42.4876926],[-71.45986569999999, 42.4876476],[-71.4597914, 42.4876726],[-71.4597167, 42.4875508],[-71.4598155, 42.4875176],[-71.4598299, 42.487541]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4679411, 42.4947686],[-71.46792960000001, 42.4947847],[-71.46810619999999, 42.494854],[-71.46805519999999, 42.4949251],[-71.46792430000001, 42.4948738],[-71.4679403, 42.4948515],[-71.4678988, 42.4948352],[-71.4678925, 42.494844],[-71.4678043, 42.4948094],[-71.4678571, 42.4947357],[-71.4679411, 42.4947686]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4718345, 42.4758399],[-71.471852, 42.4758401],[-71.47185090000001, 42.4758847],[-71.4718366, 42.4758845],[-71.4718357, 42.47592],[-71.4716853, 42.475918],[-71.47168739999999, 42.4758347],[-71.4717516, 42.4758355],[-71.4717527, 42.4757908],[-71.4718357, 42.4757919],[-71.4718345, 42.4758399]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46360199999999, 42.4726633],[-71.46354909999999, 42.4726563],[-71.4635191, 42.4727813],[-71.46343400000001, 42.4727701],[-71.46344910000001, 42.4727072],[-71.4634348, 42.4727053],[-71.4634468, 42.4726553],[-71.46346440000001, 42.4726576],[-71.4634803, 42.4725916],[-71.4636149, 42.4726093],[-71.46360199999999, 42.4726633]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Bulette Road","addr:city":"Acton","building":"yes","addr:housenumber":"15","addr:state":"MA","source:datetime":"2015-01-20T23:47:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4698308, 42.4993147],[-71.4698831, 42.4993267],[-71.46985979999999, 42.4993824],[-71.46980360000001, 42.4993695],[-71.4697875, 42.4994081],[-71.4697352, 42.4993961],[-71.4697522, 42.4993555],[-71.4696953, 42.4993425],[-71.46975430000001, 42.4992014],[-71.4698674, 42.4992273],[-71.4698308, 42.4993147]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"29","addr:state":"MA","source:datetime":"2014-11-08T14:01:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4540293, 42.4906893],[-71.4540157, 42.4906918],[-71.4540865, 42.4909029],[-71.4539808, 42.4909223],[-71.4539444, 42.4908137],[-71.45396580000001, 42.4908098],[-71.45395310000001, 42.4907719],[-71.4539284, 42.4907765],[-71.453897, 42.4906829],[-71.4540196, 42.4906604],[-71.4540293, 42.4906893]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.454504, 42.4722273],[-71.4545262, 42.4722327],[-71.4544957, 42.4723016],[-71.4544834, 42.4722986],[-71.4544677, 42.4723339],[-71.4543824, 42.4723132],[-71.4544315, 42.4722024],[-71.4544065, 42.4721963],[-71.4544492, 42.4720999],[-71.45454960000001, 42.4721242],[-71.454504, 42.4722273]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687418, 42.4866298],[-71.4688237, 42.4865169],[-71.4689049, 42.4865491],[-71.468895, 42.4865628],[-71.4690172, 42.4866114],[-71.4689618, 42.4866879],[-71.4689197, 42.4866711],[-71.4689101, 42.4866844],[-71.4688345, 42.4866544],[-71.4688276, 42.4866639],[-71.4687418, 42.4866298]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Massachusetts Avenue","addr:city":"Acton","building":"yes","addr:housenumber":"615","addr:state":"MA","source:datetime":"2014-11-18T02:34:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4769893, 42.4766763],[-71.4770224, 42.4766795],[-71.47701499999999, 42.4767223],[-71.47691330000001, 42.4767126],[-71.4769036, 42.4767685],[-71.47683069999999, 42.4767615],[-71.4768444, 42.4766829],[-71.47678000000001, 42.4766767],[-71.4767933, 42.4766002],[-71.4769991, 42.4766199],[-71.4769893, 42.4766763]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4645836, 42.4915184],[-71.4647386, 42.4915448],[-71.46470739999999, 42.4916451],[-71.464614, 42.4916292],[-71.4646254, 42.4915926],[-71.4644131, 42.4915565],[-71.4644323, 42.4914947],[-71.46450160000001, 42.4915065],[-71.46452309999999, 42.4914373],[-71.46460449999999, 42.4914511],[-71.4645836, 42.4915184]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474755, 42.4780596],[-71.4748008, 42.4780546],[-71.47479420000001, 42.4780215],[-71.4748584, 42.4780144],[-71.4748817, 42.47813],[-71.4747763, 42.4781416],[-71.4747741, 42.4781307],[-71.4747201, 42.4781367],[-71.4747108, 42.4780907],[-71.47476020000001, 42.4780852],[-71.474755, 42.4780596]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46924559999999, 42.486083],[-71.4692859, 42.4861045],[-71.4693664, 42.486022],[-71.46944209999999, 42.4860624],[-71.4692795, 42.4862291],[-71.4691857, 42.4861789],[-71.4692062, 42.486158],[-71.4692312, 42.4861713],[-71.4692555, 42.4861463],[-71.4692084, 42.4861211],[-71.46924559999999, 42.486083]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4710901, 42.4853118],[-71.4711471, 42.485371],[-71.47104830000001, 42.4854231],[-71.4710367, 42.4854112],[-71.4709537, 42.4854549],[-71.47091260000001, 42.4854122],[-71.4709897, 42.4853715],[-71.4709699, 42.485351],[-71.47099969999999, 42.4853353],[-71.4710152, 42.4853513],[-71.4710901, 42.4853118]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713892, 42.4965408],[-71.47141379999999, 42.4966453],[-71.4711312, 42.4966818],[-71.47111030000001, 42.4965932],[-71.4711645, 42.4965862],[-71.4711564, 42.4965521],[-71.47123430000001, 42.496542],[-71.47123190000001, 42.4965316],[-71.4713074, 42.4965219],[-71.47131419999999, 42.4965505],[-71.4713892, 42.4965408]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4529354, 42.4722586],[-71.452997, 42.4722323],[-71.4530865, 42.4723524],[-71.45315309999999, 42.4723231],[-71.453233, 42.4724207],[-71.4531597, 42.4724519],[-71.4531279, 42.4724111],[-71.4530517, 42.4724436],[-71.45293820000001, 42.4722977],[-71.4529589, 42.4722889],[-71.4529354, 42.4722586]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48046220000001, 42.472083],[-71.48056870000001, 42.4721173],[-71.4805021, 42.4722309],[-71.4804865, 42.4722259],[-71.4804517, 42.4722852],[-71.48037619999999, 42.4722609],[-71.4803974, 42.4722249],[-71.48041670000001, 42.4722311],[-71.4804302, 42.4722081],[-71.4803953, 42.4721969],[-71.48046220000001, 42.472083]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47787940000001, 42.4845492],[-71.4779114, 42.4846802],[-71.4778122, 42.4846934],[-71.4777804, 42.4845631],[-71.4777923, 42.4845615],[-71.47779420000001, 42.4845529],[-71.477806, 42.4845416],[-71.4778206, 42.4845376],[-71.4778395, 42.4845404],[-71.477853, 42.4845514],[-71.47787940000001, 42.4845492]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47041110000001, 42.4855719],[-71.4704716, 42.485589],[-71.4704339, 42.4856625],[-71.4703794, 42.4856472],[-71.4703561, 42.4856924],[-71.47025240000001, 42.4856632],[-71.4703148, 42.4855418],[-71.47035870000001, 42.4855541],[-71.47037539999999, 42.4855216],[-71.4704292, 42.4855368],[-71.47041110000001, 42.4855719]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4515708, 42.4841496],[-71.4516484, 42.4842064],[-71.45157260000001, 42.4842632],[-71.4514743, 42.4841913],[-71.45149069999999, 42.4841791],[-71.4514264, 42.484132],[-71.4514837, 42.4840891],[-71.45151920000001, 42.4841151],[-71.4515573, 42.4840866],[-71.45160660000001, 42.4841228],[-71.4515708, 42.4841496]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46414919999999, 42.4802799],[-71.46408340000001, 42.4803649],[-71.4640674, 42.4803582],[-71.4639967, 42.4804497],[-71.4639169, 42.4804159],[-71.4639614, 42.4803583],[-71.4639148, 42.4803386],[-71.4639406, 42.4803052],[-71.4639856, 42.4803242],[-71.4640589, 42.4802379],[-71.46414919999999, 42.4802799]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"23","addr:state":"MA","source:datetime":"2014-11-08T13:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4574509, 42.4956921],[-71.45749120000001, 42.4957004],[-71.4574777, 42.4957362],[-71.457448, 42.49573],[-71.457413, 42.4958228],[-71.45730380000001, 42.4958002],[-71.45733989999999, 42.4957045],[-71.45735500000001, 42.4957076],[-71.45738369999999, 42.4956315],[-71.4574673, 42.4956488],[-71.4574509, 42.4956921]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4607917, 42.47824],[-71.46076600000001, 42.4782371],[-71.4607429, 42.478348],[-71.4606588, 42.4783384],[-71.4606655, 42.4783063],[-71.46062790000001, 42.478302],[-71.46063820000001, 42.4782522],[-71.46067050000001, 42.4782559],[-71.46069850000001, 42.4781217],[-71.46081359999999, 42.4781348],[-71.4607917, 42.47824]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45238380000001, 42.4914162],[-71.4524682, 42.4914075],[-71.4524882, 42.4915134],[-71.45250799999999, 42.4915114],[-71.4525273, 42.4916134],[-71.4524189, 42.4916246],[-71.45239960000001, 42.4915225],[-71.452354, 42.4915272],[-71.45234120000001, 42.4914594],[-71.45239100000001, 42.4914543],[-71.45238380000001, 42.4914162]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4653673, 42.4792116],[-71.4654122, 42.479219],[-71.4653953, 42.4792753],[-71.4653429, 42.4792667],[-71.4653254, 42.4793252],[-71.4652432, 42.4793118],[-71.4652739, 42.4792091],[-71.4652553, 42.4792061],[-71.465282, 42.4791169],[-71.4653903, 42.4791347],[-71.4653673, 42.4792116]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696823, 42.4852654],[-71.46977819999999, 42.4852898],[-71.4697407, 42.4853706],[-71.4697092, 42.4853626],[-71.4696886, 42.4854071],[-71.4695991, 42.4853843],[-71.4696117, 42.4853572],[-71.4695686, 42.4853462],[-71.4695804, 42.4853207],[-71.4696486, 42.485338],[-71.4696823, 42.4852654]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"15","addr:state":"MA","source:datetime":"2014-11-08T14:01:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.454399, 42.4933511],[-71.4545095, 42.4933542],[-71.4545033, 42.4934743],[-71.454517, 42.4934747],[-71.45450870000001, 42.4935697],[-71.4543987, 42.4935645],[-71.4544037, 42.493468],[-71.4544191, 42.4934684],[-71.4544209, 42.493433],[-71.4543948, 42.4934323],[-71.454399, 42.4933511]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4726014, 42.4908148],[-71.47251919999999, 42.490797],[-71.4725401, 42.490744],[-71.4725212, 42.4907399],[-71.472532, 42.4907126],[-71.47252020000001, 42.49071],[-71.472553, 42.490627],[-71.4726423, 42.4906463],[-71.472599, 42.4907561],[-71.4726225, 42.4907612],[-71.4726014, 42.4908148]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45095860000001, 42.4730556],[-71.4511206, 42.4730572],[-71.4511193, 42.473128],[-71.4510245, 42.473127],[-71.4510239, 42.4731606],[-71.45094880000001, 42.4731599],[-71.45094949999999, 42.4731245],[-71.4508201, 42.4731232],[-71.4508215, 42.4730411],[-71.4509588, 42.4730424],[-71.45095860000001, 42.4730556]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47096259999999, 42.4919938],[-71.4709747, 42.4919839],[-71.4710207, 42.4920144],[-71.47098, 42.492048],[-71.47095349999999, 42.4920305],[-71.4709151, 42.4920623],[-71.4708119, 42.4919939],[-71.4708253, 42.4919828],[-71.4707679, 42.4919448],[-71.47082159999999, 42.4919004],[-71.47096259999999, 42.4919938]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4710273, 42.4806569],[-71.4710168, 42.4806615],[-71.4710461, 42.4806975],[-71.4710615, 42.4807548],[-71.4709718, 42.4807669],[-71.4709629, 42.4807141],[-71.47094800000001, 42.4806936],[-71.4709232, 42.4807034],[-71.47085610000001, 42.4806108],[-71.4709572, 42.4805706],[-71.4710273, 42.4806569]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4789782, 42.4803673],[-71.4789272, 42.4802706],[-71.4789638, 42.4802601],[-71.47895750000001, 42.4802482],[-71.4790215, 42.4802297],[-71.4790175, 42.480222],[-71.47906829999999, 42.4802073],[-71.4791184, 42.4803023],[-71.4790304, 42.4803277],[-71.4790416, 42.480349],[-71.4789782, 42.4803673]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46359200000001, 42.4818848],[-71.4636404, 42.4819067],[-71.46358859999999, 42.4819693],[-71.4635378, 42.4819463],[-71.4634928, 42.4820007],[-71.4634221, 42.4819686],[-71.4634966, 42.4818786],[-71.4634744, 42.4818685],[-71.4635404, 42.4817888],[-71.4636357, 42.4818321],[-71.46359200000001, 42.4818848]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46238390000001, 42.4991656],[-71.46242580000001, 42.4991479],[-71.4624407, 42.4991672],[-71.46245070000001, 42.4991629],[-71.4624968, 42.4992215],[-71.4624465, 42.4992424],[-71.46243130000001, 42.4992228],[-71.4623953, 42.499238],[-71.4623653, 42.4991991],[-71.4623988, 42.499185],[-71.46238390000001, 42.4991656]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4663791, 42.4791328],[-71.4663594, 42.479129],[-71.46634709999999, 42.4791637],[-71.4663649, 42.4791671],[-71.4663427, 42.4792298],[-71.4662141, 42.4792049],[-71.4662364, 42.4791417],[-71.46625640000001, 42.4791456],[-71.4663055, 42.4790068],[-71.4664161, 42.4790282],[-71.4663791, 42.4791328]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4693935, 42.4719912],[-71.4694619, 42.4720049],[-71.4694379, 42.4720708],[-71.46932080000001, 42.4720474],[-71.469328, 42.4720274],[-71.46929249999999, 42.4720203],[-71.46930380000001, 42.4719894],[-71.4693372, 42.4719961],[-71.46935670000001, 42.4719425],[-71.4694075, 42.4719526],[-71.4693935, 42.4719912]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4513311, 42.4866303],[-71.4514745, 42.4866412],[-71.4514638, 42.4867183],[-71.4513742, 42.4867115],[-71.4513702, 42.48674],[-71.45130640000001, 42.4867352],[-71.4513108, 42.4867035],[-71.4511925, 42.4866945],[-71.4512041, 42.4866104],[-71.45133250000001, 42.4866202],[-71.4513311, 42.4866303]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4776975, 42.487191],[-71.477715, 42.4871876],[-71.4777376, 42.4872434],[-71.4777234, 42.4872467],[-71.47774320000001, 42.4872976],[-71.4776471, 42.4873173],[-71.4776064, 42.4872136],[-71.47755530000001, 42.4872245],[-71.4775332, 42.487168],[-71.47767639999999, 42.4871373],[-71.4776975, 42.487191]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752901, 42.4867481],[-71.4752629, 42.4867317],[-71.4752415, 42.4867511],[-71.475256, 42.4867598],[-71.4751986, 42.4868118],[-71.475134, 42.4867728],[-71.475306, 42.4866167],[-71.4754117, 42.4866806],[-71.4753765, 42.4867125],[-71.4753482, 42.4866955],[-71.4752901, 42.4867481]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4646987, 42.487215],[-71.4646778, 42.4872226],[-71.46468969999999, 42.4872407],[-71.4645799, 42.4872803],[-71.4645194, 42.4871884],[-71.4645326, 42.4871837],[-71.4644697, 42.4870883],[-71.4645761, 42.4870499],[-71.4646547, 42.487169],[-71.4646657, 42.4871651],[-71.4646987, 42.487215]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46418629999999, 42.4812257],[-71.4642266, 42.4812456],[-71.4641967, 42.4812788],[-71.46416189999999, 42.4812617],[-71.4640797, 42.481353],[-71.464006, 42.4813166],[-71.4640849, 42.481229],[-71.4640618, 42.4812176],[-71.4641359, 42.4811353],[-71.4642271, 42.4811803],[-71.46418629999999, 42.4812257]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47004029999999, 42.4845274],[-71.47006450000001, 42.4845345],[-71.47000749999999, 42.4846069],[-71.4699925, 42.4846038],[-71.46996300000001, 42.4846698],[-71.46983659999999, 42.484636],[-71.4698649, 42.4845684],[-71.46988349999999, 42.4845739],[-71.46994119999999, 42.4844656],[-71.4700555, 42.484499],[-71.47004029999999, 42.4845274]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4588236, 42.4821792],[-71.4588402, 42.4821755],[-71.45885319999999, 42.482208],[-71.45883329999999, 42.4822123],[-71.4588602, 42.4822792],[-71.4587762, 42.4822977],[-71.4587362, 42.4821983],[-71.4587236, 42.4822011],[-71.4586795, 42.4820914],[-71.45877950000001, 42.4820694],[-71.4588236, 42.4821792]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:45:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4682368, 42.4864557],[-71.4682483, 42.4864419],[-71.468204, 42.4864218],[-71.4682043, 42.486393],[-71.4681557, 42.4863927],[-71.4681568, 42.4862962],[-71.4682475, 42.4862921],[-71.4682554, 42.4863889],[-71.4683581, 42.4864354],[-71.46831280000001, 42.4864902],[-71.4682368, 42.4864557]]]}}, +{"type":"Feature","properties":{"source:datetime":"2012-09-27T18:27:42Z","highway":"residential"},"geometry": {"type":"LineString","coordinates": [[-71.4571286, 42.4789977],[-71.4587772, 42.478793],[-71.4590565, 42.4788467],[-71.45917799999999, 42.4789363],[-71.45931760000001, 42.4790796],[-71.4594026, 42.4792363],[-71.4595423, 42.4806156],[-71.4592652, 42.4805927],[-71.4591643, 42.4806132],[-71.4590999, 42.4806433],[-71.4590441, 42.4807098],[-71.45894199999999, 42.480902]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-09-11T05:17:38Z","highway":"service"},"geometry": {"type":"Polygon","coordinates": [[[-71.4573441, 42.4793278],[-71.4574744, 42.4794367],[-71.4575794, 42.4794948],[-71.4576888, 42.4795496],[-71.4578333, 42.4795496],[-71.4579471, 42.4795303],[-71.4580521, 42.4794883],[-71.4580828, 42.4794399],[-71.4580959, 42.479356],[-71.45806090000001, 42.4792882],[-71.4579908, 42.4792301],[-71.45784639999999, 42.4792333],[-71.45770640000001, 42.4792656],[-71.4573441, 42.4793278]]]}}, +{"type":"Feature","properties":{"source:datetime":"2012-08-05T16:55:54Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4716717, 42.4761425],[-71.4716682, 42.4762629]]}}, +{"type":"Feature","properties":{"source:datetime":"2012-08-05T16:55:54Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4716676, 42.4762853],[-71.47166489999999, 42.476379]]}}, +{"type":"Feature","properties":{"source:datetime":"2012-08-05T16:55:54Z","highway":"steps"},"geometry": {"type":"LineString","coordinates": [[-71.4716682, 42.4762629],[-71.4716676, 42.4762853]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Massachusetts Avenue","addr:city":"Acton","building":"yes","addr:housenumber":"541","addr:state":"MA","name":"Twin Seafood","amenity":"cafe","source:datetime":"2015-01-23T23:16:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47155410000001, 42.4761628],[-71.47145380000001, 42.4761649],[-71.471452, 42.4762847],[-71.47155549999999, 42.4762854],[-71.47155410000001, 42.4761628]]]}}, +{"type":"Feature","properties":{"access":"private","amenity":"parking","source:datetime":"2012-07-15T19:11:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47833110000001, 42.4830038],[-71.47811900000001, 42.483086],[-71.4776277, 42.4825428],[-71.4776894, 42.4825038],[-71.47778030000001, 42.4824584],[-71.4778311, 42.4825125],[-71.4778798, 42.4824854],[-71.47833110000001, 42.4830038]]]}}, +{"type":"Feature","properties":{"landuse":"farmland","source:datetime":"2012-07-15T19:11:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.477993, 42.4824868],[-71.4784259, 42.4830452],[-71.4782787, 42.4831231],[-71.47920070000001, 42.4842572],[-71.479573, 42.4841187],[-71.4791401, 42.4835906],[-71.4791098, 42.483465],[-71.4791271, 42.4831447],[-71.47843020000001, 42.4824218],[-71.4781965, 42.4824218],[-71.477993, 42.4824868]]]}}, +{"type":"Feature","properties":{"access":"private","service":"driveway","source:datetime":"2014-11-16T04:49:19Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4774862, 42.4823139],[-71.4776069, 42.4823693],[-71.47766, 42.4824156],[-71.4777047, 42.4824575],[-71.47814649999999, 42.4829485],[-71.478207, 42.4830135],[-71.47814649999999, 42.4829485]]}}, +{"type":"Feature","properties":{"access":"private","source:datetime":"2012-07-15T19:11:16Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.47814649999999, 42.4829485],[-71.4779645, 42.4830524],[-71.4777936, 42.4831476],[-71.4777945, 42.4832255]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-07-15T19:11:16Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4782957, 42.4844744],[-71.4781182, 42.48439],[-71.4779321, 42.4842774],[-71.4777552, 42.4841418]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-07-15T19:11:15Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47814200000001, 42.4840502],[-71.4779104, 42.4841714],[-71.4777552, 42.4841418]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-07-15T19:11:15Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4773868, 42.4830135],[-71.4774323, 42.4833468],[-71.4774582, 42.4834031]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-07-15T19:11:15Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4777945, 42.4832255],[-71.4774582, 42.4834031]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-07-01T13:07:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4537095, 42.4744897],[-71.4536332, 42.4745194],[-71.4536698, 42.474583],[-71.4535113, 42.4746503],[-71.45347889999999, 42.4747007],[-71.45346139999999, 42.4747315],[-71.4534556, 42.4747845],[-71.4535547, 42.4747855],[-71.4535781, 42.4747712],[-71.45362419999999, 42.4747765],[-71.45371849999999, 42.4747468],[-71.45371110000001, 42.4747214],[-71.4537265, 42.4747065],[-71.4537509, 42.4747145],[-71.4537959, 42.474645],[-71.45381449999999, 42.4746138],[-71.4538495, 42.4745348],[-71.4538484, 42.4745056],[-71.4537095, 42.4744897]]]}}, +{"type":"Feature","properties":{"note":"very rough position","building":"yes","source:datetime":"2012-07-01T13:07:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4514495, 42.4744286],[-71.45121829999999, 42.4744159],[-71.4512395, 42.4742568],[-71.451477, 42.474312],[-71.4514495, 42.4744286]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-21T01:44:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45463049999999, 42.4740906],[-71.4543839, 42.4740678],[-71.45438230000001, 42.4740774],[-71.45436309999999, 42.4741947],[-71.45435449999999, 42.4742471],[-71.45452520000001, 42.4742717],[-71.4545366, 42.4742129],[-71.4546144, 42.47423],[-71.45463049999999, 42.4740906]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-02-21T01:44:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4539291, 42.4745061],[-71.45371280000001, 42.4744804],[-71.4537716, 42.4741001],[-71.45328979999999, 42.4740451],[-71.45330300000001, 42.473972],[-71.4533979, 42.4739066],[-71.4540713, 42.4739986],[-71.4540951, 42.474084],[-71.4539291, 42.4745061]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-21T01:44:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45189120000001, 42.4749215],[-71.4518731, 42.4749965],[-71.4519196, 42.4750809],[-71.4515468, 42.4751824],[-71.45151370000001, 42.4750989],[-71.4516654, 42.47506],[-71.45161040000001, 42.4748902],[-71.45189120000001, 42.4749215]]]}}, +{"type":"Feature","properties":{"cuisine":"Mexican","addr:street":"Main Street","addr:city":"Acton","building":"yes","addr:housenumber":"263","addr:state":"MA","name":"Bueno Y Sano","amenity":"restaurant","source:datetime":"2015-02-28T16:47:26Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4535906, 42.4744918],[-71.4536416, 42.4745662],[-71.4534994, 42.47463],[-71.4534585, 42.4746977],[-71.4533532, 42.4746642],[-71.4534156, 42.4745649],[-71.4535906, 42.4744918]]]}}, +{"type":"Feature","properties":{"website":"http://actonbowladrome.com","addr:street":"Main Street","addr:city":"Acton","leisure":"sports_centre","phone":"(978) 263-7638","alt_name":"Acton's Bowladrome","building":"yes","addr:housenumber":"257","name":"Acton Bowladrome & Arcade","sport":"10pin","source:datetime":"2015-02-28T16:43:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4537014, 42.4744491],[-71.45321389999999, 42.4744027],[-71.45327930000001, 42.4740697],[-71.4537299, 42.4741229],[-71.45371369999999, 42.4741987],[-71.4537507, 42.4742025],[-71.4537014, 42.4744491]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-21T01:44:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45211689999999, 42.4745127],[-71.45194239999999, 42.4744937],[-71.451967, 42.4744008],[-71.4521245, 42.4744217],[-71.45211689999999, 42.4745127]]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","shop":"stationery","addr:city":"Acton","building":"yes","addr:housenumber":"258","addr:state":"MA","name":"Quill And Press","source:datetime":"2015-02-28T17:00:30Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4527022, 42.4754744],[-71.4525002, 42.4755463],[-71.4523494, 42.4752904],[-71.4525666, 42.4752336],[-71.4527022, 42.4754744]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-21T01:44:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4531034, 42.4745962],[-71.4530503, 42.4745905],[-71.4530597, 42.4745374],[-71.45311479999999, 42.474545],[-71.4531034, 42.4745962]]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","addr:city":"Acton","building":"yes","addr:housenumber":"255","addr:state":"MA","source:datetime":"2015-02-28T16:43:49Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4538508, 42.4739325],[-71.4536786, 42.4738945],[-71.45378100000001, 42.4735348],[-71.4539997, 42.4735777],[-71.4538508, 42.4739325]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2013-09-05T23:12:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4540274, 42.4735701],[-71.4539116, 42.4738402],[-71.4540801, 42.473889],[-71.454183, 42.473613],[-71.4540274, 42.4735701]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-21T01:44:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4507797, 42.4742749],[-71.4505838, 42.4742696],[-71.450549, 42.4739074],[-71.4507368, 42.4738886],[-71.4507797, 42.4742749]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-02-21T01:44:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4506373, 42.4751645],[-71.4507615, 42.4751484],[-71.4509493, 42.475081],[-71.4509085, 42.4750336],[-71.4509967, 42.4750402],[-71.45103090000001, 42.4748591],[-71.4509778, 42.4748534],[-71.4509683, 42.4748173],[-71.4510337, 42.4747974],[-71.45100050000001, 42.4747168],[-71.4509247, 42.4747377],[-71.45092750000001, 42.4746959],[-71.4506373, 42.4751645]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-21T01:44:02Z"},"geometry": {"type":"LineString","coordinates": [[-71.45065150000001, 42.4752546],[-71.4505908, 42.4755525],[-71.45065150000001, 42.4752546]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-02-21T01:44:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543845, 42.4740394],[-71.4546622, 42.4740541],[-71.4546247, 42.4743251],[-71.4549372, 42.4743519],[-71.45510350000001, 42.4745048],[-71.4560626, 42.4745867],[-71.4561525, 42.474635],[-71.45612439999999, 42.4749877],[-71.4565925, 42.4750374],[-71.4567038, 42.4744928],[-71.4555958, 42.4744056],[-71.4557246, 42.4735363],[-71.4545737, 42.4734626],[-71.4543845, 42.4740394]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-21T01:44:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45574879999999, 42.4736135],[-71.4556312, 42.4743837],[-71.45582280000001, 42.4744045],[-71.45582659999999, 42.474359],[-71.4560409, 42.474378],[-71.4560371, 42.4744216],[-71.4562174, 42.474433],[-71.4562287, 42.4743723],[-71.4565114, 42.4743969],[-71.456519, 42.4743267],[-71.4566366, 42.4743343],[-71.4567334, 42.4737026],[-71.45574879999999, 42.4736135]]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:44:02Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45475999999999, 42.473568],[-71.4547458, 42.473746],[-71.45472820000001, 42.4739663],[-71.45471689999999, 42.4740746],[-71.45469780000001, 42.4742566],[-71.454937, 42.4742728],[-71.455575, 42.4743162]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-02-21T01:44:01Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45145960000001, 42.4748314],[-71.45153639999999, 42.4750202],[-71.4514842, 42.4750866],[-71.4515497, 42.4752374],[-71.4514672, 42.4752877],[-71.4513867, 42.4753217]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:44:01Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45065289999999, 42.4747362],[-71.4508976, 42.4747836],[-71.4508426, 42.4750397],[-71.4507155, 42.4750833],[-71.4505952, 42.4750766]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-02-21T01:44:01Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4519975, 42.4746955],[-71.4519547, 42.4748912],[-71.45145960000001, 42.4748314],[-71.4511263, 42.4747946]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:44:01Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45449670000001, 42.473733],[-71.4547458, 42.473746],[-71.4548818, 42.4737576],[-71.4556462, 42.4738067]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-02-21T01:44:00Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.454142, 42.474141],[-71.45394520000001, 42.4740906],[-71.4538361, 42.4744653]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:44:00Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4556189, 42.4739743],[-71.4548686, 42.4739227],[-71.4548818, 42.4737576]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-02-21T01:44:00Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45427599999999, 42.473791],[-71.45407710000001, 42.4737431],[-71.4541186, 42.4736412]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:43:59Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45648300000001, 42.4749544],[-71.45625339999999, 42.4749129],[-71.45630300000001, 42.4745626]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:43:59Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45394520000001, 42.4740906],[-71.4538854, 42.4740214],[-71.4533742, 42.4739578]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:43:59Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.454937, 42.4742728],[-71.4550753, 42.4743912],[-71.455585, 42.474446]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-02-21T01:43:59Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45404619999999, 42.4743868],[-71.45439140000001, 42.4744701],[-71.4543726, 42.4746566]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:43:59Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4555968, 42.4741577],[-71.4548496, 42.4740839],[-71.45471689999999, 42.4740746]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-02-21T01:43:59Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.454223, 42.4739293],[-71.4544114, 42.4739397],[-71.45472820000001, 42.4739663]]}}, +{"type":"Feature","properties":{"source:datetime":"2012-02-21T01:43:58Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.45436309999999, 42.4741947],[-71.4542295, 42.4741799]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:43:57Z","highway":"service"},"geometry": {"type":"Point","coordinates": [-71.4504354, 42.4747065]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2012-02-21T01:43:57Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4548496, 42.4740839],[-71.4548686, 42.4739227]]}}, +{"type":"Feature","properties":{"source:datetime":"2012-02-21T01:43:57Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.45438230000001, 42.4740774],[-71.4542788, 42.4740575]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-12-27T16:52:14Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4505757, 42.4751912],[-71.4505952, 42.4750766],[-71.45065289999999, 42.4747362],[-71.4504354, 42.4747065]]}}, +{"type":"Feature","properties":{"building":"yes","name":"Verizon","source:datetime":"2012-02-21T01:43:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4549655, 42.4748524],[-71.4545497, 42.4748229],[-71.4545993, 42.4745788],[-71.45474419999999, 42.4746003],[-71.4547335, 42.4747076],[-71.454987, 42.4747317],[-71.4549655, 42.4748524]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-02-12T04:12:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47527169999999, 42.4730038],[-71.4752258, 42.4731709],[-71.4757268, 42.4732307],[-71.47575190000001, 42.473118],[-71.4754193, 42.4730706],[-71.4754276, 42.4730233],[-71.47527169999999, 42.4730038]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-01-15T13:09:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728664, 42.4773726],[-71.47299599999999, 42.4776328],[-71.4731953, 42.4775544],[-71.47315469999999, 42.477477],[-71.4730831, 42.4774993],[-71.47306380000001, 42.4774712],[-71.4729998, 42.4773552],[-71.4729864, 42.477331],[-71.4728664, 42.4773726]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-01-15T13:09:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47406290000001, 42.4771453],[-71.474209, 42.4774335],[-71.47410259999999, 42.4774848],[-71.4739817, 42.4774654],[-71.4738656, 42.4772565],[-71.4739295, 42.4772333],[-71.474002, 42.4771588],[-71.47406290000001, 42.4771453]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-01-15T13:09:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4753817, 42.4760764],[-71.4751302, 42.4760599],[-71.4751689, 42.4758665],[-71.4754262, 42.4756895],[-71.47558770000001, 42.4759033],[-71.4754349, 42.476],[-71.475402, 42.475971],[-71.4753817, 42.4760764]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-01-15T13:09:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4727949, 42.4762873],[-71.4727899, 42.4764081],[-71.4728142, 42.4764063],[-71.47281529999999, 42.4764298],[-71.4728906, 42.4764334],[-71.4728935, 42.4762902],[-71.4727949, 42.4762873]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-01-15T13:09:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714591, 42.4764411],[-71.4721255, 42.4764479],[-71.4721323, 42.4764024],[-71.4721265, 42.476326],[-71.47157129999999, 42.4762902],[-71.47145329999999, 42.476297],[-71.4714591, 42.4764411]]]}}, +{"type":"Feature","properties":{"addr:country":"US","gnis:state_id":"25","addr:street":"Arlington Street","gnis:county_id":"017","addr:city":"Acton","ele":"70","building":"yes","gnis:created":"01/15/2003","addr:housenumber":"241","addr:state":"MA","gnis:feature_id":"1974021","name":"West Acton Post Office","amenity":"post_office","source:datetime":"2015-01-23T23:16:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.473293, 42.4774654],[-71.4731972, 42.4774964],[-71.4731769, 42.4774509],[-71.4731073, 42.4774722],[-71.4730512, 42.477359],[-71.4732156, 42.4773068],[-71.473293, 42.4774654]]]}}, +{"type":"Feature","properties":{"access":"private","amenity":"parking","source:datetime":"2012-01-15T13:09:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4755432, 42.4749274],[-71.4756225, 42.4749629],[-71.476011, 42.4749739],[-71.4760684, 42.4747085],[-71.4755705, 42.474677],[-71.4755432, 42.4749274]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-01-15T13:09:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4731614, 42.4763337],[-71.4732369, 42.476503],[-71.4730309, 42.4764982],[-71.4729622, 42.4763241],[-71.47297380000001, 42.4763231],[-71.4731614, 42.4763337]]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2012-01-15T13:09:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728848, 42.4761896],[-71.4729119, 42.4763192],[-71.4731286, 42.4763202],[-71.4730792, 42.4761954],[-71.4728848, 42.4761896]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Arlington Street","shop":"furniture","addr:city":"Acton","building":"yes","addr:housenumber":"240","addr:state":"MA","name":"Tables to Teapots","source:datetime":"2015-01-23T23:15:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.473117, 42.4770418],[-71.47287129999999, 42.4771404],[-71.47273490000001, 42.4769441],[-71.4729999, 42.4768396],[-71.473117, 42.4770418]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Central Street","addr:city":"Acton","building":"yes","addr:housenumber":"250","addr:state":"MA","name":"Theatre III","amenity":"theatre","source:datetime":"2015-01-23T23:16:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4753434, 42.4745608],[-71.4750179, 42.4745334],[-71.4750439, 42.4744171],[-71.47536940000001, 42.4744335],[-71.4753434, 42.4745608]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Spruce Street","addr:city":"Acton","building":"yes","addr:housenumber":"5","addr:state":"MA","source:datetime":"2015-01-23T23:15:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47232769999999, 42.4766568],[-71.4722551, 42.4764692],[-71.47213619999999, 42.4764604],[-71.4722319, 42.4766752],[-71.47232769999999, 42.4766568]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Arlington Street","addr:city":"Acton","phone":"978-263-8743","building":"yes","addr:housenumber":"251","addr:state":"MA","name":"The Local Table","amenity":"cafe","source:datetime":"2015-01-23T23:15:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4740668, 42.4770853],[-71.4739314, 42.4771172],[-71.4738492, 42.4769431],[-71.4739943, 42.4769064],[-71.4740668, 42.4770853]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Massachusetts Avenue","addr:city":"Acton","building":"yes","addr:housenumber":"543","addr:state":"MA","source:datetime":"2015-01-23T23:15:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47212159999999, 42.4762883],[-71.4717996, 42.4762747],[-71.47180539999999, 42.476178],[-71.47212260000001, 42.4761828],[-71.47212159999999, 42.4762883]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Central Street","addr:city":"Acton","address":"School Street, Acton, MA, 01720","building":"yes","addr:housenumber":"258","addr:state":"MA","name":"West Acton Fire Station","office":"Station 3","amenity":"fire_station","attribution":"Office of Geographic and Environmental Information (MassGIS), Massachusetts Emergency Management Agency","source_url":"http://mass.gov/mgis/firestations.htm","source:datetime":"2015-01-23T23:16:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47532289999999, 42.4748685],[-71.4750521, 42.4748521],[-71.4750658, 42.4747181],[-71.4753284, 42.474729],[-71.47532289999999, 42.4748685]]]}}, +{"type":"Feature","properties":{"addr:country":"US","denomination":"baptist","gnis:state_id":"25","addr:street":"Massachusetts Avenue","gnis:county_id":"017","religion":"christian","addr:city":"Acton","ele":"66","building":"yes","gnis:created":"01/15/2003","addr:housenumber":"502","addr:state":"MA","gnis:feature_id":"1974661","name":"West Acton Baptist Church","amenity":"place_of_worship","source:datetime":"2015-01-23T23:16:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47504600000001, 42.4760532],[-71.4748845, 42.4760522],[-71.4748961, 42.4759013],[-71.47480419999999, 42.4758974],[-71.4748062, 42.4757611],[-71.4750528, 42.4757562],[-71.4750499, 42.4757862],[-71.4750828, 42.475792],[-71.4750799, 42.4759265],[-71.4750518, 42.4759245],[-71.47504600000001, 42.4760532]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Arlington Street","shop":"car_parts","addr:city":"Acton","building":"yes","addr:housenumber":"245","addr:state":"MA","name":"West Acton NAPA","source:datetime":"2015-01-23T23:16:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4734777, 42.4771927],[-71.473381, 42.4772188],[-71.4733907, 42.4772449],[-71.4733365, 42.4772633],[-71.4733713, 42.477331],[-71.47342070000001, 42.4773126],[-71.473471, 42.4774258],[-71.473589, 42.4773832],[-71.47355899999999, 42.4773097],[-71.47354060000001, 42.4773184],[-71.4734777, 42.4771927]]]}}, +{"type":"Feature","properties":{"access":"private","service":"driveway","source:datetime":"2012-01-15T13:09:06Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4747567, 42.4748863],[-71.47552260000001, 42.4749014],[-71.47599460000001, 42.4748945],[-71.47601779999999, 42.4748001],[-71.47553910000001, 42.4747824],[-71.47536530000001, 42.4747153],[-71.4747782, 42.4747011]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-01-15T13:09:06Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47231739999999, 42.4764234],[-71.47242919999999, 42.4764053],[-71.47250270000001, 42.4764065],[-71.47273490000001, 42.4764102],[-71.4727426, 42.4762215],[-71.4727378, 42.4761035]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-01-15T13:09:06Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.475253, 42.476168],[-71.4752685, 42.4760329],[-71.47530329999999, 42.4759294],[-71.47549290000001, 42.4758065]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-01-15T13:09:05Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47373020000001, 42.4768939],[-71.4738912, 42.4771707],[-71.4739589, 42.4772133],[-71.4740886, 42.4774512]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-01-15T13:09:05Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4747895, 42.474562],[-71.47491669999999, 42.4745635],[-71.4749618, 42.4744842],[-71.4749601, 42.4743327]]}}, +{"type":"Feature","properties":{"source:datetime":"2012-01-15T13:09:05Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4730589, 42.4772062],[-71.47311790000001, 42.4773039],[-71.4729998, 42.4773552]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-01-15T13:09:05Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.472905, 42.4761084],[-71.4729545, 42.4762699],[-71.4730725, 42.4762709]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-01-15T13:09:04Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47306279999999, 42.4775215],[-71.47291679999999, 42.47758]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-01-15T13:09:04Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47250270000001, 42.4764065],[-71.4725096, 42.476097]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-08-05T16:55:54Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47229900000001, 42.476382],[-71.47166489999999, 42.476379],[-71.47149, 42.4763782]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-12-16T19:47:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.478419, 42.4985239],[-71.4783723, 42.4985639],[-71.478494, 42.4986411],[-71.4783352, 42.498777],[-71.4782352, 42.4987134],[-71.47825349999999, 42.4986978],[-71.4781361, 42.4986231],[-71.4783233, 42.498463],[-71.478419, 42.4985239]]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"bing","source:datetime":"2014-07-20T02:42:33Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4775741, 42.4996709],[-71.4774151, 42.4995786],[-71.4772332, 42.4994729],[-71.47703009999999, 42.4993455],[-71.47685989999999, 42.4992386],[-71.4768363, 42.4992238],[-71.4767983, 42.4991309],[-71.47680250000001, 42.4989788],[-71.476811, 42.4988986],[-71.4767392, 42.4987044],[-71.4765618, 42.4985354],[-71.4764705, 42.4984984],[-71.4764267, 42.4984806],[-71.4761269, 42.4983201],[-71.4759308, 42.498281]]}}, +{"type":"Feature","properties":{"building":"yes","amenity":"public_building","source:datetime":"2011-11-30T23:08:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45577489999999, 42.4753987],[-71.4553007, 42.4753597],[-71.4552612, 42.4756203],[-71.45537880000001, 42.47563],[-71.4553878, 42.4755705],[-71.4556541, 42.4755924],[-71.4556427, 42.4756675],[-71.455733, 42.4756749],[-71.45577489999999, 42.4753987]]]}}, +{"type":"Feature","properties":{"source:datetime":"2011-11-30T23:08:51Z","highway":"residential"},"geometry": {"type":"LineString","coordinates": [[-71.456127, 42.475717],[-71.45597619999999, 42.4757688],[-71.4557836, 42.4758005],[-71.4554773, 42.4758084],[-71.4550857, 42.4757609],[-71.4547585, 42.4758401],[-71.454442, 42.4759627],[-71.45428099999999, 42.4762041],[-71.45429300000001, 42.476295]]}}, +{"type":"Feature","properties":{"name":"Richard E Dow Track","source:datetime":"2014-03-31T02:11:12Z","highway":"footway"},"geometry": {"type":"Polygon","coordinates": [[[-71.4592432, 42.477991],[-71.45922710000001, 42.4782126],[-71.4590233, 42.4783471],[-71.45880870000001, 42.4783985],[-71.45768219999999, 42.4785647],[-71.4574408, 42.4785647],[-71.457312, 42.4785054],[-71.4571833, 42.4783788],[-71.4571511, 42.4782165],[-71.45722619999999, 42.4780899],[-71.4573496, 42.4779792],[-71.45752659999999, 42.4779198],[-71.45874430000001, 42.4777378],[-71.4589428, 42.4777695],[-71.4591788, 42.4778723],[-71.4592432, 42.477991]]]}}, +{"type":"Feature","properties":{"amenity":"parking","source:datetime":"2011-11-30T19:58:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4597083, 42.4789208],[-71.45950070000001, 42.4789762],[-71.4596241, 42.4792492],[-71.4598118, 42.4806339],[-71.45989230000001, 42.4806418],[-71.4598548, 42.4807051],[-71.45962950000001, 42.4806378],[-71.45970459999999, 42.4806181],[-71.4596026, 42.4797674],[-71.4595114, 42.4797635],[-71.45952219999999, 42.4796646],[-71.4593666, 42.4796765],[-71.4593773, 42.4798149],[-71.4590716, 42.4798426],[-71.4591413, 42.4805429],[-71.4594524, 42.480535],[-71.4594202, 42.4806576],[-71.4598816, 42.4807763],[-71.45997680000001, 42.4806968],[-71.4600519, 42.4806185],[-71.4598708, 42.4792294],[-71.4597083, 42.4789208]]]}}, +{"type":"Feature","properties":{"building":"yes","name":"Acton-Boxborough Regional High School","amenity":"school","source:datetime":"2011-12-05T03:45:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4593505, 42.4794549],[-71.4593022, 42.4792333],[-71.4590501, 42.4792887],[-71.4589804, 42.478992],[-71.458798, 42.4790157],[-71.4587872, 42.4789841],[-71.458696, 42.478988],[-71.45871750000001, 42.4790553],[-71.4582723, 42.4791186],[-71.4583044, 42.4792333],[-71.4582723, 42.4792333],[-71.4583634, 42.4795657],[-71.45828299999999, 42.4795894],[-71.4582508, 42.4795538],[-71.4575749, 42.4797912],[-71.4576071, 42.4798347],[-71.4575481, 42.4798624],[-71.45743, 42.4797081],[-71.45747299999999, 42.4796844],[-71.4574032, 42.4795736],[-71.45674339999999, 42.479807],[-71.45681310000001, 42.4799217],[-71.456856, 42.4799059],[-71.4569687, 42.4800642],[-71.4568775, 42.4801077],[-71.4569419, 42.4801987],[-71.4570231, 42.480167],[-71.45716179999999, 42.4803569],[-71.45729590000001, 42.4803095],[-71.4573335, 42.4803609],[-71.4575373, 42.4803451],[-71.45768219999999, 42.4802343],[-71.4576714, 42.4802026],[-71.45782699999999, 42.4801591],[-71.4579665, 42.4803609],[-71.45789139999999, 42.4803925],[-71.4580577, 42.4806576],[-71.45821890000001, 42.4805904],[-71.4582508, 42.4806378],[-71.45832590000001, 42.4806101],[-71.4583688, 42.4806814],[-71.4585568, 42.4805983],[-71.4586048, 42.4806537],[-71.45872900000001, 42.4806185],[-71.4586692, 42.4805073],[-71.45889990000001, 42.4804163],[-71.45874430000001, 42.4801749],[-71.4588516, 42.4801275],[-71.4587229, 42.4799336],[-71.4590984, 42.479807],[-71.45908230000001, 42.4797595],[-71.45916269999999, 42.4797398],[-71.45903939999999, 42.4795578],[-71.4593505, 42.4794549]]]}}, +{"type":"Feature","properties":{"building":"yes","name":"McCarthy-Towne Elementary School","amenity":"school","source:datetime":"2011-12-05T03:45:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45442629999999, 42.4770869],[-71.4542491, 42.4767088],[-71.4538139, 42.4768197],[-71.45384180000001, 42.4768793],[-71.4537122, 42.4769123],[-71.4536724, 42.4768275],[-71.4535721, 42.4768531],[-71.45353420000001, 42.4767722],[-71.4536389, 42.4767455],[-71.45349349999999, 42.4764353],[-71.452966, 42.4765698],[-71.453194, 42.4770562],[-71.45328000000001, 42.4770343],[-71.4533299, 42.4771407],[-71.4534275, 42.4771158],[-71.4534621, 42.4771897],[-71.45352990000001, 42.4771724],[-71.4535594, 42.4772354],[-71.45374289999999, 42.4771886],[-71.45377329999999, 42.4772534],[-71.45442629999999, 42.4770869]]]}}, +{"type":"Feature","properties":{"amenity":"parking","source:datetime":"2011-11-30T19:58:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4567756, 42.4758308],[-71.45683459999999, 42.47608],[-71.4568936, 42.4761512],[-71.45695259999999, 42.4764322],[-71.4564913, 42.4765034],[-71.456266, 42.4757754],[-71.4567756, 42.4758308]]]}}, +{"type":"Feature","properties":{"leisure":"park","source:datetime":"2011-11-30T19:58:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4573707, 42.4767518],[-71.45728750000001, 42.4768773],[-71.4574739, 42.4769445],[-71.4575571, 42.476819],[-71.4573707, 42.4767518]]]}}, +{"type":"Feature","properties":{"amenity":"parking","source:datetime":"2011-11-30T19:58:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4561372, 42.476357],[-71.455928, 42.476539],[-71.45559, 42.4766775],[-71.45532179999999, 42.4767131],[-71.45501609999999, 42.4767249],[-71.4546888, 42.4766775],[-71.4546727, 42.4767249],[-71.45456540000001, 42.4766656],[-71.4546727, 42.4769742],[-71.4549839, 42.4771008],[-71.45535940000001, 42.4770889],[-71.4557295, 42.4770217],[-71.4563089, 42.4767487],[-71.4561372, 42.4765983],[-71.4560621, 42.4765983],[-71.45622299999999, 42.4764836],[-71.4561372, 42.476357]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","name":"Leary Field","sport":"american_football","source:datetime":"2014-03-31T02:11:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4589931, 42.4783015],[-71.4588455, 42.4777861],[-71.4574503, 42.4780035],[-71.4575979, 42.4785189],[-71.4589931, 42.4783015]]]}}, +{"type":"Feature","properties":{"building":"yes","name":"Merriam Elementary School","amenity":"school","source:datetime":"2011-12-05T03:45:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4554972, 42.4773902],[-71.4553406, 42.4772836],[-71.45524109999999, 42.4773449],[-71.4552084, 42.4773119],[-71.4548844, 42.4775453],[-71.454919, 42.4775837],[-71.45484519999999, 42.4776363],[-71.45500850000001, 42.4777621],[-71.4549712, 42.477838],[-71.4548997, 42.4777889],[-71.4547936, 42.477873],[-71.4548776, 42.4779307],[-71.4549588, 42.4778665],[-71.45499030000001, 42.4778666],[-71.4551714, 42.4777342],[-71.45525689999999, 42.4777606],[-71.4550984, 42.4778785],[-71.4554334, 42.4781202],[-71.4556139, 42.4780132],[-71.4555809, 42.4779795],[-71.45564640000001, 42.4779271],[-71.455733, 42.4779594],[-71.45587140000001, 42.4778486],[-71.45561120000001, 42.4776662],[-71.4554607, 42.4777774],[-71.4555124, 42.4778131],[-71.4554736, 42.4778464],[-71.4551668, 42.4776353],[-71.4554972, 42.4773902]]]}}, +{"type":"Feature","properties":{"building":"yes","name":"Raymond J Grey Regional Junior High School","amenity":"school","source:datetime":"2011-12-05T03:45:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4589428, 42.4763016],[-71.45867459999999, 42.4762541],[-71.45864779999999, 42.4763332],[-71.4585297, 42.4763135],[-71.45855659999999, 42.4761473],[-71.4582991, 42.4761156],[-71.4583044, 42.4760365],[-71.4579128, 42.4759851],[-71.45789670000001, 42.4761473],[-71.45795579999999, 42.4761512],[-71.4579397, 42.4762027],[-71.45786459999999, 42.4761869],[-71.4577465, 42.4766023],[-71.4576339, 42.4766458],[-71.4571725, 42.4765627],[-71.4569204, 42.4769623],[-71.4574461, 42.4771404],[-71.45722619999999, 42.4774173],[-71.4575481, 42.477536],[-71.45761779999999, 42.4774371],[-71.4575802, 42.4774173],[-71.45768219999999, 42.4772591],[-71.45835270000001, 42.4774965],[-71.4583688, 42.4774767],[-71.4584546, 42.4774886],[-71.4584976, 42.4775321],[-71.4585673, 42.477536],[-71.4589428, 42.4763016]]]}}, +{"type":"Feature","properties":{"amenity":"parking","source:datetime":"2011-11-30T19:58:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45887310000001, 42.4809623],[-71.45832590000001, 42.4811363],[-71.4582454, 42.4810256],[-71.4581381, 42.4809781],[-71.4580684, 42.4810058],[-71.4580362, 42.4809148],[-71.4586853, 42.4806972],[-71.4587765, 42.480713],[-71.45892670000001, 42.4808673],[-71.45887310000001, 42.4809623]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"tennis","source:datetime":"2011-11-30T19:58:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4571186, 42.4753855],[-71.45636930000001, 42.4753227],[-71.4563203, 42.4756404],[-71.4570696, 42.4757032],[-71.4571186, 42.4753855]]]}}, +{"type":"Feature","properties":{"leisure":"park","source:datetime":"2011-11-30T19:58:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45846539999999, 42.4769544],[-71.4583688, 42.4772789],[-71.45777339999999, 42.477081],[-71.4579236, 42.4768595],[-71.45846539999999, 42.4769544]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","name":"Acton-Boxborough Tennis Courts","sport":"tennis","source:datetime":"2011-11-30T19:58:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45649659999999, 42.4799613],[-71.4565771, 42.4802857],[-71.4560299, 42.4803807],[-71.455928, 42.4800523],[-71.4560997, 42.4800167],[-71.4560192, 42.4797041],[-71.4563947, 42.4796408],[-71.45649659999999, 42.4799613]]]}}, +{"type":"Feature","properties":{"source:datetime":"2011-11-30T19:58:12Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4555632, 42.4773224],[-71.4554613, 42.4772393],[-71.4552733, 42.4771468]]}}, +{"type":"Feature","properties":{"amenity":"parking","source:datetime":"2011-11-30T19:58:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4560567, 42.4762343],[-71.4560031, 42.4761948],[-71.45605140000001, 42.4761315],[-71.4559012, 42.4760682],[-71.4558744, 42.4761196],[-71.45583139999999, 42.4760959],[-71.45584220000001, 42.4760484],[-71.4559119, 42.4760325],[-71.4559173, 42.4760009],[-71.4557671, 42.4759376],[-71.4556812, 42.4761552],[-71.45541299999999, 42.4762897],[-71.4551233, 42.4763174],[-71.45478, 42.4762739],[-71.45456009999999, 42.4761948],[-71.4544045, 42.4761829],[-71.4543455, 42.4762976],[-71.4545011, 42.4765706],[-71.4547049, 42.4765469],[-71.45502140000001, 42.4766142],[-71.45533260000001, 42.4765825],[-71.4556705, 42.4765192],[-71.4559173, 42.4763966],[-71.4560567, 42.4762343]]]}}, +{"type":"Feature","properties":{"leisure":"park","source:datetime":"2011-11-30T19:58:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4585855, 42.4765419],[-71.45852259999999, 42.4767625],[-71.4580948, 42.4766961],[-71.4581577, 42.4764756],[-71.4585855, 42.4765419]]]}}, +{"type":"Feature","properties":{"source:datetime":"2011-11-30T19:58:11Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4555632, 42.4776389],[-71.4555149, 42.4775756],[-71.4555632, 42.4773224],[-71.4559728, 42.4769698]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2011-11-23T23:50:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47624020000001, 42.4930737],[-71.47620449999999, 42.4931893],[-71.4759718, 42.4931417],[-71.47578489999999, 42.4930092],[-71.4757323, 42.4928546],[-71.47575260000001, 42.4926083],[-71.4758291, 42.4926168],[-71.47583590000001, 42.4925709],[-71.4757747, 42.4925607],[-71.4758291, 42.492435],[-71.47591060000001, 42.4923331],[-71.47604819999999, 42.4922499],[-71.4760856, 42.4923144],[-71.4761433, 42.4922923],[-71.4761196, 42.4922363],[-71.47620790000001, 42.4922125],[-71.4763217, 42.4922074],[-71.4764576, 42.4922091],[-71.4765239, 42.4922957],[-71.47650520000001, 42.4924078],[-71.47624190000001, 42.4924044],[-71.4761943, 42.4923467],[-71.47614160000001, 42.4923688],[-71.4761671, 42.4924061],[-71.4761111, 42.4924333],[-71.47603119999999, 42.4925098],[-71.4760074, 42.4925879],[-71.4759565, 42.4927408],[-71.4759565, 42.4928359],[-71.4759667, 42.4929345],[-71.4760465, 42.493016],[-71.476089, 42.4930415],[-71.4761366, 42.49305],[-71.47624020000001, 42.4930737]]]}}, +{"type":"Feature","properties":{"addr:country":"US","denomination":"methodist","gnis:state_id":"25","addr:street":"Central Street","gnis:county_id":"017","religion":"christian","addr:city":"Acton","ele":"65","building":"yes","gnis:created":"01/15/2003","addr:housenumber":"431","addr:state":"MA","gnis:feature_id":"1974553","name":"Saint Matthews Church","amenity":"place_of_worship","source:datetime":"2015-01-23T22:33:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47647120000001, 42.4924894],[-71.47643720000001, 42.4926372],[-71.4764084, 42.4926338],[-71.4764017, 42.4926657],[-71.4763761, 42.4927901],[-71.4763217, 42.4927901],[-71.4762911, 42.4929531],[-71.47613149999999, 42.4929294],[-71.47615519999999, 42.4928274],[-71.4761196, 42.4928274],[-71.4760703, 42.4928274],[-71.47608049999999, 42.4927918],[-71.47599390000001, 42.4927748],[-71.4760193, 42.4926576],[-71.476179, 42.4926694],[-71.4761869, 42.4926379],[-71.476196, 42.4926015],[-71.4761603, 42.4925947],[-71.476196, 42.492435],[-71.47647120000001, 42.4924894]]]}}, +{"type":"Feature","properties":{"source:datetime":"2011-11-23T23:50:04Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4765719, 42.49234],[-71.4764984, 42.4926762],[-71.4764017, 42.4926657]]}}, +{"type":"Feature","properties":{"source:datetime":"2011-11-23T23:50:03Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4760464, 42.4931047],[-71.47611310000001, 42.4928521],[-71.4761196, 42.4928274]]}}, +{"type":"Feature","properties":{"source:datetime":"2011-11-23T23:50:03Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4758489, 42.4927904],[-71.47611310000001, 42.4928521]]}}, +{"type":"Feature","properties":{"source:datetime":"2011-11-23T23:50:03Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4758855, 42.4925956],[-71.4761869, 42.4926379]]}}, +{"type":"Feature","properties":{"oneway":"yes","service":"driveway","source:datetime":"2011-11-23T23:50:03Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4770094, 42.4924777],[-71.4767396, 42.4924112],[-71.4765719, 42.49234],[-71.4765476, 42.4923297],[-71.4764016, 42.492294],[-71.47625720000001, 42.4923042],[-71.47603119999999, 42.4923688],[-71.4759038, 42.4925268],[-71.4758855, 42.4925956],[-71.4758461, 42.4927442],[-71.4758489, 42.4927904],[-71.475858, 42.4929412],[-71.47601419999999, 42.4930941],[-71.4760464, 42.4931047],[-71.4762623, 42.4931757],[-71.476653, 42.4932096],[-71.4768682, 42.4932452]]}}, +{"type":"Feature","properties":{"leisure":"pitch","name":"Lower Fields","sport":"baseball","source:datetime":"2014-03-31T02:11:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4559794, 42.4795569],[-71.4556552, 42.4802114],[-71.45478490000001, 42.4799513],[-71.4548702, 42.4797876],[-71.4549669, 42.479666],[-71.4552399, 42.4795611],[-71.4554845, 42.4795066],[-71.4557348, 42.4794982],[-71.4559794, 42.4795569]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","name":"Lower Fields","sport":"baseball","source:datetime":"2014-03-31T02:11:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45446630000001, 42.4789444],[-71.4541933, 42.4794227],[-71.4540056, 42.4793597],[-71.4538634, 42.4792758],[-71.453778, 42.4791625],[-71.45374959999999, 42.4790367],[-71.45374959999999, 42.4789024],[-71.4538463, 42.4787598],[-71.45446630000001, 42.4789444]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","name":"Lower Fields","sport":"baseball","source:datetime":"2014-03-31T02:11:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4564345, 42.4789444],[-71.45678719999999, 42.4793723],[-71.4561956, 42.4796408],[-71.4561159, 42.4795485],[-71.4560477, 42.4793849],[-71.45608180000001, 42.479301],[-71.4561387, 42.4791709],[-71.4562411, 42.4790493],[-71.4564345, 42.4789444]]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-08T02:08:50Z","highway":"unclassified","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45102079999999, 42.4745852],[-71.4511263, 42.4747946],[-71.45125899999999, 42.475058],[-71.4513867, 42.4753217],[-71.451509, 42.475574],[-71.451669, 42.475853],[-71.45177820000001, 42.4759991],[-71.451885, 42.476142]]}}, +{"type":"Feature","properties":{"name":"Reed Farm Road","source:datetime":"2014-11-16T04:49:17Z","highway":"residential"},"geometry": {"type":"LineString","coordinates": [[-71.4850826, 42.4877666],[-71.4848902, 42.487763],[-71.48481820000001, 42.4877564],[-71.484723, 42.4877457],[-71.4846079, 42.4877262],[-71.4844574, 42.4876875],[-71.48433559999999, 42.4876513],[-71.4842171, 42.4876082],[-71.4839863, 42.4875151],[-71.4836579, 42.487376]]}}, +{"type":"Feature","properties":{"name":"Inches Brook Lane","source:datetime":"2014-11-16T04:49:13Z","highway":"residential"},"geometry": {"type":"LineString","coordinates": [[-71.4850471, 42.4843495],[-71.48495, 42.4844695],[-71.48494100000001, 42.4844821],[-71.4849339, 42.4844954],[-71.4849286, 42.4845091],[-71.48492539999999, 42.4845232],[-71.4848051, 42.4852958],[-71.4848015, 42.485353],[-71.48480840000001, 42.4854099],[-71.4848257, 42.4854657],[-71.484853, 42.4855192],[-71.4848899, 42.4855696],[-71.48493569999999, 42.4856157],[-71.4849895, 42.4856569],[-71.48505040000001, 42.4856924],[-71.4851172, 42.4857214],[-71.4851888, 42.4857435]]}}, +{"type":"Feature","properties":{"TODO":"continues - probably to RED","source:datetime":"2011-05-30T15:19:25Z","highway":"footway","surface":"unpaved"},"geometry": {"type":"LineString","coordinates": [[-71.478116, 42.487734],[-71.4782799, 42.487686],[-71.47847729999999, 42.4876391]]}}, +{"type":"Feature","properties":{"name":"Jenks Land","amenity":"parking","source:datetime":"2011-05-30T15:19:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4781782, 42.487683],[-71.4782447, 42.487669],[-71.47826019999999, 42.487717],[-71.4781983, 42.4877309],[-71.4781782, 42.487683]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Central Street","fixme":"overgrown farm stand, really","shop":"supermarket","addr:city":"Acton","building":"yes","addr:housenumber":"364","addr:state":"MA","name":"Idylwilde Farms","source:datetime":"2015-01-23T22:33:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4782151, 42.4838117],[-71.4779782, 42.4839231],[-71.4775865, 42.4834235],[-71.47785279999999, 42.48332],[-71.4782151, 42.4838117]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-02-14T14:47:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4750799, 42.4830141],[-71.47487839999999, 42.483041],[-71.474819, 42.4827986],[-71.47502040000001, 42.4827717],[-71.4750799, 42.4830141]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-02-14T14:47:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4760928, 42.482886],[-71.47599339999999, 42.4826483],[-71.4758022, 42.4826918],[-71.4759016, 42.4829294],[-71.4760928, 42.482886]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-02-14T14:47:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47533540000001, 42.482677],[-71.47500650000001, 42.4827148],[-71.47497559999999, 42.4825685],[-71.4753046, 42.4825308],[-71.47533540000001, 42.482677]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-02-14T14:47:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4783827, 42.4842763],[-71.47825280000001, 42.4843295],[-71.4781413, 42.4841785],[-71.4782787, 42.4841319],[-71.4783827, 42.4842763]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-02-14T14:47:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.475601, 42.4829481],[-71.475396, 42.4829732],[-71.47534090000001, 42.4827277],[-71.47554599999999, 42.4827027],[-71.475601, 42.4829481]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-02-14T14:47:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759509, 42.4825591],[-71.4757554, 42.4826007],[-71.475661, 42.4823595],[-71.47585650000001, 42.4823179],[-71.4759509, 42.4825591]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2011-02-14T14:47:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4786345, 42.4842901],[-71.4785072, 42.4843371],[-71.4782635, 42.4840479],[-71.4784008, 42.4839958],[-71.4786345, 42.4842901]]]}}, +{"type":"Feature","properties":{"note":"TODO: continues","name":"Red","source:datetime":"2010-06-13T17:23:21Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.480361, 42.4877016],[-71.4798784, 42.4875782]]}}, +{"type":"Feature","properties":{"name":"unblazed","source:datetime":"2010-06-13T17:23:21Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4824349, 42.4864704],[-71.48251019999999, 42.4862561],[-71.483043, 42.4862877],[-71.4831047, 42.4861933],[-71.4837376, 42.485226]]}}, +{"type":"Feature","properties":{"name":"Red","source:datetime":"2010-06-13T17:23:21Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4845497, 42.4781135],[-71.48456640000001, 42.4780037],[-71.4845309, 42.4779406],[-71.4843583, 42.4777728]]}}, +{"type":"Feature","properties":{"name":"Red","source:datetime":"2014-11-16T04:49:17Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4833587, 42.4798547],[-71.4833888, 42.4797184],[-71.4834134, 42.4796633],[-71.4834, 42.479615],[-71.4833357, 42.4795347],[-71.4833277, 42.4794674],[-71.4833484, 42.4794117],[-71.48340880000001, 42.4793692],[-71.483463, 42.479331],[-71.4835822, 42.4792834],[-71.4836892, 42.4792205]]}}, +{"type":"Feature","properties":{"name":"Blue","source:datetime":"2010-06-13T17:23:21Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4845512, 42.4837835],[-71.4847321, 42.4835404]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Yellow","source:datetime":"2010-06-13T17:23:21Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4834953, 42.4803369],[-71.483598, 42.4803798]]}}, +{"type":"Feature","properties":{"name":"Blue","source:datetime":"2010-06-13T17:23:21Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4848212, 42.4834206],[-71.4849255, 42.4832803]]}}, +{"type":"Feature","properties":{"note":"TODO/very approximate","name":"blue","source:datetime":"2010-06-13T17:23:21Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48506759999999, 42.4823134],[-71.4849608, 42.4820604]]}}, +{"type":"Feature","properties":{"name":"Yellow","source:datetime":"2014-11-16T04:49:18Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.483598, 42.4803798],[-71.4837718, 42.48048],[-71.4838342, 42.4805391],[-71.4838622, 42.4805945],[-71.4838584, 42.4806706],[-71.4838266, 42.4807977],[-71.48376930000001, 42.4809954],[-71.4837297, 42.4811728],[-71.4845072, 42.4811807]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2010-06-13T17:23:20Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.484374, 42.4775573],[-71.4843583, 42.4777728]]}}, +{"type":"Feature","properties":{"name":"Red","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48386669999999, 42.4791202],[-71.4840494, 42.4788956],[-71.4844243, 42.4785207],[-71.48451900000001, 42.4783984],[-71.48452690000001, 42.4782642]]}}, +{"type":"Feature","properties":{"motor_vehicle":"no","sidewalk":"none","name":"unblazed","foot":"yes","source:datetime":"2012-10-31T22:05:19Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48140909999999, 42.4875624],[-71.482226, 42.4870651],[-71.48239890000001, 42.4865729]]}}, +{"type":"Feature","properties":{"name":"Yellow","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48506759999999, 42.4823134],[-71.4843454, 42.4820885],[-71.4842309, 42.48212],[-71.4839547, 42.4820687],[-71.48378889999999, 42.4821555],[-71.4835126, 42.482266],[-71.48330350000001, 42.4822463],[-71.4830943, 42.4822818],[-71.4829917, 42.4824239],[-71.4825141, 42.4824239],[-71.4821116, 42.4824713],[-71.4818708, 42.4824752],[-71.48172479999999, 42.4823687],[-71.4815512, 42.4823608],[-71.48144069999999, 42.4822305],[-71.4813894, 42.482045]]}}, +{"type":"Feature","properties":{"name":"Yellow","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4813662, 42.4819649],[-71.4817209, 42.4818556],[-71.4822576, 42.4816543],[-71.48251809999999, 42.4816622],[-71.4827983, 42.4814649],[-71.48280219999999, 42.4813465],[-71.4828457, 42.4812676],[-71.4828772, 42.4810623],[-71.4827707, 42.4809795],[-71.4828689, 42.4808077]]}}, +{"type":"Feature","properties":{"name":"Yellow","source:datetime":"2014-11-16T04:49:18Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48296980000001, 42.4807158],[-71.48308280000001, 42.4805913],[-71.4831298, 42.4804703],[-71.4831614, 42.4803085],[-71.48327980000001, 42.4801191],[-71.4833587, 42.4798547]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Blue","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4844801, 42.4838792],[-71.4845512, 42.4837835]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Blue","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4849255, 42.4832803],[-71.4849522, 42.4832038]]}}, +{"type":"Feature","properties":{"note":"TODO: continues/approximate","name":"Red","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4831047, 42.4861933],[-71.48343370000001, 42.4863684]]}}, +{"type":"Feature","properties":{"name":"Yellow","source:datetime":"2014-11-16T04:49:18Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4833587, 42.4798547],[-71.48344849999999, 42.4802866],[-71.48346600000001, 42.4803097],[-71.4834953, 42.4803369]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Blue","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4847321, 42.4835404],[-71.4848212, 42.4834206]]}}, +{"type":"Feature","properties":{"name":"Blue","source:datetime":"2010-06-13T17:23:20Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4837376, 42.485226],[-71.4839769, 42.4844358],[-71.4843296, 42.4840815],[-71.4844801, 42.4838792]]}}, +{"type":"Feature","properties":{"name":"Blue","source:datetime":"2010-06-13T17:23:19Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4849522, 42.4832038],[-71.48507549999999, 42.4828501]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Red","source:datetime":"2010-06-13T17:23:19Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4836892, 42.4792205],[-71.48386669999999, 42.4791202]]}}, +{"type":"Feature","properties":{"amenity":"parking","source:datetime":"2010-06-13T17:23:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4844204, 42.4776012],[-71.4844006, 42.4777156],[-71.4844559, 42.4777906],[-71.4844125, 42.4778853],[-71.484274, 42.4778054],[-71.48432219999999, 42.4775863],[-71.4844204, 42.4776012]]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Red","source:datetime":"2014-11-16T04:49:17Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4813894, 42.482045],[-71.4813662, 42.4819649]]}}, +{"type":"Feature","properties":{"name":"Yellow","source:datetime":"2010-06-13T17:23:19Z","highway":"footway"},"geometry": {"type":"Polygon","coordinates": [[[-71.48140909999999, 42.4875624],[-71.48099860000001, 42.4878426],[-71.480361, 42.4877016],[-71.4806312, 42.4879537],[-71.4806987, 42.4883162],[-71.4805369, 42.4885451],[-71.4800791, 42.4887859],[-71.4799015, 42.4895081],[-71.47997650000001, 42.4896699],[-71.4801777, 42.4904],[-71.4807342, 42.489587],[-71.48157550000001, 42.4883789],[-71.4818432, 42.4880321],[-71.48140909999999, 42.4875624]]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Yellow","source:datetime":"2010-06-13T17:23:19Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4828689, 42.4808077],[-71.48296980000001, 42.4807158]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"unblazed","source:datetime":"2010-06-13T17:23:19Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48239890000001, 42.4865729],[-71.4824349, 42.4864704]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Red","source:datetime":"2010-06-13T17:23:19Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48452690000001, 42.4782642],[-71.4845497, 42.4781135]]}}, +{"type":"Feature","properties":{"note":"TODO/approximate","name":"Blue","source:datetime":"2010-06-13T17:23:18Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48144069999999, 42.4822305],[-71.4812988, 42.4823695]]}}, +{"type":"Feature","properties":{"note":"TODO: ends observed, middle approximate","name":"Blue","source:datetime":"2010-06-13T17:23:18Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.48157550000001, 42.4883789],[-71.4806987, 42.4883162]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2014-11-08T02:08:49Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4504781, 42.4768382],[-71.45138009999999, 42.4761304],[-71.45140379999999, 42.4761787],[-71.45158549999999, 42.4760964],[-71.45177820000001, 42.4759991]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","addr:city":"Maynard","building":"yes","addr:housenumber":"291","addr:state":"MA","name":"Middlesex Savings Bank","amenity":"bank","source:datetime":"2014-11-08T02:08:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4512332, 42.4758365],[-71.45096150000001, 42.4757163],[-71.4510417, 42.4756094],[-71.45109069999999, 42.4756317],[-71.4511218, 42.4755782],[-71.4511708, 42.4755872],[-71.4511486, 42.4756406],[-71.4513267, 42.4757296],[-71.4512332, 42.4758365]]]}}, +{"type":"Feature","properties":{"access":"destination","amenity":"parking","source:datetime":"2014-11-08T02:08:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4505448, 42.4766942],[-71.451723, 42.476117],[-71.4515213, 42.475738],[-71.4513147, 42.4758485],[-71.4511708, 42.4760591],[-71.4512154, 42.476117],[-71.4511219, 42.4761697],[-71.4510999, 42.4761341],[-71.4505396, 42.476536],[-71.4505046, 42.4765234],[-71.4505448, 42.4766942]]]}}, +{"type":"Feature","properties":{"width":"10.4","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Arlington Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-23T19:57:47Z","highway":"tertiary","lanes":"2","massgis:way_id":"154676"},"geometry": {"type":"LineString","coordinates": [[-71.459756, 42.491148],[-71.459559, 42.491426],[-71.459473, 42.491551],[-71.459216, 42.491875],[-71.458924, 42.492188],[-71.45872300000001, 42.492425],[-71.458609, 42.49259],[-71.458369, 42.493015],[-71.45797399999999, 42.493848],[-71.457773, 42.494256],[-71.457385, 42.494925],[-71.457284, 42.495138],[-71.457142, 42.495432],[-71.456857, 42.495795],[-71.456632, 42.496089],[-71.45643200000001, 42.496476],[-71.45631, 42.496744],[-71.456154, 42.49707],[-71.4559326, 42.4976759],[-71.4559126, 42.4977304],[-71.455786, 42.498077],[-71.45571200000001, 42.49836],[-71.45564299999999, 42.498646],[-71.45564, 42.498764],[-71.45564, 42.49883]]}}, +{"type":"Feature","properties":{"width":"10.4","condition":"fair","bridge":"yes","source":"massgis_import_v0.1_20071009100433","name":"Arlington Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-12-21T21:01:03Z","highway":"tertiary","lanes":"2","massgis:way_id":"154676"},"geometry": {"type":"LineString","coordinates": [[-71.459926, 42.490837],[-71.459902, 42.490891],[-71.459771, 42.491121],[-71.459756, 42.491148]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Hayward Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-12-21T21:00:57Z","highway":"tertiary","lanes":"2","massgis:way_id":"162893"},"geometry": {"type":"Point","coordinates": [-71.450616, 42.481331]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","bridge":"yes","source":"massgis_import_v0.1_20071009100433","name":"Hayward Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-12-21T21:00:57Z","highway":"tertiary","lanes":"2","massgis:way_id":"162893"},"geometry": {"type":"LineString","coordinates": [[-71.45106989999999, 42.4812225],[-71.450969, 42.481246],[-71.450821, 42.48128],[-71.45072399999999, 42.481303],[-71.450616, 42.481331]]}}, +{"type":"Feature","properties":{"width":"10.4","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Arlington Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-17T05:18:39Z","highway":"tertiary","lanes":"2","massgis:way_id":"154676"},"geometry": {"type":"LineString","coordinates": [[-71.47475900000001, 42.47647],[-71.474608, 42.476531],[-71.4742066, 42.476699],[-71.47373020000001, 42.4768939],[-71.4735768, 42.476953],[-71.4735269, 42.4769731],[-71.473035, 42.4771717],[-71.47276789999999, 42.4772818],[-71.472635, 42.477333],[-71.47241099999999, 42.477444],[-71.47221999999999, 42.477559],[-71.47204600000001, 42.477683],[-71.47190999999999, 42.477788],[-71.471655, 42.478039],[-71.4714613, 42.4782183],[-71.47110960000001, 42.4785476],[-71.4709441, 42.4786927],[-71.4706832, 42.4789342]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2012-07-15T19:11:18Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4772023, 42.4834519],[-71.4773045, 42.4834372],[-71.4774582, 42.4834031],[-71.4777552, 42.4841418],[-71.477558, 42.4841985],[-71.47742, 42.48421]]}}, +{"type":"Feature","properties":{"access":"private","amenity":"parking","source:datetime":"2012-07-15T19:11:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4773764, 42.4835165],[-71.4776346, 42.4842213],[-71.477717, 42.4841929],[-71.477788, 42.4843778],[-71.4781316, 42.4844938],[-71.4782508, 42.484548],[-71.4784068, 42.4844771],[-71.47833079999999, 42.4843564],[-71.4782512, 42.4843758],[-71.47814320000001, 42.4843614],[-71.4781969, 42.4843423],[-71.4780487, 42.484147],[-71.4781993, 42.484063],[-71.4781558, 42.4839432],[-71.4779399, 42.4840377],[-71.4775015, 42.4834063],[-71.4778247, 42.4832444],[-71.4777368, 42.4831449],[-71.47752970000001, 42.4832466],[-71.4774875, 42.4829746],[-71.4772564, 42.4829897],[-71.47733220000001, 42.4833752],[-71.4773543, 42.4834459],[-71.4773764, 42.4835165]]]}}, +{"type":"Feature","properties":{"building:levels":"3","addr:street":"Central Street","addr:city":"Boxborough","building":"office","addr:housenumber":"90","name":"AMD / Qualcomm","source:datetime":"2013-10-18T02:44:28Z","addr:postcode":"01719"},"geometry": {"type":"Polygon","coordinates": [[[-71.47871259999999, 42.4992327],[-71.4782728, 42.49938],[-71.4783113, 42.4993664],[-71.4783108, 42.4992862],[-71.47871259999999, 42.4992327]]]}}, +{"type":"Feature","properties":{"addr:street":"Central Street","addr:city":"Boxborough","building":"yes","addr:housenumber":"80","source:datetime":"2013-10-18T02:41:54Z","addr:postcode":"01719"},"geometry": {"type":"Polygon","coordinates": [[[-71.477197, 42.4983704],[-71.47669209999999, 42.4983198],[-71.47667, 42.4983695],[-71.47650659999999, 42.4983303],[-71.4765397, 42.4983026],[-71.47625789999999, 42.4982741],[-71.47636439999999, 42.4977257],[-71.47731109999999, 42.4978234],[-71.477197, 42.4983704]]]}}, +{"type":"Feature","properties":{"width":"15.2","condition":"good","ramp":"yes","oneway":"yes","source":"massgis_import_v0.1_20071009101726","orig_name":"Ramp-rt 2 Eb To Central St","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-12-21T20:57:25Z","highway":"motorway_link","lanes":"1","massgis:way_id":"130217"},"geometry": {"type":"LineString","coordinates": [[-71.476007, 42.499683],[-71.475948, 42.499618],[-71.47588500000001, 42.499549],[-71.47583299999999, 42.499451],[-71.47581700000001, 42.499378],[-71.475801, 42.499279],[-71.475779, 42.499047]]}}, +{"type":"Feature","properties":{"massgis:INTSYM":"CR","massgis:PRIM_PURP":"B","massgis:TOWN_ID":"37","source:datetime":"2013-10-10T18:53:22Z","massgis:OLI_1_ABRV":"M037","area":"yes","landuse":"conservation","massgis:SOURCE_MAP":"DCS","massgis:SITE_NAME":"CR #9","ownership":"private","massgis:FY_FUNDING":"2002","massgis:DEED_ACRES":"0.00000000","massgis:CAL_DATE_R":"1/1/2001","massgis:OWNER_TYPE":"P","massgis:PROJ_ID1":"DCS-CR9","start_date":"2001","massgis:FEESYM":"P","massgis:EOEAINVOLV":"5","massgis:FEE_OWNER":"BPF TECH CENTRAL","massgis:ASSESS_LOT":"252","massgis:ATT_DATE":"2002/04/04","protected":"perpetuity","massgis:ASSESS_ACR":"5.09000000","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:POLY_ID":"1433","owner":"Bpf Tech Central","massgis:BASE_MAP":"USGSOQ","massgis:DCAM_ID":"0","massgis:OLI_1_INT":"CR","massgis:PUB_ACCESS":"Y","massgis:OS_DEED_BO":"0","massgis:OLI_1_ORG":"Town of Boxborough","leisure":"recreation_ground","access":"yes","massgis:LEV_PROT":"P","massgis:ASSESS_MAP":"12","massgis:ARTICLE97":"9","massgis:OS_ID":"37-1433","name":"Cr #9","massgis:OLI_1_TYPE":"M","massgis:OS_DEED_PA":"0"},"geometry": {"type":"Polygon","coordinates": [[[-71.4789938, 42.4988305],[-71.479347, 42.4983663],[-71.4793944, 42.497772],[-71.4794344, 42.4972714],[-71.4805662, 42.4968203],[-71.4808273, 42.4974647],[-71.4809142, 42.4982057],[-71.48021749999999, 42.4988818],[-71.4789938, 42.4988305]]]}}, +{"type":"Feature","properties":{"massgis:INTSYM":"CR","massgis:PRIM_PURP":"C","massgis:TOWN_ID":"2","source:datetime":"2009-01-06T06:53:08Z","massgis:OLI_1_ABRV":"M002CC","area":"yes","landuse":"conservation","massgis:SOURCE_MAP":"DCS","massgis:SITE_NAME":"CR #3","ownership":"private","massgis:FY_FUNDING":"0","massgis:DEED_ACRES":"0.00000000","massgis:CAL_DATE_R":"1/1/1997","massgis:OWNER_TYPE":"P","massgis:PROJ_ID1":"DCS-CR3","start_date":"1997","massgis:FEESYM":"P","created_by":"polyshp2osm","massgis:EOEAINVOLV":"5","massgis:FEE_OWNER":"THE HAARTZ CORP","massgis:ATT_DATE":"2002/02/26","massgis:COMMENTS":"FIRST CR W/ SCANNED AMS CD","protected":"perpetuity","massgis:ASSESS_ACR":"14.50000000","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:POLY_ID":"6189","owner":"The Haartz Corp","massgis:BASE_MAP":"AMSCD","massgis:DCAM_ID":"0","massgis:OLI_1_INT":"CR","massgis:PUB_ACCESS":"N","massgis:OS_DEED_BO":"11316","massgis:OLI_1_ORG":"Town of Acton Conservation Commission","leisure":"nature_reserve","access":"private","massgis:LEV_PROT":"P","massgis:ASSESS_MAP":"E3-10","massgis:ARTICLE97":"9","massgis:OS_ID":"2-6189","name":"Cr #3","massgis:OLI_1_TYPE":"M","massgis:OS_DEED_PA":"750"},"geometry": {"type":"Polygon","coordinates": [[[-71.4584637, 42.4821556],[-71.4587839, 42.4825074],[-71.4581706, 42.4829202],[-71.45854180000001, 42.483443],[-71.4589692, 42.4834291],[-71.4588577, 42.4834842],[-71.4588947, 42.4837317],[-71.45818730000001, 42.4857397],[-71.45720180000001, 42.4864685],[-71.4570719, 42.4863857],[-71.4565893, 42.4854504],[-71.4567583, 42.4828234],[-71.4568513, 42.4827407],[-71.45685140000001, 42.4826169],[-71.45713019999999, 42.482576],[-71.4573347, 42.4824247],[-71.4584637, 42.4821556]]]}}, +{"type":"Feature","properties":{"gnis:feature_id":"1972727","massgis:ASSESS_ACR":"0.00000000","leisure":"park","massgis:PRIM_PURP":"X","addr:city":"Acton","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","ele":"70","massgis:POLY_ID":"6182","massgis:SOURCE_MAP":"1","addr:housenumber":"530","massgis:FEESYM":"X","name":"Gardner Field","gnis:state_id":"25","massgis:OS_ID":"2-6182","addr:country":"US","massgis:TOWN_ID":"2","massgis:ARTICLE97":"9","massgis:FEE_OWNER":"X","massgis:OWNER_TYPE":"X","massgis:OS_DEED_BO":"0","massgis:DCAM_ID":"0","addr:state":"MA","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","addr:street":"Massachusetts Avenue","gnis:county_id":"017","source:datetime":"2015-01-23T23:15:57Z","massgis:SITE_NAME":"Gardener Field","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/09","gnis:created":"01/15/2003","massgis:LEV_PROT":"X","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703732, 42.4759318],[-71.4703964, 42.475292],[-71.47046159999999, 42.4745765],[-71.4709826, 42.4747528],[-71.4709728, 42.4759448],[-71.4703732, 42.4759318]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","leisure":"nature_reserve","massgis:PRIM_PURP":"R","massgis:PUB_ACCESS":"Y","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:POLY_ID":"6175","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Jackson Drive Woods","massgis:OS_ID":"2-6175","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"conservation","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"limited","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","massgis:COMMENTS":"ADDITIONAL SCORP ID IS 2068","access":"yes","source:datetime":"2015-01-22T00:34:28Z","massgis:SITE_NAME":"JACKSON DRIVE WOODS","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/09","massgis:LEV_PROT":"L","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4553509, 42.4905788],[-71.4563692, 42.4902026],[-71.45739639999999, 42.4898186],[-71.4577427, 42.4901156],[-71.4574985, 42.4912674],[-71.4553509, 42.4905788]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","leisure":"nature_reserve","massgis:PRIM_PURP":"B","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton School Department","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002SD","massgis:POLY_ID":"6139","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Arlington St. Woods","massgis:OS_ID":"2-6139","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"conservation","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"limited","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","massgis:COMMENTS":"FUTURE SCHOOL SITE","access":"yes","source:datetime":"2015-01-22T00:34:26Z","massgis:SITE_NAME":"ARLINGTON ST. WOODS","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"L","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4553509, 42.4905788],[-71.4574985, 42.4912674],[-71.45873020000001, 42.4919754],[-71.4583927, 42.4924597],[-71.4585601, 42.4925199],[-71.4583322, 42.492928],[-71.4579681, 42.4936737],[-71.45770539999999, 42.4942282],[-71.4573199, 42.4948806],[-71.4570284, 42.4954994],[-71.456699, 42.4949003],[-71.4557215, 42.4951589],[-71.45512220000001, 42.4952681],[-71.4548216, 42.4953439],[-71.4548589, 42.4949425],[-71.45510659999999, 42.4928465],[-71.4551937, 42.4923329],[-71.4552802, 42.4912148],[-71.4553509, 42.4905788]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","massgis:PRIM_PURP":"W","massgis:PUB_ACCESS":"N","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Water Department","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002WD","area":"yes","massgis:POLY_ID":"6125","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Mass. Avenue Area","massgis:OS_ID":"2-6125","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"reservoir_watershed","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","massgis:COMMENTS":"WATER SUPPLY & HEADQUARTERS","access":"private","source:datetime":"2014-12-05T01:49:08Z","massgis:SITE_NAME":"MASS. AVENUE AREA","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4843872, 42.477467],[-71.4843444, 42.4774395],[-71.4843314, 42.4774037],[-71.48454630000001, 42.4759568],[-71.4845225, 42.4756889],[-71.4844842, 42.4755306],[-71.4842955, 42.4751908],[-71.484875, 42.4750959],[-71.4848357, 42.4749392],[-71.484452, 42.4746941],[-71.4842257, 42.4745596],[-71.484061, 42.4743817],[-71.4837149, 42.4740212],[-71.48316610000001, 42.4734775],[-71.483513, 42.4734837],[-71.4837912, 42.4734031],[-71.48394330000001, 42.4731884],[-71.4848382, 42.4775454],[-71.4843872, 42.477467]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","leisure":"nature_reserve","massgis:PRIM_PURP":"C","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Conservation Commission","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","area":"yes","massgis:MANAGR_ABR":"M002CC","massgis:POLY_ID":"6138","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Porter/Jenks Land","massgis:OS_ID":"2-6138","massgis:TOWN_ID":"2","owner":"Town Of Acton","created_by":"polyshp2osm","massgis:FEE_OWNER":"Town of Acton","landuse":"conservation","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","access":"yes","source:datetime":"2009-01-06T02:30:09Z","massgis:SITE_NAME":"PORTER/JENKS LAND","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"LineString","coordinates": [[-71.4504937, 42.4823427],[-71.45117569999999, 42.4834496],[-71.45119699999999, 42.4834846],[-71.4509675, 42.4836284],[-71.4506784, 42.4833426],[-71.4505441, 42.4834635]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","leisure":"nature_reserve","massgis:PRIM_PURP":"C","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Conservation Commission","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002CC","massgis:POLY_ID":"6126","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Guggins Brook Conservation Land","massgis:OS_ID":"2-6126","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"conservation","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","access":"yes","source:datetime":"2014-11-18T02:44:57Z","massgis:SITE_NAME":"CACCIATORE LAND","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.48248150000001, 42.4773091],[-71.4843065, 42.4776245],[-71.4834037, 42.4807341],[-71.4841527, 42.4808336],[-71.48498600000001, 42.4808628],[-71.48427580000001, 42.4845402],[-71.47962029999999, 42.4832121],[-71.4794911, 42.4829639],[-71.479434, 42.4829991],[-71.479399, 42.4830017],[-71.4793671, 42.4829991],[-71.47934549999999, 42.4829918],[-71.4793267, 42.4829773],[-71.4793102, 42.4829643],[-71.4792984, 42.4829442],[-71.47927989999999, 42.4829104],[-71.47927610000001, 42.4828944],[-71.4792784, 42.4828749],[-71.4793579, 42.4827538],[-71.4795215, 42.482568],[-71.47948839999999, 42.4824948],[-71.47949869999999, 42.4824436],[-71.4813959, 42.4794921],[-71.4818648, 42.4796565],[-71.48248150000001, 42.4773091]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","leisure":"nature_reserve","massgis:PRIM_PURP":"C","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Conservation Commission","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002CC","area":"yes","massgis:POLY_ID":"6135","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Woods On Mass. Ave.","massgis:OS_ID":"2-6135","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"conservation","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","access":"yes","source:datetime":"2010-05-16T19:34:24Z","massgis:SITE_NAME":"WOODS ON MASS. AVE.","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4646829, 42.4757918],[-71.4647054, 42.4752398],[-71.464583, 42.4748751],[-71.46428969999999, 42.4745527],[-71.46422, 42.47404],[-71.4641728, 42.4735455],[-71.4641585, 42.4730002],[-71.46426580000001, 42.472486],[-71.46441040000001, 42.4720022],[-71.4644577, 42.4718081],[-71.46814019999999, 42.4718643],[-71.4683285, 42.4724771],[-71.4663728, 42.4730313],[-71.4681569, 42.4732242],[-71.468141, 42.4739298],[-71.46780819999999, 42.4738009],[-71.467569, 42.4738182],[-71.4674066, 42.4738317],[-71.46697880000001, 42.4736442],[-71.46685770000001, 42.4737579],[-71.4649819, 42.4735193],[-71.4655114, 42.474238],[-71.4659106, 42.4747816],[-71.46622429999999, 42.4751682],[-71.4661442, 42.475871],[-71.4646829, 42.4757918]]]}}, +{"type":"Feature","properties":{"ownership":"municipal","gnis:state_id":"25","protected":"perpetuity","access":"yes","gnis:county_id":"017","leisure":"nature_reserve","ele":"95","gnis:created":"01/15/2003","gnis:feature_id":"1972763","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","name":"Grassy Pond","owner":"Town Of Acton","landuse":"conservation","source:datetime":"2015-01-22T00:34:27Z"},"geometry": {"type":"LineString","coordinates": [[-71.45054810000001, 42.4967083],[-71.4506775, 42.4961495],[-71.4516916, 42.4959434],[-71.4523669, 42.4967275],[-71.4527502, 42.4971501],[-71.45341639999999, 42.496839],[-71.4535095, 42.4969473],[-71.4528571, 42.4972633],[-71.4532907, 42.497737],[-71.4523006, 42.498293],[-71.4513742, 42.4983572]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","leisure":"nature_reserve","massgis:PRIM_PURP":"C","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Conservation Commission","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","area":"yes","massgis:MANAGR_ABR":"M002CC","massgis:POLY_ID":"6127","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Jenks Land","massgis:OS_ID":"2-6127","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:GRANTPROG1":"DCS-SH","massgis:PROJ_ID1":"DCS-SH24","massgis:FEE_OWNER":"Town of Acton","landuse":"conservation","massgis:ARTICLE97":"1","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:GRANTTYPE1":"S","massgis:EOEAINVOLV":"1","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","access":"yes","source:datetime":"2010-06-13T17:24:27Z","massgis:SITE_NAME":"Jenks Conservation Land","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1997/06/25","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4795329, 42.4866322],[-71.4795438, 42.486442],[-71.4795546, 42.4862586],[-71.47956809999999, 42.4860234],[-71.479578, 42.4858518],[-71.4795938, 42.4855771],[-71.4796862, 42.4854675],[-71.47971510000001, 42.4851619],[-71.483036, 42.4863801],[-71.4815888, 42.4883888],[-71.4798476, 42.4910081],[-71.4789833, 42.4906275],[-71.479479, 42.4888153],[-71.47882989999999, 42.48713],[-71.4795329, 42.4866322]]]}}, +{"type":"Feature","properties":{"massgis:school_id":"00020020","website":"http://douglas.abschools.org/","addr:street":"Elm Street","addr:city":"Acton","phone":"978-266-2560","grades":"K,1,2,3,4,5,6","addr:housenumber":"21","name":"CT Douglas Elementary School","amenity":"school","source:datetime":"2015-01-23T23:29:19Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4755802, 42.481489],[-71.4750524, 42.4822352],[-71.474951, 42.4823734],[-71.47456099999999, 42.4822406],[-71.4737674, 42.4819473],[-71.4730318, 42.4817504],[-71.47271739999999, 42.4822216],[-71.4725275, 42.4825369],[-71.4721894, 42.4824143],[-71.4707865, 42.4819263],[-71.47048909999999, 42.481808],[-71.47068830000001, 42.4815155],[-71.4713119, 42.4805921],[-71.4714305, 42.4804036],[-71.4723122, 42.4806426],[-71.47452250000001, 42.4808784],[-71.4755802, 42.481489]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"38.10000000","leisure":"nature_reserve","massgis:PRIM_PURP":"C","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Boxborough Conservation Commission","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M037CC","area":"yes","massgis:POLY_ID":"1327","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Half Moon Meadow","massgis:OS_ID":"37-1327","massgis:TOWN_ID":"37","owner":"Town Of Boxborough","massgis:FEE_OWNER":"Town of Boxborough","landuse":"conservation","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M037","access":"yes","source:datetime":"2013-10-10T23:01:19Z","massgis:SITE_NAME":"HALF MOON MEADOW","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1901/01/01","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4841775, 42.4905869],[-71.48430949999999, 42.4905851],[-71.4846082, 42.4903465],[-71.4846091, 42.4901091],[-71.48497639999999, 42.4896252],[-71.4841775, 42.4905869]]]}}, +{"type":"Feature","properties":{"massgis:PRIM_PURP":"C","massgis:TOWN_ID":"2","source:datetime":"2015-01-22T00:32:29Z","area":"yes","landuse":"conservation","massgis:GRANTTYPE1":"S","massgis:SOURCE_MAP":"1","massgis:OWNER_ABRV":"M002","massgis:SITE_NAME":"TOWN FOREST","ownership":"municipal","massgis:FY_FUNDING":"0","massgis:DEED_ACRES":"0.00000000","massgis:OWNER_TYPE":"M","massgis:PROJ_ID1":"DCS-SH1","massgis:MANAGR_TYP":"M","massgis:FEESYM":"M","massgis:EOEAINVOLV":"1","massgis:FEE_OWNER":"Town of Acton","massgis:ATT_DATE":"1997/06/25","massgis:COMMENTS":"ADDITIONAL SCORP ID\"S ARE 2020 & 2002","massgis:GRANTPROG1":"DCS-SH","protected":"perpetuity","massgis:ASSESS_ACR":"0.00000000","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:POLY_ID":"6129","owner":"Town Of Acton","massgis:DCAM_ID":"0","massgis:PUB_ACCESS":"Y","massgis:OS_DEED_BO":"0","leisure":"nature_reserve","massgis:MANAGER":"Town of Acton Conservation Commission","access":"yes","massgis:LEV_PROT":"P","massgis:ARTICLE97":"1","massgis:OS_ID":"2-6129","massgis:MANAGR_ABR":"M002CC","name":"Bulette Town Forest","massgis:OS_DEED_PA":"0"},"geometry": {"type":"Polygon","coordinates": [[[-71.45943490000001, 42.49177],[-71.45952010000001, 42.4916533],[-71.46083710000001, 42.4927671],[-71.46212490000001, 42.493783],[-71.46350579999999, 42.4947962],[-71.46450129999999, 42.4953796],[-71.4689881, 42.4978884],[-71.4687447, 42.4984667],[-71.46938900000001, 42.4987158],[-71.4694585, 42.498684],[-71.46938129999999, 42.4988424],[-71.4692627, 42.4987645],[-71.4686719, 42.4985564],[-71.4683352, 42.4990941],[-71.46812540000001, 42.4990744],[-71.46766650000001, 42.4989289],[-71.4666407, 42.4978518],[-71.46577859999999, 42.4983294],[-71.4649388, 42.4984961],[-71.4642807, 42.4985064],[-71.46376600000001, 42.4985553],[-71.4629281, 42.4977844],[-71.4621686, 42.4970494],[-71.4615932, 42.4972987],[-71.4613452, 42.4959656],[-71.460686, 42.4943235],[-71.4601502, 42.4937042],[-71.4608508, 42.4929172],[-71.45943490000001, 42.49177]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","massgis:PRIM_PURP":"R","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton School Department","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002SD","area":"yes","massgis:POLY_ID":"6137","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","massgis:OS_ID":"2-6137","massgis:TOWN_ID":"2","owner":"Town Of Acton","amenity":"school","massgis:FEE_OWNER":"Town of Acton","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"limited","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","massgis:COMMENTS":"ADDITIONAL SCORP ID\"S ARE 2049, 2044, 2046","access":"yes","source:datetime":"2014-03-31T02:01:12Z","massgis:SITE_NAME":"HIGH,JR HIGH,MERRIAM,MCCARTHY","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"L","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.45551639999999, 42.4811187],[-71.45537349999999, 42.4805869],[-71.455192, 42.4805654],[-71.4543324, 42.4804638],[-71.45371590000001, 42.481119],[-71.4511658, 42.4805576],[-71.45046379999999, 42.4789773],[-71.4506291, 42.4785357],[-71.4506284, 42.4777908],[-71.4506281, 42.477716],[-71.45217940000001, 42.476858],[-71.45438350000001, 42.4758635],[-71.45449910000001, 42.4749481],[-71.4560627, 42.4751052],[-71.457733, 42.4752414],[-71.4593632, 42.4753658],[-71.4592632, 42.475817],[-71.4597364, 42.4765982],[-71.4606077, 42.4774228],[-71.4604149, 42.4781232],[-71.4600892, 42.4781844],[-71.4612733, 42.4808012],[-71.4608147, 42.4809045],[-71.4603444, 42.4809274],[-71.46000859999999, 42.4808677],[-71.4595341, 42.4808101],[-71.4595898, 42.4813457],[-71.4592463, 42.48125],[-71.4578571, 42.4811178],[-71.45551639999999, 42.4811187]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","massgis:PRIM_PURP":"W","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Water Department","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002WD","area":"yes","massgis:POLY_ID":"6136","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Flannery Land","massgis:OS_ID":"2-6136","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"reservoir_watershed","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","massgis:COMMENTS":"WATER SUPPLY PROTECTION","access":"yes","source:datetime":"2011-01-29T15:17:52Z","massgis:SITE_NAME":"FLANNERY LAND","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4684535, 42.475992],[-71.4680982, 42.4759745],[-71.4678759, 42.4758141],[-71.4676485, 42.4757268],[-71.4674746, 42.4756604],[-71.4672973, 42.4754533],[-71.46710349999999, 42.475227],[-71.4670107, 42.4749496],[-71.466825, 42.47481],[-71.4666096, 42.4745635],[-71.4664848, 42.4744205],[-71.4665007, 42.4742417],[-71.46651540000001, 42.4740779],[-71.4666678, 42.4739356],[-71.46685770000001, 42.4737579],[-71.46697880000001, 42.4736442],[-71.4674066, 42.4738317],[-71.467569, 42.4738182],[-71.46780819999999, 42.4738009],[-71.468141, 42.4739298],[-71.4696376, 42.4741846],[-71.469689, 42.474593],[-71.46960369999999, 42.4749582],[-71.4694358, 42.4752699],[-71.4692215, 42.4754195],[-71.4689231, 42.4755555],[-71.468733, 42.475677],[-71.4685166, 42.4759948],[-71.4684535, 42.475992]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"35.32000000","leisure":"nature_reserve","massgis:PRIM_PURP":"C","massgis:PUB_ACCESS":"Y","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Boxborough Conservation Commission","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","area":"yes","massgis:MANAGR_ABR":"M037CC","massgis:POLY_ID":"1326","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Rolling Meadows","massgis:ASSESS_MAP":"12","massgis:OS_ID":"37-1326","massgis:TOWN_ID":"37","owner":"Town Of Boxborough","massgis:FEE_OWNER":"Town of Boxborough","landuse":"conservation","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:ASSESS_LOT":"194","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M037","access":"yes","source:datetime":"2013-10-10T22:30:56Z","massgis:SITE_NAME":"ROLLING MEADOWS","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1901/01/01","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000","massgis:ASSESS_BLK":"6"},"geometry": {"type":"LineString","coordinates": [[-71.4834048, 42.4993464],[-71.48427150000001, 42.4989857],[-71.48456179999999, 42.4980906],[-71.4848854, 42.497711],[-71.4847267, 42.4974167],[-71.4846411, 42.49737],[-71.4848047, 42.4969878]]}}, +{"type":"Feature","properties":{"width":"9.1","condition":"deficient","source":"massgis_import_v0.1_20071009101726","name":"Sargent Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-17T05:18:40Z","highway":"residential","lanes":"1","massgis:way_id":"182760"},"geometry": {"type":"LineString","coordinates": [[-71.484813, 42.494068],[-71.484568, 42.494126],[-71.48432200000001, 42.494184],[-71.483457, 42.494365],[-71.482856, 42.494497],[-71.48215500000001, 42.49464],[-71.482016, 42.494685],[-71.481961, 42.494705],[-71.48187799999999, 42.494755],[-71.481838, 42.49479],[-71.481723, 42.494905],[-71.481419, 42.495244],[-71.48119699999999, 42.495485],[-71.481094, 42.495576],[-71.481043, 42.495608],[-71.48095600000001, 42.495625],[-71.48086600000001, 42.495646]]}}, +{"type":"Feature","properties":{"width":"13.1","condition":"deficient","source":"massgis_import_v0.1_20071009101726","name":"Central Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-12-16T19:47:33Z","highway":"tertiary","lanes":"2","massgis:way_id":"144710"},"geometry": {"type":"LineString","coordinates": [[-71.47633399999999, 42.496098],[-71.476238, 42.496583],[-71.476099, 42.497365],[-71.47608700000001, 42.497433],[-71.4759308, 42.498281],[-71.47590700000001, 42.49841],[-71.475779, 42.499047],[-71.475748, 42.499199]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"massgis_import_v0.1_20071009101726","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-07-20T02:42:32Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.478776, 42.499015],[-71.47876960000001, 42.498962],[-71.4787553, 42.4989279],[-71.4787183, 42.4988398],[-71.4786666, 42.4987411],[-71.4786233, 42.4986725],[-71.47857, 42.498556],[-71.478493, 42.498486],[-71.4783542, 42.4983649],[-71.478297, 42.498315],[-71.4781426, 42.4982286],[-71.4779925, 42.4981445],[-71.477822, 42.498049],[-71.477305, 42.497745],[-71.477116, 42.4976387],[-71.47687999999999, 42.497506],[-71.476696, 42.497421],[-71.47657599999999, 42.497391],[-71.476446, 42.497367],[-71.4763554, 42.4973626],[-71.47626200000001, 42.497358],[-71.476099, 42.497365]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009101726","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T20:02:17Z","highway":"residential","massgis:way_id":"37"},"geometry": {"type":"LineString","coordinates": [[-71.484081, 42.493421],[-71.48409100000001, 42.493537],[-71.48411, 42.493611],[-71.484145, 42.493678],[-71.484205, 42.493739],[-71.48434399999999, 42.493862],[-71.484476, 42.493997],[-71.484568, 42.494126]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"deficient","source":"massgis_import_v0.1_20071009101726","name":"Littlefield Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-12-19T02:01:07Z","highway":"residential","lanes":"2","massgis:way_id":"183738"},"geometry": {"type":"LineString","coordinates": [[-71.478691, 42.49267],[-71.479079, 42.492924],[-71.479383, 42.493112],[-71.479506, 42.493212],[-71.479609, 42.493297],[-71.47969999999999, 42.493394],[-71.479771, 42.493485],[-71.47983000000001, 42.493592],[-71.479902, 42.493745],[-71.48, 42.494021],[-71.480127, 42.494355],[-71.48021799999999, 42.494593],[-71.480311, 42.494774],[-71.480469, 42.495041],[-71.48086600000001, 42.495646]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"deficient","source":"massgis_import_v0.1_20071009101726","name":"Littlefield Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2013-10-10T18:53:21Z","highway":"residential","lanes":"2","massgis:way_id":"183738"},"geometry": {"type":"LineString","coordinates": [[-71.48086600000001, 42.495646],[-71.48094399999999, 42.495755],[-71.481055, 42.495911],[-71.481104, 42.495988],[-71.48120900000001, 42.496089],[-71.481342, 42.496217],[-71.4815, 42.496346],[-71.481848, 42.496673],[-71.481972, 42.496791],[-71.482074, 42.496929],[-71.48218300000001, 42.49712],[-71.4822083, 42.4971678],[-71.48228, 42.497303],[-71.482495, 42.497709],[-71.482704, 42.498085],[-71.482844, 42.498347],[-71.48297700000001, 42.498606],[-71.483159, 42.498946],[-71.483283, 42.499188],[-71.483385, 42.4994],[-71.483524, 42.499699]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Cherokee Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:59Z","highway":"residential","lanes":"2","massgis:way_id":"184091"},"geometry": {"type":"LineString","coordinates": [[-71.46728899999999, 42.492896],[-71.467201, 42.49302],[-71.467095, 42.493152],[-71.46695699999999, 42.493224],[-71.46672700000001, 42.493299],[-71.466568, 42.493358],[-71.46645700000001, 42.493434],[-71.466387, 42.493536],[-71.466342, 42.493625],[-71.466307, 42.493777],[-71.46631499999999, 42.493941],[-71.46633799999999, 42.49405],[-71.466408, 42.494148],[-71.46655, 42.494247],[-71.46678900000001, 42.494405],[-71.467134, 42.494582],[-71.46725000000001, 42.494651],[-71.467589, 42.494853],[-71.467815, 42.494978],[-71.46794300000001, 42.49502],[-71.468067, 42.495063],[-71.468209, 42.495093],[-71.468328, 42.495116],[-71.468536, 42.495129],[-71.46873100000001, 42.495139],[-71.468958, 42.495183],[-71.46913499999999, 42.495236],[-71.469387, 42.495341],[-71.46956400000001, 42.495423],[-71.469674, 42.495453]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Flint Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:59Z","highway":"residential","lanes":"2","massgis:way_id":"106494"},"geometry": {"type":"LineString","coordinates": [[-71.463482, 42.471871],[-71.463416, 42.471942],[-71.463374, 42.472036],[-71.463311, 42.472201],[-71.46325899999999, 42.472413],[-71.463257, 42.472456],[-71.463249, 42.472639],[-71.46323599999999, 42.472901],[-71.463232, 42.473071],[-71.46324799999999, 42.4733],[-71.463258, 42.473379],[-71.463278, 42.473543],[-71.46330399999999, 42.473699],[-71.463353, 42.473935],[-71.463401, 42.474146],[-71.463509, 42.47463],[-71.46353499999999, 42.474722],[-71.46355800000001, 42.474807],[-71.463601, 42.474985],[-71.463686, 42.475114],[-71.46375500000001, 42.475248],[-71.46379399999999, 42.475326],[-71.46381700000001, 42.475523],[-71.463813, 42.475701]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Coolidge Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2013-11-18T02:38:40Z","highway":"residential","lanes":"2","massgis:way_id":"120412"},"geometry": {"type":"LineString","coordinates": [[-71.451899, 42.4874349],[-71.451756, 42.487413],[-71.451446, 42.487366],[-71.45115699999999, 42.487337]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Mallard Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:56Z","highway":"residential","lanes":"2","massgis:way_id":"184083"},"geometry": {"type":"LineString","coordinates": [[-71.463258, 42.473379],[-71.462675, 42.473344],[-71.462186, 42.473304],[-71.461904, 42.473289],[-71.46179600000001, 42.473302],[-71.461658, 42.473348],[-71.46159, 42.473397],[-71.46155, 42.473452],[-71.46153099999999, 42.473586],[-71.461534, 42.47372],[-71.461671, 42.474301],[-71.461727, 42.474435],[-71.461789, 42.474506],[-71.46189, 42.474557],[-71.462002, 42.474591],[-71.462189, 42.474613],[-71.462447, 42.474635],[-71.46298400000001, 42.474679],[-71.46353499999999, 42.474722]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Newtown Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-20T23:47:52Z","highway":"tertiary","lanes":"2","massgis:way_id":"109289"},"geometry": {"type":"LineString","coordinates": [[-71.450782, 42.495095],[-71.45123599999999, 42.495317],[-71.45155099999999, 42.495472],[-71.451802, 42.495598],[-71.45209199999999, 42.495755],[-71.45236, 42.495919],[-71.452614, 42.496098],[-71.452753, 42.496198],[-71.45290799999999, 42.496301],[-71.453057, 42.496404],[-71.4531995, 42.4965192],[-71.453371, 42.496683],[-71.45351530000001, 42.4968419],[-71.453597, 42.496939],[-71.453828, 42.497197],[-71.45407, 42.497457],[-71.45427100000001, 42.497694],[-71.454554, 42.497958],[-71.45479760000001, 42.4981577],[-71.455054, 42.49836],[-71.4551635, 42.4984481],[-71.455524, 42.498738],[-71.45564, 42.49883],[-71.455746, 42.498916],[-71.45611599999999, 42.499234]]}}, +{"type":"Feature","properties":{"width":"7.6","condition":"deficient","source":"massgis_import_v0.1_20071009100433","name":"Elm Court","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:52Z","highway":"residential","lanes":"1","massgis:way_id":"120992","surface":"unpaved"},"geometry": {"type":"LineString","coordinates": [[-71.473175, 42.482807],[-71.472747, 42.483465]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","oneway":"yes","source":"massgis_import_v0.1_20071009100433","name":"Elm Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:17Z","highway":"residential","lanes":"2","massgis:way_id":"186306"},"geometry": {"type":"LineString","coordinates": [[-71.4765956, 42.482987],[-71.4766753, 42.4829796],[-71.4767399, 42.4829806],[-71.4767926, 42.4829846],[-71.4768457, 42.4829951],[-71.4769288, 42.4830124],[-71.4770034, 42.4830341],[-71.4770259, 42.483044],[-71.4770484, 42.4830579],[-71.4770706, 42.4830731],[-71.4770912, 42.4830946],[-71.4771307, 42.4831583]]}}, +{"type":"Feature","properties":{"width":"15.2","condition":"fair","maxspeed":"25 mph","source":"massgis_import_v0.1_20071009100433","name":"Olde Barn Way","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-07-24T08:00:42Z","highway":"residential","lanes":"2","massgis:way_id":"145213","source:maxspeed":"massgis"},"geometry": {"type":"LineString","coordinates": [[-71.462074, 42.489793],[-71.462142, 42.490107],[-71.462225, 42.490542]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Nadine Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:37Z","highway":"residential","lanes":"2","massgis:way_id":"121863"},"geometry": {"type":"LineString","coordinates": [[-71.453078, 42.472937],[-71.45219899999999, 42.472882],[-71.45189000000001, 42.472871],[-71.45129799999999, 42.472827],[-71.450835, 42.472808]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Captain Forbush Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:34Z","highway":"residential","lanes":"2","massgis:way_id":"169375"},"geometry": {"type":"LineString","coordinates": [[-71.461141, 42.477973],[-71.461178, 42.477984],[-71.461353, 42.478035],[-71.46160999999999, 42.478093],[-71.46176699999999, 42.478153],[-71.46186, 42.478211],[-71.461934, 42.478295],[-71.46199, 42.478354],[-71.462035, 42.47842],[-71.46206100000001, 42.478489],[-71.46207200000001, 42.478586],[-71.462042, 42.478705],[-71.462042, 42.478887],[-71.46204899999999, 42.479028],[-71.46208300000001, 42.479158],[-71.462217, 42.47939],[-71.462384, 42.479653],[-71.462596, 42.479979],[-71.462666, 42.480058],[-71.462774, 42.480155],[-71.46293, 42.480237],[-71.46320900000001, 42.480345],[-71.46373800000001, 42.480528],[-71.464159, 42.480693]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Haynes Court","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:33Z","highway":"residential","lanes":"2","massgis:way_id":"151348"},"geometry": {"type":"LineString","coordinates": [[-71.480841, 42.472053],[-71.480518, 42.471941],[-71.48016699999999, 42.471825]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Lincoln Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:31Z","highway":"residential","lanes":"2","massgis:way_id":"108833"},"geometry": {"type":"LineString","coordinates": [[-71.453622, 42.49055],[-71.453653, 42.490633],[-71.453715, 42.49087],[-71.45379200000001, 42.491126],[-71.453874, 42.491435],[-71.453946, 42.491731],[-71.45407899999999, 42.49216],[-71.454156, 42.492476],[-71.454202, 42.492813],[-71.454207, 42.492973],[-71.45417999999999, 42.49332],[-71.454176, 42.49337],[-71.45412899999999, 42.493618],[-71.45406199999999, 42.49397],[-71.45399500000001, 42.494272],[-71.45394899999999, 42.494505],[-71.453912, 42.494861],[-71.453912, 42.495089],[-71.453958, 42.495353],[-71.453999, 42.495579],[-71.454005, 42.495759],[-71.453958, 42.495911],[-71.453834, 42.496053],[-71.4536433, 42.4962392],[-71.4533552, 42.4964095],[-71.4531995, 42.4965192]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Oneida Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:22Z","highway":"residential","lanes":"2","massgis:way_id":"127721"},"geometry": {"type":"LineString","coordinates": [[-71.47295099999999, 42.489391],[-71.472666, 42.48927],[-71.472409, 42.489159],[-71.47225899999999, 42.489111],[-71.472185, 42.489096],[-71.47210699999999, 42.489106],[-71.472042, 42.489128],[-71.471957, 42.489171],[-71.47172, 42.48938],[-71.471429, 42.489629],[-71.471042, 42.489994],[-71.470597, 42.490391],[-71.47002500000001, 42.490907],[-71.469791, 42.491118],[-71.469662, 42.491229],[-71.46950099999999, 42.491467],[-71.469472, 42.491509],[-71.469421, 42.491605],[-71.46937, 42.491794],[-71.46933199999999, 42.491948],[-71.469302, 42.492105],[-71.469261, 42.492288],[-71.469159, 42.492503],[-71.469081, 42.492649],[-71.46892800000001, 42.492798],[-71.46869700000001, 42.492967],[-71.468558, 42.493057],[-71.46837499999999, 42.493229],[-71.468228, 42.493376],[-71.468064, 42.493578],[-71.46796500000001, 42.4937],[-71.467742, 42.494002],[-71.46725000000001, 42.494651]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Church Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-01-15T13:09:14Z","highway":"residential","lanes":"2","massgis:way_id":"130778"},"geometry": {"type":"LineString","coordinates": [[-71.476252, 42.47438],[-71.476225, 42.474392],[-71.47601, 42.474398],[-71.475414, 42.474355],[-71.4749601, 42.4743327],[-71.47480400000001, 42.474325]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Sioux Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:12Z","highway":"residential","lanes":"2","massgis:way_id":"187072"},"geometry": {"type":"LineString","coordinates": [[-71.467883, 42.488105],[-71.467107, 42.487854],[-71.46667100000001, 42.487693]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Charter Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:10Z","highway":"residential","lanes":"2","massgis:way_id":"129746"},"geometry": {"type":"LineString","coordinates": [[-71.457489, 42.480403],[-71.457486, 42.480441],[-71.457522, 42.480557],[-71.45761899999999, 42.480825],[-71.457713, 42.481108],[-71.457745, 42.481194],[-71.457757, 42.481219],[-71.458107, 42.481789],[-71.458223, 42.481975],[-71.45831200000001, 42.482134],[-71.458358, 42.482233],[-71.458455, 42.482461],[-71.45854300000001, 42.482663],[-71.458586, 42.482716],[-71.458626, 42.482761],[-71.45871200000001, 42.482826],[-71.458786, 42.482891],[-71.458868, 42.482968],[-71.458932, 42.483043],[-71.45902599999999, 42.483211],[-71.459146, 42.483405],[-71.459234, 42.483566],[-71.45928000000001, 42.483736],[-71.459305, 42.483863],[-71.459295, 42.484022],[-71.459219, 42.484332],[-71.45907, 42.484765],[-71.458967, 42.485065],[-71.458887, 42.485297],[-71.45876800000001, 42.485619],[-71.45871099999999, 42.48578],[-71.45869500000001, 42.485907],[-71.45869500000001, 42.486032],[-71.45873400000001, 42.486151],[-71.45876, 42.486229],[-71.458851, 42.486385],[-71.458928, 42.486507],[-71.45907099999999, 42.486745],[-71.459194, 42.48695],[-71.459411, 42.487296],[-71.459913, 42.488151],[-71.460151, 42.488579],[-71.460246, 42.488751],[-71.46031499999999, 42.488874],[-71.460384, 42.489008],[-71.460464, 42.489156],[-71.46056900000001, 42.489301],[-71.460632, 42.489364]]}}, +{"type":"Feature","properties":{"width":"11.0","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Homestead Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:37:08Z","highway":"residential","lanes":"2","massgis:way_id":"163327"},"geometry": {"type":"LineString","coordinates": [[-71.480896, 42.472659],[-71.480383, 42.47253],[-71.479933, 42.472418],[-71.479401, 42.472289],[-71.479074, 42.47221],[-71.47845100000001, 42.47205],[-71.477682, 42.47187]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-12T02:24:11Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4753157, 42.4831217],[-71.4753772, 42.4832777]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-12T02:24:12Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.47469, 42.482917],[-71.474734, 42.483149],[-71.474738, 42.483243],[-71.4747358, 42.48331]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:19Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4750714, 42.482452],[-71.4753443, 42.4824075],[-71.4753864, 42.4824038],[-71.4754216, 42.482405],[-71.475444, 42.4824107],[-71.47547830000001, 42.4824227],[-71.47549840000001, 42.482434],[-71.4755172, 42.482453],[-71.4755365, 42.4824813],[-71.475584, 42.48266],[-71.475639, 42.48284],[-71.475662, 42.482923],[-71.4756661, 42.4829506],[-71.4756653, 42.4829724],[-71.4756615, 42.4829924],[-71.47565040000001, 42.4830128],[-71.475628, 42.483033],[-71.47561020000001, 42.483046],[-71.47558549999999, 42.4830598],[-71.4755534, 42.4830696],[-71.4753157, 42.4831217],[-71.47525160000001, 42.4829898],[-71.47519010000001, 42.4828729]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-12T02:24:11Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.475945, 42.482276],[-71.476257, 42.483053]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:36:51Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.46897199999999, 42.482385],[-71.46887700000001, 42.482315],[-71.468789, 42.482271],[-71.468689, 42.482232],[-71.46845399999999, 42.482149],[-71.467939, 42.48196],[-71.467521, 42.481793]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-12T02:24:11Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.470905, 42.481488],[-71.470743, 42.481745]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:19Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4736606, 42.4790661],[-71.473679, 42.4790991],[-71.4736881, 42.4791265],[-71.47368899999999, 42.47915],[-71.473669, 42.479177],[-71.47359400000001, 42.479218],[-71.473541, 42.479248],[-71.473455, 42.479267],[-71.473373, 42.479299],[-71.47329000000001, 42.479336],[-71.473251, 42.479363],[-71.473175, 42.479394],[-71.47312959999999, 42.479434],[-71.4730787, 42.4794163],[-71.47299270000001, 42.479378],[-71.4728532, 42.4793338],[-71.47274090000001, 42.4792993],[-71.4726495, 42.4792765],[-71.47259409999999, 42.4792667],[-71.4725308, 42.479258],[-71.4724607, 42.4792534],[-71.47239190000001, 42.479255],[-71.47217999999999, 42.479282],[-71.472007, 42.479306],[-71.4718408, 42.4793413],[-71.47176279999999, 42.4793515],[-71.4716911, 42.4793536],[-71.4716348, 42.4793488],[-71.47158779999999, 42.4793413],[-71.4715372, 42.4793274],[-71.47149589999999, 42.4793106],[-71.4714618, 42.479294],[-71.47141929999999, 42.4792611],[-71.47137600000001, 42.4792197],[-71.471236, 42.47904],[-71.4709719, 42.4787278],[-71.4709441, 42.4786927]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:36:49Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45591400000001, 42.481828],[-71.45618399999999, 42.481841],[-71.456571, 42.481863],[-71.45674200000001, 42.481898],[-71.456807, 42.481978],[-71.45681500000001, 42.48215],[-71.456776, 42.4823],[-71.456703, 42.482376],[-71.456613, 42.482423],[-71.456467, 42.482471],[-71.456309, 42.48249],[-71.456137, 42.482503],[-71.45600399999999, 42.482515],[-71.45582400000001, 42.482534],[-71.45569, 42.482553],[-71.45554, 42.482585],[-71.455377, 42.482636],[-71.45527800000001, 42.482687],[-71.455145, 42.482732],[-71.45495, 42.482792],[-71.454724, 42.482841],[-71.454532, 42.482866],[-71.45434400000001, 42.482874],[-71.45416299999999, 42.48289],[-71.453974, 42.482882],[-71.453835, 42.482866],[-71.45368499999999, 42.482864],[-71.453518, 42.48284],[-71.453379, 42.482814],[-71.45326, 42.482773],[-71.453198, 42.482739],[-71.453131, 42.482664],[-71.452989, 42.482486],[-71.452923, 42.482364],[-71.45290900000001, 42.48231],[-71.452916, 42.482264],[-71.45292600000001, 42.482214],[-71.452958, 42.482165],[-71.453093, 42.481933],[-71.45313899999999, 42.481865],[-71.453181, 42.481721],[-71.453216, 42.481638],[-71.453243, 42.481559],[-71.45329599999999, 42.481476],[-71.453352, 42.481402],[-71.453385, 42.481333],[-71.45340299999999, 42.481267],[-71.453441, 42.481122],[-71.453456, 42.480976]]}}, +{"type":"Feature","properties":{"created_by":"Potlatch 0.10f","source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-01-14T15:28:51Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45894199999999, 42.480902],[-71.458873, 42.481168],[-71.45886400000001, 42.481225],[-71.45891399999999, 42.481323]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:36:48Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45600899999999, 42.480497],[-71.456014, 42.480577],[-71.45595299999999, 42.480727],[-71.45582899999999, 42.481024]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","name":"Charter Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-09-11T05:17:38Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.456543, 42.477165],[-71.45684300000001, 42.47818],[-71.4569798, 42.4786845],[-71.4571286, 42.4789977],[-71.4573441, 42.4793278],[-71.45726790000001, 42.4793786],[-71.4570547, 42.4794686],[-71.4568968, 42.4795235],[-71.45669030000001, 42.4796511],[-71.45656150000001, 42.4797101],[-71.4567412, 42.4801567],[-71.4567823, 42.4802384],[-71.456793, 42.4802984],[-71.4567675, 42.4803482],[-71.4567345, 42.4803813],[-71.45667469999999, 42.4803866],[-71.4566214, 42.4803945],[-71.456554, 42.4803955],[-71.45627, 42.4804452],[-71.4561214, 42.4804598],[-71.45600899999999, 42.480497]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-11-30T19:58:24Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4564199, 42.4767554],[-71.4562311, 42.4768571],[-71.4559728, 42.4769698],[-71.455648, 42.4771115],[-71.4553433, 42.4771483],[-71.4552733, 42.4771468],[-71.45496780000001, 42.4771404],[-71.4546459, 42.4770415],[-71.45451, 42.476654]]}}, +{"type":"Feature","properties":{"oneway":"-1","service":"driveway","source:datetime":"2013-10-11T02:01:45Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"Polygon","coordinates": [[[-71.4525903, 42.4743816],[-71.4525161, 42.4742011],[-71.4524679, 42.4740785],[-71.4524684, 42.4739823],[-71.4525644, 42.4738668],[-71.4526669, 42.47383],[-71.4528037, 42.473828],[-71.4529319, 42.4738613],[-71.452973, 42.473941],[-71.452969, 42.474045],[-71.45294199999999, 42.474138],[-71.452912, 42.474213],[-71.452893, 42.474258],[-71.45283999999999, 42.474289],[-71.45274499999999, 42.474323],[-71.452673, 42.474345],[-71.4525903, 42.4743816]]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:54Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4562531, 42.4764106],[-71.456354, 42.476526]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-11-30T23:08:51Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4557836, 42.4758005],[-71.4558206, 42.4759073],[-71.455848, 42.47593],[-71.4560256, 42.4760371],[-71.45612730000001, 42.4762185],[-71.45618279999999, 42.4763222],[-71.4562531, 42.4764106],[-71.4560238, 42.4765647],[-71.4558609, 42.4766868],[-71.4557207, 42.4767576],[-71.4555761, 42.476789],[-71.45542, 42.4768007],[-71.455196, 42.4768254],[-71.454866, 42.476807],[-71.4546679, 42.476773],[-71.45451, 42.476654],[-71.45443899999999, 42.47656],[-71.454346, 42.476372],[-71.45429300000001, 42.476295],[-71.45423599999999, 42.476251],[-71.454166, 42.476242],[-71.45406199999999, 42.476256],[-71.453976, 42.476297],[-71.453897, 42.476385],[-71.453778, 42.476432],[-71.453737, 42.476513],[-71.45375199999999, 42.476561],[-71.453799, 42.47661],[-71.453878, 42.47663],[-71.453971, 42.476621],[-71.454014, 42.476586],[-71.454019, 42.476524],[-71.454004, 42.476457],[-71.453897, 42.476385]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:47Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.482736, 42.472947],[-71.48265499999999, 42.472942],[-71.48253699999999, 42.472925],[-71.482399, 42.472897],[-71.482212, 42.472853],[-71.482101, 42.472846],[-71.48201400000001, 42.472849],[-71.481859, 42.472874],[-71.48169900000001, 42.472903],[-71.481551, 42.472914],[-71.481438, 42.472908],[-71.481278, 42.472884],[-71.481128, 42.472851],[-71.48102400000001, 42.472839],[-71.480912, 42.47284]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","name":"Half Moon Hill","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2010-04-28T00:40:59Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.47800599999999, 42.481026],[-71.47788199999999, 42.481032],[-71.477762, 42.481032],[-71.47767, 42.48103],[-71.477594, 42.481039],[-71.477514, 42.48106],[-71.477429, 42.481097]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:39Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.46156000000001, 42.475547],[-71.461474, 42.475753],[-71.461387, 42.47595],[-71.46136, 42.476036],[-71.46136199999999, 42.476156],[-71.46138500000001, 42.47632],[-71.461398, 42.476389],[-71.46142999999999, 42.476445]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2013-03-05T14:34:30Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45902700000001, 42.475341],[-71.45892600000001, 42.47548],[-71.458764, 42.475759],[-71.458668, 42.475889],[-71.458546, 42.475918],[-71.45829999999999, 42.475906],[-71.45811500000001, 42.475888],[-71.457877, 42.475861],[-71.4577767, 42.475853],[-71.45727599999999, 42.475813],[-71.45690999999999, 42.475774]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:37Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.459957, 42.484881],[-71.459514, 42.484839],[-71.459239, 42.484802],[-71.45907, 42.484765]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:32Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.47150499999999, 42.494594],[-71.47135, 42.494508],[-71.471234, 42.494457],[-71.471144, 42.494441],[-71.47091500000001, 42.494402],[-71.47077299999999, 42.494378],[-71.470663, 42.494326],[-71.470483, 42.494233],[-71.470297, 42.494115]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:31Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.450667, 42.49344],[-71.450602, 42.493497],[-71.45049400000001, 42.493646]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:31Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45116899999999, 42.492763],[-71.45108500000001, 42.492844],[-71.450979, 42.492981],[-71.45074200000001, 42.493251],[-71.45066799999999, 42.49334],[-71.450658, 42.493385],[-71.450667, 42.49344],[-71.45081, 42.493407],[-71.451008, 42.4934],[-71.451189, 42.49341]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:35:28Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.464423, 42.489169],[-71.46426700000001, 42.489536],[-71.46420000000001, 42.489695]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-22T00:36:26Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.46445919999999, 42.4987244],[-71.4645273, 42.498796],[-71.46443410000001, 42.4989088],[-71.4644505, 42.498984],[-71.4645398, 42.4990545],[-71.4646525, 42.4991038],[-71.464718, 42.49924],[-71.464697, 42.499379],[-71.46475, 42.499556],[-71.46476149999999, 42.4995932]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-22T00:39:01Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4636761, 42.4991125],[-71.4638463, 42.4991549],[-71.4639518, 42.4991972],[-71.4640313, 42.4992709],[-71.4640654, 42.4993527],[-71.4640921, 42.4994748],[-71.4641384, 42.4995608],[-71.4642576, 42.4996097],[-71.46437299999999, 42.499638],[-71.464502, 42.49966],[-71.4646073, 42.4996107],[-71.46476149999999, 42.4995932]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-02-21T01:44:09Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.454379, 42.473514],[-71.454538, 42.47354],[-71.45475999999999, 42.473568],[-71.454908, 42.473575],[-71.45514, 42.473586],[-71.45538999999999, 42.473603],[-71.455595, 42.47362],[-71.455641, 42.473634],[-71.455659, 42.473669],[-71.45565499999999, 42.473744],[-71.4556462, 42.4738067],[-71.4556189, 42.4739743],[-71.4555968, 42.4741577],[-71.455575, 42.4743162],[-71.45557100000001, 42.474422],[-71.455585, 42.474446],[-71.455663, 42.474466],[-71.456053, 42.474501],[-71.45630300000001, 42.4745626],[-71.456547, 42.4745776],[-71.45648300000001, 42.4749544],[-71.456451, 42.474987],[-71.45643699999999, 42.475097]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:59Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.457489, 42.480403],[-71.457489, 42.480401],[-71.45751300000001, 42.480375],[-71.457537, 42.480357],[-71.45759, 42.480344],[-71.45766999999999, 42.480353],[-71.457717, 42.480362],[-71.457767, 42.48039],[-71.457826, 42.480443],[-71.45786200000001, 42.480489],[-71.457894, 42.480546],[-71.457998, 42.480935],[-71.458043, 42.481226]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","name":"Gregory Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-18T02:39:46Z","highway":"residential"},"geometry": {"type":"LineString","coordinates": [[-71.479876, 42.476779],[-71.479844, 42.476876],[-71.479844, 42.476992],[-71.479884, 42.477119],[-71.47994799999999, 42.477226],[-71.47997599999999, 42.477294],[-71.4799649, 42.4773694]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:49Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.47184900000001, 42.495881],[-71.47184, 42.496005],[-71.471827, 42.496115],[-71.471835, 42.496205],[-71.47185399999999, 42.496265],[-71.47184799999999, 42.496313],[-71.471761, 42.49662]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2013-05-07T22:42:49Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"Polygon","coordinates": [[[-71.47971200000001, 42.480733],[-71.47977299999999, 42.480847],[-71.479795, 42.480914],[-71.479788, 42.480979],[-71.479752, 42.481047],[-71.479687, 42.48111],[-71.47962099999999, 42.48115],[-71.479553, 42.48117],[-71.479496, 42.481174],[-71.47938499999999, 42.481162],[-71.479203, 42.481133],[-71.479114, 42.481129],[-71.479046, 42.481132],[-71.478928, 42.481162],[-71.478831, 42.48118],[-71.47873, 42.481178],[-71.478623, 42.481163],[-71.478509, 42.481138],[-71.478387, 42.481121],[-71.478244, 42.481107],[-71.478117, 42.481093],[-71.478067, 42.481078],[-71.47800599999999, 42.481026],[-71.47802900000001, 42.48098],[-71.478058, 42.480962],[-71.47809700000001, 42.480931],[-71.478144, 42.48089],[-71.478227, 42.480799],[-71.478266, 42.480776],[-71.478331, 42.480754],[-71.47845599999999, 42.480712],[-71.478565, 42.480664],[-71.478613, 42.480606],[-71.478632, 42.480579],[-71.478644, 42.480524],[-71.47863599999999, 42.480423],[-71.47862499999999, 42.480332],[-71.47862000000001, 42.480283],[-71.47863700000001, 42.480225],[-71.478666, 42.480184],[-71.478724, 42.480139],[-71.478801, 42.480098],[-71.47886699999999, 42.480086],[-71.47895200000001, 42.480084],[-71.479056, 42.480084],[-71.47919400000001, 42.480076],[-71.47928, 42.48006],[-71.47939, 42.480026],[-71.479471, 42.480012],[-71.479598, 42.480012],[-71.47969500000001, 42.480018],[-71.47978000000001, 42.480034],[-71.47985300000001, 42.480072],[-71.47989800000001, 42.480115],[-71.479919, 42.480163],[-71.479924, 42.480214],[-71.479917, 42.480258],[-71.479899, 42.480296],[-71.479859, 42.480332],[-71.479817, 42.480362],[-71.47977400000001, 42.480406],[-71.479738, 42.480481],[-71.479721, 42.480541],[-71.479714, 42.480615],[-71.47971200000001, 42.480733]]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","name":"Jesse Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:18Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.471633, 42.482269],[-71.4715609, 42.4823775],[-71.4715403, 42.4824218],[-71.47152199999999, 42.48247],[-71.4714879, 42.4825676],[-71.4714713, 42.4826064],[-71.471456, 42.4826344],[-71.471434, 42.4826627],[-71.4714223, 42.4826735],[-71.471408, 42.482684],[-71.47136159999999, 42.4827108],[-71.471307, 42.482732],[-71.4709701, 42.4828953]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-12-31T20:45:25Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.471311, 42.474391],[-71.471304, 42.474312],[-71.47129099999999, 42.474257],[-71.47126, 42.474204],[-71.47119600000001, 42.474138],[-71.47112799999999, 42.47409],[-71.471046, 42.474054],[-71.470978, 42.474039],[-71.470879, 42.474032],[-71.47077, 42.474039],[-71.47062, 42.474039],[-71.47050400000001, 42.474029],[-71.470348, 42.474014],[-71.470167, 42.473988],[-71.46996300000001, 42.473948],[-71.469758, 42.473895],[-71.469724, 42.473859],[-71.469684, 42.473801],[-71.469633, 42.473741]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:28Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"Polygon","coordinates": [[[-71.47323799999999, 42.471818],[-71.473213, 42.471823],[-71.47315, 42.47186],[-71.473005, 42.471886],[-71.472889, 42.471895],[-71.472678, 42.471926],[-71.472517, 42.471958],[-71.472438, 42.47196],[-71.47237800000001, 42.471949],[-71.472337, 42.471918],[-71.472309, 42.471881],[-71.472309, 42.471829],[-71.47323799999999, 42.471818]]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:15Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.460246, 42.488751],[-71.460077, 42.48881],[-71.459847, 42.488883],[-71.459683, 42.488938]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:14Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45907099999999, 42.486745],[-71.458781, 42.486845],[-71.458686, 42.48688],[-71.458601, 42.486931],[-71.45855, 42.486967],[-71.45847999999999, 42.487028],[-71.45842399999999, 42.48709],[-71.458354, 42.48716],[-71.458287, 42.487227],[-71.458113, 42.487381],[-71.457966, 42.487492],[-71.457855, 42.48757],[-71.457809, 42.487595]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:12Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.47315, 42.486314],[-71.47302500000001, 42.486427],[-71.47287799999999, 42.486583],[-71.472645, 42.48678],[-71.472459, 42.486948],[-71.472408, 42.487062],[-71.472402, 42.487162],[-71.472424, 42.487259],[-71.472396, 42.487343],[-71.472283, 42.487486],[-71.47194, 42.48794]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-07-18T20:15:32Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.464169, 42.485311],[-71.46440699999999, 42.485414],[-71.464651, 42.48552],[-71.464825, 42.485612],[-71.465099, 42.485734],[-71.465315, 42.485807],[-71.46550999999999, 42.485843],[-71.465566, 42.485853],[-71.465611, 42.485797],[-71.46564600000001, 42.485778],[-71.465684, 42.485765],[-71.46574, 42.485752],[-71.46579199999999, 42.485755],[-71.465834, 42.485763],[-71.465886, 42.485786],[-71.465914, 42.485815],[-71.465935, 42.485856],[-71.46592099999999, 42.48591],[-71.465862, 42.485957],[-71.465813, 42.485975],[-71.46574699999999, 42.48598],[-71.465695, 42.485969],[-71.465628, 42.485926],[-71.465566, 42.485853]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-07-18T20:15:31Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.464027, 42.484142],[-71.463769, 42.484181],[-71.463584, 42.4843],[-71.463275, 42.484473],[-71.463097, 42.484602],[-71.462886, 42.484795],[-71.462756, 42.484946],[-71.46267400000001, 42.485032],[-71.46256700000001, 42.485073],[-71.462411, 42.485111],[-71.46218, 42.485161],[-71.462036, 42.485188],[-71.461917, 42.485197],[-71.461906, 42.485197],[-71.461783, 42.485224],[-71.461724, 42.485227],[-71.46167199999999, 42.485227],[-71.461631, 42.485216],[-71.46159400000001, 42.485197],[-71.46157599999999, 42.485175],[-71.46157599999999, 42.485149],[-71.46158699999999, 42.485114],[-71.461602, 42.485086],[-71.461657, 42.485075],[-71.461702, 42.485075],[-71.461742, 42.485094],[-71.46178, 42.485117],[-71.461839, 42.485169],[-71.461917, 42.485197]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:10Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.453992, 42.475356],[-71.453929, 42.475444],[-71.45379200000001, 42.475582],[-71.453638, 42.4757]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:34:07Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.45430500000001, 42.475285],[-71.454125, 42.475282],[-71.454083, 42.475296],[-71.454033, 42.475322],[-71.453992, 42.475356],[-71.45355000000001, 42.475126]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-03-31T02:11:12Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.455956, 42.475058],[-71.45603199999999, 42.475343],[-71.456127, 42.475717],[-71.456388, 42.475738],[-71.456632, 42.475754],[-71.45690999999999, 42.475774],[-71.45707400000001, 42.475885],[-71.457205, 42.476064],[-71.45728800000001, 42.476199],[-71.457278, 42.476246],[-71.457194, 42.47637],[-71.457116, 42.476475],[-71.457082, 42.476526],[-71.456965, 42.47671],[-71.456796, 42.476979],[-71.456688, 42.477126],[-71.45661, 42.47716],[-71.456543, 42.477165],[-71.4564914, 42.4769933],[-71.4564199, 42.4767554],[-71.456354, 42.476526],[-71.456127, 42.475717]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-08T02:08:49Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.450785, 42.475216],[-71.45074700000001, 42.475311],[-71.450681, 42.475438],[-71.4506145, 42.4755675],[-71.45056270000001, 42.4756152],[-71.45050000000001, 42.475635],[-71.4505757, 42.4751912],[-71.450681, 42.475202],[-71.450785, 42.475216],[-71.45125899999999, 42.475058]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-12T02:24:10Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.47117299999999, 42.481101],[-71.470905, 42.481488],[-71.47107800000001, 42.481524],[-71.471266, 42.481591],[-71.471323, 42.481628],[-71.471339, 42.481659],[-71.471334, 42.481726],[-71.471304, 42.481774],[-71.471254, 42.481813],[-71.471199, 42.481834],[-71.471125, 42.481843],[-71.471068, 42.481835],[-71.470973, 42.481806],[-71.470873, 42.481774],[-71.470743, 42.481745],[-71.470641, 42.481927]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-12-31T20:45:24Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47171299999999, 42.474325],[-71.47160700000001, 42.474363],[-71.471458, 42.474391],[-71.471311, 42.474391],[-71.47117799999999, 42.474411],[-71.471059, 42.474421],[-71.470879, 42.474426],[-71.470538, 42.47441],[-71.47031699999999, 42.474398],[-71.470164, 42.474396],[-71.47006500000001, 42.474386],[-71.469932, 42.474352],[-71.46984999999999, 42.474322],[-71.469813, 42.474304],[-71.469765, 42.474284],[-71.469643, 42.474264],[-71.469568, 42.474249]]}}, +{"type":"Feature","properties":{"width":"15.2","condition":"good","ramp":"yes","oneway":"yes","destination":"Littleton Road;West Acton;Littleton","source":"massgis_import_v0.1_20071009090120;massgis_import_v0.1_20071009100433","orig_name":"Ramp-rt 2 Wb To Newtown Rd","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-10-23T18:10:42Z","highway":"motorway_link","lanes":"1","massgis:way_id":"205819;190705"},"geometry": {"type":"LineString","coordinates": [[-71.472939, 42.499278],[-71.47311000000001, 42.49943],[-71.473225, 42.499522],[-71.473275, 42.499585],[-71.473313, 42.499662]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Orchard Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:33:15Z","highway":"residential","lanes":"2","massgis:way_id":"155732"},"geometry": {"type":"LineString","coordinates": [[-71.478002, 42.487412],[-71.477873, 42.487423],[-71.47734199999999, 42.487464],[-71.47693200000001, 42.487505],[-71.476822, 42.487535],[-71.47677299999999, 42.487565],[-71.476767, 42.487569],[-71.476725, 42.487596],[-71.47666700000001, 42.487759],[-71.47663900000001, 42.487936],[-71.47659899999999, 42.488207],[-71.476547, 42.488422],[-71.47645900000001, 42.488649],[-71.476343, 42.488942],[-71.476197, 42.489194],[-71.47606500000001, 42.489414],[-71.47585100000001, 42.489764],[-71.475714, 42.490039],[-71.475571, 42.490334],[-71.475199, 42.491027]]}}, +{"type":"Feature","properties":{"width":"11.0","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Pearl Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:33:13Z","highway":"residential","lanes":"2","massgis:way_id":"188282"},"geometry": {"type":"LineString","coordinates": [[-71.474712, 42.475276],[-71.473578, 42.475252]]}}, +{"type":"Feature","properties":{"width":"11.0","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Joseph Reed Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:33:08Z","highway":"residential","lanes":"2","massgis:way_id":"165590"},"geometry": {"type":"LineString","coordinates": [[-71.46348399999999, 42.476626],[-71.463621, 42.476738],[-71.463751, 42.476821],[-71.463803, 42.47688],[-71.46384399999999, 42.476968],[-71.46387, 42.477109],[-71.463955, 42.477478],[-71.46401400000001, 42.477843],[-71.46407000000001, 42.477965],[-71.46413699999999, 42.478034],[-71.46438999999999, 42.478347],[-71.464699, 42.47863],[-71.464911, 42.478821],[-71.465007, 42.478919],[-71.46505500000001, 42.478974],[-71.465059, 42.479055],[-71.46502099999999, 42.47927],[-71.464956, 42.47939],[-71.464842, 42.479596],[-71.464716, 42.479816],[-71.4646, 42.48002],[-71.464309, 42.480458],[-71.464159, 42.480693],[-71.46413800000001, 42.480726],[-71.463832, 42.481173],[-71.463673, 42.481396],[-71.463528, 42.481531],[-71.463346, 42.48165],[-71.46328, 42.481731],[-71.463204, 42.481814],[-71.463139, 42.481914],[-71.46307299999999, 42.482031],[-71.463027, 42.482175],[-71.46299999999999, 42.482271],[-71.46298400000001, 42.482398],[-71.462974, 42.482568]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Deacon Hunt Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:33:06Z","highway":"residential","massgis:way_id":"206929"},"geometry": {"type":"LineString","coordinates": [[-71.464628, 42.475754],[-71.464575, 42.475849],[-71.464484, 42.475964],[-71.46438499999999, 42.476021],[-71.464308, 42.476044],[-71.464163, 42.476071]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Wright Terrace","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:33:06Z","highway":"residential","lanes":"2","massgis:way_id":"157350"},"geometry": {"type":"LineString","coordinates": [[-71.47794, 42.47643],[-71.477901, 42.476532],[-71.477625, 42.47718],[-71.47751599999999, 42.477507]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Prospect Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-04-30T00:40:42Z","highway":"residential","lanes":"2","massgis:way_id":"114765"},"geometry": {"type":"LineString","coordinates": [[-71.458848, 42.47238],[-71.458849, 42.472407],[-71.458859, 42.472565],[-71.4588749, 42.4728511],[-71.4589455, 42.4732299],[-71.459, 42.47338],[-71.45913400000001, 42.473732],[-71.459222, 42.473986],[-71.459422, 42.474481],[-71.45950000000001, 42.474691],[-71.45968000000001, 42.475145],[-71.459784, 42.475404]]}}, +{"type":"Feature","properties":{"width":"10.4","condition":"fair","oneway":"yes","source":"massgis_import_v0.1_20071009100433","name":"Arlington Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-09-20T20:30:25Z","highway":"unclassified","lanes":"2","massgis:way_id":"154676"},"geometry": {"type":"LineString","coordinates": [[-71.47475900000001, 42.47647],[-71.475199, 42.476289],[-71.475466, 42.476195],[-71.475516, 42.476168]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Kinsley Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:32:45Z","highway":"residential","lanes":"2","massgis:way_id":"112886"},"geometry": {"type":"LineString","coordinates": [[-71.4699876, 42.4719119],[-71.4700998, 42.47224],[-71.4701905, 42.4725243],[-71.470258, 42.472707],[-71.470337, 42.472913],[-71.470431, 42.473074],[-71.47051399999999, 42.473194],[-71.47063300000001, 42.47334],[-71.470741, 42.473456],[-71.47095899999999, 42.473658],[-71.471056, 42.473743],[-71.47117900000001, 42.473851],[-71.471446, 42.474079],[-71.47171299999999, 42.474325],[-71.47196099999999, 42.474543],[-71.472077, 42.474687],[-71.472145, 42.474789],[-71.47221, 42.474949],[-71.472255, 42.475106],[-71.47228, 42.475213],[-71.472292, 42.475409],[-71.472292, 42.475538],[-71.47228, 42.475729],[-71.47227100000001, 42.475901],[-71.472272, 42.4760871]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"West Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:32:18Z","highway":"residential","lanes":"2","massgis:way_id":"138769"},"geometry": {"type":"LineString","coordinates": [[-71.470561, 42.4774347],[-71.470564, 42.477578],[-71.47058, 42.477784],[-71.47059900000001, 42.477965],[-71.47064, 42.478124],[-71.47072799999999, 42.478271],[-71.470831, 42.47837],[-71.470968, 42.478469],[-71.47110960000001, 42.4785476]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Mohawk Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:32:17Z","highway":"residential","lanes":"2","massgis:way_id":"181982"},"geometry": {"type":"LineString","coordinates": [[-71.477338, 42.483912],[-71.47719499999999, 42.483936],[-71.47671099999999, 42.484008],[-71.476488, 42.484039],[-71.476293, 42.484067],[-71.476161, 42.484111],[-71.476091, 42.48416],[-71.476039, 42.484222],[-71.476032, 42.484298],[-71.476039, 42.484444],[-71.476084, 42.484711],[-71.476157, 42.485239],[-71.476185, 42.485456],[-71.476202, 42.48558],[-71.476212, 42.485701],[-71.47620499999999, 42.485813],[-71.476178, 42.485939],[-71.476118, 42.486045],[-71.476024, 42.486226],[-71.475927, 42.486347],[-71.475707, 42.48661],[-71.475432, 42.48696],[-71.47500700000001, 42.487461],[-71.47457, 42.487843],[-71.47430799999999, 42.488024],[-71.47401600000001, 42.488215],[-71.473839, 42.488348],[-71.47358800000001, 42.488535],[-71.47357100000001, 42.488547],[-71.47340800000001, 42.488744],[-71.473286, 42.488914],[-71.473083, 42.489185],[-71.47299, 42.48933],[-71.47295099999999, 42.489391],[-71.47283299999999, 42.48958],[-71.472717, 42.489793],[-71.472598, 42.49002],[-71.472418, 42.490375],[-71.472269, 42.490647],[-71.47217000000001, 42.490892],[-71.472167, 42.491071],[-71.47219699999999, 42.491169],[-71.472295, 42.4913],[-71.472382, 42.491397],[-71.47268699999999, 42.49171],[-71.47305, 42.492065]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Spruce Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-01-15T13:09:15Z","highway":"residential","lanes":"2","massgis:way_id":"206283"},"geometry": {"type":"LineString","coordinates": [[-71.472272, 42.4760871],[-71.472272, 42.476201],[-71.47229900000001, 42.476382],[-71.47231739999999, 42.4764234],[-71.472365, 42.476531],[-71.47256299999999, 42.476918],[-71.47276789999999, 42.4772818],[-71.47291679999999, 42.47758],[-71.47304699999999, 42.477839],[-71.473271, 42.478243],[-71.473485, 42.478693],[-71.4736606, 42.4790661]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Baxter Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:16Z","highway":"residential","lanes":"2","massgis:way_id":"164456"},"geometry": {"type":"LineString","coordinates": [[-71.4696515, 42.4724472],[-71.46954599999999, 42.472615],[-71.46938, 42.472901],[-71.469207, 42.473145],[-71.469127, 42.47325],[-71.4690808, 42.4733001],[-71.46902300000001, 42.473349],[-71.4689513, 42.4733992],[-71.46888319999999, 42.4734409],[-71.4688221, 42.4734696],[-71.4686093, 42.4735655]]}}, +{"type":"Feature","properties":{"width":"7.9","condition":"deficient","source":"massgis_import_v0.1_20071009100433","name":"Mead Terrace","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:32:10Z","highway":"residential","lanes":"1","massgis:way_id":"210186"},"geometry": {"type":"LineString","coordinates": [[-71.476586, 42.476224],[-71.476579, 42.476532],[-71.476536, 42.477066]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Cherry Ridge Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:32:01Z","highway":"residential","lanes":"2","massgis:way_id":"160331"},"geometry": {"type":"LineString","coordinates": [[-71.482024, 42.473615],[-71.481858, 42.473947],[-71.48171600000001, 42.474216],[-71.481613, 42.474405],[-71.481594, 42.474449],[-71.481594, 42.474499],[-71.481621, 42.474537],[-71.481649, 42.474584],[-71.48170399999999, 42.474625],[-71.481877, 42.474695],[-71.482129, 42.474798],[-71.48233399999999, 42.474879],[-71.48251500000001, 42.474975],[-71.482719, 42.475154],[-71.48285199999999, 42.475286],[-71.483092, 42.475552],[-71.483234, 42.475757],[-71.48330900000001, 42.475918],[-71.48333599999999, 42.476035],[-71.483317, 42.476286],[-71.483282, 42.476461]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Captain Brown Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:57Z","highway":"residential","lanes":"2","massgis:way_id":"140443"},"geometry": {"type":"LineString","coordinates": [[-71.463094, 42.477094],[-71.46299500000001, 42.477059],[-71.462827, 42.477014],[-71.46268999999999, 42.47697],[-71.462571, 42.476948],[-71.46246600000001, 42.476943],[-71.462399, 42.476946],[-71.462306, 42.476989],[-71.462191, 42.47705],[-71.461952, 42.477216],[-71.461721, 42.477385],[-71.461539, 42.477517],[-71.461365, 42.477705],[-71.46122699999999, 42.477866],[-71.461141, 42.477973],[-71.46111500000001, 42.478006],[-71.46107000000001, 42.478124],[-71.460977, 42.478332],[-71.460921, 42.47854],[-71.460898, 42.478757],[-71.460898, 42.478972],[-71.46091699999999, 42.479186],[-71.46095800000001, 42.479338],[-71.46103599999999, 42.479491],[-71.46123299999999, 42.479832],[-71.461511, 42.4803],[-71.461679, 42.48058],[-71.46187999999999, 42.480884],[-71.46204, 42.481131],[-71.4622, 42.481319],[-71.462371, 42.481395],[-71.462574, 42.481445],[-71.462936, 42.481511],[-71.463257, 42.481604],[-71.463346, 42.48165]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Summer Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-07-15T18:59:52Z","highway":"tertiary","lanes":"2","massgis:way_id":"120021"},"geometry": {"type":"LineString","coordinates": [[-71.485174, 42.472276],[-71.48495800000001, 42.472241],[-71.48476100000001, 42.4722],[-71.484588, 42.47215],[-71.48428699999999, 42.472043],[-71.483876, 42.471887]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Juniper Ridge Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:42Z","highway":"residential","lanes":"2","massgis:way_id":"114671"},"geometry": {"type":"LineString","coordinates": [[-71.484368, 42.476668],[-71.48385399999999, 42.476575],[-71.483627, 42.476533],[-71.483282, 42.476461],[-71.48316800000001, 42.476437],[-71.482715, 42.476364],[-71.482462, 42.476326],[-71.482321, 42.476295],[-71.48223400000001, 42.476263],[-71.482167, 42.476213],[-71.482116, 42.476123],[-71.48203700000001, 42.475921],[-71.48192299999999, 42.475737],[-71.481837, 42.475664],[-71.48171000000001, 42.475579],[-71.481605, 42.475555],[-71.481499, 42.475544],[-71.481432, 42.475537],[-71.481381, 42.475555],[-71.481306, 42.475593],[-71.481251, 42.475652],[-71.481168, 42.475774],[-71.48100100000001, 42.476122],[-71.48078599999999, 42.476533],[-71.48063399999999, 42.476915]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Lothrop Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:38Z","highway":"residential","lanes":"2","massgis:way_id":"112554"},"geometry": {"type":"LineString","coordinates": [[-71.460077, 42.471812],[-71.460077, 42.471902],[-71.46008399999999, 42.47205],[-71.46010200000001, 42.472298]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Algonquin Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:37Z","highway":"residential","lanes":"2","massgis:way_id":"176937"},"geometry": {"type":"LineString","coordinates": [[-71.46950099999999, 42.491467],[-71.46964800000001, 42.491545],[-71.470066, 42.491782],[-71.470398, 42.491973],[-71.470574, 42.492068],[-71.47071, 42.492167],[-71.470809, 42.492265],[-71.47083600000001, 42.492348],[-71.47081900000001, 42.492439],[-71.47078500000001, 42.492509],[-71.47069, 42.49259],[-71.470568, 42.49266],[-71.470401, 42.492769],[-71.470082, 42.492979],[-71.46979, 42.493201],[-71.469566, 42.493397],[-71.469345, 42.493621],[-71.469104, 42.493876],[-71.46897199999999, 42.494031]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Spencer Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:36Z","highway":"residential","lanes":"2","massgis:way_id":"184452"},"geometry": {"type":"LineString","coordinates": [[-71.463257, 42.472456],[-71.46216099999999, 42.472452],[-71.46203300000001, 42.472452],[-71.46181, 42.47245],[-71.461597, 42.472447],[-71.461446, 42.472432],[-71.46125600000001, 42.472406],[-71.461099, 42.472369],[-71.460786, 42.472299],[-71.460599, 42.472277],[-71.46051, 42.472274],[-71.460195, 42.472291],[-71.46010200000001, 42.472298],[-71.459946, 42.472308],[-71.459553, 42.472344],[-71.459177, 42.472373],[-71.458849, 42.472407]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Durkee Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:31Z","highway":"residential","lanes":"2","massgis:way_id":"205053"},"geometry": {"type":"LineString","coordinates": [[-71.462238, 42.471807],[-71.46212, 42.472162],[-71.46206100000001, 42.472364],[-71.46203300000001, 42.472452]]}}, +{"type":"Feature","properties":{"width":"9.1","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Hillside Terrace","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:26Z","highway":"residential","lanes":"1","massgis:way_id":"158407"},"geometry": {"type":"LineString","coordinates": [[-71.475728, 42.476172],[-71.475827, 42.476509]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Hayward Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-12-21T21:01:00Z","highway":"tertiary","lanes":"2","massgis:way_id":"162893"},"geometry": {"type":"LineString","coordinates": [[-71.467674, 42.481635],[-71.46745300000001, 42.481587],[-71.467315, 42.481563],[-71.467179, 42.481551],[-71.46695200000001, 42.481543],[-71.466613, 42.481594],[-71.466455, 42.481634],[-71.46604000000001, 42.481746],[-71.465446, 42.481976],[-71.46496500000001, 42.482176],[-71.46450400000001, 42.482352],[-71.464055, 42.482509],[-71.463838, 42.482553],[-71.463587, 42.482582],[-71.463267, 42.482578],[-71.462974, 42.482568],[-71.462677, 42.482522],[-71.461989, 42.482318],[-71.461539, 42.482161],[-71.460948, 42.481964],[-71.460235, 42.481735],[-71.45977999999999, 42.481549],[-71.459315, 42.481397],[-71.459125, 42.481348],[-71.45891399999999, 42.481323],[-71.458043, 42.481226],[-71.457745, 42.481194],[-71.457454, 42.481148],[-71.45685400000001, 42.481074],[-71.456504, 42.481049],[-71.45588499999999, 42.481025],[-71.45582899999999, 42.481024],[-71.455141, 42.481017],[-71.45441, 42.48101],[-71.453456, 42.480976],[-71.452887, 42.48095],[-71.452646, 42.480938],[-71.452348, 42.480962],[-71.452127, 42.480995],[-71.451892, 42.48104],[-71.451624, 42.481103],[-71.45146800000001, 42.481136],[-71.45116899999999, 42.4812],[-71.451145, 42.481205],[-71.45106989999999, 42.4812225]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Doris Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:23Z","highway":"residential","lanes":"2","massgis:way_id":"150206"},"geometry": {"type":"Point","coordinates": [-71.452546, 42.471964]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Quaboag Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:22Z","highway":"residential","lanes":"2","massgis:way_id":"152803"},"geometry": {"type":"LineString","coordinates": [[-71.469583, 42.483454],[-71.469635, 42.483509],[-71.46973199999999, 42.4836],[-71.46981599999999, 42.483654],[-71.46993399999999, 42.483709],[-71.470052, 42.483745],[-71.470185, 42.483791],[-71.470359, 42.483861],[-71.470451, 42.483924],[-71.47051399999999, 42.483976],[-71.47054900000001, 42.484035],[-71.470556, 42.4841],[-71.470517, 42.484206],[-71.470455, 42.484379],[-71.470266, 42.485027],[-71.47019, 42.485223],[-71.470085, 42.485403],[-71.46996300000001, 42.485582],[-71.469705, 42.485984],[-71.469517, 42.486274],[-71.469098, 42.486904]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Deacon Hunt Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:13Z","highway":"residential","lanes":"2","massgis:way_id":"206929"},"geometry": {"type":"LineString","coordinates": [[-71.464232, 42.475728],[-71.464258, 42.475779],[-71.464276, 42.475859],[-71.464258, 42.475933],[-71.46424, 42.475994],[-71.464163, 42.476071],[-71.464023, 42.476125],[-71.463914, 42.476192],[-71.463842, 42.476249],[-71.463765, 42.476336],[-71.46357999999999, 42.476528],[-71.46348399999999, 42.476626],[-71.463312, 42.476802],[-71.463189, 42.476948],[-71.463094, 42.477094],[-71.463058, 42.47715],[-71.462987, 42.47733],[-71.462958, 42.477476],[-71.462946, 42.477719],[-71.46296100000001, 42.477938],[-71.46300599999999, 42.478231],[-71.463035, 42.478375],[-71.463095, 42.478563],[-71.46320299999999, 42.478713],[-71.463311, 42.478818],[-71.46354100000001, 42.479009],[-71.46392, 42.479318],[-71.46412100000001, 42.479498],[-71.464315, 42.479631],[-71.464434, 42.479694],[-71.464716, 42.479816]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Fraser Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:18Z","highway":"residential","lanes":"2","massgis:way_id":"207687"},"geometry": {"type":"LineString","coordinates": [[-71.4701905, 42.4725243],[-71.4701183, 42.472534],[-71.4701017, 42.4725355],[-71.4700849, 42.4725356],[-71.4700682, 42.4725343],[-71.4700519, 42.4725316],[-71.4696515, 42.4724472],[-71.46855720000001, 42.4721979]]}}, +{"type":"Feature","properties":{"width":"10.4","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Arlington Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:05Z","highway":"residential","lanes":"2","massgis:way_id":"154676"},"geometry": {"type":"LineString","coordinates": [[-71.48084, 42.472045],[-71.480841, 42.472053],[-71.480896, 42.472659],[-71.480909, 42.472795],[-71.480912, 42.47284],[-71.48093299999999, 42.473112],[-71.48092200000001, 42.473408],[-71.480921, 42.473418],[-71.480896, 42.473544],[-71.48083099999999, 42.473733],[-71.480721, 42.473937],[-71.480577, 42.4742],[-71.48043699999999, 42.47447],[-71.48030199999999, 42.474674],[-71.480197, 42.474744],[-71.48007699999999, 42.474792],[-71.479788, 42.474855],[-71.479705, 42.474859],[-71.479151, 42.474947],[-71.478393, 42.475077],[-71.477979, 42.475151],[-71.477814, 42.475192],[-71.47759499999999, 42.475251],[-71.47729, 42.47535],[-71.476919, 42.475487],[-71.476579, 42.475606],[-71.476333, 42.47572],[-71.475882, 42.475974],[-71.475516, 42.476168]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Huron Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:31:01Z","highway":"residential","lanes":"2","massgis:way_id":"199658"},"geometry": {"type":"LineString","coordinates": [[-71.470016, 42.494565],[-71.469768, 42.494439],[-71.46952, 42.494305],[-71.469123, 42.494108],[-71.46897199999999, 42.494031],[-71.468723, 42.493904],[-71.46841499999999, 42.493754],[-71.468064, 42.493578]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Central Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-11-23T23:50:04Z","highway":"tertiary","lanes":"2","massgis:way_id":"151804"},"geometry": {"type":"LineString","coordinates": [[-71.477518, 42.490754],[-71.4774, 42.490987],[-71.47729, 42.491217],[-71.477243, 42.491365],[-71.47720200000001, 42.491492],[-71.47718500000001, 42.491578],[-71.477131, 42.491856],[-71.47705999999999, 42.492212],[-71.4770094, 42.4924777],[-71.476967, 42.4927],[-71.47689699999999, 42.49309],[-71.4768682, 42.4932452],[-71.47680800000001, 42.493569],[-71.47664, 42.494497],[-71.476437, 42.495583],[-71.47633399999999, 42.496098]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Church Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:55Z","highway":"residential","lanes":"1","massgis:way_id":"130778"},"geometry": {"type":"LineString","coordinates": [[-71.476347, 42.47387],[-71.476326, 42.474038],[-71.47632, 42.47413],[-71.47632, 42.474246],[-71.47630599999999, 42.474326],[-71.476283, 42.474367],[-71.476252, 42.47438]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Madison Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:54Z","highway":"residential","lanes":"2","massgis:way_id":"210754"},"geometry": {"type":"LineString","coordinates": [[-71.452516, 42.490737],[-71.45253599999999, 42.490843],[-71.452618, 42.491141],[-71.452721, 42.491538],[-71.45286400000001, 42.492035],[-71.453039, 42.492621],[-71.45311599999999, 42.492819],[-71.453219, 42.492961],[-71.453332, 42.493075],[-71.45346600000001, 42.493148],[-71.453569, 42.493198],[-71.453749, 42.493263],[-71.45393900000001, 42.493301],[-71.45417999999999, 42.49332]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"deficient","source":"massgis_import_v0.1_20071009100433","name":"Central Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T04:49:08Z","highway":"tertiary","lanes":"2","massgis:way_id":"151804"},"geometry": {"type":"LineString","coordinates": [[-71.4776494, 42.4813715],[-71.4776657, 42.4814095],[-71.4776873, 42.4814536],[-71.4777098, 42.4815212],[-71.47773100000001, 42.481599],[-71.4777387, 42.4816508],[-71.47774, 42.4817045],[-71.4777394, 42.481746],[-71.4777352, 42.4817856],[-71.477718, 42.4818645],[-71.477694, 42.4819423],[-71.477675, 42.4819891]]}}, +{"type":"Feature","properties":{"width":"9.1","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Seneca Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:48Z","highway":"residential","lanes":"2","massgis:way_id":"130764"},"geometry": {"type":"LineString","coordinates": [[-71.47358800000001, 42.488535],[-71.47329999999999, 42.488435],[-71.473032, 42.488332],[-71.472802, 42.488244],[-71.47224, 42.488046],[-71.47194, 42.48794],[-71.471665, 42.487844],[-71.47129, 42.487702],[-71.470539, 42.487436],[-71.470229, 42.487319],[-71.469972, 42.487222],[-71.46912, 42.486912],[-71.469098, 42.486904],[-71.468768, 42.486788],[-71.468644, 42.486744],[-71.46813400000001, 42.486566],[-71.467748, 42.486416],[-71.467569, 42.486345]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Jackson Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2013-11-18T02:38:40Z","highway":"residential","lanes":"2","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4551017, 42.4894111],[-71.45496869999999, 42.489493],[-71.4548497, 42.489619],[-71.45479690000001, 42.4897027],[-71.4547115, 42.4898173],[-71.4546287, 42.4899126],[-71.45452, 42.4900247],[-71.4544109, 42.4901239],[-71.45430949999999, 42.4902152],[-71.4541882, 42.4903121],[-71.4540803, 42.4903817],[-71.45398520000001, 42.490442],[-71.4538905, 42.4904855],[-71.4537696, 42.490524],[-71.453622, 42.49055],[-71.453526, 42.490569],[-71.453013, 42.490657],[-71.452771, 42.490695],[-71.452516, 42.490737],[-71.45239599999999, 42.490756],[-71.45218, 42.49079],[-71.452004, 42.490806],[-71.45178300000001, 42.490825],[-71.4516824, 42.4908263],[-71.4515748, 42.4908225],[-71.45147110000001, 42.4908168],[-71.4513812, 42.4908043],[-71.451274, 42.490781],[-71.4511846, 42.4907592],[-71.45099930000001, 42.4907138],[-71.4508658, 42.4906689],[-71.4507579, 42.4906226],[-71.4506246, 42.4905613],[-71.45046550000001, 42.4904634]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Seminole Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:44Z","highway":"residential","lanes":"2","massgis:way_id":"142239"},"geometry": {"type":"LineString","coordinates": [[-71.47154399999999, 42.48533],[-71.47128600000001, 42.485736],[-71.47083000000001, 42.486402],[-71.47056499999999, 42.486828],[-71.47032799999999, 42.487164],[-71.470229, 42.487319],[-71.470004, 42.487668],[-71.469516, 42.488379],[-71.469043, 42.489114],[-71.46896599999999, 42.489276],[-71.46894500000001, 42.489392],[-71.468941, 42.489633],[-71.468932, 42.489916],[-71.46889400000001, 42.490079],[-71.468852, 42.490172],[-71.46872, 42.490303],[-71.46858, 42.490428],[-71.468448, 42.490518],[-71.468271, 42.490587],[-71.46819499999999, 42.490608],[-71.468073, 42.490608],[-71.46793700000001, 42.490574],[-71.46781900000001, 42.49053],[-71.46754, 42.490435],[-71.467164, 42.490301],[-71.46668099999999, 42.490168],[-71.466521, 42.490186]]}}, +{"type":"Feature","properties":{"width":"15.2","condition":"fair","maxspeed":"25 mph","source":"massgis_import_v0.1_20071009100433","name":"Freedom Farme Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-07-24T07:23:50Z","highway":"residential","lanes":"2","massgis:way_id":"191604","source:maxspeed":"massgis"},"geometry": {"type":"LineString","coordinates": [[-71.466521, 42.490186],[-71.46594, 42.489982],[-71.465711, 42.489912],[-71.46552699999999, 42.489868],[-71.46473400000001, 42.489765],[-71.464299, 42.489708],[-71.46420000000001, 42.489695],[-71.46384399999999, 42.489648],[-71.463646, 42.489619],[-71.463503, 42.489609],[-71.463308, 42.489614],[-71.463106, 42.489643],[-71.462599, 42.489717],[-71.46226799999999, 42.489769],[-71.462074, 42.489793],[-71.461972, 42.489805],[-71.46165999999999, 42.4898],[-71.461535, 42.489774],[-71.461382, 42.489725],[-71.461197, 42.489649],[-71.46100300000001, 42.489549],[-71.460757, 42.489428],[-71.460632, 42.489364]]}}, +{"type":"Feature","properties":{"width":"6.1","condition":"deficient","source":"massgis_import_v0.1_20071009100433","name":"Cedar Terrace","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:42Z","highway":"residential","lanes":"1","massgis:way_id":"207475","surface":"unpaved"},"geometry": {"type":"LineString","coordinates": [[-71.46633300000001, 42.476629],[-71.46624300000001, 42.476664],[-71.465919, 42.476759],[-71.46566199999999, 42.476827],[-71.465498, 42.476859],[-71.465293, 42.476874],[-71.465079, 42.476874],[-71.464848, 42.47685],[-71.46462200000001, 42.476818]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Kinsley Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:42Z","highway":"residential","lanes":"2","massgis:way_id":"196826"},"geometry": {"type":"LineString","coordinates": [[-71.471222, 42.472821],[-71.470991, 42.472949],[-71.470944, 42.472984],[-71.47090900000001, 42.473042],[-71.470924, 42.473171],[-71.471058, 42.473648],[-71.471056, 42.473743]]}}, +{"type":"Feature","properties":{"width":"15.2","condition":"fair","maxspeed":"25 mph","source":"massgis_import_v0.1_20071009100433","name":"Wampanoag Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-07-24T08:34:27Z","highway":"residential","lanes":"2","massgis:way_id":"147470","source:maxspeed":"massgis"},"geometry": {"type":"LineString","coordinates": [[-71.472099, 42.495941],[-71.47184900000001, 42.495881],[-71.47172999999999, 42.495852],[-71.47118500000001, 42.495593],[-71.47030100000001, 42.495139],[-71.46986699999999, 42.494944]]}}, +{"type":"Feature","properties":{"width":"9.1","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Mohegan Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:36Z","highway":"residential","lanes":"2","massgis:way_id":"157763"},"geometry": {"type":"LineString","coordinates": [[-71.468768, 42.486788],[-71.46843200000001, 42.487283],[-71.46819000000001, 42.48767],[-71.467883, 42.488105],[-71.467449, 42.488799],[-71.46713200000001, 42.489253],[-71.466841, 42.489692],[-71.46669199999999, 42.489925],[-71.466521, 42.490186],[-71.46644499999999, 42.490303],[-71.466291, 42.490525],[-71.466026, 42.490914],[-71.465627, 42.491551]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Birch Ridge Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:35Z","highway":"residential","lanes":"2","massgis:way_id":"197755"},"geometry": {"type":"LineString","coordinates": [[-71.484247, 42.4775328],[-71.484368, 42.476668],[-71.484386, 42.476533],[-71.48443, 42.47628],[-71.484448, 42.476099],[-71.484469, 42.475886],[-71.484454, 42.475722],[-71.48439500000001, 42.475512],[-71.484308, 42.475334],[-71.484218, 42.47522],[-71.484088, 42.475121],[-71.483942, 42.475001],[-71.4838, 42.474878],[-71.483625, 42.474722],[-71.483074, 42.474175],[-71.482964, 42.474067],[-71.482861, 42.473991],[-71.48271200000001, 42.473895],[-71.48259, 42.47383],[-71.48238499999999, 42.473725],[-71.482196, 42.473661],[-71.482024, 42.473615],[-71.48193999999999, 42.473594],[-71.48155800000001, 42.473515],[-71.48092200000001, 42.473408]]}}, +{"type":"Feature","properties":{"width":"11.0","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Agawam Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:29Z","highway":"residential","lanes":"2","massgis:way_id":"166413"},"geometry": {"type":"LineString","coordinates": [[-71.470454, 42.481856],[-71.469921, 42.482708],[-71.46978799999999, 42.482936],[-71.469663, 42.483138],[-71.469624, 42.483279],[-71.469583, 42.483454],[-71.469555, 42.483574],[-71.469465, 42.48384],[-71.469301, 42.484208],[-71.469176, 42.484487],[-71.468988, 42.484766],[-71.46885899999999, 42.48492],[-71.468706, 42.485129],[-71.46841999999999, 42.485474],[-71.46780099999999, 42.486129],[-71.46760999999999, 42.486302],[-71.467569, 42.486345],[-71.467456, 42.486462],[-71.467294, 42.486677],[-71.467113, 42.486966],[-71.466869, 42.487347],[-71.46675, 42.487535],[-71.466691, 42.487667],[-71.46667100000001, 42.487693],[-71.466604, 42.487781],[-71.466545, 42.48784],[-71.466482, 42.487879],[-71.46631499999999, 42.487907],[-71.466179, 42.487923],[-71.46592200000001, 42.487868],[-71.465633, 42.487781],[-71.465029, 42.48761],[-71.464733, 42.487528],[-71.464521, 42.487468],[-71.464364, 42.48737],[-71.464249, 42.487251],[-71.46422800000001, 42.487145],[-71.464229, 42.487013],[-71.464236, 42.486887],[-71.464337, 42.486717],[-71.464448, 42.486606],[-71.46452499999999, 42.486482],[-71.46451399999999, 42.486412],[-71.46441, 42.486335],[-71.46427799999999, 42.486257],[-71.46392299999999, 42.486071],[-71.463578, 42.485912]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Willow Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2012-01-15T13:22:47Z","highway":"tertiary","lanes":"2","massgis:way_id":"147601"},"geometry": {"type":"LineString","coordinates": [[-71.475865, 42.471965],[-71.475651, 42.472204],[-71.47540600000001, 42.472462],[-71.475151, 42.47281],[-71.47503, 42.472983],[-71.474943, 42.473134],[-71.47486499999999, 42.473324]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Bullette Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-22T00:40:55Z","highway":"residential","lanes":"2","massgis:way_id":"179702"},"geometry": {"type":"LineString","coordinates": [[-71.469691, 42.498316],[-71.4694312, 42.4988248],[-71.46935000000001, 42.498987],[-71.46916899999999, 42.499465]]}}, +{"type":"Feature","properties":{"width":"11.0","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Lillian Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2009-11-13T19:58:11Z","highway":"residential","lanes":"2","massgis:way_id":"122676"},"geometry": {"type":"Point","coordinates": [-71.47090300000001, 42.499098]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Windsor Avenue","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:14Z","highway":"residential","lanes":"2","massgis:way_id":"136366"},"geometry": {"type":"LineString","coordinates": [[-71.473528, 42.472092],[-71.473568, 42.472554],[-71.473596, 42.472852],[-71.473636, 42.473206],[-71.47365600000001, 42.47338],[-71.47366100000001, 42.473618],[-71.47365000000001, 42.473895],[-71.47363, 42.474291],[-71.4736, 42.47473],[-71.47358699999999, 42.474935],[-71.473578, 42.475252],[-71.47357700000001, 42.475294],[-71.47354900000001, 42.475858],[-71.4735378, 42.4761234]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Wilson Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:11Z","highway":"residential","lanes":"2","massgis:way_id":"199090"},"geometry": {"type":"LineString","coordinates": [[-71.4522486, 42.4865512],[-71.45159649999999, 42.4864776],[-71.4511899, 42.4864412],[-71.450459, 42.486358]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Nashoba Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:30:05Z","highway":"residential","lanes":"2","massgis:way_id":"117763"},"geometry": {"type":"LineString","coordinates": [[-71.477243, 42.491365],[-71.47687999999999, 42.491256],[-71.476602, 42.491171],[-71.476355, 42.491105],[-71.47616600000001, 42.491064],[-71.476017, 42.491039],[-71.475876, 42.491021],[-71.475641, 42.491005],[-71.475448, 42.491003],[-71.47532, 42.491007],[-71.475199, 42.491027],[-71.47514, 42.491036],[-71.474932, 42.491095],[-71.47464100000001, 42.49123],[-71.473947, 42.491593],[-71.473388, 42.491876],[-71.47305, 42.492065],[-71.473023, 42.49208],[-71.47272100000001, 42.492249],[-71.472413, 42.492402],[-71.472196, 42.492542],[-71.471937, 42.492756],[-71.471649, 42.493041],[-71.471418, 42.493258],[-71.47122299999999, 42.49341],[-71.470978, 42.49358],[-71.47069399999999, 42.493793],[-71.47047600000001, 42.493958],[-71.470297, 42.494115],[-71.470265, 42.494143],[-71.47015500000001, 42.494285],[-71.470069, 42.494426],[-71.470016, 42.494565],[-71.47000800000001, 42.494584],[-71.46986699999999, 42.494944],[-71.469786, 42.495154],[-71.469674, 42.495453],[-71.469635, 42.495558],[-71.46956400000001, 42.495784],[-71.469528, 42.495966],[-71.469537, 42.496162],[-71.469582, 42.496386],[-71.469697, 42.496647]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Washington Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2013-11-18T02:38:41Z","highway":"residential","lanes":"2","massgis:way_id":"158622"},"geometry": {"type":"LineString","coordinates": [[-71.45052200000001, 42.484224],[-71.45070200000001, 42.484154],[-71.4508, 42.484142],[-71.45101099999999, 42.484151],[-71.451125, 42.484177],[-71.451211, 42.484206],[-71.451262, 42.484247],[-71.451317, 42.484288],[-71.451375, 42.484334],[-71.45143400000001, 42.484404],[-71.451559, 42.484599],[-71.451825, 42.485006],[-71.451966, 42.485227],[-71.452263, 42.485655],[-71.452314, 42.485754],[-71.45234739999999, 42.4858284],[-71.4523686, 42.4859075],[-71.4523832, 42.4859896],[-71.4523858, 42.4860762],[-71.45237950000001, 42.486165],[-71.4523664, 42.4862535],[-71.4523195, 42.4864071],[-71.4522486, 42.4865512],[-71.452201, 42.4866989],[-71.4521234, 42.4868695],[-71.45203069999999, 42.4871009],[-71.45194739999999, 42.487311],[-71.451899, 42.4874349],[-71.45183249999999, 42.4875947],[-71.4517436, 42.487812],[-71.451502, 42.48837],[-71.451348, 42.488769],[-71.4512806, 42.4889703],[-71.4512525, 42.4890549],[-71.45123289999999, 42.489138],[-71.4512204, 42.4892317],[-71.4512118, 42.4893254],[-71.451204, 42.4894068],[-71.4512034, 42.4894706],[-71.4512098, 42.489565],[-71.451245, 42.4898035],[-71.45129, 42.490065],[-71.45131499999999, 42.490306],[-71.4513129, 42.4903599],[-71.4513058, 42.4904157],[-71.4512823, 42.490499],[-71.4512521, 42.4905766],[-71.4511846, 42.4907592]]}}, +{"type":"Feature","properties":{"width":"15.2","condition":"fair","maxspeed":"25 mph","source":"massgis_import_v0.1_20071009100433","name":"Knowlton Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-07-24T07:38:45Z","highway":"residential","lanes":"2","massgis:way_id":"157653","source:maxspeed":"massgis"},"geometry": {"type":"LineString","coordinates": [[-71.46657399999999, 42.475874],[-71.466559, 42.475951],[-71.466523, 42.476229],[-71.466505, 42.47634],[-71.466464, 42.476434],[-71.466397, 42.476529],[-71.46635000000001, 42.476601],[-71.46633300000001, 42.476629],[-71.46626999999999, 42.476737],[-71.46624300000001, 42.47685],[-71.466234, 42.476977],[-71.466234, 42.477086],[-71.466258, 42.477298],[-71.466312, 42.477468],[-71.46638, 42.477573],[-71.466514, 42.477715],[-71.466728, 42.477933],[-71.46683400000001, 42.478047],[-71.466892, 42.478135],[-71.46692299999999, 42.478215],[-71.466947, 42.478423],[-71.466959, 42.47863],[-71.466926, 42.478813],[-71.466843, 42.479104],[-71.466813, 42.479183],[-71.46672100000001, 42.479449],[-71.466666, 42.479567],[-71.466571, 42.479653],[-71.466458, 42.479726],[-71.466348, 42.479753],[-71.466217, 42.479762],[-71.46607899999999, 42.479764],[-71.465942, 42.47973],[-71.465704, 42.479662],[-71.465383, 42.479544],[-71.464956, 42.47939]]}}, +{"type":"Feature","properties":{"width":"15.2","condition":"fair","maxspeed":"25 mph","source":"massgis_import_v0.1_20071009100433","name":"Kennedy Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2011-07-24T07:37:56Z","highway":"residential","lanes":"2","massgis:way_id":"168242","source:maxspeed":"massgis"},"geometry": {"type":"LineString","coordinates": [[-71.46528499999999, 42.484272],[-71.46508900000001, 42.484207],[-71.46493700000001, 42.48416],[-71.464733, 42.484119],[-71.464507, 42.484105],[-71.464184, 42.484118],[-71.464027, 42.484142]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Wachusett Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:29:52Z","highway":"residential","lanes":"2","massgis:way_id":"178791"},"geometry": {"type":"LineString","coordinates": [[-71.47305, 42.492065],[-71.47313699999999, 42.492091],[-71.473243, 42.492167],[-71.473606, 42.492435],[-71.473743, 42.492551],[-71.473833, 42.492643],[-71.473893, 42.492719],[-71.473905, 42.49282],[-71.473884, 42.492988],[-71.473747, 42.493185],[-71.47364899999999, 42.493324],[-71.473581, 42.493435],[-71.473585, 42.493536],[-71.473653, 42.493818]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Beverly Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:29:51Z","highway":"residential","lanes":"2","massgis:way_id":"112147"},"geometry": {"type":"LineString","coordinates": [[-71.452584, 42.471811],[-71.45255, 42.47192],[-71.452546, 42.471964],[-71.452539, 42.472027],[-71.45256500000001, 42.472144],[-71.452631, 42.472267],[-71.452708, 42.472373],[-71.45281300000001, 42.472543],[-71.453045, 42.472901],[-71.453078, 42.472937],[-71.453214, 42.473081],[-71.453478, 42.47327],[-71.45371, 42.47336],[-71.45415199999999, 42.473467],[-71.454379, 42.473514]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Littlefield Road","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:29:44Z","highway":"residential","lanes":"2","massgis:way_id":"138561"},"geometry": {"type":"LineString","coordinates": [[-71.478691, 42.49267],[-71.47838, 42.492466],[-71.477665, 42.49197],[-71.47740899999999, 42.491773],[-71.47718500000001, 42.491578]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"good","source":"massgis_import_v0.1_20071009100433","name":"Hennessey Drive","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:29:43Z","highway":"residential","lanes":"2","massgis:way_id":"207315"},"geometry": {"type":"Point","coordinates": [-71.4562717, 42.4721664]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Central Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:16Z","highway":"tertiary","lanes":"2","massgis:way_id":"151804"},"geometry": {"type":"LineString","coordinates": [[-71.4776494, 42.4813715],[-71.4776226, 42.4813257],[-71.4775923, 42.4812805],[-71.47754329999999, 42.4812143],[-71.4774845, 42.4811502],[-71.477429, 42.481097],[-71.47729820000001, 42.4809948],[-71.4771149, 42.480859],[-71.4769009, 42.4807004],[-71.4765594, 42.4804559],[-71.47632369999999, 42.4802707],[-71.476218, 42.4801738],[-71.4761218, 42.4800795],[-71.475903, 42.479837],[-71.47569799999999, 42.479585],[-71.4755694, 42.4794205],[-71.47550579999999, 42.4793373],[-71.4754473, 42.4792544],[-71.4753965, 42.4791769],[-71.4753493, 42.4791001],[-71.4753068, 42.4790191],[-71.47527100000001, 42.478938],[-71.475216, 42.47879],[-71.47518100000001, 42.478661],[-71.475128, 42.478382],[-71.47502900000001, 42.477868],[-71.47488800000001, 42.477119],[-71.474802, 42.476684],[-71.474773, 42.476531],[-71.47475900000001, 42.47647],[-71.47471, 42.476252],[-71.4747, 42.476152],[-71.47467899999999, 42.475956],[-71.4746921, 42.4756079],[-71.47470800000001, 42.475307],[-71.474712, 42.475276],[-71.474746, 42.474995],[-71.4747567, 42.4748863],[-71.474761, 42.474842],[-71.4747782, 42.4747011],[-71.474784, 42.474654],[-71.4747895, 42.474562],[-71.474799, 42.474404],[-71.47480400000001, 42.474325],[-71.474827, 42.474003],[-71.47485500000001, 42.473574],[-71.47486499999999, 42.473324],[-71.474885, 42.472659],[-71.47490000000001, 42.472183],[-71.474915, 42.471931]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","oneway":"yes","source":"massgis_import_v0.1_20071009100433","name":"Elm Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:17Z","highway":"residential","massgis:way_id":"186306"},"geometry": {"type":"LineString","coordinates": [[-71.47718690000001, 42.4827943],[-71.4771185, 42.4828431],[-71.47705190000001, 42.482882],[-71.4770183, 42.4828934],[-71.4769781, 42.4829043],[-71.4768943, 42.4829224],[-71.476812, 42.4829373],[-71.47672849999999, 42.4829573],[-71.4765956, 42.482987]]}}, +{"type":"Feature","properties":{"width":"7.9","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Hammond Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:29:33Z","highway":"residential","lanes":"2","massgis:way_id":"124137"},"geometry": {"type":"LineString","coordinates": [[-71.45123599999999, 42.495317],[-71.450965, 42.495319],[-71.450588, 42.495329]]}}, +{"type":"Feature","properties":{"width":"9.8","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Houghton Lane","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2007-10-15T15:29:30Z","highway":"residential","lanes":"2","massgis:way_id":"106569"},"geometry": {"type":"LineString","coordinates": [[-71.469651, 42.47984],[-71.469894, 42.479999],[-71.47006, 42.480109],[-71.47020000000001, 42.480263],[-71.470338, 42.480492],[-71.470445, 42.480641],[-71.470501, 42.480694],[-71.470495, 42.48077],[-71.47051399999999, 42.480829],[-71.470534, 42.480862],[-71.470572, 42.480891],[-71.4706, 42.480907],[-71.47066, 42.480909],[-71.470724, 42.480895],[-71.47078399999999, 42.480844],[-71.470782, 42.480783],[-71.470743, 42.480727],[-71.47066599999999, 42.480696],[-71.470572, 42.480684],[-71.470501, 42.480694]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2015-02-28T17:17:36Z"},"geometry": {"type":"LineString","coordinates": [[-71.45059879999999, 42.4764383],[-71.450517, 42.4765125],[-71.45059879999999, 42.4764383]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","shop":"alcohol","addr:city":"Acton","building":"yes","addr:housenumber":"305","addr:state":"MA","name":"Acton Wine and Spirits","source:datetime":"2015-02-28T17:17:36Z","addr:postcode":"01720"},"geometry": {"type":"LineString","coordinates": [[-71.4507249, 42.4763513],[-71.45059879999999, 42.4764383],[-71.4507249, 42.4763513]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","addr:city":"Acton","building":"yes","addr:housenumber":"303","addr:state":"MA","name":"Not Your Average Joes","amenity":"restaurant","source:datetime":"2015-02-28T17:17:35Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4508429, 42.4762662],[-71.4507249, 42.4763513],[-71.4504888, 42.4759913],[-71.4508429, 42.4762662]]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","shop":"other","addr:city":"Acton","building":"yes","addr:housenumber":"301","addr:state":"MA","name":"Sleepy's","source:datetime":"2015-02-28T17:17:35Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4509777, 42.4761771],[-71.4508429, 42.4762662],[-71.4504888, 42.4759913],[-71.4506082, 42.4759082],[-71.45075129999999, 42.4760126],[-71.4509777, 42.4761771]]]}}, +{"type":"Feature","properties":{"cuisine":"donut","addr:street":"Main Street","addr:city":"Acton","building":"yes","addr:housenumber":"299","addr:state":"MA","area":"yes","name":"Dunkin Donuts","amenity":"cafe","source:datetime":"2015-02-28T17:17:35Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.45103949999999, 42.4761327],[-71.4509777, 42.4761771],[-71.45075129999999, 42.4760126],[-71.4508132, 42.4759585],[-71.45086139999999, 42.4759818],[-71.450836, 42.476009],[-71.45103949999999, 42.4761327]]]}}, +{"type":"Feature","properties":{"cuisine":"sandwich","addr:street":"Main Street","addr:city":"Acton","building":"yes","addr:housenumber":"297","addr:state":"MA","name":"TC Landos","amenity":"fast_food","source:datetime":"2015-02-28T17:17:35Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.45109650000001, 42.4760774],[-71.45103949999999, 42.4761327],[-71.450836, 42.476009],[-71.45086139999999, 42.4759818],[-71.45087909999999, 42.4759629],[-71.45109650000001, 42.4760774]]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","shop":"mobile_phone","addr:city":"Acton","building":"yes","addr:housenumber":"295","addr:state":"MA","area":"yes","name":"Verizon Wireless","source:datetime":"2015-02-28T17:17:35Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4511535, 42.4760138],[-71.45109650000001, 42.4760774],[-71.45087909999999, 42.4759629],[-71.4509288, 42.4759093],[-71.4511535, 42.4760138]]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","shop":"hairdresser","addr:city":"Acton","building":"yes","addr:housenumber":"293","addr:state":"MA","area":"yes","name":"Supercuts","source:datetime":"2015-02-28T17:00:27Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4512138, 42.4759309],[-71.4509858, 42.4758324],[-71.4509288, 42.4759093],[-71.4511535, 42.4760138],[-71.4512138, 42.4759309]]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2015-02-28T17:00:27Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45294149999999, 42.4754517],[-71.4525311, 42.4750854]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-02-28T17:00:27Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4526365, 42.4757833],[-71.4525751, 42.4756379],[-71.45294149999999, 42.4754517],[-71.45307010000001, 42.4755322]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-02-28T17:00:27Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.453176, 42.4749761],[-71.4529529, 42.4749461],[-71.4529064, 42.4747864]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-02-28T17:00:27Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4533126, 42.4748231],[-71.453176, 42.4749761],[-71.45335350000001, 42.4751346]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-02-28T17:00:27Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45348180000001, 42.4751887],[-71.45335350000001, 42.4751346],[-71.45324599999999, 42.4752728],[-71.45325939999999, 42.4753895]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2015-02-28T16:47:26Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4537426, 42.4745678],[-71.45356510000001, 42.4747047]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-02-28T16:47:26Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45395190000001, 42.4745841],[-71.4537426, 42.4745678]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-23T23:51:00Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4746921, 42.4756079],[-71.4742578, 42.4755938]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2015-01-23T23:51:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4745477, 42.475672],[-71.47424479999999, 42.4756622],[-71.4742545, 42.4755009],[-71.4745624, 42.4754993],[-71.4745477, 42.475672]]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-23T20:15:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4621664, 42.487519],[-71.4631869, 42.4880072],[-71.4635761, 42.4882157],[-71.4637601, 42.4883297],[-71.4638839, 42.4884518],[-71.4643415, 42.4882222],[-71.4644278, 42.4881994],[-71.4648447, 42.4884616]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-23T20:15:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.46232910000001, 42.4873407],[-71.463379, 42.4878541]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2015-01-23T20:15:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4626523, 42.4871287],[-71.4620051, 42.4878289],[-71.46289179999999, 42.4882711],[-71.4631636, 42.4880454],[-71.4639305, 42.4885613],[-71.46439119999999, 42.488278],[-71.4643612, 42.4881928],[-71.4644234, 42.4881467],[-71.46413320000001, 42.4879325],[-71.46401109999999, 42.4878841],[-71.4635068, 42.4881536],[-71.46319579999999, 42.4879601],[-71.4631981, 42.487898],[-71.46294020000001, 42.4877644],[-71.46313360000001, 42.4874408],[-71.46286189999999, 42.4873118],[-71.4626523, 42.4871287]]]}}, +{"type":"Feature","properties":{"access":"private","source:datetime":"2015-01-23T20:01:28Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.458416, 42.4980509],[-71.4585464, 42.4981604],[-71.458776, 42.4981987]]}}, +{"type":"Feature","properties":{"source:datetime":"2015-01-23T20:01:28Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4582822, 42.4978853],[-71.4583502, 42.4979829],[-71.4582712, 42.4980212],[-71.45830290000001, 42.4980855],[-71.45839410000001, 42.4980326],[-71.458416, 42.4980509]]}}, +{"type":"Feature","properties":{"source:datetime":"2015-01-23T19:57:47Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4559326, 42.4976759],[-71.4561642, 42.497654],[-71.45643130000001, 42.4977664],[-71.45696390000001, 42.4977208],[-71.4572977, 42.4974472],[-71.4579947, 42.497426]]}}, +{"type":"Feature","properties":{"access":"private","source:datetime":"2015-01-23T20:01:28Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.458776, 42.4981987],[-71.45974940000001, 42.4979783],[-71.46022360000001, 42.4975177],[-71.4615976, 42.4971001]]}}, +{"type":"Feature","properties":{"source:datetime":"2015-01-23T19:57:47Z","highway":"footway"},"geometry": {"type":"Polygon","coordinates": [[[-71.4580519, 42.4969525],[-71.4588949, 42.4961049],[-71.4594292, 42.4958608],[-71.4598115, 42.4957088],[-71.4600856, 42.4959967],[-71.46006250000001, 42.4962339],[-71.4598046, 42.4966623],[-71.45907219999999, 42.4969663],[-71.4590261, 42.4971966],[-71.45851709999999, 42.4974269],[-71.45832369999999, 42.4976596],[-71.4582822, 42.4978853],[-71.4579947, 42.497426],[-71.4580519, 42.4969525]]]}}, +{"type":"Feature","properties":{"oneway":"yes","service":"driveway","source:datetime":"2015-01-23T19:54:45Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4559126, 42.4977304],[-71.4556498, 42.4977075],[-71.4553341, 42.4977984]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-23T20:12:59Z","highway":"service"},"geometry": {"type":"Polygon","coordinates": [[[-71.4553341, 42.4977984],[-71.4554973, 42.4982359],[-71.45526049999999, 42.4983416],[-71.45497570000001, 42.4979972],[-71.4553341, 42.4977984]]]}}, +{"type":"Feature","properties":{"oneway":"yes","service":"driveway","source:datetime":"2015-01-23T19:54:45Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45479760000001, 42.4981577],[-71.45497570000001, 42.4979972]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-22T00:39:00Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.46407139999999, 42.4996923],[-71.46414369999999, 42.4996491],[-71.4642576, 42.4996097]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-22T00:36:25Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.46475, 42.499556],[-71.4647846, 42.4994577],[-71.4648212, 42.4992965],[-71.4648876, 42.4991985],[-71.46496070000001, 42.4990906],[-71.4650338, 42.4990225],[-71.465067, 42.4989344],[-71.4651052, 42.4988347],[-71.4651418, 42.4987085],[-71.4651866, 42.4986454],[-71.4652365, 42.4986121],[-71.4653577, 42.4986072]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-22T00:30:37Z","highway":"service"},"geometry": {"type":"Point","coordinates": [-71.46476149999999, 42.4995932]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-20T23:59:47Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4607396, 42.4995848],[-71.4608096, 42.4995213],[-71.4609212, 42.4994667],[-71.4610165, 42.499448],[-71.4610954, 42.4994969],[-71.4610995, 42.499584],[-71.46107259999999, 42.4996752]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-20T23:59:47Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.46138000000001, 42.4990117],[-71.46157580000001, 42.499242],[-71.46162409999999, 42.4994124],[-71.46159419999999, 42.499523],[-71.461456, 42.499645]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2015-01-20T23:59:47Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4627685, 42.4993386],[-71.46282840000001, 42.4994837],[-71.4628652, 42.4996495]]}}, +{"type":"Feature","properties":{"oneway":"yes","service":"driveway","source:datetime":"2015-01-23T19:54:45Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45526049999999, 42.4983416],[-71.4551635, 42.4984481]]}}, +{"type":"Feature","properties":{"access":"private","source:datetime":"2015-01-23T20:01:28Z","highway":"track"},"geometry": {"type":"LineString","coordinates": [[-71.4574995, 42.4994647],[-71.45784380000001, 42.4995706],[-71.45816170000001, 42.4996028],[-71.45838120000001, 42.4995744],[-71.4585685, 42.4995158],[-71.4587066, 42.4994079],[-71.45880870000001, 42.4992944],[-71.458839, 42.4991487],[-71.4588258, 42.499056],[-71.458769, 42.4989614],[-71.4587009, 42.4988649],[-71.4585893, 42.4987798],[-71.4583793, 42.4987457],[-71.458776, 42.4981987]]}}, +{"type":"Feature","properties":{"leisure":"nature_reserve","name":"Anderson Parcel","landuse":"conservation","source:datetime":"2015-01-23T19:54:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45730399999999, 42.4989143],[-71.4559928, 42.4977649],[-71.45614980000001, 42.4973378],[-71.456254, 42.4971018],[-71.456324, 42.4969466],[-71.4563857, 42.4968225],[-71.45657780000001, 42.4963946],[-71.45671830000001, 42.4961221],[-71.4568014, 42.4960016],[-71.4569611, 42.4958233],[-71.4572776, 42.4973298],[-71.45742, 42.4972454],[-71.4589501, 42.4958304],[-71.4594474, 42.4953379],[-71.4590733, 42.4949225],[-71.4592782, 42.4946888],[-71.45967880000001, 42.494244],[-71.4601473, 42.4937081],[-71.4606836, 42.4943252],[-71.4612717, 42.4958161],[-71.45730399999999, 42.4989143]]]}}, +{"type":"Feature","properties":{"natural":"wetland","source:datetime":"2015-01-17T06:31:58Z"},"geometry": {"type":"Point","coordinates": [-71.4714865, 42.4718607]}}, +{"type":"Feature","properties":{"tunnel":"culvert","waterway":"stream","name":"Fort Pond Brook","source:datetime":"2015-01-17T06:31:58Z"},"geometry": {"type":"LineString","coordinates": [[-71.4796109, 42.4851566],[-71.47971699999999, 42.4851053]]}}, +{"type":"Feature","properties":{"waterway":"stream","name":"Fort Pond Brook","source:datetime":"2015-01-17T06:31:57Z"},"geometry": {"type":"LineString","coordinates": [[-71.47971699999999, 42.4851053],[-71.4797774, 42.4849987],[-71.4797205, 42.4848784],[-71.4796783, 42.4848091],[-71.47965379999999, 42.4847378],[-71.4796633, 42.4846645],[-71.4796865, 42.4845963],[-71.4797341, 42.4844296],[-71.4797382, 42.4843352],[-71.47973140000001, 42.4842388],[-71.4797477, 42.4841906],[-71.4797858, 42.4841605],[-71.4797981, 42.4840352],[-71.47974910000001, 42.4839145],[-71.4797178, 42.4837228],[-71.47961189999999, 42.483411],[-71.47959659999999, 42.4832699],[-71.4795484, 42.483121],[-71.47952309999999, 42.4830346],[-71.479465, 42.4829464],[-71.4794229, 42.4829267],[-71.4793829, 42.4829026],[-71.4793428, 42.4828662],[-71.4793408, 42.4828189],[-71.47937020000001, 42.4827387],[-71.4794016, 42.482655],[-71.4794272, 42.4822058],[-71.4793207, 42.4821858],[-71.47925720000001, 42.4821656],[-71.47921049999999, 42.482144],[-71.4791711, 42.4821154],[-71.47918180000001, 42.4820903],[-71.47920120000001, 42.4820721],[-71.4792118, 42.4820302],[-71.4791898, 42.4819968],[-71.479117, 42.4819589],[-71.4790483, 42.4819274],[-71.4789875, 42.4818954],[-71.4789148, 42.4818737],[-71.4788393, 42.481857],[-71.4787512, 42.4818471],[-71.4786718, 42.4818491],[-71.4785596, 42.4818491],[-71.4784635, 42.4818412],[-71.47836599999999, 42.4818378],[-71.4782926, 42.4818417],[-71.4782592, 42.4818638],[-71.4782505, 42.4819072],[-71.47826190000001, 42.4819569],[-71.4782632, 42.4819948],[-71.4782472, 42.4820342],[-71.4782192, 42.4820701],[-71.47815439999999, 42.4820918],[-71.47809030000001, 42.4820879],[-71.4780209, 42.4820741],[-71.47795480000001, 42.4820972],[-71.4779027, 42.4821263],[-71.4778458, 42.4821163],[-71.477755, 42.4820903],[-71.4774984, 42.4819898],[-71.477313, 42.4819437],[-71.47720270000001, 42.4818595],[-71.47706959999999, 42.4817544],[-71.47703490000001, 42.4817451],[-71.476989, 42.4817502],[-71.4769032, 42.4817955],[-71.4767071, 42.4818962],[-71.4765587, 42.4819315],[-71.4765045, 42.4819408],[-71.4764641, 42.4819378],[-71.47643429999999, 42.4819327],[-71.4764198, 42.4819163],[-71.4764131, 42.4818581],[-71.47640629999999, 42.4817729],[-71.47638999999999, 42.4817146],[-71.47636540000001, 42.4816543],[-71.4763081, 42.4815692],[-71.4762624, 42.4815131],[-71.47622819999999, 42.4814868],[-71.4761955, 42.4814559],[-71.4761657, 42.4814474],[-71.47613200000001, 42.4814545],[-71.4761069, 42.481463],[-71.4760679, 42.4814776],[-71.4760381, 42.4814854],[-71.4759876, 42.4814854],[-71.47582970000001, 42.4814776],[-71.47570210000001, 42.4814754],[-71.4756249, 42.4814897],[-71.47556230000001, 42.4815073],[-71.4755085, 42.4815128],[-71.47546699999999, 42.4815093],[-71.47544310000001, 42.4814907],[-71.4754384, 42.4814762],[-71.4754472, 42.4814335],[-71.4754738, 42.4813989],[-71.4755037, 42.4813517],[-71.4755255, 42.4812899],[-71.4755255, 42.4812563],[-71.47551799999999, 42.4812212],[-71.4754949, 42.4812041],[-71.4754588, 42.4812036],[-71.4754248, 42.4812101],[-71.4753771, 42.4812111],[-71.4753281, 42.481196],[-71.4753022, 42.48118],[-71.4752804, 42.4811318],[-71.47526209999999, 42.4810746],[-71.47522119999999, 42.4810439],[-71.4751783, 42.481072],[-71.4751674, 42.4810926],[-71.47515660000001, 42.4811187],[-71.4751416, 42.4811308],[-71.475098, 42.4811433],[-71.4750299, 42.4811343],[-71.4750116, 42.4811067],[-71.47496599999999, 42.4810479],[-71.47491290000001, 42.4810244],[-71.4748012, 42.4809942],[-71.4747498, 42.4809736],[-71.47473530000001, 42.4809364],[-71.4747471, 42.4809088],[-71.474772, 42.4808869],[-71.4747678, 42.4808685],[-71.4747519, 42.4808527],[-71.4747395, 42.4808395],[-71.4747139, 42.4808339],[-71.47468069999999, 42.4808364],[-71.4746447, 42.4808486],[-71.4745769, 42.4808563],[-71.47439919999999, 42.480868],[-71.4742401, 42.4808782],[-71.47414120000001, 42.4808925],[-71.4741073, 42.4808925],[-71.47407200000001, 42.4808876],[-71.47401739999999, 42.4808726],[-71.473969, 42.4808492],[-71.4738813, 42.4807975],[-71.47382229999999, 42.4807645],[-71.4737981, 42.4807502],[-71.4737636, 42.4807497],[-71.4737442, 42.4807568],[-71.47373589999999, 42.4807762],[-71.4737234, 42.4808354],[-71.4737221, 42.4808803],[-71.4737138, 42.4809027],[-71.47368470000001, 42.4809129],[-71.4736308, 42.4809068],[-71.4735969, 42.4808869],[-71.4735623, 42.4808737],[-71.473527, 42.4808588],[-71.473471, 42.4808548],[-71.47340869999999, 42.480871],[-71.4733865, 42.4808728],[-71.4733762, 42.4808713],[-71.4733686, 42.4808654],[-71.47336420000001, 42.4808571],[-71.4733652, 42.480848],[-71.4733672, 42.4808271],[-71.4733628, 42.4808006],[-71.47335270000001, 42.4807807],[-71.4733227, 42.4807445],[-71.4732686, 42.4807336],[-71.4731759, 42.4807409],[-71.47309079999999, 42.4807467],[-71.4730058, 42.4807424],[-71.47288349999999, 42.4807171],[-71.4724932, 42.4806753],[-71.4723915, 42.4806803],[-71.4722976, 42.4806241],[-71.47214889999999, 42.4805317],[-71.4719552, 42.4805447],[-71.4717327, 42.4805285],[-71.47162659999999, 42.4805086],[-71.4715229, 42.4804423],[-71.471386, 42.4803095],[-71.4713605, 42.4803003],[-71.47133340000001, 42.4802951],[-71.47131090000001, 42.4802891],[-71.4713015, 42.4802832],[-71.4712944, 42.4802715],[-71.4712913, 42.4802547],[-71.4712969, 42.4802254],[-71.471306, 42.4802176],[-71.4713268, 42.4802039],[-71.4713788, 42.4801902],[-71.4714079, 42.4801793],[-71.47143079999999, 42.4801593],[-71.47143149999999, 42.4801414],[-71.4714192, 42.4801267],[-71.4714005, 42.4801176],[-71.47137170000001, 42.4801059],[-71.47133770000001, 42.4800974],[-71.4712948, 42.4800717],[-71.47127860000001, 42.4800588],[-71.4712674, 42.480029],[-71.4712526, 42.4799974],[-71.47123329999999, 42.47998],[-71.4712105, 42.4799647],[-71.4711641, 42.4799297],[-71.471136, 42.4799072],[-71.4711159, 42.4798992],[-71.4711015, 42.4798986],[-71.4710924, 42.4799007],[-71.4710836, 42.4799049],[-71.4710798, 42.4799129],[-71.47107339999999, 42.4799347],[-71.47107269999999, 42.4799432],[-71.4710675, 42.4799647],[-71.47105449999999, 42.4799842],[-71.4710383, 42.4800002],[-71.4710225, 42.4800064],[-71.4710067, 42.4800075],[-71.4709842, 42.4800033],[-71.47097719999999, 42.4799956],[-71.47093289999999, 42.4799564],[-71.4707818, 42.4798675],[-71.4707413, 42.4798474],[-71.4706949, 42.4798252],[-71.4706635, 42.4798005],[-71.4706585, 42.4797747],[-71.4706813, 42.4797426],[-71.4707049, 42.4797184],[-71.4706963, 42.4796673],[-71.47067989999999, 42.4796446],[-71.47063559999999, 42.4796088],[-71.4705921, 42.4795788],[-71.4705621, 42.4795488],[-71.4705592, 42.4795188],[-71.4705628, 42.4794914],[-71.4706035, 42.4794667],[-71.4706249, 42.4794456],[-71.47067560000001, 42.4794208],[-71.4707106, 42.4793861],[-71.4707415, 42.4793581],[-71.47076130000001, 42.4792882],[-71.47076269999999, 42.4792355],[-71.47076629999999, 42.4791792],[-71.4707363, 42.4791133],[-71.4706999, 42.4790349],[-71.4705921, 42.4789069],[-71.47055709999999, 42.4788743],[-71.4705007, 42.4788648],[-71.47043859999999, 42.4788716],[-71.4703579, 42.4788816],[-71.4702872, 42.4788622],[-71.4702058, 42.4788327],[-71.4701216, 42.4788048],[-71.470088, 42.478789],[-71.47005559999999, 42.4787862],[-71.4700222, 42.4787949],[-71.4699578, 42.4788183],[-71.4698935, 42.478832],[-71.4698406, 42.4788436],[-71.469765, 42.4788383],[-71.4696879, 42.4788257],[-71.4696422, 42.4787994],[-71.4696122, 42.4787572],[-71.4696136, 42.478713],[-71.4696593, 42.478653],[-71.4696907, 42.4786003],[-71.4697436, 42.4785245],[-71.46978780000001, 42.4784255],[-71.469795, 42.4782833],[-71.4698264, 42.4780601],[-71.4698564, 42.4779316],[-71.4698535, 42.4778031],[-71.4698435, 42.4777252],[-71.469815, 42.4776736],[-71.4697721, 42.4776304],[-71.4697164, 42.4776051],[-71.46962929999999, 42.4776125],[-71.4695622, 42.4776399],[-71.46950940000001, 42.4776725],[-71.46943229999999, 42.4777094],[-71.4693795, 42.4777252],[-71.4693181, 42.4777241],[-71.469221, 42.4777009],[-71.46918530000001, 42.4776788],[-71.46917670000001, 42.4776346],[-71.46918530000001, 42.4775809],[-71.4691696, 42.4775314],[-71.4691296, 42.4775135],[-71.469041, 42.4774861],[-71.4689654, 42.477465],[-71.4689297, 42.4774334],[-71.46892819999999, 42.4774071],[-71.4689968, 42.4773545],[-71.469061, 42.4773355],[-71.4690896, 42.4772702],[-71.46909239999999, 42.4772418],[-71.4690653, 42.4772228],[-71.46901990000001, 42.4771566],[-71.46899519999999, 42.4771084],[-71.4689836, 42.4770667],[-71.468985, 42.4770153],[-71.4690489, 42.4769393],[-71.4690982, 42.4768997],[-71.4691461, 42.4768644],[-71.4691591, 42.4768034],[-71.4691591, 42.4767457],[-71.46914169999999, 42.4766044],[-71.4691498, 42.4764638],[-71.4679052, 42.4762818],[-71.4675512, 42.4755063],[-71.4665534, 42.4748732],[-71.46616710000001, 42.4738999],[-71.4668645, 42.4732352]]}}, +{"type":"Feature","properties":{"tunnel":"culvert","waterway":"stream","source:datetime":"2015-01-17T06:31:57Z"},"geometry": {"type":"LineString","coordinates": [[-71.4835157, 42.4897066],[-71.483419, 42.4897611]]}}, +{"type":"Feature","properties":{"waterway":"stream","source:datetime":"2015-01-17T06:31:57Z"},"geometry": {"type":"LineString","coordinates": [[-71.483419, 42.4897611],[-71.4828863, 42.4900743],[-71.48249850000001, 42.4905869],[-71.48208870000001, 42.4910617],[-71.4814301, 42.4911913],[-71.4808227, 42.4912614],[-71.4799055, 42.4911771]]}}, +{"type":"Feature","properties":{"bridge":"yes","source:datetime":"2015-01-17T06:31:57Z","highway":"track"},"geometry": {"type":"LineString","coordinates": [[-71.4806268, 42.4913077],[-71.48064340000001, 42.491185]]}}, +{"type":"Feature","properties":{"waterway":"stream","source:datetime":"2015-01-17T06:31:57Z"},"geometry": {"type":"LineString","coordinates": [[-71.4851768, 42.4898368],[-71.48486219999999, 42.4895239],[-71.48465729999999, 42.4893674],[-71.48394740000001, 42.4894915],[-71.48362539999999, 42.4896426],[-71.4835157, 42.4897066]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","massgis:PRIM_PURP":"W","massgis:PUB_ACCESS":"N","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Water Department","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002WD","area":"yes","massgis:POLY_ID":"6125","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Mass. Avenue Area","massgis:OS_ID":"2-6125","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"reservoir_watershed","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","massgis:COMMENTS":"WATER SUPPLY & HEADQUARTERS","access":"private","source:datetime":"2014-12-05T01:49:07Z","massgis:SITE_NAME":"MASS. AVENUE AREA","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4843065, 42.4776245],[-71.48498600000001, 42.4808628],[-71.4841527, 42.4808336],[-71.4834037, 42.4807341],[-71.4843065, 42.4776245]]]}}, +{"type":"Feature","properties":{"massgis:ASSESS_ACR":"0.00000000","massgis:PRIM_PURP":"W","massgis:PUB_ACCESS":"N","massgis:MANAGR_TYP":"M","massgis:MANAGER":"Town of Acton Water Department","source":"MassGIS OpenSpace (http://www.mass.gov/mgis/osp.htm)","massgis:MANAGR_ABR":"M002WD","area":"yes","massgis:POLY_ID":"6125","massgis:SOURCE_MAP":"1","massgis:FEESYM":"M","name":"Mass. Avenue Area","massgis:OS_ID":"2-6125","massgis:TOWN_ID":"2","owner":"Town Of Acton","massgis:FEE_OWNER":"Town of Acton","landuse":"reservoir_watershed","massgis:ARTICLE97":"9","massgis:OWNER_TYPE":"M","massgis:OS_DEED_BO":"0","protected":"perpetuity","massgis:DCAM_ID":"0","massgis:EOEAINVOLV":"0","massgis:OS_DEED_PA":"0","massgis:OWNER_ABRV":"M002","massgis:COMMENTS":"WATER SUPPLY & HEADQUARTERS","access":"private","source:datetime":"2014-11-18T12:26:36Z","massgis:SITE_NAME":"MASS. AVENUE AREA","massgis:FY_FUNDING":"0","massgis:ATT_DATE":"1993/12/08","massgis:LEV_PROT":"P","ownership":"municipal","massgis:DEED_ACRES":"0.00000000"},"geometry": {"type":"Polygon","coordinates": [[[-71.4843065, 42.4776245],[-71.48498600000001, 42.4808628],[-71.4841527, 42.4808336],[-71.4834037, 42.4807341],[-71.4843065, 42.4776245]]]}}, +{"type":"Feature","properties":{"name":"Acton Water District","landuse":"industrial","source:datetime":"2014-11-18T02:56:27Z"},"geometry": {"type":"Point","coordinates": [-71.4847161, 42.4796142]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2014-11-18T02:39:46Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47954319999999, 42.4781443],[-71.4795933, 42.4781057],[-71.47966820000001, 42.4780435],[-71.4797794, 42.4779807],[-71.4799423, 42.477926],[-71.4799682, 42.4778443],[-71.47999470000001, 42.4776744],[-71.4799982, 42.477508],[-71.4799649, 42.4773694]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2014-11-18T02:39:46Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4791843, 42.4777596],[-71.4792494, 42.4776],[-71.47937159999999, 42.4775235],[-71.4796338, 42.4774404],[-71.4798602, 42.477385],[-71.4799649, 42.4773694]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2014-11-18T02:39:45Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47886339999999, 42.4775854],[-71.4789937, 42.4775968],[-71.4791566, 42.4775951],[-71.4792494, 42.4776]]}}, +{"type":"Feature","properties":{"leisure":"nature_reserve","name":"Wright Hill","landuse":"conservation","source:datetime":"2014-11-18T02:34:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4765364, 42.4785929],[-71.4773773, 42.4787729],[-71.47783579999999, 42.47832],[-71.4782469, 42.4776606],[-71.47830810000001, 42.4775643],[-71.4790052, 42.4777695],[-71.47874969999999, 42.4785556],[-71.47945420000001, 42.4788247],[-71.4779535, 42.4809779],[-71.4772681, 42.4808578],[-71.4765783, 42.4803618],[-71.47623299999999, 42.4800687],[-71.47608320000001, 42.4799123],[-71.47634050000001, 42.4798015],[-71.4765364, 42.4785929]]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2014-11-18T02:34:25Z"},"geometry": {"type":"LineString","coordinates": [[-71.47784249999999, 42.4781909],[-71.4780871, 42.4778049]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2014-11-18T02:34:25Z"},"geometry": {"type":"LineString","coordinates": [[-71.47649920000001, 42.4785889],[-71.4762504, 42.4785774]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-11-16T05:11:15Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4742376, 42.4790659],[-71.4743215, 42.4793361],[-71.4743227, 42.4793442],[-71.47432139999999, 42.4793523],[-71.4743177, 42.47936],[-71.4743117, 42.4793669],[-71.47430369999999, 42.4793725],[-71.4742942, 42.4793768],[-71.4742837, 42.4793793],[-71.4741519, 42.4793991],[-71.4741411, 42.4793998],[-71.47413040000001, 42.4793988],[-71.4741203, 42.479396],[-71.4741112, 42.4793917],[-71.4741037, 42.4793859],[-71.474098, 42.4793792],[-71.4740946, 42.4793716],[-71.47401240000001, 42.4790951]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Elm Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T05:11:15Z","highway":"residential","lanes":"2","massgis:way_id":"186306"},"geometry": {"type":"LineString","coordinates": [[-71.4765956, 42.482987],[-71.476257, 42.483053],[-71.4758824, 42.4831557],[-71.4753772, 42.4832777],[-71.47525589999999, 42.4833085],[-71.47517929999999, 42.4833232],[-71.4751085, 42.4833346],[-71.47505030000001, 42.4833416],[-71.4749826, 42.4833429],[-71.4749235, 42.4833411],[-71.4748831, 42.4833378],[-71.4748426, 42.483333],[-71.474789, 42.4833229],[-71.4747358, 42.48331],[-71.47461989999999, 42.4832794],[-71.474401, 42.483211],[-71.474085, 42.4831],[-71.47381799999999, 42.483011],[-71.473404, 42.482883],[-71.473175, 42.482807],[-71.472826, 42.482692],[-71.47226499999999, 42.482493],[-71.471633, 42.482269],[-71.47148900000001, 42.482218],[-71.47104899999999, 42.482067],[-71.470641, 42.481927],[-71.470454, 42.481856],[-71.470106, 42.481732],[-71.469752, 42.481605],[-71.46914099999999, 42.481384],[-71.468645, 42.481217],[-71.46828499999999, 42.481082]]}}, +{"type":"Feature","properties":{"access":"destination","amenity":"parking","source:datetime":"2014-11-16T05:11:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744433, 42.4793813],[-71.474008, 42.4794476],[-71.4739324, 42.4791775],[-71.4743678, 42.4791112],[-71.4744433, 42.4793813]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"basketball","source:datetime":"2014-11-16T05:11:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744496, 42.4804619],[-71.4740931, 42.4804944],[-71.474071, 42.4803628],[-71.4744276, 42.4803303],[-71.4744496, 42.4804619]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"baseball","source:datetime":"2014-11-16T05:11:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4727013, 42.4794659],[-71.47262069999999, 42.479422],[-71.4719097, 42.4795208],[-71.4719254, 42.4796053],[-71.4719338, 42.4796365],[-71.47194519999999, 42.4796671],[-71.47195929999999, 42.4796971],[-71.47197629999999, 42.4797263],[-71.471996, 42.4797546],[-71.4720183, 42.4797818],[-71.47204309999999, 42.4798078],[-71.4720702, 42.4798324],[-71.4720997, 42.4798556],[-71.47213120000001, 42.4798772],[-71.47216469999999, 42.4798972],[-71.4722, 42.4799154],[-71.47223700000001, 42.4799318],[-71.47227530000001, 42.4799462],[-71.47231499999999, 42.4799586],[-71.47235569999999, 42.479969],[-71.4723973, 42.4799773],[-71.4724396, 42.4799834],[-71.4724823, 42.4799873],[-71.4725253, 42.4799891],[-71.4725684, 42.4799886],[-71.4726113, 42.4799859],[-71.4726539, 42.4799811],[-71.4727449, 42.4799686],[-71.4728221, 42.4799521],[-71.4727013, 42.4794659]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"baseball","source:datetime":"2014-11-16T05:11:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47302190000001, 42.4800904],[-71.4729439, 42.4800495],[-71.4723027, 42.4801441],[-71.47230930000001, 42.4801755],[-71.4723188, 42.4802065],[-71.4723312, 42.4802369],[-71.4723465, 42.4802666],[-71.4723645, 42.4802955],[-71.47238520000001, 42.4803234],[-71.47240840000001, 42.4803501],[-71.4724341, 42.4803756],[-71.4724622, 42.4803997],[-71.47249239999999, 42.4804223],[-71.47252469999999, 42.4804434],[-71.4725589, 42.4804627],[-71.4725948, 42.4804802],[-71.4726323, 42.4804958],[-71.47267119999999, 42.4805095],[-71.4727113, 42.4805212],[-71.4727524, 42.4805308],[-71.4727942, 42.4805382],[-71.4728367, 42.4805435],[-71.4728796, 42.4805466],[-71.4729227, 42.4805475],[-71.4729657, 42.4805462],[-71.47300850000001, 42.4805427],[-71.4730509, 42.480537],[-71.4730926, 42.4805292],[-71.47302190000001, 42.4800904]]]}}, +{"type":"Feature","properties":{"leisure":"playground","source:datetime":"2014-11-16T05:11:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47381110000001, 42.4804612],[-71.4736228, 42.4804593],[-71.4736233, 42.4804888],[-71.4734109, 42.4804937],[-71.4733971, 42.4804608],[-71.47329860000001, 42.4804438],[-71.4731206, 42.4804529],[-71.4730955, 42.4802599],[-71.4732576, 42.4802562],[-71.4732611, 42.4803224],[-71.4733695, 42.480327],[-71.4733767, 42.4803842],[-71.4738033, 42.4803575],[-71.47381110000001, 42.4804612]]]}}, +{"type":"Feature","properties":{"waterway":"stream","source:datetime":"2014-11-16T04:49:05Z"},"geometry": {"type":"LineString","coordinates": [[-71.48494599999999, 42.4777275],[-71.4849366, 42.4777874],[-71.48494599999999, 42.4778166],[-71.48503100000001, 42.477818],[-71.48506500000001, 42.4778013],[-71.4851027, 42.4777916],[-71.4851424, 42.4778013],[-71.4851688, 42.4778264],[-71.4851764, 42.477857],[-71.4851764, 42.4778863],[-71.4851669, 42.4779447],[-71.4851556, 42.4779754],[-71.4851008, 42.4780603],[-71.48501589999999, 42.4781703],[-71.4849328, 42.4783166],[-71.4848875, 42.4784419],[-71.4848667, 42.4785352],[-71.484827, 42.4785895],[-71.4847326, 42.4786856],[-71.48469299999999, 42.4787385],[-71.4845948, 42.4788722],[-71.4845646, 42.4789293],[-71.4845174, 42.478985],[-71.4844664, 42.4790685],[-71.48442489999999, 42.4791215],[-71.48435120000001, 42.479237],[-71.4842738, 42.4792969],[-71.4842342, 42.4793554],[-71.4841926, 42.4794069],[-71.48413600000001, 42.4794849],[-71.4841114, 42.4795086],[-71.48404909999999, 42.4796166],[-71.48397439999999, 42.4796796],[-71.4839477, 42.479717],[-71.4839317, 42.4797564],[-71.48392629999999, 42.4797997],[-71.48392629999999, 42.4798903],[-71.48390759999999, 42.4799297],[-71.4838329, 42.4799986],[-71.4838008, 42.480038],[-71.4837848, 42.4800813],[-71.48377410000001, 42.4801266],[-71.48375540000001, 42.480166],[-71.483726, 42.4802015],[-71.4836913, 42.4802369],[-71.4836486, 42.4802704],[-71.4836219, 42.4803078],[-71.48358450000001, 42.4803393],[-71.4834991, 42.4803866],[-71.4834377, 42.4804595],[-71.4834003, 42.4804929],[-71.48327209999999, 42.4805855],[-71.48318399999999, 42.4806367],[-71.4831439, 42.4806682],[-71.48301309999999, 42.4807529],[-71.4829757, 42.4807824],[-71.48295570000001, 42.4808051],[-71.4829824, 42.4808661],[-71.48305310000001, 42.4809616],[-71.48307560000001, 42.4810157]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"deficient","source":"massgis_import_v0.1_20071009100433","name":"Central Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-17T05:18:39Z","highway":"tertiary","lanes":"2","massgis:way_id":"151804"},"geometry": {"type":"LineString","coordinates": [[-71.4776088, 42.4821115],[-71.4774862, 42.4823139],[-71.4774116, 42.4824356],[-71.4773763, 42.4824917],[-71.47734869999999, 42.4825336],[-71.47729889999999, 42.4826092],[-71.47718690000001, 42.4827943],[-71.4771556, 42.4828524],[-71.477146, 42.4828768],[-71.4771289, 42.482936],[-71.4771219, 42.4830016],[-71.4771202, 42.4830565],[-71.4771307, 42.4831583],[-71.4771465, 42.4832441],[-71.4771762, 42.4833575],[-71.4772023, 42.4834519],[-71.477322, 42.483857],[-71.477338, 42.483912],[-71.47742, 42.48421],[-71.4776372, 42.4849309],[-71.4777129, 42.4852024],[-71.4777337, 42.4853129],[-71.4777472, 42.4854235],[-71.477768, 42.485699],[-71.477812, 42.486439],[-71.477825, 42.486742],[-71.477847, 42.486941],[-71.47792200000001, 42.487187],[-71.478002, 42.487412],[-71.478015, 42.487449],[-71.478116, 42.487734],[-71.478173, 42.487934],[-71.478191, 42.488089],[-71.478195, 42.488301],[-71.47816899999999, 42.488465],[-71.478072, 42.488914],[-71.47791700000001, 42.489619],[-71.477802, 42.490022],[-71.477678, 42.490418],[-71.477577, 42.490636],[-71.47752, 42.49075],[-71.477518, 42.490754]]}}, +{"type":"Feature","properties":{"width":"12.2","condition":"deficient","bridge":"yes","layer":"1","source":"massgis_import_v0.1_20071009100433","name":"Central Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T04:49:03Z","highway":"tertiary","lanes":"2","massgis:way_id":"151804"},"geometry": {"type":"LineString","coordinates": [[-71.477675, 42.4819891],[-71.4776545, 42.4820315],[-71.4776088, 42.4821115]]}}, +{"type":"Feature","properties":{"width":"10.4","condition":"fair","source":"massgis_import_v0.1_20071009100433","name":"Arlington Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2015-01-23T20:15:41Z","highway":"tertiary","lanes":"2","massgis:way_id":"154676"},"geometry": {"type":"LineString","coordinates": [[-71.4705584, 42.4790489],[-71.469853, 42.479656],[-71.469651, 42.47984],[-71.469115, 42.480329],[-71.468743, 42.480668],[-71.46828499999999, 42.481082],[-71.467674, 42.481635],[-71.467646, 42.48166],[-71.467521, 42.481793],[-71.467133, 42.482204],[-71.46681599999999, 42.482561],[-71.465582, 42.48393],[-71.465344, 42.48421],[-71.46528499999999, 42.484272],[-71.464991, 42.484582],[-71.46454, 42.485001],[-71.464169, 42.485311],[-71.464157, 42.485321],[-71.463959, 42.485497],[-71.463813, 42.485668],[-71.463604, 42.485882],[-71.463578, 42.485912],[-71.463089, 42.486478],[-71.462946, 42.486638],[-71.46232910000001, 42.4873407],[-71.4621664, 42.487519],[-71.461617, 42.488121],[-71.461457, 42.488318],[-71.461224, 42.488595],[-71.461102, 42.488758],[-71.460938, 42.488969],[-71.460736, 42.489232],[-71.460632, 42.489364],[-71.46047799999999, 42.489614],[-71.46036599999999, 42.489837],[-71.460302, 42.489978],[-71.46011900000001, 42.490383],[-71.460047, 42.490562],[-71.459926, 42.490837]]}}, +{"type":"Feature","properties":{"width":"10.4","condition":"fair","bridge":"yes","layer":"1","source":"massgis_import_v0.1_20071009100433","name":"Arlington Street","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2014-11-16T04:49:02Z","highway":"tertiary","lanes":"2","massgis:way_id":"154676"},"geometry": {"type":"LineString","coordinates": [[-71.4706832, 42.4789342],[-71.4705584, 42.4790489]]}}, +{"type":"Feature","properties":{"leisure":"playground","source:datetime":"2014-11-16T04:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47286579999999, 42.4819597],[-71.4729389, 42.4818515],[-71.4723805, 42.4816465],[-71.47230740000001, 42.4817547],[-71.47258410000001, 42.4818563],[-71.4727858, 42.4819304],[-71.47286579999999, 42.4819597]]]}}, +{"type":"Feature","properties":{"leisure":"playground","source:datetime":"2014-11-16T04:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715454, 42.481097],[-71.471615, 42.4809782],[-71.47129990000001, 42.4808778],[-71.4712303, 42.4809966],[-71.4715454, 42.481097]]]}}, +{"type":"Feature","properties":{"natural":"water","source:datetime":"2014-11-16T04:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4700354, 42.4770249],[-71.47002809999999, 42.4770774],[-71.47000490000001, 42.4771191],[-71.4699222, 42.4772464],[-71.4698831, 42.4772807],[-71.46978729999999, 42.4772946],[-71.4696988, 42.4772978],[-71.46952330000001, 42.4773181],[-71.4694377, 42.4773352],[-71.4693826, 42.4773395],[-71.469297, 42.4773385],[-71.4692708, 42.477331],[-71.4692534, 42.4773128],[-71.4692259, 42.4772507],[-71.4691998, 42.4771266],[-71.4691983, 42.4769565],[-71.4692041, 42.4769351],[-71.46923889999999, 42.476873],[-71.4692766, 42.4768345],[-71.4693303, 42.4768067],[-71.46936220000001, 42.476796],[-71.4694623, 42.4767831],[-71.46985840000001, 42.4767853],[-71.4698889, 42.4767895],[-71.4699353, 42.4768259],[-71.470018, 42.4769318],[-71.4700368, 42.4769832],[-71.4700354, 42.4770249]]]}}, +{"type":"Feature","properties":{"natural":"water","source:datetime":"2014-11-16T04:49:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47021959999999, 42.4782908],[-71.47023419999999, 42.4783143],[-71.47023849999999, 42.4783624],[-71.47021239999999, 42.4784299],[-71.4702051, 42.4784791],[-71.47022250000001, 42.4785497],[-71.47022250000001, 42.4785978],[-71.47021239999999, 42.4786685],[-71.4701587, 42.4786866],[-71.46995130000001, 42.4786866],[-71.4698947, 42.4786653],[-71.469841, 42.4786524],[-71.4697931, 42.4786321],[-71.4697743, 42.478615],[-71.469767, 42.4785946],[-71.46976119999999, 42.4785336],[-71.469767, 42.4785122],[-71.46987, 42.4783164],[-71.4699179, 42.4780896],[-71.4699324, 42.4780489],[-71.46995560000001, 42.4780083],[-71.46997880000001, 42.4779944],[-71.4700107, 42.4779901],[-71.47007170000001, 42.4779954],[-71.4701457, 42.4780447],[-71.4701732, 42.4780907],[-71.4702051, 42.4781827],[-71.47021959999999, 42.4782908]]]}}, +{"type":"Feature","properties":{"natural":"wetland","source:datetime":"2014-11-16T04:49:01Z"},"geometry": {"type":"LineString","coordinates": [[-71.485091, 42.4811054],[-71.4850172, 42.4810841],[-71.48497399999999, 42.4810788],[-71.484938, 42.4810788],[-71.48475070000001, 42.4810549],[-71.48464989999999, 42.4810244],[-71.4845887, 42.4809979],[-71.4844825, 42.4809979],[-71.4844069, 42.4809766],[-71.48437269999999, 42.48097],[-71.4843295, 42.4809647],[-71.48429350000001, 42.4809647],[-71.48425570000001, 42.480966],[-71.4842197, 42.4809753],[-71.48414409999999, 42.4809832],[-71.4840397, 42.4810018],[-71.4838903, 42.4810032],[-71.4838633, 42.4809859],[-71.4838507, 42.4809607],[-71.48383990000001, 42.4809089],[-71.4838381, 42.4808571],[-71.4838525, 42.4807775],[-71.4838903, 42.4806739],[-71.4839029, 42.4806487],[-71.4839281, 42.4806288],[-71.4840073, 42.4806155],[-71.48416570000001, 42.4805823],[-71.4842503, 42.4805717],[-71.4843331, 42.4805385],[-71.48437269999999, 42.4805266],[-71.48447710000001, 42.4804814],[-71.4845761, 42.4804336],[-71.48468769999999, 42.4804031],[-71.4847741, 42.4803872],[-71.4848137, 42.4803845],[-71.4848551, 42.4803779],[-71.48488930000001, 42.4803659],[-71.4849506, 42.4803327],[-71.4849902, 42.4802969],[-71.4849938, 42.4802717],[-71.4849938, 42.4800858],[-71.4849902, 42.4800314],[-71.4849938, 42.4800035],[-71.4850028, 42.4799783],[-71.48503700000001, 42.4799199],[-71.4850802, 42.4798747],[-71.4851342, 42.4798349],[-71.48516840000001, 42.4798177]]}}, +{"type":"Feature","properties":{"massgis:school_id":"00020025","website":"http://gates.abschools.org/","addr:street":"Spruce Street","addr:city":"Acton","phone":"978-266-2570","grades":"K,1,2,3,4,5,6","addr:housenumber":"75","name":"Gates Elementary School","amenity":"school","source:datetime":"2015-01-23T23:29:20Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714305, 42.4804036],[-71.4713026, 42.4802529],[-71.47131, 42.4802216],[-71.4714089, 42.4801059],[-71.4713144, 42.4800635],[-71.4711289, 42.4798781],[-71.4710307, 42.4799815],[-71.47097290000001, 42.4799775],[-71.4706979, 42.4798281],[-71.4706745, 42.4797777],[-71.4707152, 42.4796827],[-71.4706805, 42.4796155],[-71.4705446, 42.4795428],[-71.47054489999999, 42.4795251],[-71.4706769, 42.4793983],[-71.4707866, 42.4792676],[-71.47078380000001, 42.4791976],[-71.4707117, 42.4790114],[-71.4707355, 42.4789896],[-71.47082279999999, 42.4792172],[-71.4712627, 42.4791968],[-71.47094730000001, 42.4787983],[-71.4710669, 42.4786833],[-71.4714706, 42.4791882],[-71.4722687, 42.479156],[-71.47361119999999, 42.4791005],[-71.4744749, 42.4788481],[-71.4747405, 42.4792972],[-71.4749097, 42.4795875],[-71.4752486, 42.480083],[-71.47553600000001, 42.4804793],[-71.4759351, 42.4809874],[-71.4755802, 42.481489],[-71.47452250000001, 42.4808784],[-71.4723122, 42.4806426],[-71.4714305, 42.4804036]]]}}, +{"type":"Feature","properties":{"leisure":"park","gnis:feature_id":"1972537","name":"Elm Street Playground","source:datetime":"2014-11-12T02:24:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47366220000001, 42.4829166],[-71.47440539999999, 42.4831436],[-71.474951, 42.4823734],[-71.47456099999999, 42.4822406],[-71.4737674, 42.4819473],[-71.4730318, 42.4817504],[-71.47271739999999, 42.4822216],[-71.4725275, 42.4825369],[-71.47366220000001, 42.4829166]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"basketball","source:datetime":"2014-11-12T02:24:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47258410000001, 42.4818563],[-71.4722407, 42.4823649],[-71.4724425, 42.482439],[-71.4727858, 42.4819304],[-71.47258410000001, 42.4818563]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"tennis","source:datetime":"2014-11-12T02:24:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4740777, 42.4826021],[-71.473906, 42.4829242],[-71.4743126, 42.4830421],[-71.4744843, 42.48272],[-71.4740777, 42.4826021]]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"baseball","source:datetime":"2014-11-12T02:24:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47361600000001, 42.482814],[-71.47371099999999, 42.482779],[-71.4740765, 42.4821068],[-71.47383910000001, 42.4820298],[-71.47362080000001, 42.4819667],[-71.4733976, 42.4819702],[-71.47316499999999, 42.4820088],[-71.4730036, 42.4820683],[-71.4728185, 42.4821733],[-71.4726903, 42.4823274],[-71.47259529999999, 42.4825129],[-71.47361600000001, 42.482814]]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-11-08T02:08:48Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45141750000001, 42.4758186],[-71.45158549999999, 42.4760964]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2014-11-08T02:10:08Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4504209, 42.4758948],[-71.4506306, 42.4758136],[-71.45098710000001, 42.4757954],[-71.4512163, 42.4758948],[-71.4512735, 42.475978],[-71.45138009999999, 42.4761304]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-07-20T02:42:32Z","highway":"service","surface":"paved"},"geometry": {"type":"LineString","coordinates": [[-71.4791214, 42.4991871],[-71.4790852, 42.4991297],[-71.4789656, 42.4990846],[-71.4788595, 42.4991015]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-07-20T02:42:32Z","highway":"service","surface":"paved"},"geometry": {"type":"LineString","coordinates": [[-71.47703009999999, 42.4993455],[-71.4781426, 42.4982286]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-07-20T02:42:32Z","highway":"service","surface":"paved"},"geometry": {"type":"LineString","coordinates": [[-71.47685989999999, 42.4992386],[-71.4779925, 42.4981445]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-07-20T02:42:32Z","highway":"service","surface":"paved"},"geometry": {"type":"LineString","coordinates": [[-71.4786233, 42.4986725],[-71.4775741, 42.4996709]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-07-20T02:42:31Z","highway":"service","surface":"paved"},"geometry": {"type":"LineString","coordinates": [[-71.4774151, 42.4995786],[-71.4781924, 42.4988057]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-07-20T02:42:31Z","highway":"service","surface":"paved"},"geometry": {"type":"LineString","coordinates": [[-71.4764705, 42.4984984],[-71.47641520000001, 42.498574],[-71.4763011, 42.4985576],[-71.4761815, 42.4986273],[-71.4761037, 42.4992753],[-71.4761871, 42.4992876],[-71.47634290000001, 42.499263],[-71.47664880000001, 42.4995337],[-71.477096, 42.4995961],[-71.4772332, 42.4994729],[-71.4783542, 42.4983649]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2014-07-20T02:42:31Z","highway":"service","surface":"paved"},"geometry": {"type":"LineString","coordinates": [[-71.477096, 42.4995961],[-71.4767378, 42.499386],[-71.4766293, 42.4992937],[-71.4765737, 42.4991933],[-71.476557, 42.4990784],[-71.4766154, 42.4987954],[-71.4766182, 42.4987442],[-71.47656809999999, 42.4986785],[-71.47641520000001, 42.498574],[-71.47634290000001, 42.499263]]}}, +{"type":"Feature","properties":{"leisure":"pitch","name":"Lower Fields","sport":"american_football","source:datetime":"2014-03-31T02:11:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4545809, 42.4799622],[-71.45531630000001, 42.4792271],[-71.45472119999999, 42.4789032],[-71.4539858, 42.4796384],[-71.4545809, 42.4799622]]]}}, +{"type":"Feature","properties":{"oneway":"yes","source:datetime":"2014-03-31T02:11:12Z","highway":"road"},"geometry": {"type":"LineString","coordinates": [[-71.4559288, 42.4775626],[-71.4559811, 42.4774619],[-71.4560807, 42.4773675],[-71.45623519999999, 42.477231],[-71.4563531, 42.4771321],[-71.456543, 42.477165]]}}, +{"type":"Feature","properties":{"oneway":"yes","source:datetime":"2014-03-31T02:11:11Z","highway":"road"},"geometry": {"type":"LineString","coordinates": [[-71.4564914, 42.4769933],[-71.4559825, 42.477379],[-71.4556867, 42.4774653],[-71.4555974, 42.4775358],[-71.4556128, 42.4776085],[-71.4558069, 42.4777607],[-71.4558993, 42.477763],[-71.4559702, 42.4777017],[-71.4559702, 42.4776176],[-71.4559288, 42.4775626],[-71.4559086, 42.4775358],[-71.4557976, 42.4774903],[-71.4556867, 42.4774653]]}}, +{"type":"Feature","properties":{"waterway":"stream","name":"Grassy Pond Brook","source:datetime":"2014-03-23T15:45:26Z"},"geometry": {"type":"LineString","coordinates": [[-71.4621064, 42.4893676],[-71.46243080000001, 42.4899443],[-71.46291359999999, 42.490419],[-71.4629994, 42.4917243],[-71.46040309999999, 42.4941926],[-71.4601241, 42.4960832],[-71.4601134, 42.4969692],[-71.4598881, 42.4973964],[-71.4592873, 42.497515],[-71.4586328, 42.4978236],[-71.4579033, 42.4982744],[-71.4570128, 42.4984722],[-71.4560364, 42.4986858],[-71.4551352, 42.4990892]]}}, +{"type":"Feature","properties":{"waterway":"stream","name":"Fort Pond Brook","source:datetime":"2015-01-17T06:32:06Z"},"geometry": {"type":"LineString","coordinates": [[-71.47436399999999, 42.4992415],[-71.47482530000001, 42.4977543],[-71.474718, 42.4972085],[-71.4750828, 42.4970978],[-71.4755763, 42.4954919],[-71.47790449999999, 42.4948037],[-71.4791168, 42.4945347],[-71.4802648, 42.4932373],[-71.4805223, 42.4927785],[-71.4802863, 42.4921931],[-71.4799055, 42.4911771],[-71.47969620000001, 42.4906187],[-71.4790847, 42.4898197],[-71.47905249999999, 42.4887754],[-71.4796211, 42.4884114],[-71.4795889, 42.4877785],[-71.47931, 42.4869873],[-71.47987860000001, 42.4861329],[-71.47950109999999, 42.4852308],[-71.4796109, 42.4851566]]}}, +{"type":"Feature","properties":{"waterway":"stream","name":"Guggins Brook","source:datetime":"2014-11-16T04:49:13Z"},"geometry": {"type":"LineString","coordinates": [[-71.4848951, 42.4808451],[-71.4844829, 42.4807853],[-71.4840628, 42.4808079],[-71.4837235, 42.4807954],[-71.48348059999999, 42.4808361],[-71.4832907, 42.480885],[-71.48321350000001, 42.4809126],[-71.4831464, 42.4809551],[-71.48307560000001, 42.4810157],[-71.48299849999999, 42.4810591],[-71.4829897, 42.4811027],[-71.48299160000001, 42.481141],[-71.48301050000001, 42.4812176],[-71.48304450000001, 42.4813652],[-71.4830398, 42.4814439],[-71.4830001, 42.4815128],[-71.48295760000001, 42.481579],[-71.48288530000001, 42.4816392],[-71.48281129999999, 42.4816785],[-71.482715, 42.4817126],[-71.4825318, 42.4817516],[-71.4823308, 42.4817649],[-71.4822477, 42.4817711],[-71.4821646, 42.4817899],[-71.4820919, 42.4818324],[-71.48202860000001, 42.481877],[-71.4819597, 42.4819069],[-71.48184639999999, 42.4819278],[-71.4816106, 42.4819602],[-71.4814433, 42.4819939],[-71.4812677, 42.4820155],[-71.4810074, 42.4820207],[-71.4809326, 42.482013],[-71.48088850000001, 42.4820117],[-71.4808498, 42.4820024],[-71.48078599999999, 42.481995],[-71.4807048, 42.4819867],[-71.48063399999999, 42.481993],[-71.4805509, 42.4819992],[-71.48046600000001, 42.4819937],[-71.4803886, 42.4819902],[-71.48031210000001, 42.4820027],[-71.48025920000001, 42.4820208],[-71.4801875, 42.4820501],[-71.4801308, 42.4820494],[-71.4800695, 42.4820508],[-71.4800053, 42.4820751],[-71.4799675, 42.482096],[-71.4798923, 42.4821566],[-71.47987310000001, 42.4821866],[-71.4798457, 42.4822009],[-71.47979170000001, 42.4822048],[-71.47975959999999, 42.4821999],[-71.4797129, 42.4822004],[-71.4796802, 42.4821955],[-71.4796308, 42.4821822],[-71.4795747, 42.4821797],[-71.47951860000001, 42.4821856],[-71.47947120000001, 42.4821915],[-71.4794272, 42.4822058]]}}, +{"type":"Feature","properties":{"waterway":"stream","name":"Conant Brook","source:datetime":"2014-06-12T00:01:59Z"},"geometry": {"type":"LineString","coordinates": [[-71.4505441, 42.4834635],[-71.4516196, 42.4831295],[-71.4527784, 42.4829554],[-71.4525852, 42.4823066],[-71.4525423, 42.4818477],[-71.45286419999999, 42.4811989],[-71.4528856, 42.4808033],[-71.4536581, 42.4805817],[-71.4543324, 42.4804638],[-71.455192, 42.4805654],[-71.45537349999999, 42.4805869],[-71.4535723, 42.474647]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-10-11T19:07:03Z","highway":"path"},"geometry": {"type":"LineString","coordinates": [[-71.4803472, 42.4951337],[-71.4804302, 42.495443],[-71.48044950000001, 42.4956979],[-71.4804879, 42.4957758],[-71.4806799, 42.4958608],[-71.48085279999999, 42.4959741],[-71.4809008, 42.4961015],[-71.4809104, 42.4961935],[-71.4807855, 42.4964343],[-71.48071830000001, 42.496583],[-71.4806895, 42.4967954],[-71.48071830000001, 42.4968945],[-71.48094879999999, 42.4970219],[-71.4812369, 42.4970786],[-71.4814769, 42.497114],[-71.4816402, 42.4972485],[-71.481765, 42.4973759],[-71.4818441, 42.4974749]]}}, +{"type":"Feature","properties":{"parking":"surface","amenity":"parking","source:datetime":"2013-10-11T02:01:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4529029, 42.4745363],[-71.4528493, 42.4746629],[-71.4526079, 42.4746233],[-71.4526025, 42.4745936],[-71.4526374, 42.4744809],[-71.4529029, 42.4745363]]]}}, +{"type":"Feature","properties":{"parking":"surface","amenity":"parking","source:datetime":"2013-10-11T02:01:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4528681, 42.4739032],[-71.4529646, 42.4738122],[-71.4527769, 42.4737172],[-71.4524979, 42.4736876],[-71.452433, 42.4738806],[-71.45255160000001, 42.4739388],[-71.4528681, 42.4739032]]]}}, +{"type":"Feature","properties":{"service":"driveway","source":"massgis_import_v0.1_20071009100433","source:datetime":"2013-10-11T02:01:44Z","highway":"service","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.4525903, 42.4743816],[-71.45247999999999, 42.474455],[-71.4524334, 42.4745307],[-71.452412, 42.474592],[-71.452382, 42.474739]]}}, +{"type":"Feature","properties":{"oneway":"no","service":"parking_aisle","source:datetime":"2013-10-11T02:01:44Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4524334, 42.4745307],[-71.45286, 42.4745679]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-10-10T18:53:21Z","highway":"path"},"geometry": {"type":"LineString","coordinates": [[-71.480469, 42.495041],[-71.4803472, 42.4951337],[-71.48021110000001, 42.4952926],[-71.48014310000001, 42.4953302],[-71.4800977, 42.495535],[-71.480024, 42.4957734],[-71.4799673, 42.4959071],[-71.4796781, 42.4960618],[-71.4794966, 42.4961747],[-71.47944560000001, 42.4963503],[-71.479508, 42.4964423],[-71.4797291, 42.4965677],[-71.47975750000001, 42.4966471],[-71.4795817, 42.4971447],[-71.47959299999999, 42.497291],[-71.4795987, 42.4973788],[-71.47936060000001, 42.4976129],[-71.4793944, 42.497772],[-71.47948529999999, 42.497801],[-71.4796214, 42.4978178],[-71.4796838, 42.4979933],[-71.4797461, 42.498102],[-71.4797121, 42.4982902],[-71.4797915, 42.4984072],[-71.47987089999999, 42.4985327],[-71.47990489999999, 42.4987333]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-10-10T18:53:21Z","highway":"path"},"geometry": {"type":"LineString","coordinates": [[-71.4787553, 42.4989279],[-71.4789013, 42.4988922],[-71.47906, 42.4988713],[-71.4793925, 42.4988481],[-71.4796384, 42.4987835],[-71.47990489999999, 42.4987333],[-71.4800693, 42.498633],[-71.4803359, 42.4985201],[-71.48050600000001, 42.4984365],[-71.48065339999999, 42.4983153],[-71.4807838, 42.4982651],[-71.48099360000001, 42.4982818]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-10-10T18:53:20Z","highway":"path"},"geometry": {"type":"LineString","coordinates": [[-71.4793925, 42.4988481],[-71.47939719999999, 42.4988886],[-71.4794059, 42.4989633],[-71.4793662, 42.4990929],[-71.47939460000001, 42.4992225],[-71.4794796, 42.4993019],[-71.47953630000001, 42.4993604],[-71.4797121, 42.4993981],[-71.47979719999999, 42.4994148],[-71.4799503, 42.4993981],[-71.4800183, 42.4994524],[-71.4800183, 42.4995235],[-71.4799106, 42.4996029],[-71.4814586, 42.4996238],[-71.48148689999999, 42.4995402],[-71.48143589999999, 42.4993771],[-71.4813225, 42.4991848],[-71.4812091, 42.498934],[-71.48107299999999, 42.4988295],[-71.4811467, 42.4986121],[-71.4810957, 42.4984741],[-71.4810276, 42.4983362],[-71.48099360000001, 42.4982818],[-71.48106730000001, 42.4981397],[-71.4811297, 42.4979892],[-71.48128850000001, 42.4978052],[-71.48143589999999, 42.4977467],[-71.481572, 42.4977467],[-71.48174210000001, 42.4977132],[-71.48183849999999, 42.497638],[-71.4818952, 42.4975627],[-71.4818441, 42.4974749],[-71.4819576, 42.4974206],[-71.4820766, 42.4973704],[-71.48228, 42.497303]]}}, +{"type":"Feature","properties":{"natural":"water","source:datetime":"2013-09-07T01:52:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46395080000001, 42.4927033],[-71.46395099999999, 42.4924743],[-71.46397279999999, 42.4921868],[-71.46393689999999, 42.4921069],[-71.4638147, 42.492032],[-71.4635414, 42.4919892],[-71.4630021, 42.4918613],[-71.4628584, 42.4918505],[-71.4628081, 42.4918297],[-71.4629017, 42.4916699],[-71.4629736, 42.4915901],[-71.4630023, 42.4915794],[-71.4630456, 42.4916058],[-71.4630959, 42.4917707],[-71.4632323, 42.4918298],[-71.46354150000001, 42.4918828],[-71.4638435, 42.4919521],[-71.4640232, 42.4920428],[-71.4640733, 42.4921655],[-71.4640732, 42.4924743],[-71.4639724, 42.492943],[-71.4639866, 42.4929908],[-71.4640297, 42.4930229],[-71.46434619999999, 42.4930337],[-71.46469829999999, 42.4932251],[-71.46520169999999, 42.4933851],[-71.46538150000001, 42.4934223],[-71.4654172, 42.4934864],[-71.4653381, 42.4935292],[-71.465338, 42.4935714],[-71.4654243, 42.4936141],[-71.4654604, 42.4936727],[-71.46543870000001, 42.4937632],[-71.46533789999999, 42.4938274],[-71.4651942, 42.4938858],[-71.4651581, 42.4938802],[-71.4650503, 42.493838],[-71.464806, 42.4936725],[-71.46441780000001, 42.493449],[-71.4643028, 42.4934011],[-71.46418060000001, 42.4933212],[-71.4639219, 42.4930972],[-71.4638788, 42.4929745],[-71.4639725, 42.4927033],[-71.46395080000001, 42.4927033]]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-09-05T23:25:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4563066, 42.4727609],[-71.4561602, 42.472742],[-71.4561592, 42.4729477],[-71.45625870000001, 42.4729441],[-71.45627349999999, 42.4729344],[-71.4562924, 42.4729156],[-71.4563066, 42.4727609]]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-09-05T23:25:28Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45769679999999, 42.472317],[-71.4575464, 42.4724057],[-71.45736890000001, 42.4724736],[-71.45712210000001, 42.4725443],[-71.4568701, 42.4725822]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-09-05T23:20:42Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45636570000001, 42.4729196],[-71.4564103, 42.4731028],[-71.4564426, 42.4732562],[-71.4564838, 42.473415],[-71.45695360000001, 42.473332],[-71.4569175, 42.4731826],[-71.45687650000001, 42.473023],[-71.45683150000001, 42.4728482]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4589455, 42.4732299],[-71.45844839999999, 42.4734048],[-71.45841160000001, 42.473462],[-71.4581075, 42.4735763],[-71.4579033, 42.4736967]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-09-05T23:20:41Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.45636570000001, 42.4729196],[-71.4563339, 42.4728623],[-71.45632879999999, 42.4727684],[-71.4563564, 42.4726909],[-71.45639199999999, 42.4726453]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4579999, 42.4725191],[-71.4577384, 42.4726251],[-71.4574569, 42.4727117],[-71.4569456, 42.4728307]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-09-05T23:20:41Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4563564, 42.4726909],[-71.4562901, 42.4726939],[-71.4561952, 42.4726917]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-09-05T23:20:41Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4568476, 42.4724459],[-71.4567728, 42.4724418],[-71.4565869, 42.4724768]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45683150000001, 42.4728482],[-71.45681020000001, 42.4727976],[-71.4567122, 42.4727098]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4569175, 42.4731826],[-71.4564426, 42.4732562]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45687650000001, 42.473023],[-71.4564103, 42.4731028]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45800130000001, 42.4738702],[-71.4579033, 42.4736967]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.45860759999999, 42.4736681],[-71.45844839999999, 42.4734048]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-09-05T23:20:41Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4562901, 42.4726939],[-71.45626350000001, 42.4727459]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-09-05T23:20:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4588749, 42.4728511],[-71.4586379, 42.4727962],[-71.45822219999999, 42.4726432],[-71.4579999, 42.4725191],[-71.45769679999999, 42.472317],[-71.4575768, 42.4722561],[-71.45744929999999, 42.472203],[-71.4573319, 42.4721964],[-71.4572079, 42.4722188],[-71.4570298, 42.4722831],[-71.4569068, 42.4723551],[-71.4568476, 42.4724459],[-71.45684249999999, 42.4725163],[-71.4568701, 42.4725822],[-71.4569456, 42.4728307],[-71.45683150000001, 42.4728482],[-71.45636570000001, 42.4729196]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-09-05T23:20:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4561949, 42.472704],[-71.4561617, 42.4727062],[-71.45616029999999, 42.4726744],[-71.45619569999999, 42.4726744],[-71.4561952, 42.4726917],[-71.4561949, 42.472704]]]}}, +{"type":"Feature","properties":{"parking":"surface","access":"private","amenity":"parking","source:datetime":"2014-07-26T02:31:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4777223, 42.4980541],[-71.47741910000001, 42.4978552],[-71.47705759999999, 42.4976645],[-71.47697580000001, 42.4977202],[-71.4765628, 42.4976688],[-71.47635029999999, 42.4976627],[-71.4763116, 42.4975957],[-71.47627420000001, 42.4975551],[-71.47629190000001, 42.4974333],[-71.47639820000001, 42.4973491],[-71.47670890000001, 42.4973622],[-71.4768839, 42.4974304],[-71.4770482, 42.4975021],[-71.4775337, 42.4977914],[-71.4778302, 42.4979662],[-71.4784464, 42.4983587],[-71.47811160000001, 42.4986283],[-71.47833540000001, 42.4988268],[-71.4784916, 42.4986959],[-71.4785761, 42.4988141],[-71.4777483, 42.4996337],[-71.4777045, 42.4996064],[-71.4776433, 42.4996699],[-71.4773299, 42.499514],[-71.4772936, 42.4995542],[-71.4767751, 42.4996601],[-71.47661069999999, 42.4995714],[-71.4763234, 42.4993242],[-71.4761547, 42.4993391],[-71.4760484, 42.4993028],[-71.47600679999999, 42.4992588],[-71.476108, 42.4985529],[-71.47614950000001, 42.4985624],[-71.4761781, 42.4985127],[-71.47629740000001, 42.4984955],[-71.4765232, 42.4985777],[-71.47670220000001, 42.4987805],[-71.4767281, 42.4989183],[-71.47670479999999, 42.4991249],[-71.4767939, 42.4992561],[-71.47693339999999, 42.4990168],[-71.476887, 42.4988733],[-71.4771427, 42.4985954],[-71.4770443, 42.4985451],[-71.4770248, 42.4985991],[-71.47669070000001, 42.4985677],[-71.47672540000001, 42.4983679],[-71.47717609999999, 42.4984108],[-71.4771622, 42.4984888],[-71.4772428, 42.498497],[-71.4777223, 42.4980541]]]}}, +{"type":"Feature","properties":{"parking":"surface","amenity":"parking","source:datetime":"2014-07-26T02:31:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4786922, 42.4991125],[-71.47853120000001, 42.499148],[-71.4784215, 42.4992266],[-71.4783223, 42.4991639],[-71.4784215, 42.4990802],[-71.4786278, 42.4990373],[-71.4788111, 42.499007],[-71.4788477, 42.4990492],[-71.4789765, 42.4990413],[-71.47916960000001, 42.4991401],[-71.47927799999999, 42.499606],[-71.47809580000001, 42.4995577],[-71.4781468, 42.4995489],[-71.4782714, 42.4994676],[-71.4787843, 42.4991963],[-71.4788556, 42.4991752],[-71.47880000000001, 42.4990788],[-71.4787221, 42.4990644],[-71.4786922, 42.4991125]]]}}, +{"type":"Feature","properties":{"source":"Bing","source:datetime":"2014-11-16T05:11:19Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47359400000001, 42.479218],[-71.4736381, 42.4792468],[-71.47369070000001, 42.4792659],[-71.4737476, 42.4792847],[-71.4737923, 42.4793106],[-71.4738282, 42.4793408],[-71.473839, 42.479382],[-71.4738361, 42.4794285],[-71.4738128, 42.4794747],[-71.47377299999999, 42.4795146],[-71.4737186, 42.4795323],[-71.47363369999999, 42.4795535],[-71.47355399999999, 42.4795601],[-71.4734655, 42.479558],[-71.4733741, 42.4795478],[-71.47327919999999, 42.4795242],[-71.4731993, 42.4794874],[-71.47312959999999, 42.479434]]}}, +{"type":"Feature","properties":{"source":"Bing","source:datetime":"2014-11-16T05:11:19Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4730505, 42.4795809],[-71.4731594, 42.4796224],[-71.4732796, 42.4796472],[-71.473328, 42.4796671],[-71.4733559, 42.4796949],[-71.4733696, 42.4797446],[-71.4733578, 42.4797835],[-71.47332110000001, 42.4798204],[-71.4732762, 42.4798364],[-71.4732453, 42.4798423],[-71.4732061, 42.4798439],[-71.473147, 42.4798279],[-71.4730797, 42.4797993],[-71.4730433, 42.4797738],[-71.47301419999999, 42.4797394],[-71.4730074, 42.4796992],[-71.4730107, 42.4796567],[-71.4730505, 42.4795809],[-71.4730787, 42.4794163]]}}, +{"type":"Feature","properties":{"source":"Bing","source:datetime":"2014-11-16T05:11:19Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4747718, 42.4802126],[-71.4747206, 42.479889],[-71.4746368, 42.4795871],[-71.4745163, 42.4792884],[-71.47446100000001, 42.4791275],[-71.4744233, 42.4790825],[-71.4743747, 42.4790531],[-71.47433409999999, 42.4790413],[-71.47428619999999, 42.4790424],[-71.4742387, 42.4790512],[-71.47401240000001, 42.4790951],[-71.47376749999999, 42.4791424],[-71.47368899999999, 42.47915]]}}, +{"type":"Feature","properties":{"natural":"water","source":"Bing","source:datetime":"2013-06-02T17:03:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4592409, 42.4872342],[-71.4590575, 42.4875582],[-71.4591015, 42.4877326],[-71.4587571, 42.4878272],[-71.45842450000001, 42.4879775],[-71.4584792, 42.4880886],[-71.4588225, 42.4883814],[-71.4588869, 42.4885633],[-71.4590146, 42.4885155],[-71.4592195, 42.4885396],[-71.45902529999999, 42.4883889],[-71.4591015, 42.4883339],[-71.45882140000001, 42.488112],[-71.4590585, 42.4879779],[-71.4590585, 42.4878434],[-71.4593589, 42.487788],[-71.4596057, 42.4880016],[-71.4597559, 42.4881994],[-71.459315, 42.4884601],[-71.4593686, 42.4885629],[-71.4596808, 42.4884763],[-71.4598632, 42.4882944],[-71.4594555, 42.4875348],[-71.4593579, 42.4874158],[-71.4592409, 42.4872342]]]}}, +{"type":"Feature","properties":{"natural":"water","source":"Bing","source:datetime":"2014-03-23T15:45:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46343539999999, 42.4885554],[-71.46298419999999, 42.4883652],[-71.4627375, 42.4884443],[-71.46237379999999, 42.4883497],[-71.4624274, 42.488239],[-71.46196500000001, 42.4879775],[-71.4618577, 42.4879775],[-71.4617811, 42.4880537],[-71.46173, 42.4881045],[-71.46208300000001, 42.4882465],[-71.46214740000001, 42.4885392],[-71.462362, 42.4886025],[-71.4622225, 42.48865],[-71.46214740000001, 42.4887291],[-71.4618373, 42.4886345],[-71.4619221, 42.4885076],[-71.46171820000001, 42.4883652],[-71.4615787, 42.4883968],[-71.4612676, 42.4887449],[-71.46152619999999, 42.4888561],[-71.46168710000001, 42.4888007],[-71.4619757, 42.4889506],[-71.4620723, 42.4893066],[-71.4619768, 42.4894415],[-71.4621064, 42.4893676],[-71.4622128, 42.489307],[-71.4621592, 42.4889194],[-71.46240589999999, 42.4888719],[-71.4625454, 42.488682],[-71.46343539999999, 42.4885554]]]}}, +{"type":"Feature","properties":{"waterway":"stream","source":"Bing","name":"Grassy Pond Brook","source:datetime":"2014-03-23T15:45:29Z"},"geometry": {"type":"LineString","coordinates": [[-71.4617811, 42.4880537],[-71.4615798, 42.4880174],[-71.4612901, 42.4877484],[-71.46108630000001, 42.4877722],[-71.46086099999999, 42.487701],[-71.46088140000001, 42.4875503],[-71.4609683, 42.487432],[-71.46088140000001, 42.4873841],[-71.4610208, 42.487218],[-71.4608932, 42.4870443],[-71.4610745, 42.4869094],[-71.46106380000001, 42.48668],[-71.46123540000001, 42.4865296],[-71.46122579999999, 42.4862927],[-71.46132129999999, 42.4861894],[-71.4615251, 42.4858888],[-71.4619328, 42.4857464],[-71.4624049, 42.4855486],[-71.46325349999999, 42.4854382],[-71.4638854, 42.4852479],[-71.4641113, 42.485003],[-71.4646906, 42.4847894],[-71.4652593, 42.4848131],[-71.46564549999999, 42.4846153],[-71.4659352, 42.4842356],[-71.4663643, 42.4842356],[-71.46649309999999, 42.4841011],[-71.466536, 42.48384],[-71.46675, 42.4835152],[-71.4667506, 42.4832545],[-71.4669008, 42.482756],[-71.4669324, 42.4826211],[-71.4670832, 42.4824711],[-71.4672642, 42.4822322],[-71.4670362, 42.4820818],[-71.4682593, 42.4797556],[-71.46917120000001, 42.4795657],[-71.4698406, 42.4788436]]}}, +{"type":"Feature","properties":{"source":"massgis_import_v0.1_20071009100433","attribution":"Office of Geographic and Environmental Information (MassGIS)","source:datetime":"2013-05-07T22:42:49Z","highway":"residential","massgis:way_id":"2"},"geometry": {"type":"LineString","coordinates": [[-71.47971200000001, 42.480733],[-71.479608, 42.480763],[-71.47949699999999, 42.480802],[-71.479463, 42.48081],[-71.47937, 42.480806],[-71.479265, 42.480807],[-71.479163, 42.480816],[-71.47906, 42.480817],[-71.478875, 42.480796],[-71.478697, 42.480747],[-71.478617, 42.480708],[-71.478565, 42.480664]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:52Z"},"geometry": {"type":"Point","coordinates": [-71.4851658, 42.4847306]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:52Z"},"geometry": {"type":"LineString","coordinates": [[-71.48516979999999, 42.4866692],[-71.48519690000001, 42.4866098]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:52Z"},"geometry": {"type":"LineString","coordinates": [[-71.4851578, 42.4881964],[-71.4851561, 42.4881203]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.485108, 42.4837877],[-71.4851163, 42.4837518],[-71.48503409999999, 42.4838564],[-71.48505160000001, 42.4837806],[-71.485108, 42.4837877]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48499049999999, 42.4869312],[-71.4850197, 42.4868858],[-71.48512049999999, 42.4869214],[-71.4850615, 42.4870131],[-71.4849449, 42.486972],[-71.4849748, 42.4869257],[-71.48499049999999, 42.4869312]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4848083, 42.4874548],[-71.48469249999999, 42.4874148],[-71.48473920000001, 42.4873408],[-71.4848636, 42.4873837],[-71.4848866, 42.4873471],[-71.4849402, 42.4873656],[-71.48491420000001, 42.4874069],[-71.4849911, 42.4874335],[-71.4849397, 42.4875152],[-71.48485119999999, 42.4874847],[-71.4848654, 42.4874622],[-71.4848147, 42.4874447],[-71.4848083, 42.4874548]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.484588, 42.4880049],[-71.48459560000001, 42.4879731],[-71.4847386, 42.487992],[-71.484708, 42.4881197],[-71.48456400000001, 42.4881007],[-71.4845725, 42.4880651],[-71.4845254, 42.4880589],[-71.4845399, 42.4879985],[-71.484588, 42.4880049]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4847522, 42.4860156],[-71.4846522, 42.4860556],[-71.4845849, 42.4859635],[-71.4845972, 42.4859586],[-71.4845751, 42.4859283],[-71.4846628, 42.4858932],[-71.4847522, 42.4860156]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48442420000001, 42.4847313],[-71.4844308, 42.4846981],[-71.4844131, 42.4846962],[-71.4844171, 42.4846759],[-71.48438830000001, 42.4846728],[-71.4843978, 42.4846246],[-71.48443090000001, 42.4846282],[-71.4844379, 42.4845928],[-71.4845559, 42.4846056],[-71.4845282, 42.4847456],[-71.48448430000001, 42.4847408],[-71.48446920000001, 42.4848167],[-71.48438, 42.4848071],[-71.4843956, 42.4847282],[-71.48442420000001, 42.4847313]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4844068, 42.4930409],[-71.4843635, 42.4930735],[-71.4844169, 42.4931123],[-71.4842611, 42.4932297],[-71.4841512, 42.4931498],[-71.48416090000001, 42.4931425],[-71.4841301, 42.4931201],[-71.4843195, 42.4929774],[-71.4844068, 42.4930409]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48446269999999, 42.4856797],[-71.48431119999999, 42.4857442],[-71.4842824, 42.4857071],[-71.484309, 42.4856958],[-71.4842653, 42.4856396],[-71.4842848, 42.4856313],[-71.48426190000001, 42.4856018],[-71.4843295, 42.485573],[-71.4843546, 42.4856054],[-71.4843925, 42.4855892],[-71.48446269999999, 42.4856797]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48429419999999, 42.4852502],[-71.4842725, 42.485202],[-71.4842441, 42.485209],[-71.4842214, 42.4851585],[-71.4843582, 42.4851247],[-71.48437730000001, 42.4851672],[-71.48439399999999, 42.4851631],[-71.48440340000001, 42.485184],[-71.48438160000001, 42.4851894],[-71.4844162, 42.4852661],[-71.48433060000001, 42.4852872],[-71.484312, 42.4852458],[-71.48429419999999, 42.4852502]]]}}, +{"type":"Feature","properties":{"building":"garage","source:datetime":"2014-11-16T04:49:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4842427, 42.4859024],[-71.4841393, 42.4859431],[-71.4840923, 42.4858777],[-71.4841957, 42.4858371],[-71.4842427, 42.4859024]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48388919999999, 42.4880147],[-71.4838443, 42.4879953],[-71.4838633, 42.4879713],[-71.4837835, 42.4879368],[-71.4838381, 42.4878675],[-71.484075, 42.4879697],[-71.48402710000001, 42.4880306],[-71.48391479999999, 42.4879822],[-71.48388919999999, 42.4880147]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.484078, 42.487043],[-71.48406300000001, 42.4870601],[-71.4841438, 42.4870987],[-71.48415679999999, 42.4870837],[-71.4842242, 42.4871159],[-71.4841744, 42.487173],[-71.4840787, 42.4871273],[-71.48405579999999, 42.4871536],[-71.48394930000001, 42.4871027],[-71.4840239, 42.4870172],[-71.484078, 42.487043]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4838362, 42.4984435],[-71.4836889, 42.4984708],[-71.4836696, 42.4984135],[-71.4838168, 42.4983863],[-71.4838362, 42.4984435]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4836816, 42.4977049],[-71.4836318, 42.4977199],[-71.4836092, 42.49768],[-71.48365939999999, 42.4976648],[-71.4836816, 42.4977049]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48367450000001, 42.498949],[-71.4837144, 42.4989357],[-71.4837392, 42.4989765],[-71.48369510000001, 42.4989912],[-71.48368979999999, 42.4989825],[-71.48359019999999, 42.4990158],[-71.4835425, 42.4989374],[-71.48352180000001, 42.4989443],[-71.4834869, 42.4988871],[-71.4836114, 42.4988456],[-71.48367450000001, 42.498949]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48358330000001, 42.4986732],[-71.4835178, 42.4986903],[-71.48349760000001, 42.4986481],[-71.4835631, 42.4986309],[-71.48358330000001, 42.4986732]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4834213, 42.4986268],[-71.4833228, 42.4986523],[-71.4832894, 42.4985815],[-71.4833878, 42.498556],[-71.4834213, 42.4986268]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48288580000001, 42.4975365],[-71.4829836, 42.4975208],[-71.48300209999999, 42.497584],[-71.4830711, 42.4975729],[-71.4830973, 42.4976623],[-71.483155, 42.4976531],[-71.483158, 42.4976635],[-71.48327260000001, 42.4976451],[-71.483284, 42.497684],[-71.48317710000001, 42.4977011],[-71.4831825, 42.4977195],[-71.48305999999999, 42.4977392],[-71.4830422, 42.4976782],[-71.483007, 42.4976838],[-71.4829984, 42.4976544],[-71.4829239, 42.4976664],[-71.48288580000001, 42.4975365]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48305259999999, 42.487104],[-71.48303900000001, 42.4870933],[-71.4831253, 42.4870261],[-71.4831008, 42.4870088],[-71.4831403, 42.4869781],[-71.4831581, 42.4869906],[-71.48322020000001, 42.4869423],[-71.48329339999999, 42.4869939],[-71.48322020000001, 42.4870509],[-71.48328069999999, 42.4870934],[-71.483232, 42.4871314],[-71.48327070000001, 42.4871586],[-71.4832438, 42.4871796],[-71.4832088, 42.487155],[-71.48314329999999, 42.487206],[-71.48310770000001, 42.4871812],[-71.4830647, 42.4872111],[-71.48298680000001, 42.4871497],[-71.48305259999999, 42.487104]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4833142, 42.4877451],[-71.4832085, 42.4877784],[-71.483154, 42.4876834],[-71.48316939999999, 42.4876785],[-71.4831495, 42.4876439],[-71.4832399, 42.4876155],[-71.4833142, 42.4877451]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4830098, 42.4980087],[-71.4831159, 42.4979809],[-71.4831906, 42.4981367],[-71.48312490000001, 42.498154],[-71.4831133, 42.4981297],[-71.4831251, 42.4981267],[-71.4831152, 42.4981062],[-71.4830498, 42.4981234],[-71.4830266, 42.498075],[-71.48303989999999, 42.4980715],[-71.4830098, 42.4980087]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4830252, 42.4876285],[-71.48297530000001, 42.487647],[-71.4829561, 42.4876188],[-71.4830061, 42.4876002],[-71.4830252, 42.4876285]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48263799999999, 42.4984169],[-71.4826235, 42.4984205],[-71.48265019999999, 42.4984759],[-71.4825469, 42.4985035],[-71.4824959, 42.4983978],[-71.4825103, 42.498394],[-71.4824945, 42.4983614],[-71.48254, 42.4983494],[-71.4825563, 42.4983831],[-71.4826142, 42.4983678],[-71.48263799999999, 42.4984169]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4824042, 42.4968951],[-71.4823876, 42.4968494],[-71.4823688, 42.4968532],[-71.4823608, 42.496831],[-71.4823344, 42.4968362],[-71.4823176, 42.4967897],[-71.4824627, 42.4967609],[-71.48249, 42.4968361],[-71.4825633, 42.4968216],[-71.48260670000001, 42.4969414],[-71.48252410000001, 42.4969578],[-71.48249490000001, 42.4968771],[-71.4824042, 42.4968951]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4821676, 42.4948958],[-71.48221700000001, 42.4948754],[-71.4822617, 42.4949346],[-71.48223659999999, 42.494945],[-71.4822638, 42.4949809],[-71.4822211, 42.4949986],[-71.4821918, 42.4949598],[-71.4820957, 42.4949996],[-71.4820448, 42.4949323],[-71.48215930000001, 42.4948848],[-71.4821676, 42.4948958]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4819935, 42.4964197],[-71.4820062, 42.4964139],[-71.481973, 42.4963739],[-71.48204490000001, 42.4963414],[-71.48205129999999, 42.496349],[-71.4820984, 42.4963277],[-71.4821317, 42.496368],[-71.48207840000001, 42.4963924],[-71.4821633, 42.4964949],[-71.482176, 42.4964889],[-71.48222370000001, 42.4965465],[-71.4821365, 42.496586],[-71.4820862, 42.4965253],[-71.4820708, 42.4965323],[-71.4820177, 42.4964681],[-71.4820292, 42.4964629],[-71.4819935, 42.4964197]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4819859, 42.4954515],[-71.4820199, 42.4954345],[-71.4820778, 42.495498],[-71.4819244, 42.4955745],[-71.4818655, 42.4955099],[-71.4819042, 42.4954906],[-71.48189429999999, 42.4954797],[-71.4819749, 42.4954395],[-71.4819859, 42.4954515]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4819998, 42.4956313],[-71.4819231, 42.4956809],[-71.4818602, 42.4956276],[-71.48193689999999, 42.495578],[-71.4819998, 42.4956313]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.481872, 42.4960102],[-71.481842, 42.496033],[-71.4819445, 42.4961067],[-71.4818721, 42.4961618],[-71.4817394, 42.4960663],[-71.4818417, 42.4959884],[-71.481872, 42.4960102]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4819219, 42.4982952],[-71.48170330000001, 42.4983671],[-71.4816581, 42.4982919],[-71.48187679999999, 42.49822],[-71.4819219, 42.4982952]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4818039, 42.4945248],[-71.4817089, 42.4945982],[-71.4816577, 42.4945619],[-71.4816714, 42.4945514],[-71.4816095, 42.4945075],[-71.4816909, 42.4944447],[-71.4818039, 42.4945248]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4815703, 42.4947475],[-71.4815378, 42.4947265],[-71.4815795, 42.4946912],[-71.4816454, 42.4947339],[-71.4816137, 42.4947606],[-71.4816525, 42.4947858],[-71.481661, 42.4947786],[-71.4816972, 42.494802],[-71.4816307, 42.4948582],[-71.48160059999999, 42.4948388],[-71.4815634, 42.4948702],[-71.4814851, 42.4948195],[-71.4815703, 42.4947475]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48125949999999, 42.4957557],[-71.4812634, 42.4957282],[-71.4813163, 42.4957323],[-71.4813132, 42.4957539],[-71.48133369999999, 42.4957555],[-71.4813295, 42.4957849],[-71.4814135, 42.4957915],[-71.4814037, 42.4958599],[-71.4813225, 42.4958535],[-71.48132579999999, 42.4958308],[-71.4812336, 42.4958236],[-71.48124350000001, 42.4957545],[-71.48125949999999, 42.4957557]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4808037, 42.4944113],[-71.4807296, 42.4945063],[-71.480692, 42.4944902],[-71.4806079, 42.4945981],[-71.480465, 42.494537],[-71.4806232, 42.4943342],[-71.4808037, 42.4944113]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2013-03-25T02:06:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4796235, 42.4929566],[-71.4795313, 42.4930326],[-71.47937810000001, 42.4929306],[-71.4794703, 42.4928547],[-71.4796235, 42.4929566]]]}}, +{"type":"Feature","properties":{"source:datetime":"2013-03-05T14:34:30Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.457116, 42.476475],[-71.457261, 42.4765086],[-71.4573479, 42.4765226],[-71.4574769, 42.4765432],[-71.45758240000001, 42.4765296],[-71.457656, 42.4764852],[-71.4576895, 42.4764296],[-71.45770349999999, 42.4763374],[-71.4577767, 42.475853]]}}, +{"type":"Feature","properties":{"service":"parking_aisle","source:datetime":"2013-03-05T14:34:30Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4573479, 42.4765226],[-71.4573999, 42.4764543],[-71.4575104, 42.476379],[-71.45760919999999, 42.4763556],[-71.45770349999999, 42.4763374]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.4667366, 42.4979431],[-71.4668513, 42.4980487],[-71.46761410000001, 42.4988655],[-71.466938, 42.4987537]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.46724210000001, 42.497274],[-71.4674201, 42.4974591],[-71.46719899999999, 42.4976825],[-71.4668513, 42.4980487]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.4678554, 42.4978529],[-71.4677013, 42.4978129],[-71.4675755, 42.4977802],[-71.46719899999999, 42.4976825]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.4677172, 42.4986889],[-71.4678554, 42.4978529],[-71.4678785, 42.4976433],[-71.4678577, 42.4974614]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.46815410000001, 42.4976632],[-71.4681495, 42.4982482],[-71.46812180000001, 42.498406]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.4675755, 42.4977802],[-71.46723590000001, 42.4982928]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.4677013, 42.4978129],[-71.467533, 42.4984011]]}}, +{"type":"Feature","properties":{"barrier":"wall","source:datetime":"2013-02-18T23:54:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.46864119999999, 42.4972671],[-71.4683706, 42.4971335]]}}, +{"type":"Feature","properties":{"motor_vehicle":"no","access":"designated","sidewalk":"none","name":"Red","foot":"yes","source:datetime":"2013-02-06T21:14:29Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4694312, 42.4988248],[-71.46872999999999, 42.49847],[-71.46854, 42.4984],[-71.46845, 42.49828],[-71.46847, 42.49782],[-71.46839, 42.49768],[-71.46803, 42.49739],[-71.46775, 42.49725],[-71.46759, 42.49712],[-71.46733, 42.49698],[-71.46724810000001, 42.4970075],[-71.4670998, 42.4970981],[-71.46686, 42.49722],[-71.46615199999999, 42.497501]]}}, +{"type":"Feature","properties":{"motor_vehicle":"no","access":"designated","sidewalk":"none","name":"Yellow","foot":"designated","source:datetime":"2013-02-09T00:48:07Z","highway":"footway"},"geometry": {"type":"Polygon","coordinates": [[[-71.46615199999999, 42.497501],[-71.466087, 42.497434],[-71.4653991, 42.4972586],[-71.4652205, 42.4972617],[-71.465017, 42.4972882],[-71.46484700000001, 42.4973677],[-71.46469999999999, 42.4974512],[-71.4645656, 42.4975235],[-71.4644517, 42.4975839],[-71.46429000000001, 42.49762],[-71.4642508, 42.4975114],[-71.464302, 42.4974153],[-71.46435459999999, 42.4972836],[-71.4645994, 42.4971199],[-71.4648541, 42.4971055],[-71.4650252, 42.4970273],[-71.46505000000001, 42.49689],[-71.4650779, 42.4967495],[-71.4650614, 42.4966163],[-71.46486, 42.49643],[-71.46465999999999, 42.49623],[-71.4644, 42.4962],[-71.46427, 42.49624],[-71.46407120000001, 42.4962984],[-71.46382, 42.49646],[-71.46353999999999, 42.4964026],[-71.4633, 42.4964258],[-71.4630648, 42.4963961],[-71.46254999999999, 42.49655],[-71.46218279999999, 42.4967661],[-71.4620314, 42.4968798],[-71.46210000000001, 42.49691],[-71.46252, 42.49736],[-71.46278, 42.49757],[-71.46302, 42.49763],[-71.46369, 42.4976],[-71.4637, 42.49764],[-71.4639, 42.49782],[-71.46407000000001, 42.49785],[-71.46424, 42.49783],[-71.46441, 42.49777],[-71.46521, 42.49767],[-71.46548, 42.49758],[-71.4659, 42.49757],[-71.4661, 42.49753],[-71.46615199999999, 42.497501]]]}}, +{"type":"Feature","properties":{"motor_vehicle":"no","access":"designated","sidewalk":"none","name":"Blue","foot":"yes","source:datetime":"2013-02-06T20:15:31Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.46369, 42.4976],[-71.46364199999999, 42.497539],[-71.46356, 42.49748],[-71.46352, 42.49741],[-71.46347, 42.49735],[-71.46344000000001, 42.4973],[-71.46341, 42.49724],[-71.46338, 42.49718],[-71.46339, 42.49712],[-71.46339999999999, 42.49706],[-71.46339999999999, 42.497],[-71.46344000000001, 42.49694],[-71.46359, 42.49682],[-71.46362000000001, 42.49678],[-71.46362999999999, 42.49675],[-71.46362999999999, 42.49672],[-71.46366, 42.49673],[-71.46368, 42.49672],[-71.46366999999999, 42.49669],[-71.46366, 42.49667],[-71.46366, 42.49662],[-71.4637, 42.49658],[-71.46375, 42.49654],[-71.46380000000001, 42.49651],[-71.46382, 42.49646]]}}, +{"type":"Feature","properties":{"motor_vehicle":"no","access":"designated","sidewalk":"none","name":"Blue","foot":"yes","source:datetime":"2015-01-23T23:35:47Z","highway":"footway"},"geometry": {"type":"LineString","coordinates": [[-71.4620314, 42.4968798],[-71.46188840000001, 42.4968987],[-71.4615976, 42.4971001],[-71.4616462, 42.4968767],[-71.46157890000001, 42.4967201],[-71.4615342, 42.496608],[-71.4614814, 42.496461],[-71.461455, 42.4962344],[-71.46146280000001, 42.4961298],[-71.4615611, 42.4960998],[-71.4616978, 42.4960739],[-71.4618536, 42.4960378],[-71.4619962, 42.495958],[-71.462142, 42.4958252],[-71.4622954, 42.4956988],[-71.4623737, 42.4956528],[-71.4624585, 42.4955515],[-71.46264239999999, 42.4953728],[-71.4627509, 42.495231],[-71.4628431, 42.495151],[-71.4629398, 42.4950789],[-71.4630508, 42.4950426],[-71.46313069999999, 42.4949353],[-71.463151, 42.4948631],[-71.46326620000001, 42.4947121],[-71.4633461, 42.4947362],[-71.463481, 42.494814],[-71.46395099999999, 42.494987],[-71.46401299999999, 42.494985],[-71.464077, 42.49503],[-71.46423299999999, 42.495072],[-71.4643073, 42.4950779],[-71.464736, 42.49529],[-71.464769, 42.495335],[-71.464862, 42.495356],[-71.464895, 42.495401],[-71.465146, 42.49551],[-71.465243, 42.495599],[-71.465368, 42.495642],[-71.46574699999999, 42.495863],[-71.46580899999999, 42.495862],[-71.465999, 42.495972],[-71.46618700000001, 42.496036],[-71.466409, 42.496169],[-71.466441, 42.496214],[-71.466503, 42.496213],[-71.46678799999999, 42.496389],[-71.466821, 42.496435],[-71.46701, 42.496522],[-71.46704200000001, 42.496567],[-71.467168, 42.49661],[-71.467327, 42.496721],[-71.4673289, 42.4967959],[-71.4673015, 42.4968566],[-71.46724810000001, 42.4970075]]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2013-02-02T20:54:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4556238, 42.4982924],[-71.45530239999999, 42.4984332],[-71.45527509999999, 42.4983743],[-71.4551764, 42.4983533],[-71.45488450000001, 42.4979589],[-71.4553672, 42.4977436],[-71.45540440000001, 42.4978386],[-71.4554578, 42.4978475],[-71.4556238, 42.4982924]]]}}, +{"type":"Feature","properties":{"bridge":"yes","name":"Red","foot":"designated","source:datetime":"2013-02-23T19:56:55Z","highway":"path"},"geometry": {"type":"LineString","coordinates": [[-71.4505097, 42.4984558],[-71.4507334, 42.4984514],[-71.4510794, 42.4983903],[-71.45126740000001, 42.4983557],[-71.45189360000001, 42.4982963],[-71.4520794, 42.4982973]]}}, +{"type":"Feature","properties":{"name":"Red","foot":"designated","source:datetime":"2013-02-23T19:56:55Z","highway":"path"},"geometry": {"type":"LineString","coordinates": [[-71.4520794, 42.4982973],[-71.452265, 42.4983062],[-71.4524026, 42.4978622],[-71.45248049999999, 42.4977393],[-71.4525477, 42.4975847],[-71.45264450000001, 42.4974619],[-71.45312029999999, 42.497016],[-71.45327639999999, 42.496951],[-71.45351530000001, 42.4968419]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-01-16T12:39:41Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4763554, 42.4973626],[-71.47631800000001, 42.4975281],[-71.47634360000001, 42.4975795],[-71.4764225, 42.4975998],[-71.477116, 42.4976387]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2014-07-20T02:42:32Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4788595, 42.4991015],[-71.4787932, 42.4990477],[-71.478776, 42.499015]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-01-16T12:37:19Z","highway":"service"},"geometry": {"type":"Point","coordinates": [-71.47813859999999, 42.4993875]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-01-16T12:37:19Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.477822, 42.498049],[-71.4772732, 42.4985362],[-71.4768622, 42.4989164],[-71.47680250000001, 42.4989788]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-01-16T12:37:19Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.47813859999999, 42.4993875],[-71.4784107, 42.499122],[-71.478776, 42.499015]]}}, +{"type":"Feature","properties":{"service":"driveway","source:datetime":"2013-01-16T12:37:19Z","highway":"service"},"geometry": {"type":"LineString","coordinates": [[-71.4764705, 42.4984984],[-71.4766898, 42.4984727],[-71.4772732, 42.4985362]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4746813, 42.4914846],[-71.4746174, 42.4915157],[-71.4746081, 42.4915052],[-71.4745097, 42.4915532],[-71.4745339, 42.4915803],[-71.47447440000001, 42.4916093],[-71.47443250000001, 42.4915993],[-71.4743847, 42.4915321],[-71.47462109999999, 42.4914169],[-71.4746813, 42.4914846]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46886550000001, 42.4871394],[-71.468846, 42.4871729],[-71.4688891, 42.4871887],[-71.4688431, 42.4872677],[-71.4687959, 42.4873377],[-71.4687212, 42.4873165],[-71.46876570000001, 42.487243],[-71.4687551, 42.4872396],[-71.4688216, 42.4871254],[-71.46886550000001, 42.4871394]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.451894, 42.4721062],[-71.4518523, 42.4720716],[-71.45188779999999, 42.4720482],[-71.45187970000001, 42.4720415],[-71.4519658, 42.4719848],[-71.4520773, 42.4720777],[-71.4519858, 42.4721319],[-71.4519274, 42.4720842],[-71.451894, 42.4721062]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4739465, 42.4932447],[-71.4740503, 42.4932784],[-71.47400450000001, 42.4933558],[-71.4740369, 42.4933663],[-71.474024, 42.4933881],[-71.4739922, 42.4933778],[-71.4739759, 42.4934054],[-71.4738715, 42.4933716],[-71.4739465, 42.4932447]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4669318, 42.4904854],[-71.46696420000001, 42.4904258],[-71.4671609, 42.4904843],[-71.4671297, 42.4905417],[-71.46710419999999, 42.4905341],[-71.4670864, 42.4905669],[-71.4670248, 42.4905486],[-71.46704149999999, 42.490518],[-71.4669318, 42.4904854]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4705237, 42.4724352],[-71.47053510000001, 42.472474],[-71.4705476, 42.472472],[-71.47057340000001, 42.4725599],[-71.47047449999999, 42.4725758],[-71.47044870000001, 42.4724879],[-71.4704823, 42.4724825],[-71.4704709, 42.4724436],[-71.4705237, 42.4724352]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4594735, 42.4843775],[-71.45949709999999, 42.4842753],[-71.4596257, 42.4842916],[-71.45960030000001, 42.4844014],[-71.4595841, 42.4843993],[-71.45956959999999, 42.4844621],[-71.4594733, 42.4844499],[-71.4594896, 42.4843795],[-71.4594735, 42.4843775]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46842599999999, 42.4948991],[-71.4684252, 42.4949164],[-71.46856649999999, 42.4949197],[-71.46856320000001, 42.4949958],[-71.46837120000001, 42.4949913],[-71.46837189999999, 42.4949751],[-71.4682798, 42.4949729],[-71.46828309999999, 42.4948958],[-71.46842599999999, 42.4948991]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45850969999999, 42.4815809],[-71.4585041, 42.4815711],[-71.4585842, 42.481546],[-71.45864880000001, 42.4816593],[-71.4585671, 42.4816849],[-71.45855090000001, 42.4816565],[-71.4583502, 42.4817193],[-71.4583073, 42.4816442],[-71.45850969999999, 42.4815809]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47085970000001, 42.4939244],[-71.4709119, 42.4938817],[-71.47097050000001, 42.4939209],[-71.4709211, 42.4939614],[-71.4709663, 42.4939916],[-71.4708849, 42.4940582],[-71.4707643, 42.4939775],[-71.47084289999999, 42.4939132],[-71.47085970000001, 42.4939244]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46610029999999, 42.4912811],[-71.46612500000001, 42.4912444],[-71.466184, 42.4912661],[-71.4661595, 42.4913025],[-71.46614150000001, 42.4912958],[-71.46609290000001, 42.4913681],[-71.4660068, 42.4913364],[-71.46605510000001, 42.4912644],[-71.46610029999999, 42.4912811]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45326110000001, 42.4757384],[-71.4532481, 42.4757461],[-71.4532983, 42.4757927],[-71.45318380000001, 42.4758602],[-71.4531217, 42.4758024],[-71.45316219999999, 42.4757786],[-71.453153, 42.47577],[-71.45323999999999, 42.4757188],[-71.45326110000001, 42.4757384]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4651916, 42.4766538],[-71.4651908, 42.4766312],[-71.4652517, 42.4766301],[-71.4652528, 42.4766671],[-71.46539919999999, 42.4766646],[-71.4654018, 42.4767458],[-71.4651027, 42.476751],[-71.4650997, 42.4766554],[-71.4651916, 42.4766538]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48430190000001, 42.4749388],[-71.4843961, 42.4750351],[-71.48443380000001, 42.4750148],[-71.4844841, 42.4750663],[-71.4844358, 42.4750921],[-71.4844277, 42.4750838],[-71.4843549, 42.4751228],[-71.4842187, 42.4749834],[-71.48430190000001, 42.4749388]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46627220000001, 42.480168],[-71.4661885, 42.4801541],[-71.4662012, 42.4801123],[-71.4660599, 42.4800889],[-71.4660863, 42.4800018],[-71.4662402, 42.4800274],[-71.46623700000001, 42.480038],[-71.466308, 42.4800498],[-71.46627220000001, 42.480168]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46449869999999, 42.4901428],[-71.46449490000001, 42.4901551],[-71.46457789999999, 42.4901693],[-71.4645503, 42.490257],[-71.4643914, 42.4902297],[-71.4643994, 42.4902043],[-71.4643088, 42.4901887],[-71.4643322, 42.4901142],[-71.46449869999999, 42.4901428]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46330469999999, 42.4867056],[-71.4633, 42.4866536],[-71.4634975, 42.4866438],[-71.4635019, 42.4866914],[-71.4634465, 42.4866942],[-71.4634594, 42.4868354],[-71.4633297, 42.4868418],[-71.4633173, 42.486705],[-71.46330469999999, 42.4867056]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4677655, 42.4887032],[-71.46785629999999, 42.4887442],[-71.467799, 42.4888139],[-71.4678261, 42.4888262],[-71.46781300000001, 42.4888421],[-71.4677844, 42.4888292],[-71.4677698, 42.4888469],[-71.46768040000001, 42.4888066],[-71.4677655, 42.4887032]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4701204, 42.4858469],[-71.4701405, 42.4858125],[-71.4701077, 42.485802],[-71.4701424, 42.4857425],[-71.4702253, 42.4857691],[-71.4701261, 42.4859388],[-71.47003650000001, 42.4859102],[-71.47008080000001, 42.4858342],[-71.4701204, 42.4858469]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4720298, 42.4781844],[-71.47195000000001, 42.4781029],[-71.472061, 42.4780433],[-71.4721428, 42.4781269],[-71.4721207, 42.4781387],[-71.47215009999999, 42.4781688],[-71.4720744, 42.4782094],[-71.472043, 42.4781774],[-71.4720298, 42.4781844]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4520673, 42.4725716],[-71.4520685, 42.4725461],[-71.45213579999999, 42.4725479],[-71.4521304, 42.472661],[-71.45192849999999, 42.4726557],[-71.45193039999999, 42.472616],[-71.4519009, 42.4726152],[-71.4519032, 42.4725673],[-71.4520673, 42.4725716]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4688682, 42.4905079],[-71.4688309, 42.490535],[-71.46887769999999, 42.4905702],[-71.468761, 42.4906551],[-71.4686705, 42.4905869],[-71.4687292, 42.4905442],[-71.4687155, 42.4905339],[-71.4688108, 42.4904646],[-71.4688682, 42.4905079]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4664037, 42.4788086],[-71.4664226, 42.4786686],[-71.4665344, 42.4786768],[-71.46652159999999, 42.4788079],[-71.46650699999999, 42.4788071],[-71.46649960000001, 42.4788835],[-71.46637870000001, 42.478877],[-71.4663858, 42.4788073],[-71.4664037, 42.4788086]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"148","addr:state":"MA","source:datetime":"2014-11-08T14:01:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.454116, 42.4967998],[-71.4540513, 42.4968271],[-71.4540975, 42.496887],[-71.454014, 42.4969223],[-71.4539347, 42.4968195],[-71.45397149999999, 42.4968039],[-71.4539515, 42.496778],[-71.4540629, 42.4967309],[-71.454116, 42.4967998]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4698065, 42.4742331],[-71.4698154, 42.4740944],[-71.46996040000001, 42.4740995],[-71.46995149999999, 42.4742382],[-71.4698921, 42.4742361],[-71.4698916, 42.4742446],[-71.46986099999999, 42.4742435],[-71.4698616, 42.474235],[-71.4698065, 42.4742331]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46307609999999, 42.4814016],[-71.46294, 42.4813719],[-71.4629705, 42.4812955],[-71.4632411, 42.4813547],[-71.4632178, 42.4814131],[-71.4631267, 42.4813932],[-71.4631304, 42.4813837],[-71.463087, 42.4813742],[-71.46307609999999, 42.4814016]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4629205, 42.4735627],[-71.4629166, 42.4735251],[-71.4630216, 42.4735191],[-71.4630392, 42.4736878],[-71.4629308, 42.473694],[-71.46292029999999, 42.4735933],[-71.46293559999999, 42.4735925],[-71.4629324, 42.4735621],[-71.4629205, 42.4735627]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46266009999999, 42.4894338],[-71.462641, 42.4893287],[-71.46268569999999, 42.4893243],[-71.4626925, 42.4893613],[-71.46283939999999, 42.4893466],[-71.46285399999999, 42.4894266],[-71.46271539999999, 42.4894404],[-71.4627133, 42.4894285],[-71.46266009999999, 42.4894338]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4795694, 42.4765368],[-71.4795651, 42.4765504],[-71.4797482, 42.4765817],[-71.47972110000001, 42.4766686],[-71.47947569999999, 42.4766266],[-71.4794926, 42.4765723],[-71.4794375, 42.4765629],[-71.4794519, 42.4765167],[-71.4795694, 42.4765368]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4729159, 42.4780925],[-71.4730132, 42.4780649],[-71.4730623, 42.4781596],[-71.47295889999999, 42.478189],[-71.4729636, 42.478198],[-71.47291060000001, 42.478213],[-71.47284879999999, 42.4780939],[-71.4729079, 42.4780771],[-71.4729159, 42.4780925]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757858, 42.48713],[-71.47574400000001, 42.4871707],[-71.4757099, 42.4871515],[-71.4756393, 42.4872202],[-71.4756555, 42.4872293],[-71.4756127, 42.487271],[-71.47550390000001, 42.4872098],[-71.4756591, 42.4870587],[-71.4757858, 42.48713]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4630513, 42.4861945],[-71.4629008, 42.48635],[-71.46281620000001, 42.4863044],[-71.462891, 42.4862267],[-71.462862, 42.4862115],[-71.46291069999999, 42.4861609],[-71.46289969999999, 42.4861551],[-71.4629262, 42.4861276],[-71.4630513, 42.4861945]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4635846, 42.4770034],[-71.4635079, 42.4770478],[-71.463528, 42.4770668],[-71.46345049999999, 42.4771117],[-71.4633954, 42.4770595],[-71.4634623, 42.4770208],[-71.46341030000001, 42.4769715],[-71.4634976, 42.476921],[-71.4635846, 42.4770034]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4610641, 42.4737228],[-71.4610581, 42.4736652],[-71.46109610000001, 42.473663],[-71.46109420000001, 42.4736451],[-71.4611961, 42.4736392],[-71.4612145, 42.4738139],[-71.46110969999999, 42.47382],[-71.4610993, 42.4737207],[-71.4610641, 42.4737228]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47238919999999, 42.4755629],[-71.4723836, 42.4753518],[-71.4724902, 42.4753503],[-71.4724928, 42.4754493],[-71.472511, 42.4754491],[-71.4725155, 42.4756189],[-71.47241390000001, 42.4756203],[-71.4724124, 42.4755625],[-71.47238919999999, 42.4755629]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4835418, 42.4752177],[-71.48348590000001, 42.4751561],[-71.4836181, 42.4750925],[-71.4837416, 42.4752331],[-71.4836098, 42.475306],[-71.48356130000001, 42.4752618],[-71.4836083, 42.4752402],[-71.48357350000001, 42.475203],[-71.4835418, 42.4752177]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.463915, 42.4767575],[-71.4638235, 42.4766904],[-71.4638317, 42.4766843],[-71.46377270000001, 42.4766409],[-71.4638356, 42.4765958],[-71.4640193, 42.476736],[-71.4639694, 42.4767718],[-71.4639334, 42.4767443],[-71.463915, 42.4767575]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47106429999999, 42.4996199],[-71.4710463, 42.4996977],[-71.47096019999999, 42.4996067],[-71.47106429999999, 42.4996199]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752019, 42.4723031],[-71.4752038, 42.4722757],[-71.4752556, 42.4722777],[-71.4752495, 42.4723651],[-71.4752704, 42.4723659],[-71.47526670000001, 42.4724187],[-71.4751595, 42.4724146],[-71.4751674, 42.4723018],[-71.4752019, 42.4723031]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4718141, 42.4756089],[-71.4718144, 42.475583],[-71.4719142, 42.4755836],[-71.4719133, 42.4756622],[-71.4718122, 42.4756616],[-71.4718123, 42.4756515],[-71.4717632, 42.4756513],[-71.4717636, 42.4756086],[-71.4718141, 42.4756089]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4733737, 42.4962659],[-71.4733659, 42.4963243],[-71.47341230000001, 42.4963277],[-71.4734072, 42.4963664],[-71.4733597, 42.496363],[-71.47335529999999, 42.4963967],[-71.4732396, 42.4963883],[-71.473257, 42.4962574],[-71.4733737, 42.4962659]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4621125, 42.4743719],[-71.4621181, 42.4743292],[-71.46218589999999, 42.4743341],[-71.46218090000001, 42.4743726],[-71.4622483, 42.4743774],[-71.4622385, 42.4744528],[-71.4619773, 42.4744343],[-71.46198649999999, 42.4743629],[-71.4621125, 42.4743719]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47093510000001, 42.4932373],[-71.4709766, 42.4932703],[-71.4710355, 42.4932297],[-71.47111409999999, 42.4932921],[-71.47098680000001, 42.4933798],[-71.47096639999999, 42.4933636],[-71.4709373, 42.4933837],[-71.4708377, 42.4933045],[-71.47093510000001, 42.4932373]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4792027, 42.4806592],[-71.4791007, 42.4806185],[-71.4791527, 42.4805472],[-71.47918369999999, 42.4805595],[-71.4791986, 42.4805391],[-71.4793109, 42.480584],[-71.4792523, 42.4806644],[-71.47921100000001, 42.4806479],[-71.4792027, 42.4806592]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4724148, 42.4899751],[-71.4723869, 42.4900152],[-71.4723563, 42.4900035],[-71.4723407, 42.4900258],[-71.4723728, 42.4900381],[-71.4723115, 42.4901259],[-71.4722319, 42.4900955],[-71.4723367, 42.4899453],[-71.4724148, 42.4899751]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4625018, 42.4778989],[-71.4624937, 42.4777842],[-71.4625412, 42.4777823],[-71.4625376, 42.4777302],[-71.4626543, 42.4777257],[-71.4626672, 42.4779078],[-71.462552, 42.4779122],[-71.462551, 42.477897],[-71.4625018, 42.4778989]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4681393, 42.4873322],[-71.4681054, 42.4873821],[-71.46813299999999, 42.4873924],[-71.4680815, 42.4874681],[-71.467985, 42.4874322],[-71.4680429, 42.4873469],[-71.468, 42.487331],[-71.46802750000001, 42.4872905],[-71.4681393, 42.4873322]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4804228, 42.4803596],[-71.4804056, 42.4803837],[-71.480442, 42.4803979],[-71.4804201, 42.4804287],[-71.48039869999999, 42.4804203],[-71.48035779999999, 42.4804777],[-71.4802529, 42.4804368],[-71.48033289999999, 42.4803245],[-71.4804228, 42.4803596]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46813040000001, 42.4931844],[-71.4680504, 42.4932592],[-71.4680881, 42.4932813],[-71.46800810000001, 42.493356],[-71.4679288, 42.4933095],[-71.4680314, 42.4932135],[-71.4680063, 42.4931988],[-71.46806359999999, 42.4931453],[-71.46813040000001, 42.4931844]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.480113, 42.4800197],[-71.4801135, 42.4799919],[-71.48023240000001, 42.4799931],[-71.4802311, 42.4800653],[-71.4802163, 42.4800651],[-71.48021489999999, 42.4801405],[-71.48009039999999, 42.4801392],[-71.4800925, 42.4800195],[-71.480113, 42.4800197]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47255629999999, 42.4920486],[-71.47257569999999, 42.4920675],[-71.472669, 42.4920151],[-71.47272599999999, 42.4920705],[-71.4726277, 42.4921258],[-71.4725998, 42.4920987],[-71.4725279, 42.4921391],[-71.4724794, 42.4920918],[-71.47255629999999, 42.4920486]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4772421, 42.485675],[-71.47720200000001, 42.4856172],[-71.4774348, 42.4855286],[-71.47747339999999, 42.4855841],[-71.4773163, 42.4856439],[-71.47735779999999, 42.4857036],[-71.4773007, 42.4857253],[-71.4772608, 42.4856679],[-71.4772421, 42.485675]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4520892, 42.4848455],[-71.45212359999999, 42.484835],[-71.45214470000001, 42.484873],[-71.45211519999999, 42.484882],[-71.4521513, 42.4849471],[-71.45205129999999, 42.4849775],[-71.4519865, 42.484861],[-71.45208169999999, 42.484832],[-71.4520892, 42.4848455]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48141800000001, 42.4760255],[-71.48137250000001, 42.4761124],[-71.4814356, 42.4761305],[-71.4814176, 42.4761649],[-71.4812896, 42.4761282],[-71.48130759999999, 42.4760938],[-71.48128610000001, 42.4760876],[-71.4813316, 42.4760007],[-71.48141800000001, 42.4760255]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4746289, 42.473515],[-71.47451820000001, 42.4735116],[-71.4745192, 42.4734929],[-71.47444489999999, 42.4734906],[-71.4744472, 42.4734512],[-71.47451390000001, 42.4734534],[-71.47451479999999, 42.473438],[-71.4746332, 42.4734417],[-71.4746289, 42.473515]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4607164, 42.4770196],[-71.46070779999999, 42.4770594],[-71.4607611, 42.4770657],[-71.460759, 42.4770756],[-71.46082730000001, 42.4770838],[-71.4608116, 42.4771561],[-71.4605701, 42.4771226],[-71.4605956, 42.4770053],[-71.4607164, 42.4770196]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"13","addr:state":"MA","source:datetime":"2014-11-08T14:01:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543575, 42.4939356],[-71.4543692, 42.493872],[-71.4545158, 42.4938869],[-71.4544971, 42.4939878],[-71.45444879999999, 42.4939829],[-71.45443, 42.4940844],[-71.4543197, 42.4940732],[-71.45434539999999, 42.4939344],[-71.4543575, 42.4939356]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4701768, 42.4867131],[-71.47023609999999, 42.4865975],[-71.470353, 42.4866303],[-71.4702667, 42.4867986],[-71.4702223, 42.4867861],[-71.4701982, 42.4868331],[-71.4701779, 42.4868274],[-71.470229, 42.4867278],[-71.4701768, 42.4867131]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4542465, 42.4729332],[-71.4542537, 42.4729129],[-71.4543535, 42.4729323],[-71.45432510000001, 42.4730128],[-71.4542282, 42.472994],[-71.4542342, 42.4729768],[-71.454131, 42.4729568],[-71.4541462, 42.4729138],[-71.4542465, 42.4729332]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4788999, 42.4719771],[-71.4789715, 42.4719962],[-71.4789626, 42.4720146],[-71.4790007, 42.4720248],[-71.478964, 42.4721002],[-71.47894580000001, 42.4720954],[-71.47893449999999, 42.4721186],[-71.478843, 42.4720942],[-71.4788999, 42.4719771]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4643456, 42.4777211],[-71.4643019, 42.4777214],[-71.4643023, 42.4777528],[-71.4641967, 42.4777538],[-71.46419539999999, 42.4776599],[-71.4642101, 42.4776598],[-71.4642095, 42.4776205],[-71.46434429999999, 42.4776195],[-71.4643456, 42.4777211]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4677227, 42.4812227],[-71.4677531, 42.4812029],[-71.46780649999999, 42.4812481],[-71.4677758, 42.481268],[-71.46779530000001, 42.4812845],[-71.4676687, 42.4813666],[-71.4675852, 42.4812959],[-71.4677121, 42.4812137],[-71.4677227, 42.4812227]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"10","addr:state":"MA","source:datetime":"2014-11-08T14:01:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4536749, 42.4942551],[-71.4536331, 42.4944591],[-71.453541, 42.4944488],[-71.453557, 42.4943708],[-71.4535449, 42.4943695],[-71.45356049999999, 42.4942931],[-71.4535241, 42.494289],[-71.45353420000001, 42.4942393],[-71.4536749, 42.4942551]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4653857, 42.4808355],[-71.4653701, 42.4808714],[-71.46545070000001, 42.4808943],[-71.4655307, 42.4810274],[-71.4654439, 42.481056],[-71.46539679999999, 42.4809778],[-71.4652598, 42.4809412],[-71.46532980000001, 42.4808181],[-71.4653857, 42.4808355]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697287, 42.490843],[-71.4697951, 42.4907714],[-71.4698676, 42.4908082],[-71.46975810000001, 42.4909264],[-71.4696821, 42.4908879],[-71.4696898, 42.4908796],[-71.46965369999999, 42.4908613],[-71.4696892, 42.4908229],[-71.4697287, 42.490843]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:05Z"},"geometry": {"type":"LineString","coordinates": [[-71.4782616, 42.4718334],[-71.47821930000001, 42.4718217],[-71.478188, 42.4718836],[-71.47810939999999, 42.4718618],[-71.4781404, 42.4718004]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4529551, 42.4914203],[-71.4529424, 42.4913584],[-71.4530305, 42.4913486],[-71.45307219999999, 42.4915525],[-71.4529713, 42.4915637],[-71.45294850000001, 42.4914522],[-71.4529752, 42.4914492],[-71.452969, 42.4914187],[-71.4529551, 42.4914203]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4780928, 42.4855231],[-71.47809839999999, 42.4856009],[-71.4781337, 42.4855996],[-71.478137, 42.4856446],[-71.4781019, 42.4856459],[-71.4781048, 42.4856855],[-71.47799740000001, 42.4856898],[-71.4779857, 42.4855273],[-71.4780928, 42.4855231]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46125979999999, 42.4791814],[-71.46123040000001, 42.4791825],[-71.4612343, 42.4792385],[-71.46114179999999, 42.4792419],[-71.4611343, 42.4791312],[-71.4611513, 42.4791305],[-71.46114729999999, 42.4790729],[-71.4612521, 42.479069],[-71.46125979999999, 42.4791814]]]}}, +{"type":"Feature","properties":{"addr:country":"US","denomination":"christian_science","addr:street":"Central Street","religion":"christian","addr:city":"Acton","building":"yes","addr:housenumber":"257","addr:state":"MA","name":"Christian Science Society of West Acton","amenity":"place_of_worship","source:datetime":"2015-01-23T23:51:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744728, 42.4754043],[-71.4744737, 42.4753844],[-71.47453609999999, 42.4753858],[-71.4745315, 42.4754905],[-71.47446840000001, 42.475489],[-71.4744699, 42.4754557],[-71.4743555, 42.475453],[-71.4743577, 42.4754015],[-71.4744728, 42.4754043]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4733207, 42.4728739],[-71.4731647, 42.4728798],[-71.4731665, 42.4729055],[-71.47305470000001, 42.4729098],[-71.4730515, 42.4728639],[-71.473157, 42.4728599],[-71.473151, 42.4727736],[-71.4733133, 42.4727674],[-71.4733207, 42.4728739]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4699368, 42.4850812],[-71.46996350000001, 42.4849371],[-71.47008219999999, 42.4849491],[-71.47006519999999, 42.4850412],[-71.47004560000001, 42.4850392],[-71.47002500000001, 42.4851507],[-71.46987, 42.485135],[-71.469881, 42.4850756],[-71.4699368, 42.4850812]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4506301, 42.4866935],[-71.4506267, 42.48659],[-71.45089110000001, 42.4865852],[-71.4508946, 42.4866893],[-71.4506882, 42.486693],[-71.4506887, 42.4867079],[-71.4506624, 42.4867083],[-71.4506619, 42.4866929],[-71.4506301, 42.4866935]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"14","addr:state":"MA","source:datetime":"2014-11-08T13:55:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4557798, 42.4966392],[-71.4558179, 42.4965972],[-71.4558996, 42.4966355],[-71.45586110000001, 42.4966824],[-71.455851, 42.4966779],[-71.4557346, 42.4968076],[-71.4556711, 42.4967763],[-71.4557897, 42.4966441],[-71.4557798, 42.4966392]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4672632, 42.4894819],[-71.4672853, 42.4894503],[-71.4673547, 42.4894769],[-71.46733500000001, 42.489505],[-71.46730820000001, 42.4894948],[-71.467252, 42.4895751],[-71.4671663, 42.4895423],[-71.4672202, 42.4894654],[-71.4672632, 42.4894819]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:03Z"},"geometry": {"type":"LineString","coordinates": [[-71.4778481, 42.4718104],[-71.4779415, 42.4718214],[-71.4778481, 42.4718104]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4518381, 42.4882176],[-71.45185840000001, 42.4881765],[-71.4519564, 42.4882029],[-71.4518645, 42.4883896],[-71.45177529999999, 42.4883656],[-71.4518257, 42.4882633],[-71.4518008, 42.4882566],[-71.4518221, 42.4882133],[-71.4518381, 42.4882176]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715558, 42.4898044],[-71.4716105, 42.4897545],[-71.4716856, 42.4897995],[-71.471598, 42.4898795],[-71.4715796, 42.4898685],[-71.4715174, 42.4899254],[-71.4714321, 42.4898771],[-71.4715301, 42.4897881],[-71.4715558, 42.4898044]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4515317, 42.4911198],[-71.4515272, 42.4910396],[-71.4516728, 42.4910352],[-71.4516733, 42.4910454],[-71.4517285, 42.4910437],[-71.4517294, 42.4911537],[-71.4516694, 42.491154],[-71.4516692, 42.4911175],[-71.4515317, 42.4911198]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4727264, 42.4718297],[-71.47305009999999, 42.4718443],[-71.4729, 42.4718508],[-71.4728977, 42.4718223],[-71.4727264, 42.4718297]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759632, 42.486863],[-71.4759358, 42.486891],[-71.4759796, 42.4869145],[-71.4758521, 42.4870449],[-71.4757661, 42.4869989],[-71.4758041, 42.48696],[-71.47578919999999, 42.4869521],[-71.4759062, 42.4868324],[-71.4759632, 42.486863]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48217150000001, 42.4755216],[-71.4821926, 42.4755521],[-71.482207, 42.4755467],[-71.4822829, 42.4756568],[-71.4821881, 42.4756926],[-71.48214539999999, 42.4756307],[-71.4821565, 42.4756265],[-71.48210229999999, 42.4755478],[-71.48217150000001, 42.4755216]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.470266, 42.4903539],[-71.47033, 42.4902878],[-71.47041160000001, 42.4903311],[-71.4702825, 42.4904644],[-71.4701974, 42.4904192],[-71.4702266, 42.4903891],[-71.4701654, 42.4903566],[-71.47020120000001, 42.4903196],[-71.470266, 42.4903539]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"18","source:datetime":"2014-04-11T16:40:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48167840000001, 42.4738067],[-71.481658, 42.4738529],[-71.4817223, 42.4738685],[-71.4816849, 42.4739533],[-71.4816628, 42.4739479],[-71.4816445, 42.4739893],[-71.4815394, 42.4739639],[-71.4816156, 42.4737914],[-71.48167840000001, 42.4738067]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4817142, 42.4767952],[-71.4817029, 42.4768254],[-71.4818595, 42.4768575],[-71.4818343, 42.4769248],[-71.4817089, 42.4768991],[-71.4817165, 42.4768788],[-71.4815919, 42.4768532],[-71.4816208, 42.476776],[-71.4817142, 42.4767952]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4773416, 42.4932205],[-71.477305, 42.4931432],[-71.4774571, 42.4931037],[-71.47756219999999, 42.4933253],[-71.4774469, 42.4933552],[-71.4773939, 42.4932436],[-71.4774402, 42.4932315],[-71.4774247, 42.4931989],[-71.4773416, 42.4932205]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4645517, 42.4838702],[-71.464551, 42.4838597],[-71.46460570000001, 42.4838576],[-71.4646067, 42.4838723],[-71.4646485, 42.4838707],[-71.4646542, 42.4839532],[-71.4645056, 42.4839588],[-71.4644996, 42.4838722],[-71.4645517, 42.4838702]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"26","addr:state":"MA","source:datetime":"2014-11-08T14:01:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4534869, 42.4914176],[-71.45347820000001, 42.491384],[-71.4535257, 42.4913772],[-71.45349760000001, 42.4912688],[-71.4536014, 42.4912541],[-71.4536548, 42.491461],[-71.4535364, 42.4914777],[-71.45351960000001, 42.491413],[-71.4534869, 42.4914176]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:56:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706023, 42.4743157],[-71.47061600000001, 42.4741769],[-71.47075839999999, 42.4741841],[-71.4707458, 42.4743215],[-71.47068849999999, 42.4743186],[-71.47068729999999, 42.474331],[-71.470657, 42.4743295],[-71.470658, 42.4743175],[-71.4706023, 42.4743157]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46768590000001, 42.4936686],[-71.46770669999999, 42.4936369],[-71.467755, 42.4936542],[-71.4677348, 42.493685],[-71.4677518, 42.4936912],[-71.4676971, 42.4937746],[-71.46759609999999, 42.4937384],[-71.46765019999999, 42.4936558],[-71.46768590000001, 42.4936686]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4623532, 42.4908584],[-71.46236810000001, 42.4908834],[-71.4624132, 42.4908687],[-71.46245759999999, 42.4909432],[-71.4623678, 42.4909725],[-71.462352, 42.490946],[-71.46226369999999, 42.4909748],[-71.4622202, 42.4909017],[-71.4623532, 42.4908584]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4550369, 42.4808482],[-71.4550321, 42.4807755],[-71.4551608, 42.4807708],[-71.45516019999999, 42.480761],[-71.4552644, 42.4807572],[-71.4552704, 42.4808486],[-71.4551616, 42.4808525],[-71.455161, 42.4808437],[-71.4550369, 42.4808482]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4682359, 42.4879841],[-71.4682498, 42.4879587],[-71.46832209999999, 42.4879804],[-71.4682318, 42.4881455],[-71.4681506, 42.4881211],[-71.4681824, 42.4880629],[-71.4681708, 42.4880595],[-71.46821540000001, 42.4879779],[-71.4682359, 42.4879841]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"5","source:datetime":"2014-04-11T16:40:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48330730000001, 42.4753573],[-71.48329219999999, 42.4753706],[-71.48340020000001, 42.4754376],[-71.48334490000001, 42.4754865],[-71.4832371, 42.4754196],[-71.4832498, 42.4754084],[-71.4831983, 42.4753764],[-71.4832559, 42.4753255],[-71.48330730000001, 42.4753573]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618389, 42.4736163],[-71.4618289, 42.4735342],[-71.4620954, 42.4735163],[-71.4621049, 42.4735943],[-71.4620343, 42.473599],[-71.4620386, 42.4736343],[-71.46192929999999, 42.4736416],[-71.46192550000001, 42.4736105],[-71.4618389, 42.4736163]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706936, 42.4956481],[-71.4706798, 42.4956708],[-71.4707221, 42.495685],[-71.4706875, 42.4957418],[-71.47065379999999, 42.4957305],[-71.4706281, 42.4957725],[-71.4705256, 42.4957382],[-71.4705998, 42.4956167],[-71.4706936, 42.4956481]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4596219, 42.4730123],[-71.4596229, 42.4730373],[-71.4596943, 42.4730357],[-71.4596998, 42.4731768],[-71.4596312, 42.4731783],[-71.4596315, 42.4731876],[-71.4594377, 42.4731918],[-71.45943080000001, 42.4730164],[-71.4596219, 42.4730123]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4708006, 42.4826429],[-71.47092240000001, 42.4825326],[-71.4710353, 42.4826009],[-71.4709762, 42.4826543],[-71.4709616, 42.4826455],[-71.47085130000001, 42.4827453],[-71.4707186, 42.482665],[-71.47076610000001, 42.482622],[-71.4708006, 42.4826429]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4668155, 42.4947295],[-71.4667583, 42.4946963],[-71.4667795, 42.4946762],[-71.466655, 42.4946041],[-71.4667116, 42.4945506],[-71.46696470000001, 42.4946972],[-71.4669052, 42.4947534],[-71.4668339, 42.4947122],[-71.4668155, 42.4947295]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45961, 42.4811556],[-71.45951650000001, 42.4811402],[-71.4595482, 42.4810352],[-71.4596335, 42.4810493],[-71.459641, 42.4810244],[-71.4597454, 42.4810417],[-71.4597101, 42.4811586],[-71.45961389999999, 42.4811427],[-71.45961, 42.4811556]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4634681, 42.4899906],[-71.4636931, 42.4900303],[-71.46366879999999, 42.4901058],[-71.4635538, 42.4900855],[-71.463544, 42.490116],[-71.4634736, 42.4901035],[-71.46347900000001, 42.4900869],[-71.4634393, 42.4900799],[-71.4634681, 42.4899906]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4734242, 42.4740509],[-71.4734246, 42.4740359],[-71.4735104, 42.4740371],[-71.4735079, 42.4741338],[-71.4734158, 42.4741325],[-71.4734162, 42.474119],[-71.4732613, 42.4741167],[-71.4732631, 42.4740485],[-71.4734242, 42.4740509]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"144","addr:state":"MA","source:datetime":"2014-11-08T14:01:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4531789, 42.4961498],[-71.4531077, 42.496061],[-71.45318709999999, 42.4960262],[-71.45326660000001, 42.4961254],[-71.4533242, 42.4961001],[-71.4533594, 42.496144],[-71.4532399, 42.4961964],[-71.4531965, 42.4961421],[-71.4531789, 42.4961498]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4626756, 42.4794148],[-71.46262369999999, 42.4792516],[-71.4627282, 42.4792334],[-71.4627643, 42.4793465],[-71.4628021, 42.4793399],[-71.4628331, 42.4794374],[-71.4627376, 42.4794541],[-71.4627225, 42.4794066],[-71.4626756, 42.4794148]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4526001, 42.49047],[-71.45260879999999, 42.4905058],[-71.4528262, 42.4904769],[-71.45284119999999, 42.4905388],[-71.4526998, 42.4905576],[-71.45270290000001, 42.4905704],[-71.45256980000001, 42.4905881],[-71.45254300000001, 42.4904776],[-71.4526001, 42.49047]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4682986, 42.492963],[-71.46836, 42.4929138],[-71.468436, 42.4929658],[-71.4683524, 42.4930327],[-71.4683004, 42.4929972],[-71.46820889999999, 42.4930705],[-71.46814569999999, 42.4930272],[-71.46825459999999, 42.4929329],[-71.4682986, 42.492963]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697201, 42.4736161],[-71.46969470000001, 42.4736071],[-71.4697007, 42.4735977],[-71.46964989999999, 42.4735797],[-71.4697287, 42.4734578],[-71.4698568, 42.4735032],[-71.46977800000001, 42.4736251],[-71.4697262, 42.4736067],[-71.4697201, 42.4736161]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4746798, 42.4772277],[-71.4746847, 42.477254],[-71.474698, 42.4772526],[-71.47471400000001, 42.4773379],[-71.4746256, 42.477347],[-71.47461, 42.4772638],[-71.47463689999999, 42.477261],[-71.4746316, 42.4772326],[-71.4746798, 42.4772277]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4792308, 42.4777899],[-71.4792557, 42.4778296],[-71.4792707, 42.4778245],[-71.47932470000001, 42.4779105],[-71.47921770000001, 42.4779473],[-71.4791609, 42.4778568],[-71.47917099999999, 42.4778533],[-71.4791489, 42.4778181],[-71.4792308, 42.4777899]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47377059999999, 42.4758064],[-71.4737662, 42.4759303],[-71.4739399, 42.4759336],[-71.4739364, 42.4760323],[-71.47391260000001, 42.4760475],[-71.4736249, 42.4760379],[-71.47360999999999, 42.4760216],[-71.47361770000001, 42.4758035],[-71.47377059999999, 42.4758064]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4725397, 42.4879224],[-71.4725996, 42.4879435],[-71.4726199, 42.4879118],[-71.4726788, 42.4879326],[-71.47262859999999, 42.4880105],[-71.472599, 42.488],[-71.47259149999999, 42.4880117],[-71.4725025, 42.4879803],[-71.4725397, 42.4879224]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Wright Terrace","addr:city":"Acton","building":"yes","addr:housenumber":"18","addr:state":"MA","source:datetime":"2014-11-18T02:34:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4770223, 42.4779382],[-71.47704, 42.4778894],[-71.4772133, 42.4779314],[-71.4771494, 42.4780913],[-71.4770127, 42.4780601],[-71.4770235, 42.4780341],[-71.4769391, 42.4780149],[-71.4769754, 42.4779276],[-71.4770223, 42.4779382]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Mead Terrace","addr:city":"Acton","building":"yes","addr:housenumber":"11","addr:state":"MA","source:datetime":"2014-11-18T02:34:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763042, 42.4774316],[-71.4763044, 42.4773033],[-71.47646229999999, 42.4773009],[-71.4764419, 42.4775235],[-71.47629980000001, 42.4775226],[-71.4763001, 42.4774882],[-71.4762678, 42.477488],[-71.4762684, 42.4774314],[-71.4763042, 42.4774316]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703392, 42.4739321],[-71.47031149999999, 42.4739228],[-71.4703169, 42.4739141],[-71.4702644, 42.4738965],[-71.4703385, 42.4737753],[-71.4704734, 42.4738205],[-71.4703993, 42.4739417],[-71.4703445, 42.4739233],[-71.4703392, 42.4739321]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4765989, 42.4760441],[-71.4766071, 42.4760077],[-71.4767602, 42.4760267],[-71.4767356, 42.4761353],[-71.47659520000001, 42.4761178],[-71.4765974, 42.4761081],[-71.47655709999999, 42.4761031],[-71.4765713, 42.4760407],[-71.4765989, 42.4760441]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47185570000001, 42.4782695],[-71.47177670000001, 42.4781907],[-71.4718989, 42.4781235],[-71.4719817, 42.4782062],[-71.4719595, 42.4782184],[-71.47199519999999, 42.478254],[-71.4719186, 42.478296],[-71.4718791, 42.4782566],[-71.47185570000001, 42.4782695]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4688759, 42.4950003],[-71.46883, 42.4949868],[-71.4688622, 42.4949265],[-71.46908689999999, 42.4949923],[-71.46904809999999, 42.495065],[-71.4689103, 42.4950246],[-71.4689304, 42.494987],[-71.46888939999999, 42.494975],[-71.4688759, 42.4950003]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47023969999999, 42.4931427],[-71.4702286, 42.4931339],[-71.47032110000001, 42.49307],[-71.47039719999999, 42.4931302],[-71.4703035, 42.493195],[-71.4702954, 42.4931886],[-71.47023470000001, 42.4932305],[-71.4701778, 42.4931854],[-71.47023969999999, 42.4931427]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4717665, 42.4937267],[-71.4717366, 42.4937045],[-71.4716914, 42.4937379],[-71.47160599999999, 42.4936746],[-71.4717445, 42.4935722],[-71.4719182, 42.4937009],[-71.4718402, 42.4937586],[-71.4717818, 42.4937154],[-71.4717665, 42.4937267]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45383339999999, 42.4978092],[-71.4538046, 42.4977765],[-71.45383510000001, 42.4977618],[-71.4537862, 42.4977066],[-71.45387049999999, 42.4976659],[-71.45397509999999, 42.4977844],[-71.45389230000001, 42.4978243],[-71.4538654, 42.4977938],[-71.45383339999999, 42.4978092]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738241, 42.4756081],[-71.4738201, 42.4756605],[-71.4737629, 42.4756581],[-71.47376010000001, 42.4756956],[-71.47382109999999, 42.4756981],[-71.4738169, 42.4757537],[-71.473626, 42.4757458],[-71.473637, 42.4756004],[-71.4738241, 42.4756081]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4597176, 42.4755521],[-71.4597156, 42.4755873],[-71.4597735, 42.4755891],[-71.45977070000001, 42.4756398],[-71.4596844, 42.4756371],[-71.45968480000001, 42.4756286],[-71.4595669, 42.475625],[-71.45957129999999, 42.4755476],[-71.4597176, 42.4755521]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4671541, 42.4886692],[-71.4671797, 42.4886312],[-71.4673243, 42.4886846],[-71.4672733, 42.4887603],[-71.4672257, 42.4887427],[-71.46720310000001, 42.4887762],[-71.4671531, 42.4887578],[-71.4672011, 42.4886865],[-71.4671541, 42.4886692]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4667024, 42.4872566],[-71.4666617, 42.4873237],[-71.4666793, 42.4873295],[-71.466622, 42.4874241],[-71.4665084, 42.4873864],[-71.4665705, 42.4872839],[-71.4665421, 42.4872744],[-71.4665779, 42.4872153],[-71.4667024, 42.4872566]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4727716, 42.4957541],[-71.47272289999999, 42.495601],[-71.47286219999999, 42.4955711],[-71.4728776, 42.4956104],[-71.4728453, 42.4956173],[-71.4729122, 42.4957931],[-71.4728149, 42.4958149],[-71.4727912, 42.4957502],[-71.4727716, 42.4957541]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"27","addr:state":"MA","source:datetime":"2014-11-08T13:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4578071, 42.4950196],[-71.4577424, 42.4951137],[-71.45779539999999, 42.4951336],[-71.4577726, 42.4951667],[-71.4576466, 42.4951192],[-71.4576617, 42.4950972],[-71.45764149999999, 42.4950896],[-71.45771379999999, 42.4949845],[-71.4578071, 42.4950196]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4747665, 42.4784656],[-71.4747565, 42.4784177],[-71.4749624, 42.4783941],[-71.4749711, 42.4784359],[-71.47498659999999, 42.4784341],[-71.47500170000001, 42.4785065],[-71.4748996, 42.4785182],[-71.4748857, 42.478452],[-71.4747665, 42.4784656]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4746339, 42.4721519],[-71.47463209999999, 42.4721805],[-71.47474, 42.4721843],[-71.47473530000001, 42.4722589],[-71.474621, 42.4722549],[-71.4746237, 42.4722122],[-71.4745401, 42.4722093],[-71.47454399999999, 42.4721488],[-71.4746339, 42.4721519]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4645862, 42.4805082],[-71.4646194, 42.4804576],[-71.4645823, 42.4804443],[-71.4646451, 42.4803487],[-71.4647363, 42.4803815],[-71.46460209999999, 42.480586],[-71.4645258, 42.4805586],[-71.4645641, 42.4805002],[-71.4645862, 42.4805082]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4779939, 42.4905576],[-71.4779841, 42.4905831],[-71.4780303, 42.4905929],[-71.4780109, 42.490643],[-71.4779899, 42.4906385],[-71.477975, 42.490677],[-71.4778769, 42.4906562],[-71.4779211, 42.4905422],[-71.4779939, 42.4905576]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4550746, 42.4889027],[-71.4550595, 42.4889058],[-71.4550891, 42.4889841],[-71.45491079999999, 42.489021],[-71.4548801, 42.4889398],[-71.4550258, 42.4889096],[-71.45501760000001, 42.4888877],[-71.45506520000001, 42.4888778],[-71.4550746, 42.4889027]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4514192, 42.473089],[-71.45142970000001, 42.4730152],[-71.4516976, 42.4730361],[-71.4516876, 42.4731064],[-71.4516028, 42.4730998],[-71.4515986, 42.4731296],[-71.45153860000001, 42.473125],[-71.4515423, 42.4730986],[-71.4514192, 42.473089]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46939570000001, 42.4891406],[-71.4693736, 42.4891812],[-71.4694124, 42.4891928],[-71.4693602, 42.4892888],[-71.4692754, 42.4892635],[-71.4692924, 42.4892321],[-71.469251, 42.4892198],[-71.4693082, 42.4891145],[-71.46939570000001, 42.4891406]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4664392, 42.4833275],[-71.4663554, 42.4834235],[-71.46634109999999, 42.4834167],[-71.4663216, 42.483439],[-71.4663321, 42.4834441],[-71.4662775, 42.4835066],[-71.4661916, 42.4834655],[-71.4663496, 42.4832846],[-71.4664392, 42.4833275]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4552406, 42.4899119],[-71.4552661, 42.4898354],[-71.4554861, 42.4898755],[-71.4554601, 42.4899535],[-71.45530599999999, 42.4899253],[-71.45529190000001, 42.4899676],[-71.4551508, 42.4899419],[-71.4551654, 42.4898982],[-71.4552406, 42.4899119]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"17","source:datetime":"2014-04-11T16:40:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4822162, 42.4739205],[-71.482232, 42.4738905],[-71.4823224, 42.4739167],[-71.4823071, 42.4739456],[-71.48226, 42.473932],[-71.48219659999999, 42.4740518],[-71.48209869999999, 42.4740234],[-71.4821614, 42.4739046],[-71.4822162, 42.4739205]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4768476, 42.4834152],[-71.47682930000001, 42.4833454],[-71.4769185, 42.4833326],[-71.47693649999999, 42.4834011],[-71.47698440000001, 42.4833942],[-71.4770058, 42.4834755],[-71.47692670000001, 42.4834869],[-71.47690559999999, 42.4834068],[-71.4768476, 42.4834152]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46860940000001, 42.4926965],[-71.4685947, 42.4926854],[-71.4686825, 42.4926217],[-71.4687464, 42.4926699],[-71.4686703, 42.4927252],[-71.4686612, 42.4927183],[-71.46863, 42.4927409],[-71.4685899, 42.4927107],[-71.46860940000001, 42.4926965]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4823241, 42.4769161],[-71.4822759, 42.476899],[-71.48228279999999, 42.4768885],[-71.4822269, 42.4768686],[-71.4822719, 42.4767989],[-71.48242500000001, 42.4768532],[-71.4823806, 42.4769218],[-71.4823317, 42.4769045],[-71.4823241, 42.4769161]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4629296, 42.4817362],[-71.4629118, 42.4817694],[-71.46303349999999, 42.4818053],[-71.4630001, 42.4818673],[-71.4627792, 42.4818021],[-71.46280729999999, 42.48175],[-71.4627631, 42.481737],[-71.4627863, 42.4816939],[-71.4629296, 42.4817362]]]}}, +{"type":"Feature","properties":{"addr:street":"Massachusetts Avenue","building":"yes","addr:housenumber":"418","name":"Acton Dental Building","amenity":"dentist","source:datetime":"2013-10-11T02:01:45Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.45251380000001, 42.4741518],[-71.4525088, 42.4740615],[-71.4526884, 42.474056],[-71.4526833, 42.4739645],[-71.45282539999999, 42.4739602],[-71.45284030000001, 42.4742311],[-71.45271219999999, 42.4742349],[-71.45270720000001, 42.4741459],[-71.45251380000001, 42.4741518]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4735776, 42.4832505],[-71.47347120000001, 42.4832139],[-71.4735103, 42.4831515],[-71.4736047, 42.4831839],[-71.4736324, 42.4831397],[-71.4737579, 42.4831828],[-71.4737071, 42.4832638],[-71.4735937, 42.4832248],[-71.4735776, 42.4832505]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4652327, 42.4788037],[-71.46526470000001, 42.4787903],[-71.4653057, 42.4788441],[-71.46527810000001, 42.4788556],[-71.4653175, 42.4789073],[-71.465227, 42.4789451],[-71.46513349999999, 42.4788225],[-71.46521970000001, 42.4787865],[-71.4652327, 42.4788037]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713632, 42.4879223],[-71.4713378, 42.4879606],[-71.4713801, 42.487976],[-71.471349, 42.4880229],[-71.4713026, 42.4880061],[-71.4713122, 42.4879916],[-71.47121749999999, 42.4879572],[-71.4712644, 42.4878865],[-71.4713632, 42.4879223]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4593838, 42.4732931],[-71.45931090000001, 42.4732963],[-71.4593167, 42.4733676],[-71.45918589999999, 42.4733734],[-71.45917559999999, 42.4732467],[-71.4593026, 42.4732411],[-71.45929870000001, 42.4731934],[-71.4593754, 42.47319],[-71.4593838, 42.4732931]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4743513, 42.4876663],[-71.4744177, 42.4876224],[-71.4744865, 42.4876795],[-71.4744402, 42.4877101],[-71.47446720000001, 42.4877324],[-71.47435, 42.4878099],[-71.4742048, 42.4876896],[-71.4743019, 42.4876254],[-71.4743513, 42.4876663]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46994170000001, 42.4738157],[-71.4699196, 42.4738079],[-71.46992590000001, 42.473798],[-71.4698722, 42.4737791],[-71.46995099999999, 42.4736566],[-71.4700824, 42.4737029],[-71.4700036, 42.4738254],[-71.469948, 42.4738058],[-71.46994170000001, 42.4738157]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543922, 42.4718195],[-71.45439229999999, 42.4718339],[-71.45447609999999, 42.4718335],[-71.4544762, 42.4718438],[-71.4545717, 42.4718434],[-71.45457209999999, 42.4719055],[-71.4542691, 42.4719067],[-71.4542685, 42.47182],[-71.4543922, 42.4718195]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48349229999999, 42.4770595],[-71.4834723, 42.4771032],[-71.48362419999999, 42.4771413],[-71.48359069999999, 42.4772144],[-71.4834358, 42.4771756],[-71.483451, 42.4771422],[-71.4833162, 42.4771084],[-71.4833543, 42.4770249],[-71.48349229999999, 42.4770595]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46816629999999, 42.4871074],[-71.4682328, 42.4870346],[-71.46831880000001, 42.4870777],[-71.468251, 42.487152],[-71.46821679999999, 42.4871349],[-71.46814259999999, 42.4872162],[-71.46807010000001, 42.4871799],[-71.46814569999999, 42.4870971],[-71.46816629999999, 42.4871074]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4584294, 42.4875248],[-71.4584907, 42.4875929],[-71.458404, 42.4876357],[-71.4584278, 42.4876621],[-71.458387, 42.4876822],[-71.4583627, 42.4876552],[-71.4582453, 42.4877131],[-71.4581845, 42.4876456],[-71.4584294, 42.4875248]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4624745, 42.4822101],[-71.4623897, 42.4821925],[-71.46241499999999, 42.4821257],[-71.4624745, 42.482138],[-71.46248300000001, 42.4821156],[-71.4627108, 42.4821629],[-71.46268209999999, 42.4822386],[-71.46247959999999, 42.4821966],[-71.4624745, 42.4822101]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46504899999999, 42.4797536],[-71.46507630000001, 42.4797058],[-71.4650522, 42.4796983],[-71.46508729999999, 42.4796369],[-71.465163, 42.4796607],[-71.4650458, 42.4798654],[-71.46494730000001, 42.4798345],[-71.465002, 42.4797388],[-71.46504899999999, 42.4797536]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4788775, 42.476311],[-71.47886, 42.4763683],[-71.47887919999999, 42.4763715],[-71.4788679, 42.4764084],[-71.47889240000001, 42.4764124],[-71.4788791, 42.4764561],[-71.47870399999999, 42.4764268],[-71.478746, 42.476289],[-71.4788775, 42.476311]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47227770000001, 42.4893332],[-71.47220059999999, 42.4893831],[-71.4722146, 42.4893949],[-71.47216469999999, 42.4894273],[-71.47215559999999, 42.4894196],[-71.4720717, 42.489474],[-71.4720098, 42.4894216],[-71.4722208, 42.4892851],[-71.47227770000001, 42.4893332]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4635997, 42.4734489],[-71.463605, 42.4734086],[-71.46370020000001, 42.4734154],[-71.46368649999999, 42.4735206],[-71.46366999999999, 42.4735194],[-71.4636643, 42.4735638],[-71.46356919999999, 42.473557],[-71.4635834, 42.4734478],[-71.4635997, 42.4734489]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:34Z"},"geometry": {"type":"LineString","coordinates": [[-71.45312370000001, 42.4718649],[-71.4530508, 42.4718182]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.473071, 42.4751043],[-71.47307189999999, 42.4751448],[-71.47308339999999, 42.4751446],[-71.47308409999999, 42.4751741],[-71.4730727, 42.4751743],[-71.4730744, 42.4752469],[-71.4729262, 42.4752489],[-71.4729227, 42.4751063],[-71.473071, 42.4751043]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46841209999999, 42.4805778],[-71.46847870000001, 42.4805184],[-71.46857749999999, 42.4805789],[-71.46841000000001, 42.4807286],[-71.4683108, 42.4806678],[-71.46837789999999, 42.4806079],[-71.468341, 42.4805853],[-71.4683749, 42.480555],[-71.46841209999999, 42.4805778]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757046, 42.4774974],[-71.47570899999999, 42.4774617],[-71.4758056, 42.4774681],[-71.47580120000001, 42.4775041],[-71.47582250000001, 42.4775055],[-71.47581150000001, 42.4775947],[-71.47566930000001, 42.4775852],[-71.4756802, 42.4774958],[-71.4757046, 42.4774974]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48115060000001, 42.4718866],[-71.4811818, 42.4719682],[-71.4812148, 42.4719612],[-71.48123529999999, 42.4720147],[-71.48120179999999, 42.4720218],[-71.481206, 42.4720327],[-71.4810954, 42.4720559],[-71.48103949999999, 42.4719099],[-71.48115060000001, 42.4718866]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47831069999999, 42.4887966],[-71.47832769999999, 42.4887206],[-71.4784276, 42.4887329],[-71.47841219999999, 42.4888019],[-71.478453, 42.4888069],[-71.4784396, 42.4888669],[-71.47826790000001, 42.4888459],[-71.4782798, 42.4887928],[-71.47831069999999, 42.4887966]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46091439999999, 42.4885075],[-71.46091920000001, 42.4884509],[-71.4610368, 42.4884564],[-71.4610279, 42.4885613],[-71.46090909999999, 42.4885558],[-71.46091029999999, 42.4885407],[-71.46085979999999, 42.4885384],[-71.4608626, 42.4885051],[-71.46091439999999, 42.4885075]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4711449, 42.4780763],[-71.471175, 42.4780493],[-71.4712486, 42.4780944],[-71.47113210000001, 42.4781985],[-71.471057, 42.4781524],[-71.47110189999999, 42.4781123],[-71.4710666, 42.4780906],[-71.471108, 42.4780537],[-71.4711449, 42.4780763]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.458956, 42.4838646],[-71.4589495, 42.4838249],[-71.4590118, 42.4838194],[-71.45900810000001, 42.4837965],[-71.4591045, 42.483788],[-71.45912060000001, 42.4838876],[-71.4590225, 42.4838962],[-71.4590166, 42.4838593],[-71.458956, 42.4838646]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45383099999999, 42.4899655],[-71.4538926, 42.4899159],[-71.4539577, 42.4899601],[-71.4537883, 42.4901166],[-71.4537117, 42.4900645],[-71.4538036, 42.4899906],[-71.4537813, 42.4899755],[-71.4538108, 42.4899517],[-71.45383099999999, 42.4899655]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48122360000001, 42.4763876],[-71.4811945, 42.4764559],[-71.481223, 42.4764626],[-71.4812032, 42.476509],[-71.48116400000001, 42.4764999],[-71.48113979999999, 42.4765567],[-71.4810437, 42.4765342],[-71.4811169, 42.4763627],[-71.48122360000001, 42.4763876]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4840162, 42.4721812],[-71.48400580000001, 42.472196],[-71.4840568, 42.4722158],[-71.484067, 42.4722013],[-71.4841593, 42.4722369],[-71.48408929999999, 42.4723362],[-71.4838321, 42.4722368],[-71.4839023, 42.4721372],[-71.4840162, 42.4721812]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4516634, 42.4867105],[-71.4515787, 42.4866991],[-71.451594, 42.4866368],[-71.4518779, 42.4866752],[-71.45184980000001, 42.4867889],[-71.45173819999999, 42.4867738],[-71.45175399999999, 42.4867098],[-71.4516665, 42.486698],[-71.4516634, 42.4867105]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47259440000001, 42.4909455],[-71.47259889999999, 42.4909825],[-71.472651, 42.490979],[-71.4726593, 42.491047],[-71.472615, 42.49105],[-71.4726187, 42.4910797],[-71.47252640000001, 42.491086],[-71.4725098, 42.4909512],[-71.47259440000001, 42.4909455]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"161","addr:state":"MA","source:datetime":"2014-11-08T14:01:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4542874, 42.498152],[-71.454381, 42.4982206],[-71.45440499999999, 42.4982027],[-71.4544958, 42.4982693],[-71.45445839999999, 42.4982972],[-71.4544841, 42.4983161],[-71.4544483, 42.4983428],[-71.4542383, 42.4981887],[-71.4542874, 42.498152]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Wright Terrace","addr:city":"Acton","building":"yes","addr:housenumber":"12","addr:state":"MA","source:datetime":"2014-11-18T02:34:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4777292, 42.4774459],[-71.4777407, 42.4774142],[-71.47786840000001, 42.4774395],[-71.4778348, 42.4775325],[-71.4777002, 42.4775058],[-71.4777112, 42.4774756],[-71.47768670000001, 42.4774708],[-71.4776979, 42.4774397],[-71.4777292, 42.4774459]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47101720000001, 42.4789461],[-71.470983, 42.4789785],[-71.4710171, 42.4789982],[-71.4709708, 42.479042],[-71.47088479999999, 42.4789923],[-71.4708974, 42.4789803],[-71.47083360000001, 42.4789434],[-71.4709014, 42.4788791],[-71.47101720000001, 42.4789461]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.470429, 42.4900944],[-71.4704806, 42.4901295],[-71.47055930000001, 42.4900661],[-71.4706346, 42.4901172],[-71.470561, 42.4901765],[-71.4705338, 42.490158],[-71.470488, 42.4901949],[-71.4703884, 42.4901271],[-71.470429, 42.4900944]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4661076, 42.4825446],[-71.4660901, 42.482637],[-71.466042, 42.482632],[-71.46603709999999, 42.4826577],[-71.4660626, 42.4826603],[-71.4660532, 42.4827101],[-71.4659748, 42.4827019],[-71.4660066, 42.4825341],[-71.4661076, 42.4825446]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46170480000001, 42.4904462],[-71.46173229999999, 42.4904018],[-71.4617935, 42.4904226],[-71.4617666, 42.490466],[-71.4617777, 42.4904697],[-71.46171990000001, 42.4905631],[-71.46162529999999, 42.490531],[-71.46168249999999, 42.4904387],[-71.46170480000001, 42.4904462]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"39","addr:state":"MA","source:datetime":"2014-11-08T13:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4591716, 42.4936802],[-71.4591011, 42.4937316],[-71.4591617, 42.4937772],[-71.459091, 42.4938288],[-71.4589703, 42.4937382],[-71.4589969, 42.4937188],[-71.4589842, 42.4937093],[-71.4590989, 42.4936257],[-71.4591716, 42.4936802]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4700526, 42.4820088],[-71.47010229999999, 42.4819247],[-71.4702039, 42.4819576],[-71.4701568, 42.4820374],[-71.4701263, 42.4820275],[-71.4700706, 42.4821217],[-71.4699804, 42.4820924],[-71.47003340000001, 42.4820026],[-71.4700526, 42.4820088]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4750226, 42.477123],[-71.47500770000001, 42.4770568],[-71.47518549999999, 42.4770349],[-71.4752009, 42.4771038],[-71.4751339, 42.477112],[-71.47513669999999, 42.4771245],[-71.4750698, 42.4771327],[-71.4750664, 42.4771176],[-71.4750226, 42.477123]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47585410000001, 42.4800316],[-71.4758672, 42.4800249],[-71.4759718, 42.4801373],[-71.4759598, 42.4801435],[-71.4760038, 42.4801908],[-71.4759133, 42.480237],[-71.4757242, 42.4800338],[-71.4758137, 42.4799881],[-71.47585410000001, 42.4800316]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4515322, 42.4724917],[-71.45153089999999, 42.472524],[-71.4516464, 42.4725267],[-71.4516438, 42.4725894],[-71.451576, 42.4725878],[-71.4515754, 42.4726002],[-71.4514805, 42.472598],[-71.45148500000001, 42.4724906],[-71.4515322, 42.4724917]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47903169999999, 42.4809842],[-71.479117, 42.4809623],[-71.4791477, 42.4810285],[-71.479027, 42.4810591],[-71.47903770000001, 42.4810823],[-71.4789112, 42.4811175],[-71.4788556, 42.4810171],[-71.4790266, 42.4809711],[-71.47903169999999, 42.4809842]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4700733, 42.4879539],[-71.4701026, 42.4879126],[-71.4702015, 42.4879511],[-71.4701712, 42.4879937],[-71.47013370000001, 42.4879792],[-71.4700716, 42.4880666],[-71.46997039999999, 42.4880272],[-71.47003340000001, 42.4879384],[-71.4700733, 42.4879539]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4772238, 42.4843681],[-71.4772356, 42.4844071],[-71.4771963, 42.4844137],[-71.4772043, 42.4844402],[-71.47725629999999, 42.4844315],[-71.4772981, 42.4845694],[-71.47720940000001, 42.4845841],[-71.4771477, 42.4843808],[-71.4772238, 42.4843681]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4614572, 42.4891576],[-71.46142709999999, 42.4890707],[-71.4615689, 42.4890438],[-71.4615742, 42.4890591],[-71.46165259999999, 42.4890443],[-71.46169020000001, 42.489153],[-71.46158149999999, 42.4891736],[-71.4615687, 42.4891365],[-71.4614572, 42.4891576]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4737964, 42.4747776],[-71.4737988, 42.4747204],[-71.4738535, 42.4747213],[-71.4738474, 42.4748699],[-71.4737614, 42.4748683],[-71.4737624, 42.4748442],[-71.47370789999999, 42.474843],[-71.4737107, 42.4747757],[-71.4737964, 42.4747776]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47674600000001, 42.474832],[-71.4767042, 42.4748247],[-71.4767068, 42.4748166],[-71.47665720000001, 42.4748079],[-71.4766845, 42.4747226],[-71.47692859999999, 42.4747653],[-71.4769022, 42.4748478],[-71.4767495, 42.4748211],[-71.47674600000001, 42.474832]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47521620000001, 42.4772639],[-71.47516950000001, 42.4772673],[-71.4751773, 42.4773249],[-71.47506970000001, 42.4773328],[-71.4750615, 42.4772718],[-71.4751444, 42.4772657],[-71.4751389, 42.4772245],[-71.47521020000001, 42.4772192],[-71.47521620000001, 42.4772639]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46951319999999, 42.4873616],[-71.46939690000001, 42.4873271],[-71.46942970000001, 42.4872665],[-71.4695493, 42.487302],[-71.469562, 42.4872785],[-71.4696726, 42.4873113],[-71.4696387, 42.487374],[-71.4695248, 42.4873402],[-71.46951319999999, 42.4873616]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46110349999999, 42.4741107],[-71.46110779999999, 42.4740382],[-71.46125309999999, 42.474043],[-71.4612487, 42.4741164],[-71.46123729999999, 42.474116],[-71.461232, 42.4742032],[-71.4611353, 42.4742],[-71.46114059999999, 42.4741119],[-71.46110349999999, 42.4741107]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46013170000001, 42.48987],[-71.4599956, 42.4898718],[-71.4599959, 42.4898815],[-71.4599565, 42.4898821],[-71.45995550000001, 42.4898412],[-71.4599944, 42.4898407],[-71.45999380000001, 42.4898144],[-71.4601303, 42.4898126],[-71.46013170000001, 42.48987]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48104789999999, 42.4754314],[-71.4810305, 42.4754599],[-71.48107229999999, 42.4754739],[-71.48098659999999, 42.4756141],[-71.48090089999999, 42.4755854],[-71.4809285, 42.4755402],[-71.4809178, 42.4755366],[-71.4809932, 42.4754131],[-71.48104789999999, 42.4754314]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4650931, 42.4895121],[-71.4651215, 42.4894724],[-71.4651807, 42.4894956],[-71.46515669999999, 42.4895292],[-71.46525699999999, 42.4895685],[-71.46520599999999, 42.4896397],[-71.4649964, 42.4895575],[-71.4650429, 42.4894925],[-71.4650931, 42.4895121]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4631035, 42.4718962],[-71.463249, 42.4718202],[-71.4631933, 42.4719304],[-71.46317569999999, 42.4719255],[-71.46315610000001, 42.4719643],[-71.4630568, 42.4719368],[-71.4630805, 42.4718898],[-71.4631035, 42.4718962]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47391930000001, 42.4721411],[-71.4739002, 42.4721416],[-71.47390350000001, 42.4722064],[-71.47376439999999, 42.4722103],[-71.4737609, 42.472143],[-71.4737812, 42.4721424],[-71.47377609999999, 42.4720439],[-71.47391399999999, 42.47204],[-71.47391930000001, 42.4721411]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4780994, 42.4896183],[-71.4781255, 42.4894904],[-71.47825419999999, 42.4895048],[-71.4782301, 42.4896226],[-71.4782168, 42.4896211],[-71.4782041, 42.4896831],[-71.4781024, 42.4896717],[-71.47811299999999, 42.4896198],[-71.4780994, 42.4896183]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47330100000001, 42.4756556],[-71.4733001, 42.4756107],[-71.4734217, 42.4756141],[-71.4734247, 42.4756621],[-71.4734691, 42.4756625],[-71.4734598, 42.4757703],[-71.47327110000001, 42.4757614],[-71.4732802, 42.4756546],[-71.47330100000001, 42.4756556]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4644737, 42.4860356],[-71.4645239, 42.4860722],[-71.4645072, 42.4860848],[-71.4645481, 42.4861146],[-71.4645618, 42.4861043],[-71.4646833, 42.4861928],[-71.46458370000001, 42.4862677],[-71.46437109999999, 42.4861127],[-71.4644737, 42.4860356]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46647040000001, 42.4944295],[-71.4664243, 42.494472],[-71.46651780000001, 42.4945276],[-71.4664511, 42.4945891],[-71.4663192, 42.4945106],[-71.4663781, 42.4944563],[-71.46634280000001, 42.4944354],[-71.4663967, 42.4943857],[-71.46647040000001, 42.4944295]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.459463, 42.4738025],[-71.45948780000001, 42.4737949],[-71.45952819999999, 42.4738669],[-71.4594956, 42.473877],[-71.4595011, 42.4738869],[-71.4594108, 42.4739146],[-71.4593565, 42.4738177],[-71.4594546, 42.4737875],[-71.459463, 42.4738025]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4657225, 42.4908071],[-71.46577499999999, 42.4907329],[-71.465868, 42.490769],[-71.4657868, 42.4908836],[-71.4657989, 42.4908883],[-71.4657697, 42.4909296],[-71.46563740000001, 42.4908782],[-71.46569529999999, 42.4907965],[-71.4657225, 42.4908071]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4735623, 42.4890291],[-71.4735142, 42.4891024],[-71.47353750000001, 42.4891107],[-71.47350280000001, 42.4891638],[-71.4735312, 42.489174],[-71.473512, 42.4892032],[-71.47338910000001, 42.489159],[-71.4734911, 42.4890035],[-71.4735623, 42.4890291]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.464045, 42.4750198],[-71.46406949999999, 42.4750919],[-71.463954, 42.4751134],[-71.4638977, 42.4749475],[-71.4640079, 42.474927],[-71.4640129, 42.4749419],[-71.4640486, 42.4749352],[-71.4640754, 42.4750141],[-71.464045, 42.4750198]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759031, 42.4723825],[-71.475874, 42.4723671],[-71.4758831, 42.4723577],[-71.4757908, 42.4723089],[-71.4758582, 42.4722389],[-71.4760095, 42.4723187],[-71.4759418, 42.4723889],[-71.47591199999999, 42.4723732],[-71.4759031, 42.4723825]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4620705, 42.4796566],[-71.46208559999999, 42.4796826],[-71.46210619999999, 42.479676],[-71.4621612, 42.4797705],[-71.46206549999999, 42.479801],[-71.46201170000001, 42.4797084],[-71.4620256, 42.479704],[-71.46200930000001, 42.4796761],[-71.4620705, 42.4796566]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4791547, 42.4764587],[-71.4791611, 42.4764412],[-71.4792963, 42.4764684],[-71.4792717, 42.4765354],[-71.47915380000001, 42.4765117],[-71.47914400000001, 42.4765383],[-71.4790304, 42.4765155],[-71.47905830000001, 42.4764393],[-71.4791547, 42.4764587]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"15","source:datetime":"2014-04-11T16:40:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4820332, 42.4743499],[-71.4819612, 42.4744438],[-71.4819782, 42.4744497],[-71.48195459999999, 42.4744839],[-71.48188759999999, 42.4744559],[-71.4819219, 42.4744075],[-71.48188759999999, 42.4743935],[-71.4819463, 42.4743145],[-71.4820332, 42.4743499]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"236","addr:state":"MA","source:datetime":"2015-01-20T23:47:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46441350000001, 42.4987986],[-71.4643354, 42.4988444],[-71.464326, 42.4988355],[-71.4642777, 42.4988639],[-71.4642936, 42.4988787],[-71.4641774, 42.4989469],[-71.46411209999999, 42.498886],[-71.4643547, 42.4987437],[-71.46441350000001, 42.4987986]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46476699999999, 42.4771946],[-71.46472609999999, 42.4771759],[-71.4647535, 42.4771431],[-71.46462870000001, 42.4770859],[-71.4646857, 42.4770178],[-71.46493409999999, 42.4771317],[-71.4648667, 42.4772122],[-71.4647839, 42.4771743],[-71.46476699999999, 42.4771946]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47230399999999, 42.4778575],[-71.472257, 42.4778897],[-71.47228490000001, 42.4779121],[-71.4721873, 42.4779789],[-71.47212279999999, 42.4779273],[-71.47218839999999, 42.4778824],[-71.4721651, 42.4778637],[-71.47224420000001, 42.4778096],[-71.47230399999999, 42.4778575]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4606421, 42.488814],[-71.46067530000001, 42.4887896],[-71.4607074, 42.4888136],[-71.4606744, 42.4888378],[-71.4606943, 42.4888526],[-71.46061539999999, 42.4889106],[-71.460504, 42.4888275],[-71.4605826, 42.4887697],[-71.4606421, 42.488814]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47649819999999, 42.4739651],[-71.4764984, 42.4739765],[-71.4765106, 42.4739764],[-71.4765125, 42.474073],[-71.4764861, 42.4740733],[-71.4764863, 42.4740836],[-71.47640730000001, 42.4740844],[-71.476405, 42.4739661],[-71.47649819999999, 42.4739651]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47038499999999, 42.4916565],[-71.4703708, 42.4916704],[-71.47047379999999, 42.4917284],[-71.4704181, 42.4917826],[-71.4703169, 42.4917256],[-71.47033589999999, 42.4917071],[-71.4702455, 42.4916562],[-71.4702965, 42.4916067],[-71.47038499999999, 42.4916565]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4636474, 42.4740183],[-71.46361210000001, 42.4738946],[-71.4637189, 42.4738779],[-71.4637374, 42.4739427],[-71.463801, 42.4739328],[-71.4638271, 42.4740242],[-71.4636776, 42.4740476],[-71.46366829999999, 42.474015],[-71.4636474, 42.4740183]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"6","addr:state":"MA","source:datetime":"2014-11-08T14:01:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4536114, 42.4951511],[-71.4535948, 42.4950593],[-71.4536884, 42.49505],[-71.45370459999999, 42.4951396],[-71.45373499999999, 42.4951366],[-71.4537614, 42.495283],[-71.4536576, 42.4952933],[-71.4536315, 42.4951491],[-71.4536114, 42.4951511]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4705809, 42.4872866],[-71.4705064, 42.4872591],[-71.4705424, 42.4872057],[-71.4707841, 42.4872949],[-71.4707512, 42.4873438],[-71.4707083, 42.487328],[-71.4706967, 42.4873453],[-71.4705723, 42.4872994],[-71.4705809, 42.4872866]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.476642, 42.4888846],[-71.4766604, 42.488843],[-71.4767561, 42.4888661],[-71.4766992, 42.4889952],[-71.47660930000001, 42.4889735],[-71.4766382, 42.4889078],[-71.47660999999999, 42.488901],[-71.47661960000001, 42.4888792],[-71.476642, 42.4888846]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46859240000001, 42.4876101],[-71.4686095, 42.4875866],[-71.46857110000001, 42.4875714],[-71.4686354, 42.4874825],[-71.468729, 42.4875197],[-71.4686095, 42.4876848],[-71.46853659999999, 42.4876559],[-71.4685748, 42.4876031],[-71.46859240000001, 42.4876101]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4530074, 42.4918168],[-71.4529872, 42.4917571],[-71.4531463, 42.4917276],[-71.4531672, 42.4917895],[-71.4531296, 42.4917965],[-71.45316529999999, 42.4919019],[-71.45306170000001, 42.4919211],[-71.45302529999999, 42.4918135],[-71.4530074, 42.4918168]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4649915, 42.4877687],[-71.4649988, 42.4877573],[-71.4650168, 42.4877636],[-71.4650097, 42.4877747],[-71.4650724, 42.4877966],[-71.46502889999999, 42.487865],[-71.4649086, 42.487823],[-71.4649519, 42.4877549],[-71.4649915, 42.4877687]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47158020000001, 42.4765742],[-71.47158, 42.4765563],[-71.4715338, 42.4765566],[-71.4715326, 42.4764598],[-71.4721109, 42.4764559],[-71.4721127, 42.4766078],[-71.4718228, 42.4766097],[-71.4718223, 42.4765725],[-71.47158020000001, 42.4765742]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4523571, 42.490975],[-71.4523715, 42.4910422],[-71.45238790000001, 42.4910402],[-71.4524112, 42.4911488],[-71.4522429, 42.4911687],[-71.452234, 42.4911273],[-71.4522882, 42.4911209],[-71.45225929999999, 42.4909865],[-71.4523571, 42.490975]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4640404, 42.4894164],[-71.4640349, 42.4894462],[-71.4641744, 42.4894604],[-71.46415810000001, 42.4895486],[-71.46401659999999, 42.4895342],[-71.4640189, 42.4895223],[-71.4639396, 42.4895143],[-71.46395920000001, 42.4894082],[-71.4640404, 42.4894164]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4672119, 42.4942657],[-71.4672501, 42.4942272],[-71.4673319, 42.4942716],[-71.4671728, 42.4944321],[-71.46709389999999, 42.4943893],[-71.4671779, 42.4943046],[-71.467145, 42.4942867],[-71.4671819, 42.4942495],[-71.4672119, 42.4942657]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4775346, 42.4929035],[-71.47750569999999, 42.4928403],[-71.4776456, 42.4928053],[-71.4776736, 42.4928668],[-71.4776585, 42.4928705],[-71.4777081, 42.4929794],[-71.4775985, 42.4930068],[-71.4775497, 42.4928997],[-71.4775346, 42.4929035]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"22","addr:state":"MA","source:datetime":"2014-11-08T14:01:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4536392, 42.4921459],[-71.4536356, 42.4921098],[-71.45367709999999, 42.4921075],[-71.45366559999999, 42.4919925],[-71.4537764, 42.4919864],[-71.45379800000001, 42.4922027],[-71.45368999999999, 42.4922086],[-71.4536835, 42.4921434],[-71.4536392, 42.4921459]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738795, 42.4833488],[-71.4737938, 42.4833206],[-71.4738344, 42.4832529],[-71.473947, 42.4832899],[-71.4739553, 42.483276],[-71.4740664, 42.4833125],[-71.4740231, 42.4833848],[-71.4738851, 42.4833395],[-71.4738795, 42.4833488]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4806505, 42.4734524],[-71.48067210000001, 42.4733513],[-71.48076709999999, 42.4733623],[-71.4807579, 42.4734054],[-71.48077809999999, 42.4734077],[-71.4807563, 42.4735101],[-71.4806138, 42.4734935],[-71.4806233, 42.4734492],[-71.4806505, 42.4734524]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4510222, 42.491044],[-71.450974, 42.4910275],[-71.45099740000001, 42.49099],[-71.450913, 42.4909611],[-71.4509565, 42.4908916],[-71.45120989999999, 42.4909783],[-71.4511645, 42.4910508],[-71.4510437, 42.4910095],[-71.4510222, 42.491044]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47704450000001, 42.4724011],[-71.4770922, 42.472386],[-71.47710120000001, 42.4724018],[-71.4771576, 42.472384],[-71.4771999, 42.4724576],[-71.4770125, 42.4725167],[-71.47695419999999, 42.4724153],[-71.4770376, 42.472389],[-71.47704450000001, 42.4724011]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4590343, 42.4726206],[-71.4590326, 42.4725467],[-71.45912730000001, 42.4725455],[-71.4591289, 42.4726104],[-71.4591686, 42.4726099],[-71.4591698, 42.4726588],[-71.4590547, 42.4726603],[-71.45905380000001, 42.4726204],[-71.4590343, 42.4726206]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543594, 42.4725559],[-71.45430380000001, 42.4726317],[-71.454335, 42.4726443],[-71.4542821, 42.4727165],[-71.4542565, 42.4727062],[-71.4542622, 42.4726985],[-71.45417190000001, 42.4726623],[-71.4542747, 42.4725219],[-71.4543594, 42.4725559]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46163970000001, 42.4810054],[-71.4616477, 42.4810446],[-71.4616683, 42.4810423],[-71.4616884, 42.4811404],[-71.46157820000001, 42.4811513],[-71.4615581, 42.4810537],[-71.461596, 42.4810494],[-71.4615881, 42.4810112],[-71.46163970000001, 42.4810054]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:06Z"},"geometry": {"type":"LineString","coordinates": [[-71.45751850000001, 42.471907],[-71.45745220000001, 42.4718883],[-71.45747299999999, 42.471848],[-71.4573412, 42.4718108]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47810629999999, 42.4897324],[-71.4782149, 42.4897467],[-71.47819680000001, 42.4898218],[-71.4782524, 42.4898291],[-71.47824369999999, 42.4898653],[-71.4781922, 42.4898585],[-71.478189, 42.4898722],[-71.4780763, 42.4898574],[-71.47810629999999, 42.4897324]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47056569999999, 42.4923648],[-71.47052600000001, 42.492366],[-71.470529, 42.4924235],[-71.4703641, 42.4924282],[-71.47036079999999, 42.4923646],[-71.4704641, 42.4923616],[-71.47045970000001, 42.4922776],[-71.470561, 42.4922747],[-71.47056569999999, 42.4923648]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697769, 42.4855842],[-71.46971430000001, 42.4856604],[-71.4696799, 42.4856449],[-71.4696617, 42.4856671],[-71.46968769999999, 42.4856788],[-71.4696436, 42.4857324],[-71.46957380000001, 42.485701],[-71.4696987, 42.485549],[-71.4697769, 42.4855842]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4780844, 42.4850996],[-71.47810269999999, 42.4852041],[-71.4780916, 42.4852052],[-71.4780961, 42.4852312],[-71.4781182, 42.4852291],[-71.478132, 42.4853079],[-71.47801130000001, 42.4853195],[-71.4779747, 42.4851101],[-71.4780844, 42.4850996]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4510018, 42.4875023],[-71.4510028, 42.4874928],[-71.45113430000001, 42.4875008],[-71.4511249, 42.4875851],[-71.450937, 42.4875737],[-71.4509347, 42.4875947],[-71.4508269, 42.4875881],[-71.4508376, 42.4874923],[-71.4510018, 42.4875023]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4636369, 42.4878413],[-71.4636876, 42.4878155],[-71.4637996, 42.4879358],[-71.4634903, 42.4880935],[-71.4633798, 42.4879749],[-71.4635792, 42.4878732],[-71.46356280000001, 42.4878556],[-71.46362209999999, 42.4878253],[-71.4636369, 42.4878413]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46113990000001, 42.472133],[-71.46115760000001, 42.4721101],[-71.4611951, 42.472126],[-71.46118300000001, 42.4721417],[-71.4612795, 42.4721827],[-71.46123059999999, 42.4722457],[-71.4610844, 42.4721837],[-71.46112770000001, 42.4721278],[-71.46113990000001, 42.472133]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4681369, 42.4818141],[-71.46806239999999, 42.4817657],[-71.46817950000001, 42.4816492],[-71.4682646, 42.4817045],[-71.46820870000001, 42.4817516],[-71.4682548, 42.4817815],[-71.4682128, 42.481817],[-71.46816800000001, 42.4817879],[-71.4681369, 42.4818141]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46711980000001, 42.478021],[-71.46709370000001, 42.4779893],[-71.4671651, 42.4779571],[-71.4672461, 42.4780558],[-71.46726510000001, 42.4780473],[-71.4672891, 42.4780766],[-71.4671815, 42.478125],[-71.4671025, 42.4780288],[-71.46711980000001, 42.478021]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4724672, 42.4769586],[-71.4723832, 42.4769837],[-71.4724169, 42.4770398],[-71.4722828, 42.4770772],[-71.47220660000001, 42.4769165],[-71.4722364, 42.4769088],[-71.47219939999999, 42.4768335],[-71.4723784, 42.4767819],[-71.4724672, 42.4769586]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46368440000001, 42.486268],[-71.4636587, 42.4862552],[-71.4637109, 42.4861976],[-71.4637575, 42.4862207],[-71.46377819999999, 42.4861979],[-71.4639166, 42.4862666],[-71.46385890000001, 42.4863302],[-71.46369970000001, 42.4862512],[-71.46368440000001, 42.486268]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4737824, 42.4919542],[-71.4737655, 42.4919627],[-71.4737879, 42.4919869],[-71.4737322, 42.492015],[-71.47371099999999, 42.4919921],[-71.4735392, 42.492079],[-71.4734826, 42.4920177],[-71.4737269, 42.4918941],[-71.4737824, 42.4919542]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Wright Terrace","addr:city":"Acton","building":"yes","addr:housenumber":"1","addr:state":"MA","source:datetime":"2014-11-18T02:34:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4774635, 42.4767589],[-71.4774731, 42.4767408],[-71.4775898, 42.4767717],[-71.47753059999999, 42.4768939],[-71.477417, 42.476867],[-71.4774465, 42.4768034],[-71.477423, 42.4767958],[-71.4774445, 42.4767536],[-71.4774635, 42.4767589]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728581, 42.4828795],[-71.4728289, 42.4829229],[-71.4728741, 42.4829396],[-71.472813, 42.4830306],[-71.47273130000001, 42.4830006],[-71.4727726, 42.4829391],[-71.4726816, 42.4829057],[-71.47273060000001, 42.4828327],[-71.4728581, 42.4828795]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4767162, 42.4920884],[-71.4767234, 42.492043],[-71.47677179999999, 42.4920473],[-71.4767647, 42.4920921],[-71.4768026, 42.4920954],[-71.47678449999999, 42.4922092],[-71.4766712, 42.4921994],[-71.4766892, 42.4920861],[-71.4767162, 42.4920884]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715844, 42.4881101],[-71.47150569999999, 42.4880818],[-71.4715411, 42.4880279],[-71.47162040000001, 42.4880564],[-71.4716403, 42.4880262],[-71.47175679999999, 42.4880681],[-71.47170920000001, 42.4881406],[-71.471592, 42.4880984],[-71.4715844, 42.4881101]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4704371, 42.4783997],[-71.47040579999999, 42.4783553],[-71.4705476, 42.4783005],[-71.47067199999999, 42.4784769],[-71.4705603, 42.4785201],[-71.4705149, 42.4784558],[-71.47053339999999, 42.4784486],[-71.4704857, 42.4783809],[-71.4704371, 42.4783997]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4707533, 42.4946582],[-71.4707349, 42.4946981],[-71.4707641, 42.4947053],[-71.47074069999999, 42.4947532],[-71.4707069, 42.4947423],[-71.47066049999999, 42.4948264],[-71.4705609, 42.4947963],[-71.4706537, 42.4946281],[-71.4707533, 42.4946582]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:55:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4656985, 42.4893759],[-71.4657754, 42.4894101],[-71.4658194, 42.489356],[-71.46593230000001, 42.4894063],[-71.4658901, 42.4894582],[-71.4658709, 42.4894497],[-71.4658182, 42.4895145],[-71.4656476, 42.4894385],[-71.4656985, 42.4893759]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45054639999999, 42.4942655],[-71.45054519999999, 42.494248],[-71.4505699, 42.4942471],[-71.4505686, 42.4942269],[-71.450654, 42.4942238],[-71.4506583, 42.4942898],[-71.45057509999999, 42.4942928],[-71.4505733, 42.4942646],[-71.45054639999999, 42.4942655]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4827416, 42.4766092],[-71.48281129999999, 42.4766332],[-71.482817, 42.4766242],[-71.48301240000001, 42.4766916],[-71.4829743, 42.4767522],[-71.48293580000001, 42.476739],[-71.4829259, 42.4767546],[-71.48269929999999, 42.4766765],[-71.4827416, 42.4766092]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46904790000001, 42.4886086],[-71.4690543, 42.4885993],[-71.469122, 42.4886249],[-71.4691157, 42.488634],[-71.46912879999999, 42.488639],[-71.4690585, 42.4887409],[-71.4689656, 42.4887058],[-71.4690358, 42.4886041],[-71.46904790000001, 42.4886086]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46411500000001, 42.4837163],[-71.4641519, 42.4837009],[-71.4642044, 42.4837698],[-71.464034, 42.4838409],[-71.4639803, 42.4837703],[-71.46404579999999, 42.483743],[-71.46403720000001, 42.4837318],[-71.4641051, 42.4837034],[-71.46411500000001, 42.4837163]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.475471, 42.4873397],[-71.4755139, 42.4873029],[-71.4756484, 42.4873888],[-71.4755894, 42.4874394],[-71.47555869999999, 42.4874197],[-71.475465, 42.4875],[-71.4753882, 42.4874509],[-71.4754978, 42.4873569],[-71.475471, 42.4873397]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4718546, 42.4908727],[-71.4718622, 42.4908292],[-71.4719702, 42.4908396],[-71.4719497, 42.4909565],[-71.4718409, 42.490946],[-71.4718448, 42.4909237],[-71.47179420000001, 42.4909188],[-71.4718032, 42.4908678],[-71.4718546, 42.4908727]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618126, 42.4895511],[-71.46174790000001, 42.489563],[-71.4617507, 42.4895713],[-71.4616102, 42.4895971],[-71.461584, 42.489519],[-71.4617192, 42.4894942],[-71.46170859999999, 42.4894625],[-71.4617786, 42.4894496],[-71.4618126, 42.4895511]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4524773, 42.4898899],[-71.4524977, 42.4898563],[-71.4525495, 42.4898735],[-71.4525328, 42.4899011],[-71.452611, 42.4899271],[-71.45256569999999, 42.490002],[-71.4523907, 42.489944],[-71.45243240000001, 42.4898751],[-71.4524773, 42.4898899]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.475292, 42.4864161],[-71.47536839999999, 42.4863249],[-71.4754993, 42.4863851],[-71.4754247, 42.4864741],[-71.4754131, 42.4864688],[-71.4753685, 42.4865219],[-71.475285, 42.4864835],[-71.4753278, 42.4864326],[-71.475292, 42.4864161]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.467682, 42.4945287],[-71.4676805, 42.4945823],[-71.46772679999999, 42.4945831],[-71.46772489999999, 42.494647],[-71.4677061, 42.4946467],[-71.4677049, 42.4946855],[-71.46758250000001, 42.4946836],[-71.4675871, 42.4945272],[-71.467682, 42.4945287]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47770149999999, 42.4893132],[-71.47770610000001, 42.4892933],[-71.4776889, 42.4892912],[-71.4777021, 42.4892527],[-71.47778080000001, 42.4892626],[-71.4777445, 42.4894209],[-71.4776607, 42.4894104],[-71.4776835, 42.4893109],[-71.47770149999999, 42.4893132]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48030970000001, 42.4797725],[-71.4803086, 42.4798116],[-71.4802727, 42.479811],[-71.4802711, 42.479869],[-71.48028410000001, 42.4798692],[-71.4802835, 42.4798922],[-71.4801482, 42.47989],[-71.4801516, 42.4797701],[-71.48030970000001, 42.4797725]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696673, 42.4721711],[-71.4696526, 42.4721356],[-71.4697615, 42.4721109],[-71.4697762, 42.4721464],[-71.4697111, 42.4721612],[-71.4697225, 42.4721886],[-71.46969110000001, 42.4721957],[-71.4696797, 42.4721683],[-71.4696673, 42.4721711]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46924749999999, 42.48197],[-71.4691512, 42.4821051],[-71.4691175, 42.4820919],[-71.4690737, 42.4821534],[-71.4690609, 42.4821484],[-71.4690276, 42.4821951],[-71.4689551, 42.4821668],[-71.4691285, 42.4819235],[-71.46924749999999, 42.48197]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4798717, 42.4719858],[-71.4798401, 42.4719746],[-71.4798454, 42.4719664],[-71.4798088, 42.4719534],[-71.47985009999999, 42.4718897],[-71.4799699, 42.4719323],[-71.4799288, 42.4719956],[-71.4798772, 42.4719773],[-71.4798717, 42.4719858]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47202729999999, 42.4881625],[-71.47202, 42.4881761],[-71.472103, 42.4882005],[-71.4720602, 42.48828],[-71.4719783, 42.4882559],[-71.471988, 42.4882378],[-71.471833, 42.4881921],[-71.47187340000001, 42.4881172],[-71.47202729999999, 42.4881625]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4611537, 42.4825283],[-71.46119469999999, 42.4824509],[-71.46133380000001, 42.4824913],[-71.46128539999999, 42.4825826],[-71.46126889999999, 42.4825778],[-71.46125790000001, 42.4825986],[-71.4612096, 42.4825845],[-71.46122800000001, 42.4825499],[-71.4611537, 42.4825283]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47387879999999, 42.4742711],[-71.4738792, 42.4742968],[-71.47391469999999, 42.4742965],[-71.47391519999999, 42.4743255],[-71.47387689999999, 42.4743258],[-71.47387759999999, 42.4743712],[-71.473776, 42.4743721],[-71.4737744, 42.474272],[-71.47387879999999, 42.4742711]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4724178, 42.4776033],[-71.4724966, 42.4775818],[-71.4725145, 42.4776177],[-71.4724564, 42.4776335],[-71.4724717, 42.4776644],[-71.4723752, 42.4776907],[-71.472337, 42.4776141],[-71.47241289999999, 42.4775934],[-71.4724178, 42.4776033]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46879199999999, 42.4937916],[-71.4687254, 42.493762],[-71.4687849, 42.4936886],[-71.46899639999999, 42.4937824],[-71.4689624, 42.4938244],[-71.46893059999999, 42.4938103],[-71.4689121, 42.4938331],[-71.468799, 42.4937829],[-71.46879199999999, 42.4937916]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692771, 42.4872064],[-71.46925450000001, 42.487234],[-71.4693006, 42.4872547],[-71.4692513, 42.4873149],[-71.4691995, 42.4872916],[-71.46921879999999, 42.487268],[-71.469126, 42.4872264],[-71.46917860000001, 42.4871622],[-71.4692771, 42.4872064]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4589189, 42.4843011],[-71.4589615, 42.4841765],[-71.4590427, 42.484194],[-71.4590221, 42.4842463],[-71.4590441, 42.4842511],[-71.4590007, 42.4843615],[-71.4588546, 42.48433],[-71.4588701, 42.4842906],[-71.4589189, 42.4843011]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4513291, 42.4879878],[-71.4513627, 42.4878911],[-71.45147420000001, 42.4879123],[-71.4514053, 42.4881106],[-71.45130039999999, 42.4880906],[-71.4513225, 42.4880268],[-71.45127530000001, 42.4880178],[-71.4512885, 42.4879801],[-71.4513291, 42.4879878]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4667726, 42.4929122],[-71.46678989999999, 42.492861],[-71.4668928, 42.49288],[-71.4668456, 42.4930197],[-71.46674400000001, 42.4930009],[-71.4667604, 42.4929522],[-71.46671000000001, 42.4929429],[-71.46672340000001, 42.492903],[-71.4667726, 42.4929122]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45843929999999, 42.4720035],[-71.4584912, 42.4719912],[-71.4585065, 42.4720267],[-71.4584598, 42.4720378],[-71.458473, 42.4720681],[-71.45836060000001, 42.4720947],[-71.4583279, 42.472019],[-71.45843499999999, 42.4719936],[-71.45843929999999, 42.4720035]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4647538, 42.4767493],[-71.464617, 42.4767815],[-71.4646204, 42.4767895],[-71.46447980000001, 42.4768226],[-71.46444409999999, 42.4767397],[-71.46462390000001, 42.4766973],[-71.46461410000001, 42.4766745],[-71.4647117, 42.4766514],[-71.4647538, 42.4767493]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4702619, 42.4742975],[-71.4702715, 42.4741577],[-71.4704143, 42.4741631],[-71.4704047, 42.4743029],[-71.47034600000001, 42.4743007],[-71.47034530000001, 42.4743106],[-71.4703128, 42.4743094],[-71.4703135, 42.4742995],[-71.4702619, 42.4742975]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703268, 42.4835332],[-71.4703035, 42.4835734],[-71.4705294, 42.4836454],[-71.4704821, 42.4837266],[-71.4703215, 42.4836754],[-71.4703324, 42.4836567],[-71.4702125, 42.4836184],[-71.4702722, 42.4835158],[-71.4703268, 42.4835332]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47277649999999, 42.4775995],[-71.47275550000001, 42.4776053],[-71.4727805, 42.4776552],[-71.4726908, 42.4776799],[-71.4726594, 42.4776171],[-71.4726711, 42.4776139],[-71.4726347, 42.4775414],[-71.47273370000001, 42.4775142],[-71.47277649999999, 42.4775995]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46535470000001, 42.4859411],[-71.4655453, 42.4860303],[-71.4655126, 42.4860685],[-71.4654855, 42.4860558],[-71.46542049999999, 42.4861318],[-71.4653769, 42.4861114],[-71.4654091, 42.4860737],[-71.46528929999999, 42.4860176],[-71.46535470000001, 42.4859411]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4633091, 42.4848398],[-71.4633839, 42.4847823],[-71.46346250000001, 42.4848384],[-71.46332409999999, 42.4849447],[-71.4632433, 42.4848871],[-71.4632768, 42.4848613],[-71.46326019999999, 42.4848495],[-71.4632903, 42.4848264],[-71.4633091, 42.4848398]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47638329999999, 42.4908401],[-71.47637690000001, 42.4907519],[-71.4764425, 42.4907493],[-71.476445, 42.490783],[-71.4765663, 42.4907781],[-71.47657220000001, 42.4908585],[-71.4764084, 42.4908651],[-71.4764065, 42.4908391],[-71.47638329999999, 42.4908401]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46144700000001, 42.4773477],[-71.461405, 42.4773771],[-71.4614174, 42.4773869],[-71.4613217, 42.4774539],[-71.4612484, 42.4773966],[-71.4613263, 42.477342],[-71.4612949, 42.4773173],[-71.4613547, 42.4772754],[-71.46144700000001, 42.4773477]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4522578, 42.4853001],[-71.4521958, 42.4851912],[-71.4523638, 42.4851388],[-71.45238449999999, 42.4851752],[-71.4523131, 42.4851975],[-71.4523738, 42.4853042],[-71.45232849999999, 42.4853184],[-71.452309, 42.4852841],[-71.4522578, 42.4853001]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.473406, 42.4940478],[-71.4734016, 42.4940594],[-71.4735192, 42.494084],[-71.47349269999999, 42.4941533],[-71.4733923, 42.4941323],[-71.4733957, 42.4941237],[-71.4732576, 42.4940988],[-71.4732861, 42.4940227],[-71.473406, 42.4940478]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47940199999999, 42.4750852],[-71.4794081, 42.4750522],[-71.4794632, 42.4750577],[-71.4794575, 42.4750887],[-71.47944459999999, 42.4750874],[-71.47942949999999, 42.4751701],[-71.4793423, 42.4751614],[-71.47935699999999, 42.4750807],[-71.47940199999999, 42.4750852]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618356, 42.4789306],[-71.4618193, 42.4789308],[-71.4618217, 42.4790327],[-71.4616565, 42.4790348],[-71.4616549, 42.4789663],[-71.4617295, 42.4789653],[-71.4617264, 42.478829],[-71.4618332, 42.4788277],[-71.4618356, 42.4789306]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4617611, 42.4778845],[-71.4617923, 42.477844],[-71.46186659999999, 42.4778754],[-71.4617719, 42.4779983],[-71.4616322, 42.4779393],[-71.4616424, 42.4779261],[-71.46159489999999, 42.477906],[-71.4616482, 42.4778368],[-71.4617611, 42.4778845]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4702379, 42.4796978],[-71.47015039999999, 42.4797721],[-71.4701767, 42.4797891],[-71.47013490000001, 42.4798246],[-71.47004819999999, 42.4797687],[-71.4700864, 42.4797362],[-71.4700653, 42.4797226],[-71.47015639999999, 42.4796453],[-71.4702379, 42.4796978]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48111129999999, 42.4766779],[-71.4812561, 42.4767105],[-71.4812472, 42.4767322],[-71.4812958, 42.4767431],[-71.4812807, 42.4767796],[-71.48122859999999, 42.4767679],[-71.4812215, 42.4767851],[-71.4810803, 42.4767532],[-71.48111129999999, 42.4766779]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4701364, 42.4828954],[-71.4700747, 42.4829734],[-71.47010539999999, 42.4829867],[-71.4700856, 42.4830117],[-71.4701318, 42.4830317],[-71.4701019, 42.4830694],[-71.4699426, 42.4830004],[-71.4700539, 42.4828596],[-71.4701364, 42.4828954]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695019, 42.4911442],[-71.4694439, 42.4912302],[-71.4694095, 42.4912176],[-71.46938919999999, 42.4912477],[-71.4694156, 42.4912575],[-71.4693778, 42.4913178],[-71.4692977, 42.4912903],[-71.4694093, 42.4911124],[-71.4695019, 42.4911442]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46902230000001, 42.490271],[-71.4690471, 42.4902359],[-71.4691495, 42.4902756],[-71.4691237, 42.4903121],[-71.46908259999999, 42.4902962],[-71.4690205, 42.4903837],[-71.4689309, 42.490349],[-71.4689939, 42.49026],[-71.46902230000001, 42.490271]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.475233, 42.4764679],[-71.4752076, 42.4764012],[-71.4754482, 42.4763511],[-71.4754736, 42.4764181],[-71.4754113, 42.476431],[-71.4754219, 42.4764591],[-71.4753238, 42.4764795],[-71.47531309999999, 42.4764512],[-71.475233, 42.4764679]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Massachusetts Avenue","addr:city":"Acton","building":"yes","addr:housenumber":"537","addr:state":"MA","source:datetime":"2015-01-23T23:15:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47124289999999, 42.4761812],[-71.471242, 42.476215],[-71.47127279999999, 42.4762155],[-71.4712732, 42.4762001],[-71.47139439999999, 42.4762018],[-71.4713926, 42.4762714],[-71.4710968, 42.4762671],[-71.4710991, 42.4761791],[-71.47124289999999, 42.4761812]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46438000000001, 42.4786857],[-71.4643482, 42.4787034],[-71.4644126, 42.4787669],[-71.4643428, 42.4788058],[-71.46428899999999, 42.4787528],[-71.4643014, 42.4787459],[-71.4642028, 42.4786488],[-71.46429209999999, 42.4785991],[-71.46438000000001, 42.4786857]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4813348, 42.4731526],[-71.4813417, 42.4731209],[-71.4814047, 42.4731285],[-71.4813754, 42.4732623],[-71.4812306, 42.4732449],[-71.48122170000001, 42.4732854],[-71.48113290000001, 42.4732748],[-71.48116419999999, 42.4731321],[-71.4813348, 42.4731526]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692687, 42.4896929],[-71.4692381, 42.4898079],[-71.4692632, 42.4898115],[-71.46925229999999, 42.4898525],[-71.46915509999999, 42.4898384],[-71.4691655, 42.4897993],[-71.46910819999999, 42.489791],[-71.4691394, 42.489674],[-71.4692687, 42.4896929]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Mead Terrace","addr:city":"Acton","building":"yes","addr:housenumber":"9","addr:state":"MA","source:datetime":"2014-11-18T02:34:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763336, 42.4768964],[-71.47633639999999, 42.4768463],[-71.4764558, 42.4768499],[-71.4764531, 42.4768985],[-71.4764665, 42.4768989],[-71.4764626, 42.4769704],[-71.4763121, 42.4769659],[-71.476316, 42.4768959],[-71.4763336, 42.4768964]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738012, 42.4767422],[-71.47374929999999, 42.4766716],[-71.4738681, 42.4766238],[-71.47391949999999, 42.4766937],[-71.47389200000001, 42.4767048],[-71.473906, 42.4767239],[-71.473826, 42.4767561],[-71.47381249999999, 42.4767377],[-71.4738012, 42.4767422]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4539552, 42.4751815],[-71.4540037, 42.4751226],[-71.4541195, 42.4751749],[-71.4540248, 42.4752899],[-71.4539858, 42.4752723],[-71.4539559, 42.4753086],[-71.45390020000001, 42.4752834],[-71.45397629999999, 42.475191],[-71.4539552, 42.4751815]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4718455, 42.4890372],[-71.4717672, 42.4890888],[-71.4717849, 42.4891035],[-71.4716961, 42.489162],[-71.47163399999999, 42.4891103],[-71.471716, 42.4890563],[-71.4716999, 42.489043],[-71.471785, 42.4889869],[-71.4718455, 42.4890372]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46830490000001, 42.4897453],[-71.4682495, 42.4897491],[-71.46826129999999, 42.4898447],[-71.4681181, 42.4898544],[-71.46811, 42.4897887],[-71.4682049, 42.4897823],[-71.46819050000001, 42.4896644],[-71.46829409999999, 42.4896574],[-71.46830490000001, 42.4897453]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4662381, 42.4910352],[-71.46627049999999, 42.490992],[-71.46633679999999, 42.4910193],[-71.4663044, 42.4910624],[-71.4662873, 42.4910553],[-71.4662321, 42.4911321],[-71.4661478, 42.4910989],[-71.46620350000001, 42.4910215],[-71.4662381, 42.4910352]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4707287, 42.4810968],[-71.4707923, 42.4810642],[-71.4708419, 42.4811173],[-71.4706288, 42.4812265],[-71.4705893, 42.4811843],[-71.47062649999999, 42.4811652],[-71.4706094, 42.4811468],[-71.4707217, 42.4810893],[-71.4707287, 42.4810968]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.472404, 42.4758778],[-71.47240410000001, 42.4758346],[-71.47248930000001, 42.4758348],[-71.47249069999999, 42.4758753],[-71.4725079, 42.4758749],[-71.4725104, 42.4759397],[-71.4723819, 42.4759395],[-71.4723821, 42.4758778],[-71.472404, 42.4758778]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"21","addr:state":"MA","source:datetime":"2014-11-08T14:01:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4544159, 42.4922036],[-71.4544051, 42.492149],[-71.454514, 42.4921372],[-71.4545346, 42.4922409],[-71.4545456, 42.4922397],[-71.4545588, 42.4923065],[-71.45442180000001, 42.4923214],[-71.45439880000001, 42.4922054],[-71.4544159, 42.4922036]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4764819, 42.4845616],[-71.4764674, 42.4845636],[-71.4765043, 42.4847098],[-71.4763698, 42.4847284],[-71.47633209999999, 42.484579],[-71.4764287, 42.4845656],[-71.47642020000001, 42.4845319],[-71.47647259999999, 42.4845247],[-71.4764819, 42.4845616]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4676755, 42.492992],[-71.46766049999999, 42.4929747],[-71.4676379, 42.4929855],[-71.46754439999999, 42.4928781],[-71.46763850000001, 42.4928332],[-71.467758, 42.4929704],[-71.4677047, 42.4929958],[-71.46769380000001, 42.4929833],[-71.4676755, 42.492992]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4649622, 42.4873934],[-71.46487689999999, 42.4873697],[-71.4649105, 42.4873035],[-71.46515530000001, 42.4873717],[-71.4651187, 42.4874439],[-71.46500020000001, 42.4874109],[-71.4650106, 42.4873906],[-71.4649695, 42.4873792],[-71.4649622, 42.4873934]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46685739999999, 42.4890927],[-71.46695080000001, 42.4891301],[-71.46693670000001, 42.4891494],[-71.466966, 42.4891612],[-71.4669024, 42.4892482],[-71.4668767, 42.4892379],[-71.46686080000001, 42.4892597],[-71.4667637, 42.4892208],[-71.46685739999999, 42.4890927]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45528880000001, 42.471879],[-71.4553129, 42.4718885],[-71.455325, 42.4718716],[-71.4554694, 42.471928],[-71.45542500000001, 42.4719903],[-71.45538089999999, 42.4719731],[-71.4553659, 42.4719942],[-71.45524140000001, 42.4719455],[-71.45528880000001, 42.471879]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4515478, 42.4870766],[-71.45155200000001, 42.4870603],[-71.4517481, 42.4870993],[-71.4517216, 42.4872027],[-71.45161179999999, 42.4871872],[-71.45162259999999, 42.4871452],[-71.4515047, 42.4871286],[-71.4515191, 42.4870726],[-71.4515478, 42.4870766]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4818458, 42.4763019],[-71.4818094, 42.4762385],[-71.4819031, 42.4762091],[-71.4819423, 42.4762774],[-71.48195990000001, 42.4762719],[-71.4820098, 42.4763588],[-71.48191799999999, 42.4763877],[-71.4818652, 42.4762958],[-71.4818458, 42.4763019]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4731735, 42.4866386],[-71.47325789999999, 42.4867089],[-71.4731556, 42.4867762],[-71.4731687, 42.4867872],[-71.4731317, 42.4868116],[-71.47310349999999, 42.4867881],[-71.47305679999999, 42.4868188],[-71.4729875, 42.486761],[-71.4731735, 42.4866386]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.464037, 42.4890386],[-71.46400749999999, 42.4890386],[-71.46400749999999, 42.4891085],[-71.4638961, 42.4891084],[-71.46389619999999, 42.4890067],[-71.4638525, 42.4890067],[-71.4638525, 42.4889643],[-71.464037, 42.4889644],[-71.464037, 42.4890386]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46131990000001, 42.4747329],[-71.4612527, 42.4746659],[-71.46134429999999, 42.4746156],[-71.4614833, 42.4747542],[-71.4613906, 42.4748052],[-71.4613835, 42.4747981],[-71.46134790000001, 42.4748177],[-71.4612832, 42.4747531],[-71.46131990000001, 42.4747329]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4738894, 42.4913307],[-71.4739218, 42.4913647],[-71.4739433, 42.4913534],[-71.4739982, 42.4914108],[-71.4738153, 42.4915065],[-71.4737628, 42.4914516],[-71.47386059999999, 42.4914004],[-71.4738258, 42.491364],[-71.4738894, 42.4913307]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46499970000001, 42.4851916],[-71.4650678, 42.4852405],[-71.4650563, 42.4852493],[-71.4650663, 42.4852565],[-71.46503610000001, 42.4852796],[-71.4650266, 42.4852728],[-71.4650099, 42.4852855],[-71.46494130000001, 42.4852361],[-71.46499970000001, 42.4851916]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47034480000001, 42.4876238],[-71.4703509, 42.4876411],[-71.4704217, 42.4876274],[-71.4704452, 42.4876946],[-71.47030909999999, 42.4877208],[-71.47028570000001, 42.4876541],[-71.4703245, 42.4876466],[-71.4703183, 42.4876289],[-71.47034480000001, 42.4876238]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47543570000001, 42.4901366],[-71.47542230000001, 42.4901683],[-71.4754415, 42.4901727],[-71.4754324, 42.4901944],[-71.4754138, 42.4901901],[-71.4754002, 42.4902225],[-71.4753169, 42.4902033],[-71.475353, 42.4901175],[-71.47543570000001, 42.4901366]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4805896, 42.4735796],[-71.48074149999999, 42.4736119],[-71.4807271, 42.473649],[-71.4807119, 42.4736457],[-71.4806974, 42.4736831],[-71.4806093, 42.4736643],[-71.4806126, 42.4736559],[-71.480564, 42.4736456],[-71.4805896, 42.4735796]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"24","addr:state":"MA","source:datetime":"2014-11-08T14:01:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4535985, 42.4918128],[-71.4535892, 42.4917749],[-71.45363879999999, 42.4917683],[-71.4536126, 42.4916607],[-71.45371470000001, 42.4916471],[-71.4537653, 42.4918552],[-71.45366660000001, 42.4918683],[-71.4536514, 42.4918058],[-71.4535985, 42.4918128]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47353339999999, 42.4881813],[-71.4735213, 42.4882268],[-71.4735513, 42.4882311],[-71.4735393, 42.4882761],[-71.47348650000001, 42.4882684],[-71.4734895, 42.4882571],[-71.4733481, 42.4882365],[-71.47336919999999, 42.4881574],[-71.47353339999999, 42.4881813]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763239, 42.4843104],[-71.476304, 42.4842398],[-71.4764316, 42.4842201],[-71.4764402, 42.4842506],[-71.4765038, 42.4842408],[-71.47652069999999, 42.4843008],[-71.4764569, 42.4843107],[-71.47645129999999, 42.4842907],[-71.4763239, 42.4843104]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.463905, 42.4874112],[-71.46391269999999, 42.4875086],[-71.46393620000001, 42.4875076],[-71.4639459, 42.4876301],[-71.4638187, 42.4876356],[-71.46380929999999, 42.4875169],[-71.4638205, 42.4875164],[-71.4638125, 42.4874152],[-71.463905, 42.4874112]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.471564, 42.4778657],[-71.4714721, 42.4778125],[-71.47154759999999, 42.4777411],[-71.47164410000001, 42.4777971],[-71.47162950000001, 42.4778109],[-71.4716842, 42.4778426],[-71.47163260000001, 42.4778915],[-71.47157319999999, 42.4778571],[-71.471564, 42.4778657]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.461033, 42.489394],[-71.4609009, 42.4893328],[-71.4609741, 42.4892463],[-71.4610725, 42.4892918],[-71.4610596, 42.4893071],[-71.46116929999999, 42.4893579],[-71.4611194, 42.489417],[-71.46104339999999, 42.4893818],[-71.461033, 42.489394]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4670507, 42.4864772],[-71.4671256, 42.486386],[-71.4671815, 42.4864111],[-71.4671678, 42.4864278],[-71.467207, 42.4864454],[-71.4670727, 42.4865866],[-71.4669667, 42.4865389],[-71.46702639999999, 42.4864663],[-71.4670507, 42.4864772]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47232150000001, 42.4883566],[-71.4722151, 42.4883182],[-71.472256, 42.4882562],[-71.4724459, 42.4883249],[-71.4724068, 42.488384],[-71.4723559, 42.4883656],[-71.4723664, 42.4883497],[-71.4723339, 42.4883379],[-71.47232150000001, 42.4883566]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4782139, 42.4871954],[-71.4782022, 42.4871978],[-71.4782161, 42.4872346],[-71.4781194, 42.4872547],[-71.478089, 42.4871742],[-71.47815129999999, 42.4871613],[-71.4781454, 42.4871457],[-71.47819149999999, 42.4871361],[-71.4782139, 42.4871954]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4668832, 42.4766575],[-71.4668832, 42.4765652],[-71.4670507, 42.4765652],[-71.4670507, 42.4766388],[-71.467, 42.4766388],[-71.467, 42.4767051],[-71.46689720000001, 42.4767051],[-71.46689720000001, 42.4766575],[-71.4668832, 42.4766575]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4806468, 42.47322],[-71.4806476, 42.4732109],[-71.480796, 42.473218],[-71.48078959999999, 42.4732916],[-71.48064599999999, 42.473284],[-71.4806488, 42.4732553],[-71.4806306, 42.4732543],[-71.48063399999999, 42.4732194],[-71.4806468, 42.47322]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4628544, 42.4795158],[-71.46288869999999, 42.4795632],[-71.46292269999999, 42.4795497],[-71.4629729, 42.479619],[-71.4629364, 42.4796335],[-71.4629833, 42.4796982],[-71.46289400000001, 42.4797337],[-71.4627624, 42.4795523],[-71.4628544, 42.4795158]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543992, 42.4824651],[-71.4544124, 42.4823552],[-71.4543785, 42.482353],[-71.45438710000001, 42.4823295],[-71.45440189999999, 42.4823117],[-71.4548724, 42.4823444],[-71.4548738, 42.4822354],[-71.45442269999999, 42.4822198],[-71.4544233, 42.48221],[-71.454331, 42.4822068],[-71.45433439999999, 42.4821541],[-71.4543704, 42.4821554],[-71.4543742, 42.482096],[-71.4543132, 42.4820939],[-71.4543163, 42.4820458],[-71.45425229999999, 42.4820436],[-71.4542544, 42.4820101],[-71.45365839999999, 42.4819954],[-71.45365889999999, 42.4819845],[-71.4535735, 42.4819824],[-71.4535757, 42.4819329],[-71.4536633, 42.481935],[-71.453666, 42.4818736],[-71.4535096, 42.4818697],[-71.45352440000001, 42.4815416],[-71.453834, 42.4815493],[-71.4538359, 42.4815062],[-71.4538507, 42.4815066],[-71.4538537, 42.4812912],[-71.4538976, 42.4812896],[-71.4538964, 42.4812705],[-71.4539171, 42.4812698],[-71.45391840000001, 42.4812886],[-71.4542908, 42.4813005],[-71.45429129999999, 42.481291],[-71.4543503, 42.4812929],[-71.45434969999999, 42.481303],[-71.45453689999999, 42.481309],[-71.4545383, 42.4812846],[-71.4545954, 42.4812865],[-71.454576, 42.4817626],[-71.45504, 42.481773],[-71.45506279999999, 42.4812137],[-71.45564450000001, 42.4812323],[-71.4556381, 42.481342],[-71.45670269999999, 42.4813761],[-71.45669100000001, 42.481576],[-71.45682499999999, 42.4816837],[-71.4566777, 42.4817832],[-71.4566773, 42.4817949],[-71.4567485, 42.4818479],[-71.4566748, 42.4818976],[-71.4567381, 42.4819565],[-71.45666850000001, 42.4820061],[-71.4566494, 42.4824122],[-71.4557295, 42.4823874],[-71.4557361, 42.4822534],[-71.4551156, 42.4822367],[-71.4551134, 42.4822814],[-71.4550388, 42.4822794],[-71.45504099999999, 42.4822338],[-71.4549837, 42.4822323],[-71.45498259999999, 42.4822544],[-71.4549598, 42.4822547],[-71.4549614, 42.4822759],[-71.45494119999999, 42.4822754],[-71.4549428, 42.4822427],[-71.4549019, 42.4822416],[-71.45489569999999, 42.4823663],[-71.45491250000001, 42.4823672],[-71.4549016, 42.4824776],[-71.45468080000001, 42.4824656],[-71.4546793, 42.4824815],[-71.45469610000001, 42.4824824],[-71.45466570000001, 42.4827352],[-71.4543267, 42.4827129],[-71.4543568, 42.4824623],[-71.4543992, 42.4824651]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4658403, 42.4872344],[-71.4658177, 42.4872092],[-71.4657498, 42.4872425],[-71.46568689999999, 42.4871722],[-71.4657607, 42.487136],[-71.46584470000001, 42.4870908],[-71.4659329, 42.487189],[-71.4658403, 42.4872344]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47610419999999, 42.488485],[-71.4761062, 42.4884587],[-71.47617390000001, 42.4884615],[-71.4761647, 42.4885822],[-71.4760696, 42.4885783],[-71.47607669999999, 42.4884839],[-71.47610419999999, 42.488485]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47298120000001, 42.483476],[-71.4729452, 42.4834636],[-71.4730144, 42.4833533],[-71.4731172, 42.4833886],[-71.4730545, 42.4834887],[-71.4729876, 42.4834657],[-71.47298120000001, 42.483476]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4658225, 42.4829301],[-71.4657938, 42.4829604],[-71.4658101, 42.4829688],[-71.4657334, 42.4830498],[-71.46564290000001, 42.4830028],[-71.4657483, 42.4828915],[-71.4658225, 42.4829301]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4638561, 42.4807792],[-71.4637639, 42.4809569],[-71.4636536, 42.4809256],[-71.4637097, 42.4808175],[-71.4636922, 42.4808125],[-71.4637283, 42.4807429],[-71.4638561, 42.4807792]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4544061, 42.4893192],[-71.45432, 42.4893697],[-71.45433300000001, 42.4893819],[-71.4542127, 42.4894526],[-71.45414529999999, 42.4893898],[-71.4543517, 42.4892685],[-71.4544061, 42.4893192]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4780851, 42.4762376],[-71.4783048, 42.4762699],[-71.4782876, 42.4763337],[-71.4782069, 42.4763219],[-71.4782021, 42.4763399],[-71.4780631, 42.4763195],[-71.4780851, 42.4762376]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4699174, 42.4814063],[-71.4699415, 42.4813662],[-71.4700624, 42.4814061],[-71.46999049999999, 42.4815255],[-71.46963839999999, 42.4814094],[-71.4696862, 42.48133],[-71.4699174, 42.4814063]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4591094, 42.4862924],[-71.4590006, 42.4863224],[-71.4589493, 42.4862203],[-71.45896449999999, 42.4862161],[-71.4589311, 42.4861497],[-71.45902479999999, 42.4861239],[-71.4591094, 42.4862924]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47311929999999, 42.4859337],[-71.47315089999999, 42.4860185],[-71.4730181, 42.4860456],[-71.4730136, 42.4860334],[-71.4728253, 42.4860719],[-71.47279829999999, 42.4859993],[-71.47311929999999, 42.4859337]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4514087, 42.4847324],[-71.4512525, 42.4847944],[-71.45122600000001, 42.4847578],[-71.4512797, 42.4847364],[-71.4512051, 42.4846334],[-71.4513074, 42.4845928],[-71.4514087, 42.4847324]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763181, 42.4863072],[-71.47629740000001, 42.4863374],[-71.47626649999999, 42.4863258],[-71.4762367, 42.4863693],[-71.4761544, 42.4863383],[-71.47620499999999, 42.4862647],[-71.4763181, 42.4863072]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46681719999999, 42.4903956],[-71.466762, 42.4905116],[-71.4666861, 42.4904918],[-71.4667038, 42.4904545],[-71.4666185, 42.4904323],[-71.466656, 42.4903536],[-71.46681719999999, 42.4903956]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"4","addr:state":"MA","source:datetime":"2014-11-08T14:01:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4536166, 42.4957528],[-71.4536217, 42.4955844],[-71.45372709999999, 42.4955841],[-71.4537201, 42.4958161],[-71.4535501, 42.4958121],[-71.4535527, 42.4957513],[-71.4536166, 42.4957528]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45551260000001, 42.4803247],[-71.4555039, 42.4803441],[-71.4554704, 42.4803359],[-71.4554586, 42.4803626],[-71.4553968, 42.4803475],[-71.45541729999999, 42.4803014],[-71.45551260000001, 42.4803247]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46314390000001, 42.4799882],[-71.4631728, 42.4799568],[-71.4632914, 42.4800167],[-71.4632098, 42.4801051],[-71.4630216, 42.48001],[-71.46307419999999, 42.4799529],[-71.46314390000001, 42.4799882]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4705792, 42.4849736],[-71.4705605, 42.4850932],[-71.47053289999999, 42.4850908],[-71.47052170000001, 42.4851619],[-71.4704399, 42.4851549],[-71.4704698, 42.4849642],[-71.4705792, 42.4849736]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759761, 42.4918712],[-71.47593999999999, 42.4920487],[-71.47591540000001, 42.492046],[-71.4759066, 42.4920894],[-71.4758099, 42.4920786],[-71.4758545, 42.4918576],[-71.4759761, 42.4918712]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47538369999999, 42.4782874],[-71.47526879999999, 42.4782957],[-71.4752582, 42.4782155],[-71.4752774, 42.4782141],[-71.4752755, 42.4781997],[-71.4753712, 42.4781928],[-71.47538369999999, 42.4782874]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4681812, 42.4907686],[-71.46817849999999, 42.4908282],[-71.4681336, 42.4908271],[-71.4681327, 42.4908487],[-71.46793150000001, 42.4908437],[-71.4679352, 42.4907625],[-71.4681812, 42.4907686]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4694559, 42.4881154],[-71.4694314, 42.4881442],[-71.4694563, 42.4881558],[-71.4693956, 42.488227],[-71.4693041, 42.4881842],[-71.4693893, 42.4880843],[-71.4694559, 42.4881154]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4701914, 42.4967389],[-71.4699911, 42.4967475],[-71.46998619999999, 42.4966849],[-71.47007910000001, 42.4966809],[-71.47007840000001, 42.496672],[-71.4701857, 42.4966674],[-71.4701914, 42.4967389]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4722074, 42.4966097],[-71.47219800000001, 42.4966909],[-71.4720147, 42.4966794],[-71.47201099999999, 42.496712],[-71.4718946, 42.4967014],[-71.4719113, 42.496591],[-71.4722074, 42.4966097]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4684168, 42.4868747],[-71.4684039, 42.4868912],[-71.4684711, 42.4869198],[-71.4684299, 42.4869728],[-71.468215, 42.4868879],[-71.4682729, 42.4868134],[-71.4684168, 42.4868747]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47329070000001, 42.4886119],[-71.47321359999999, 42.4887049],[-71.4731838, 42.4886914],[-71.4731405, 42.4887436],[-71.4730687, 42.488711],[-71.4731891, 42.4885657],[-71.47329070000001, 42.4886119]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46183550000001, 42.474153],[-71.4618198, 42.4740538],[-71.4619385, 42.4740435],[-71.4619703, 42.4742437],[-71.46186760000001, 42.4742527],[-71.46185149999999, 42.4741516],[-71.46183550000001, 42.474153]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4699099, 42.4942022],[-71.46992849999999, 42.4941627],[-71.4700055, 42.4941824],[-71.4699494, 42.4943022],[-71.46975860000001, 42.4942532],[-71.4697962, 42.494173],[-71.4699099, 42.4942022]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703641, 42.4810101],[-71.47034859999999, 42.4810158],[-71.470383, 42.4810669],[-71.4703035, 42.4810962],[-71.47020670000001, 42.4809522],[-71.4703016, 42.4809172],[-71.4703641, 42.4810101]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47322920000001, 42.4933235],[-71.47304560000001, 42.4933237],[-71.4730455, 42.493264],[-71.47312290000001, 42.4932639],[-71.4731228, 42.4931708],[-71.4732289, 42.4931707],[-71.47322920000001, 42.4933235]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4702575, 42.487716],[-71.47025240000001, 42.4877237],[-71.4703018, 42.4877417],[-71.4702637, 42.4877992],[-71.4701809, 42.4877691],[-71.4702242, 42.4877039],[-71.4702575, 42.487716]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4690595, 42.4923043],[-71.469022, 42.4923938],[-71.4689839, 42.4923851],[-71.4689683, 42.4924225],[-71.4689125, 42.4924097],[-71.4689657, 42.4922828],[-71.4690595, 42.4923043]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4677664, 42.4907278],[-71.4677061, 42.4908175],[-71.467668, 42.4908035],[-71.4676875, 42.4907745],[-71.4675678, 42.4907304],[-71.46760860000001, 42.4906697],[-71.4677664, 42.4907278]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"51","addr:state":"MA","source:datetime":"2014-11-08T13:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4593673, 42.4922451],[-71.4594245, 42.4921994],[-71.459484, 42.4922402],[-71.4593193, 42.4923718],[-71.45924119999999, 42.4923182],[-71.45934870000001, 42.4922323],[-71.4593673, 42.4922451]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46174360000001, 42.4722377],[-71.4617406, 42.4722827],[-71.46169089999999, 42.4722809],[-71.4616894, 42.4723041],[-71.4615699, 42.4722998],[-71.4615744, 42.4722316],[-71.46174360000001, 42.4722377]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46126409999999, 42.4989185],[-71.4612386, 42.4988829],[-71.4613346, 42.4988452],[-71.4613966, 42.4989316],[-71.46131819999999, 42.4989624],[-71.4612817, 42.4989115],[-71.46126409999999, 42.4989185]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4638661, 42.4742917],[-71.4638345, 42.4742918],[-71.46383520000001, 42.4743977],[-71.4637156, 42.4743982],[-71.4637144, 42.4742287],[-71.4638657, 42.4742281],[-71.4638661, 42.4742917]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714518, 42.4900369],[-71.4713021, 42.4901676],[-71.4712332, 42.4901244],[-71.4713053, 42.4900615],[-71.47129649999999, 42.4900559],[-71.47137669999999, 42.4899898],[-71.4714518, 42.4900369]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"22","addr:state":"MA","source:datetime":"2014-11-08T13:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4566954, 42.4955102],[-71.4565595, 42.4956279],[-71.45645159999999, 42.4955597],[-71.45652490000001, 42.4954961],[-71.4564822, 42.4954691],[-71.45654469999999, 42.4954149],[-71.4566954, 42.4955102]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48305139999999, 42.473222],[-71.4830485, 42.4733652],[-71.48296120000001, 42.4733642],[-71.48296209999999, 42.4733198],[-71.4829476, 42.4733197],[-71.4829496, 42.4732209],[-71.48305139999999, 42.473222]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715705, 42.4945085],[-71.4716129, 42.4944558],[-71.4716832, 42.4944867],[-71.47161079999999, 42.4945767],[-71.471523, 42.494538],[-71.47155290000001, 42.4945008],[-71.4715705, 42.4945085]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4673446, 42.49335],[-71.4672997, 42.493276],[-71.4674492, 42.4932263],[-71.4675143, 42.4933337],[-71.4674354, 42.4933599],[-71.4674152, 42.4933266],[-71.4673446, 42.49335]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692458, 42.4899951],[-71.469238, 42.4900357],[-71.4691978, 42.4900315],[-71.4691854, 42.4900965],[-71.4690737, 42.4900848],[-71.4690939, 42.4899792],[-71.4692458, 42.4899951]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4677958, 42.4857305],[-71.46789130000001, 42.4856225],[-71.46799, 42.4856704],[-71.4678655, 42.485811],[-71.4677267, 42.4857437],[-71.4677557, 42.485711],[-71.4677958, 42.4857305]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4702758, 42.4826964],[-71.4703422, 42.4826155],[-71.4704519, 42.4826649],[-71.47034859999999, 42.4827906],[-71.4702252, 42.482735],[-71.47026200000001, 42.4826902],[-71.4702758, 42.4826964]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715733, 42.4858732],[-71.47149760000001, 42.48598],[-71.4714692, 42.485969],[-71.4714432, 42.4860058],[-71.4713771, 42.4859802],[-71.47147889999999, 42.4858366],[-71.4715733, 42.4858732]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4823457, 42.4753372],[-71.48231509999999, 42.4753982],[-71.4822775, 42.4753878],[-71.4822932, 42.4753566],[-71.48225309999999, 42.4753455],[-71.4822681, 42.4753159],[-71.4823457, 42.4753372]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757378, 42.478289],[-71.47571569999999, 42.478298],[-71.4757321, 42.4783199],[-71.4756311, 42.4783612],[-71.4755746, 42.4782855],[-71.4756977, 42.4782352],[-71.4757378, 42.478289]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4591295, 42.474469],[-71.45910840000001, 42.4744725],[-71.4591175, 42.4745028],[-71.4590266, 42.4745177],[-71.45900279999999, 42.4744383],[-71.45911479999999, 42.4744199],[-71.4591295, 42.474469]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46022739999999, 42.4728329],[-71.4601832, 42.4728453],[-71.4602352, 42.4729468],[-71.4600961, 42.4729859],[-71.46001459999999, 42.4728271],[-71.46019800000001, 42.4727755],[-71.46022739999999, 42.4728329]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"230","addr:state":"MA","source:datetime":"2015-01-22T00:30:37Z"},"geometry": {"type":"Point","coordinates": [-71.46372599999999, 42.499687]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46568980000001, 42.4799736],[-71.46563190000001, 42.4800754],[-71.4655743, 42.4800575],[-71.46558659999999, 42.4800358],[-71.4654347, 42.4799886],[-71.4654803, 42.4799084],[-71.46568980000001, 42.4799736]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4726735, 42.4789375],[-71.47268099999999, 42.479036],[-71.472705, 42.479035],[-71.4727104, 42.4791054],[-71.472568, 42.4791114],[-71.47255509999999, 42.4789425],[-71.4726735, 42.4789375]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46326689999999, 42.4774636],[-71.4632395, 42.4776165],[-71.4631447, 42.4776072],[-71.4631559, 42.4775448],[-71.463144, 42.4775436],[-71.4631602, 42.4774532],[-71.46326689999999, 42.4774636]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.485117, 42.4725601],[-71.48512289999999, 42.4725397],[-71.48505780000001, 42.472612],[-71.4850747, 42.4725534],[-71.485117, 42.4725601]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47647190000001, 42.487808],[-71.47646, 42.4878891],[-71.47636, 42.487881],[-71.4763621, 42.4878671],[-71.47630530000001, 42.4878625],[-71.47631509999999, 42.4877954],[-71.47647190000001, 42.487808]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696866, 42.4868571],[-71.4696374, 42.4869269],[-71.4694789, 42.4868656],[-71.4694992, 42.4868369],[-71.4694528, 42.4868189],[-71.4694818, 42.4867779],[-71.4696866, 42.4868571]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4558749, 42.4827474],[-71.45570429999999, 42.4827629],[-71.4557111, 42.4828039],[-71.4556305, 42.4828112],[-71.4556103, 42.4826895],[-71.4558615, 42.4826666],[-71.4558749, 42.4827474]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4648183, 42.4890799],[-71.46476180000001, 42.4889872],[-71.4648571, 42.4889554],[-71.464951, 42.4891094],[-71.46487399999999, 42.4891351],[-71.46483670000001, 42.4890738],[-71.4648183, 42.4890799]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4800915, 42.4735926],[-71.48015669999999, 42.4736105],[-71.480148, 42.473628],[-71.480251, 42.4736579],[-71.4802262, 42.4737074],[-71.4800572, 42.473661],[-71.4800915, 42.4735926]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4721363, 42.4922426],[-71.4721832, 42.4922844],[-71.47222789999999, 42.492257],[-71.47229110000001, 42.4923132],[-71.47219, 42.4923754],[-71.4720799, 42.4922773],[-71.4721363, 42.4922426]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46397210000001, 42.4746861],[-71.46392849999999, 42.4746935],[-71.46395579999999, 42.4747814],[-71.46385720000001, 42.4747982],[-71.463815, 42.4746623],[-71.4639571, 42.4746381],[-71.46397210000001, 42.4746861]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4623878, 42.4730342],[-71.46238099999999, 42.4730687],[-71.462519, 42.4730836],[-71.4625018, 42.4731707],[-71.46231469999999, 42.4731505],[-71.4623386, 42.4730289],[-71.4623878, 42.4730342]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45320220000001, 42.4762515],[-71.4529842, 42.4763549],[-71.4529373, 42.4762948],[-71.4530764, 42.4762352],[-71.4530663, 42.4762223],[-71.4531511, 42.476186],[-71.45320220000001, 42.4762515]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4659532, 42.4792403],[-71.465929, 42.4792766],[-71.4660544, 42.4793225],[-71.4660033, 42.479399],[-71.4657689, 42.4793131],[-71.4658443, 42.4792004],[-71.4659532, 42.4792403]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4749556, 42.47865],[-71.474963, 42.4786901],[-71.4749917, 42.4786872],[-71.4750059, 42.4787646],[-71.4748884, 42.4787764],[-71.4748668, 42.478659],[-71.4749556, 42.47865]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"23","addr:state":"MA","source:datetime":"2014-11-08T14:01:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45497, 42.4920072],[-71.4550261, 42.4919831],[-71.45508340000001, 42.4920559],[-71.45492040000001, 42.4921262],[-71.4548526, 42.4920401],[-71.4549596, 42.491994],[-71.45497, 42.4920072]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4620616, 42.4725992],[-71.4620615, 42.4726223],[-71.4621229, 42.4726225],[-71.4621226, 42.4726727],[-71.4619448, 42.4726721],[-71.4619452, 42.4725988],[-71.4620616, 42.4725992]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"35","addr:state":"MA","source:datetime":"2014-11-08T13:55:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45817599999999, 42.494154],[-71.458212, 42.4940984],[-71.4582853, 42.4941245],[-71.4581776, 42.4942904],[-71.4580886, 42.4942588],[-71.45816019999999, 42.4941484],[-71.45817599999999, 42.494154]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45051119999999, 42.4870685],[-71.45052219999999, 42.4870271],[-71.4506349, 42.4870435],[-71.45060580000001, 42.4871534],[-71.45051119999999, 42.4870685]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45894370000001, 42.4739894],[-71.4589328, 42.4739922],[-71.458977, 42.4740868],[-71.4588973, 42.4741072],[-71.458809, 42.4739178],[-71.4588995, 42.4738946],[-71.45894370000001, 42.4739894]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45174299999999, 42.4922541],[-71.45170709999999, 42.4923279],[-71.4515727, 42.492292],[-71.4515782, 42.4922808],[-71.45143419999999, 42.4922423],[-71.4514647, 42.4921798],[-71.45174299999999, 42.4922541]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46450129999999, 42.4819585],[-71.46449200000001, 42.4820178],[-71.4644357, 42.482013],[-71.4644376, 42.4820007],[-71.46440870000001, 42.4819983],[-71.464416, 42.4819512],[-71.46450129999999, 42.4819585]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:54:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4729742, 42.4901666],[-71.4729119, 42.4902757],[-71.4729493, 42.4902874],[-71.4729196, 42.4903394],[-71.4727944, 42.4902924],[-71.47288260000001, 42.4901379],[-71.4729742, 42.4901666]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703111, 42.4781546],[-71.4702906, 42.4781027],[-71.47042190000001, 42.4780743],[-71.4704764, 42.4782126],[-71.470383, 42.4782328],[-71.4703489, 42.4781464],[-71.4703111, 42.4781546]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47310899999999, 42.4739366],[-71.47310709999999, 42.4738901],[-71.4731962, 42.4738882],[-71.4731985, 42.4739471],[-71.4731731, 42.4739476],[-71.4731726, 42.4739353],[-71.47310899999999, 42.4739366]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:59Z"},"geometry": {"type":"LineString","coordinates": [[-71.450467, 42.4730641],[-71.4504238, 42.4731704],[-71.450467, 42.4730641]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46379810000001, 42.4795372],[-71.46388880000001, 42.4796105],[-71.4639019, 42.4796016],[-71.4639595, 42.4796481],[-71.46387249999999, 42.4797149],[-71.46370899999999, 42.4795975],[-71.46379810000001, 42.4795372]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4511828, 42.4766927],[-71.4512055, 42.476687],[-71.4512319, 42.4767452],[-71.4510291, 42.4767956],[-71.45099089999999, 42.4767112],[-71.45117089999999, 42.4766664],[-71.4511828, 42.4766927]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47118759999999, 42.4863915],[-71.4710844, 42.4865572],[-71.4710094, 42.4865343],[-71.47106359999999, 42.4864475],[-71.4710329, 42.4864377],[-71.4710795, 42.4863572],[-71.47118759999999, 42.4863915]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4761409, 42.4907718],[-71.47611910000001, 42.4908713],[-71.4760191, 42.4908593],[-71.4760228, 42.4908424],[-71.4758817, 42.4908254],[-71.47589979999999, 42.4907429],[-71.4761409, 42.4907718]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4710091, 42.4943772],[-71.4708873, 42.4943097],[-71.4709424, 42.4942553],[-71.47095469999999, 42.4942621],[-71.4710075, 42.4942099],[-71.47111700000001, 42.4942706],[-71.4710091, 42.4943772]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4598448, 42.4725273],[-71.45974579999999, 42.4725317],[-71.4597476, 42.4725533],[-71.4596274, 42.4725587],[-71.45962179999999, 42.4724907],[-71.459841, 42.4724808],[-71.4598448, 42.4725273]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4798182, 42.4750635],[-71.4796448, 42.4750924],[-71.4796284, 42.4750386],[-71.4796834, 42.4750295],[-71.47967989999999, 42.4750177],[-71.4797983, 42.474998],[-71.4798182, 42.4750635]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4840245, 42.4771167],[-71.4840306, 42.4771023],[-71.4841081, 42.4771203],[-71.48404290000001, 42.4772734],[-71.48394140000001, 42.4772498],[-71.48400049999999, 42.4771111],[-71.4840245, 42.4771167]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4708835, 42.4843863],[-71.4708619, 42.4844464],[-71.47082330000001, 42.4844388],[-71.47079069999999, 42.4845294],[-71.47069020000001, 42.4845096],[-71.4707444, 42.4843589],[-71.4708835, 42.4843863]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45529790000001, 42.4747812],[-71.4552891, 42.474843],[-71.455319, 42.4748453],[-71.4553085, 42.4749182],[-71.4552276, 42.4749119],[-71.45524690000001, 42.4747772],[-71.45529790000001, 42.4747812]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47862050000001, 42.4879509],[-71.4784338, 42.487984],[-71.4784224, 42.4879487],[-71.4784385, 42.4879459],[-71.4784269, 42.4879097],[-71.4785974, 42.4878795],[-71.47862050000001, 42.4879509]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4627311, 42.4773558],[-71.4627112, 42.4773586],[-71.4627346, 42.4774497],[-71.4626523, 42.4774612],[-71.4626052, 42.4772781],[-71.4627074, 42.4772637],[-71.4627311, 42.4773558]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4684046, 42.4822631],[-71.4683325, 42.4823692],[-71.4683445, 42.4823736],[-71.4682787, 42.4824704],[-71.4681779, 42.4824339],[-71.4683187, 42.4822311],[-71.4684046, 42.4822631]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4617451, 42.4800534],[-71.4616493, 42.480087],[-71.46159249999999, 42.479998],[-71.46160740000001, 42.4799928],[-71.4615455, 42.4798959],[-71.4616264, 42.4798676],[-71.4617451, 42.4800534]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4612583, 42.4732322],[-71.46118010000001, 42.473405],[-71.46108409999999, 42.4733812],[-71.46111500000001, 42.473313],[-71.4610979, 42.4733087],[-71.4611452, 42.4732041],[-71.4612583, 42.4732322]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4529095, 42.4719831],[-71.4528921, 42.4721388],[-71.4527865, 42.4721323],[-71.45279050000001, 42.4720963],[-71.4527777, 42.4720955],[-71.4527911, 42.4719758],[-71.4529095, 42.4719831]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4777108, 42.4924077],[-71.47766369999999, 42.4923739],[-71.4777675, 42.4922892],[-71.47788989999999, 42.4923685],[-71.47781929999999, 42.4924273],[-71.47774699999999, 42.4923788],[-71.4777108, 42.4924077]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46690940000001, 42.476214],[-71.4669202, 42.4761631],[-71.46700800000001, 42.4761734],[-71.46697589999999, 42.476324],[-71.4668641, 42.4763109],[-71.4668854, 42.4762112],[-71.46690940000001, 42.476214]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"7","source:datetime":"2014-04-11T16:40:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4830149, 42.4750277],[-71.4829247, 42.4750811],[-71.48280560000001, 42.4749709],[-71.4828433, 42.4749486],[-71.4828145, 42.474922],[-71.482867, 42.4748909],[-71.4830149, 42.4750277]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4825098, 42.4760513],[-71.48242190000001, 42.4760935],[-71.48233070000001, 42.4759894],[-71.4823751, 42.4759681],[-71.4823468, 42.4759358],[-71.48239030000001, 42.4759149],[-71.4825098, 42.4760513]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46979090000001, 42.4957883],[-71.4697932, 42.495749],[-71.4698696, 42.4957514],[-71.46986010000001, 42.4959174],[-71.469746, 42.4959138],[-71.4697529, 42.4957871],[-71.46979090000001, 42.4957883]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728648, 42.4890629],[-71.4728477, 42.4890859],[-71.4728894, 42.4891028],[-71.4728349, 42.4891761],[-71.4727441, 42.4891391],[-71.4728157, 42.4890429],[-71.4728648, 42.4890629]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4668131, 42.4879817],[-71.4668945, 42.4881038],[-71.46683350000001, 42.4881261],[-71.46681359999999, 42.4880964],[-71.46669, 42.4881415],[-71.4666223, 42.4880514],[-71.4668131, 42.4879817]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4705513, 42.4761577],[-71.47054900000001, 42.4762036],[-71.4706242, 42.4762056],[-71.4706217, 42.4762577],[-71.47040730000001, 42.4762519],[-71.4704121, 42.476154],[-71.4705513, 42.4761577]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"47","addr:state":"MA","source:datetime":"2014-11-08T13:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4592012, 42.4925145],[-71.4593012, 42.492624],[-71.4593294, 42.49261],[-71.4593594, 42.4926428],[-71.45923740000001, 42.4927038],[-71.45910739999999, 42.4925614],[-71.4592012, 42.4925145]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4776636, 42.4898246],[-71.47765219999999, 42.4898664],[-71.4776786, 42.4898704],[-71.4776502, 42.4899744],[-71.4775501, 42.4899594],[-71.4775899, 42.4898136],[-71.4776636, 42.4898246]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47241459999999, 42.4889105],[-71.4725909, 42.4889887],[-71.4725504, 42.4890387],[-71.4725207, 42.4890255],[-71.4725049, 42.4890451],[-71.4723582, 42.4889801],[-71.47241459999999, 42.4889105]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4666618, 42.4940778],[-71.466633, 42.4939662],[-71.46673180000001, 42.4939522],[-71.4667726, 42.4941106],[-71.4666849, 42.494123],[-71.4666728, 42.4940763],[-71.4666618, 42.4940778]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4647188, 42.486725],[-71.4646891, 42.4867565],[-71.46469980000001, 42.4867622],[-71.46453959999999, 42.4869135],[-71.4644679, 42.4868687],[-71.4646412, 42.486685],[-71.4647188, 42.486725]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46033730000001, 42.4821036],[-71.46034589999999, 42.4820926],[-71.4604703, 42.4821462],[-71.46041769999999, 42.4822131],[-71.4602486, 42.4821403],[-71.46029249999999, 42.4820844],[-71.46033730000001, 42.4821036]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4629929, 42.489889],[-71.46301920000001, 42.4898534],[-71.4630784, 42.4898774],[-71.4629741, 42.4900183],[-71.4628855, 42.4899824],[-71.4629635, 42.489877],[-71.4629929, 42.489889]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4688384, 42.4731674],[-71.4688915, 42.4731051],[-71.46897439999999, 42.4731438],[-71.468891, 42.4732416],[-71.4687666, 42.4731835],[-71.4687969, 42.473148],[-71.4688384, 42.4731674]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759655, 42.4839433],[-71.4760106, 42.4839162],[-71.476056, 42.4839575],[-71.47591180000001, 42.4840442],[-71.47585890000001, 42.4839959],[-71.4759579, 42.4839364],[-71.4759655, 42.4839433]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47689, 42.4883239],[-71.4768804, 42.4883754],[-71.4768356, 42.4883708],[-71.47682709999999, 42.4884161],[-71.4767339, 42.4884065],[-71.4767521, 42.4883097],[-71.47689, 42.4883239]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4823564, 42.4766525],[-71.4821718, 42.4765812],[-71.4822174, 42.4765165],[-71.48245129999999, 42.4766068],[-71.4824138, 42.47666],[-71.4823645, 42.476641],[-71.4823564, 42.4766525]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"11","addr:state":"MA","source:datetime":"2014-11-08T14:01:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45425640000001, 42.4943407],[-71.4542714, 42.494279],[-71.4543674, 42.4942917],[-71.45432169999999, 42.4944805],[-71.4542107, 42.4944658],[-71.4542414, 42.4943387],[-71.45425640000001, 42.4943407]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4690033, 42.4799315],[-71.4691105, 42.4799972],[-71.469128, 42.4799816],[-71.46921330000001, 42.4800339],[-71.4691524, 42.4800883],[-71.46896, 42.4799703],[-71.4690033, 42.4799315]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4813125, 42.4723118],[-71.4812908, 42.4723621],[-71.4812385, 42.4723497],[-71.4811989, 42.4724413],[-71.48109030000001, 42.4724156],[-71.4811517, 42.4722736],[-71.4813125, 42.4723118]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46987470000001, 42.4875338],[-71.46981220000001, 42.4876381],[-71.46978590000001, 42.4876295],[-71.469753, 42.4876843],[-71.46966569999999, 42.4876556],[-71.46976119999999, 42.4874965],[-71.46987470000001, 42.4875338]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.473765, 42.4881499],[-71.4737306, 42.4881202],[-71.47385300000001, 42.4880427],[-71.47391039999999, 42.4880925],[-71.4738148, 42.488153],[-71.47379170000001, 42.488133],[-71.473765, 42.4881499]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4584619, 42.4741174],[-71.4584323, 42.4740962],[-71.45847910000001, 42.4740602],[-71.458538, 42.4741022],[-71.45850249999999, 42.4741295],[-71.4584732, 42.4741087],[-71.4584619, 42.4741174]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47023129999999, 42.4777768],[-71.4702291, 42.4777015],[-71.470383, 42.477699],[-71.47038860000001, 42.4778876],[-71.4702849, 42.4778892],[-71.4702815, 42.477776],[-71.47023129999999, 42.4777768]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4726821, 42.4912637],[-71.4727858, 42.4913355],[-71.4728108, 42.4913158],[-71.4728499, 42.4913429],[-71.4727544, 42.4914184],[-71.47261159999999, 42.4913194],[-71.4726821, 42.4912637]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.469303, 42.4797778],[-71.4693161, 42.4797663],[-71.4694334, 42.4798395],[-71.4693582, 42.4799001],[-71.4691689, 42.479782],[-71.46922739999999, 42.4797306],[-71.469303, 42.4797778]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692345, 42.4915958],[-71.4691985, 42.4916854],[-71.4691821, 42.4916818],[-71.4691696, 42.491713],[-71.4690865, 42.4916947],[-71.46913499999999, 42.4915739],[-71.4692345, 42.4915958]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47732449999999, 42.4863996],[-71.47731709999999, 42.4863296],[-71.4774566, 42.4863215],[-71.4774721, 42.4864677],[-71.47736399999999, 42.486474],[-71.47735590000001, 42.4863978],[-71.47732449999999, 42.4863996]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4707669, 42.4803818],[-71.4706801, 42.4804078],[-71.4707037, 42.4804509],[-71.47060690000001, 42.48048],[-71.4705541, 42.4803837],[-71.4707377, 42.4803286],[-71.4707669, 42.4803818]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4526311, 42.4919198],[-71.4526145, 42.4919223],[-71.4526414, 42.4920201],[-71.4525546, 42.4920332],[-71.4524983, 42.4918285],[-71.4526017, 42.4918129],[-71.4526311, 42.4919198]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.469932, 42.4727388],[-71.46983280000001, 42.4727542],[-71.4698403, 42.4727805],[-71.469786, 42.472789],[-71.4697587, 42.4726933],[-71.4699122, 42.4726693],[-71.469932, 42.4727388]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45075180000001, 42.4724901],[-71.4507069, 42.4724923],[-71.4507122, 42.4725521],[-71.45060460000001, 42.4725573],[-71.4505935, 42.4724321],[-71.450746, 42.4724246],[-71.45075180000001, 42.4724901]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.472514, 42.4787934],[-71.4724768, 42.4787291],[-71.472622, 42.4786831],[-71.4727134, 42.478841],[-71.4725938, 42.4788789],[-71.4725396, 42.4787853],[-71.472514, 42.4787934]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46981599999999, 42.4935053],[-71.469792, 42.4936076],[-71.4698334, 42.4936129],[-71.46982439999999, 42.4936513],[-71.4696728, 42.4936318],[-71.4697058, 42.4934912],[-71.46981599999999, 42.4935053]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757571, 42.4784985],[-71.47576170000001, 42.478539],[-71.4757766, 42.4785381],[-71.4757887, 42.4786445],[-71.4756812, 42.4786512],[-71.47566449999999, 42.4785043],[-71.4757571, 42.4784985]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4836399, 42.4742237],[-71.48360839999999, 42.4742405],[-71.48368619999999, 42.4743207],[-71.4835945, 42.4743694],[-71.4834802, 42.4742515],[-71.4836033, 42.4741861],[-71.4836399, 42.4742237]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47416699999999, 42.4769475],[-71.4740474, 42.4769796],[-71.4740001, 42.476883],[-71.4740472, 42.4768704],[-71.4740403, 42.4768562],[-71.4741128, 42.4768367],[-71.47416699999999, 42.4769475]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4768402, 42.4885795],[-71.4768136, 42.4886668],[-71.47671389999999, 42.4886501],[-71.476722, 42.4886237],[-71.4767009, 42.4886202],[-71.47671939999999, 42.4885594],[-71.4768402, 42.4885795]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47646779999999, 42.485998],[-71.4764504, 42.4860358],[-71.4764213, 42.4860284],[-71.4763986, 42.4860778],[-71.4763019, 42.4860535],[-71.476342, 42.4859663],[-71.47646779999999, 42.485998]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4669567, 42.488968],[-71.466994, 42.4889037],[-71.4670809, 42.4889313],[-71.4670273, 42.4890238],[-71.46689309999999, 42.4889812],[-71.4669095, 42.488953],[-71.4669567, 42.488968]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4699478, 42.4823047],[-71.469932, 42.4823286],[-71.46995680000001, 42.4823376],[-71.4698975, 42.4824275],[-71.46978970000001, 42.4823886],[-71.4698648, 42.4822747],[-71.4699478, 42.4823047]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4617959, 42.4803457],[-71.4617447, 42.4802541],[-71.4618397, 42.4802249],[-71.4619464, 42.4804159],[-71.46186969999999, 42.4804394],[-71.46181420000001, 42.4803401],[-71.4617959, 42.4803457]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4819016, 42.4733426],[-71.4818684, 42.473434],[-71.4817968, 42.4734197],[-71.4818016, 42.4734064],[-71.4816872, 42.4733836],[-71.48171550000001, 42.4733056],[-71.4819016, 42.4733426]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.450514, 42.4845288],[-71.4506171, 42.4844883],[-71.4506615, 42.4845503],[-71.4504398, 42.4846375],[-71.4505042, 42.4845152],[-71.450514, 42.4845288]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"50","addr:state":"MA","source:datetime":"2014-11-08T13:55:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4584115, 42.4914367],[-71.4583457, 42.491514],[-71.4583298, 42.4915065],[-71.4582827, 42.4915618],[-71.45820860000001, 42.4915272],[-71.4583214, 42.4913947],[-71.4584115, 42.4914367]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.470731, 42.4928427],[-71.4707736, 42.4928077],[-71.47082519999999, 42.4928421],[-71.4706554, 42.4929815],[-71.47057719999999, 42.4929293],[-71.47070429999999, 42.4928249],[-71.470731, 42.4928427]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46827039999999, 42.4939414],[-71.4682574, 42.4939546],[-71.4683062, 42.4939811],[-71.46825389999999, 42.4940339],[-71.4680699, 42.4939339],[-71.4681353, 42.4938679],[-71.46827039999999, 42.4939414]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47365929999999, 42.4780906],[-71.4735879, 42.478119],[-71.4735656, 42.4780882],[-71.4735798, 42.4780826],[-71.4735603, 42.4780558],[-71.4736176, 42.478033],[-71.47365929999999, 42.4780906]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47776349999999, 42.4879759],[-71.47775059999999, 42.4879062],[-71.4779411, 42.4878869],[-71.4779645, 42.4880135],[-71.47788610000001, 42.4880214],[-71.4778756, 42.4879645],[-71.47776349999999, 42.4879759]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689047, 42.4738295],[-71.4687431, 42.4739073],[-71.4687728, 42.4739411],[-71.4687248, 42.4739643],[-71.4686392, 42.4738669],[-71.4688489, 42.4737659],[-71.4689047, 42.4738295]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4743521, 42.4929231],[-71.4742995, 42.4930224],[-71.47423000000001, 42.4930023],[-71.474211, 42.493038],[-71.4741089, 42.4930084],[-71.4741805, 42.4928733],[-71.4743521, 42.4929231]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4596171, 42.4741953],[-71.45964840000001, 42.4741885],[-71.4596787, 42.4742648],[-71.4595478, 42.4742933],[-71.4595092, 42.474196],[-71.4596088, 42.4741743],[-71.4596171, 42.4741953]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4586865, 42.4862448],[-71.4585392, 42.486284],[-71.4585091, 42.4862221],[-71.4585491, 42.4862114],[-71.45853750000001, 42.4861874],[-71.45864469999999, 42.4861588],[-71.4586865, 42.4862448]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47009439999999, 42.480672],[-71.4700747, 42.480644],[-71.4701874, 42.4806005],[-71.4702494, 42.4806884],[-71.47015380000001, 42.4807254],[-71.4701115, 42.4806654],[-71.47009439999999, 42.480672]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48359859999999, 42.4767252],[-71.4835791, 42.4768018],[-71.48340899999999, 42.4767781],[-71.4834144, 42.4767568],[-71.4833764, 42.4767515],[-71.4833905, 42.4766962],[-71.48359859999999, 42.4767252]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4530226, 42.4731964],[-71.4530115, 42.4732037],[-71.4530541, 42.4732396],[-71.4529749, 42.4732911],[-71.4528656, 42.4731991],[-71.452956, 42.4731403],[-71.4530226, 42.4731964]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697917, 42.4801334],[-71.4696924, 42.4802337],[-71.4697196, 42.4802485],[-71.4696599, 42.4803051],[-71.4695438, 42.4802423],[-71.4697208, 42.4800886],[-71.4697917, 42.4801334]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47861760000001, 42.4752665],[-71.4785148, 42.4752796],[-71.47852090000001, 42.4753057],[-71.478483, 42.4753106],[-71.47846029999999, 42.4752134],[-71.478601, 42.4751954],[-71.47861760000001, 42.4752665]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48381190000001, 42.4728168],[-71.4838297, 42.4727478],[-71.483925, 42.4727613],[-71.483874, 42.4729589],[-71.4837232, 42.4729376],[-71.4837564, 42.472809],[-71.48381190000001, 42.4728168]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.461848, 42.4910612],[-71.461837, 42.491071],[-71.4619053, 42.4911131],[-71.4618437, 42.491168],[-71.4616666, 42.4910591],[-71.4617393, 42.4909944],[-71.461848, 42.4910612]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4533469, 42.497295],[-71.4532841, 42.4972422],[-71.4533923, 42.4971716],[-71.4535364, 42.4972926],[-71.4534609, 42.4973418],[-71.4533797, 42.4972736],[-71.4533469, 42.497295]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4788463, 42.47522],[-71.4788253, 42.4751554],[-71.4789521, 42.4751328],[-71.4789852, 42.475235],[-71.4789413, 42.4752428],[-71.4789291, 42.4752053],[-71.4788463, 42.47522]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47364020000001, 42.4782566],[-71.4735783, 42.4782753],[-71.4735866, 42.4782889],[-71.4734734, 42.4783232],[-71.47343720000001, 42.478259],[-71.4736123, 42.4782061],[-71.47364020000001, 42.4782566]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"3","source:datetime":"2014-04-11T16:40:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4835734, 42.4758115],[-71.4834984, 42.4756548],[-71.48360220000001, 42.4756276],[-71.4836909, 42.4758129],[-71.4836069, 42.475835],[-71.4835932, 42.4758063],[-71.4835734, 42.4758115]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689556, 42.4910204],[-71.4689194, 42.4910447],[-71.46889419999999, 42.4910241],[-71.46877809999999, 42.4911055],[-71.4687004, 42.4910465],[-71.468861, 42.4909433],[-71.4689556, 42.4910204]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:31Z"},"geometry": {"type":"LineString","coordinates": [[-71.4738983, 42.4718055],[-71.473913, 42.4718076],[-71.4738874, 42.4719029],[-71.4737897, 42.4718885]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713759, 42.4894766],[-71.4712188, 42.4896272],[-71.4711523, 42.4895892],[-71.4712394, 42.4895057],[-71.47122520000001, 42.4894976],[-71.47129529999999, 42.4894305],[-71.4713759, 42.4894766]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4636192, 42.4722292],[-71.4635937, 42.4722973],[-71.4636435, 42.4723075],[-71.4636229, 42.4723626],[-71.4634877, 42.4723348],[-71.4635339, 42.4722116],[-71.4636192, 42.4722292]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Bulette Road","addr:city":"Acton","building":"yes","addr:housenumber":"11","addr:state":"MA","source:datetime":"2015-01-20T23:47:46Z"},"geometry": {"type":"Point","coordinates": [-71.4695311, 42.4996883]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4727505, 42.492644],[-71.47272479999999, 42.4927339],[-71.47265520000001, 42.492723],[-71.4726462, 42.4927545],[-71.4725534, 42.49274],[-71.4725881, 42.4926186],[-71.4727505, 42.492644]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47822069999999, 42.4721328],[-71.478182, 42.4722199],[-71.47809119999999, 42.4721979],[-71.47808139999999, 42.4722202],[-71.4780525, 42.4722132],[-71.478101, 42.4721038],[-71.47822069999999, 42.4721328]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4638448, 42.4778987],[-71.4639001, 42.4780028],[-71.46394220000001, 42.4779906],[-71.4639729, 42.4780485],[-71.4638147, 42.4780946],[-71.4637286, 42.4779325],[-71.4638448, 42.4778987]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757864, 42.4842397],[-71.47578710000001, 42.4842614],[-71.47587780000001, 42.4842598],[-71.4758811, 42.4843596],[-71.4757257, 42.4843624],[-71.47572169999999, 42.4842408],[-71.4757864, 42.4842397]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4758448, 42.48623],[-71.475695, 42.4863395],[-71.4756192, 42.4862827],[-71.47572510000001, 42.4862053],[-71.4757056, 42.4861906],[-71.4757494, 42.4861585],[-71.4758448, 42.48623]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4626567, 42.4812436],[-71.46253590000001, 42.4811877],[-71.4625918, 42.4811215],[-71.4628776, 42.4812538],[-71.4628304, 42.4813097],[-71.46266540000001, 42.4812333],[-71.4626567, 42.4812436]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4620822, 42.4748421],[-71.4619952, 42.4748478],[-71.4619963, 42.4748574],[-71.461867, 42.474866],[-71.4618574, 42.4747868],[-71.4620738, 42.4747724],[-71.4620822, 42.4748421]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4509795, 42.4927239],[-71.4509786, 42.4927928],[-71.4508898, 42.4927921],[-71.4508897, 42.4928004],[-71.4506741, 42.4927988],[-71.45067520000001, 42.4927216],[-71.4509795, 42.4927239]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4600956, 42.4819806],[-71.4599944, 42.4820798],[-71.4599422, 42.4820497],[-71.4599759, 42.4820167],[-71.4598764, 42.4819611],[-71.4599439, 42.4818949],[-71.4600956, 42.4819806]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4526473, 42.4921853],[-71.4526643, 42.4922541],[-71.4526846, 42.4922513],[-71.4527143, 42.4923708],[-71.4526007, 42.4923863],[-71.45255400000001, 42.492198],[-71.4526473, 42.4921853]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4718639, 42.4788268],[-71.47201680000001, 42.4787618],[-71.47207280000001, 42.478834],[-71.471846, 42.4789304],[-71.4717763, 42.4788405],[-71.4718501, 42.4788091],[-71.4718639, 42.4788268]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4630497, 42.4723155],[-71.46295929999999, 42.4723166],[-71.4629597, 42.4723336],[-71.4628584, 42.4723349],[-71.4628567, 42.4722656],[-71.4630485, 42.4722631],[-71.4630497, 42.4723155]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46868859999999, 42.4845889],[-71.4687381, 42.4845048],[-71.46884970000001, 42.4845408],[-71.46877569999999, 42.4846665],[-71.4686143, 42.4846145],[-71.4686389, 42.4845728],[-71.46868859999999, 42.4845889]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4528578, 42.4958426],[-71.4529398, 42.4959278],[-71.45296860000001, 42.4959127],[-71.4530176, 42.4959621],[-71.4529036, 42.4960211],[-71.45277539999999, 42.4958893],[-71.4528578, 42.4958426]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46087060000001, 42.489726],[-71.4608612, 42.4897384],[-71.4609894, 42.4897916],[-71.46094309999999, 42.4898528],[-71.4606872, 42.4897467],[-71.4607429, 42.4896731],[-71.46087060000001, 42.489726]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47835480000001, 42.488941],[-71.4783434, 42.4889741],[-71.47839, 42.4889829],[-71.4783505, 42.4890976],[-71.47822600000001, 42.489074],[-71.47827700000001, 42.4889263],[-71.47835480000001, 42.488941]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47508259999999, 42.4724991],[-71.4750836, 42.4724543],[-71.47516, 42.4724556],[-71.4751562, 42.4725642],[-71.4750463, 42.4725621],[-71.475049, 42.4724983],[-71.47508259999999, 42.4724991]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48162910000001, 42.4757944],[-71.4815926, 42.4759028],[-71.4814947, 42.4758847],[-71.4815048, 42.4758545],[-71.4813983, 42.4758349],[-71.4814246, 42.4757566],[-71.48162910000001, 42.4757944]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4778197, 42.4754421],[-71.4777675, 42.4754544],[-71.477778, 42.4754786],[-71.47767229999999, 42.4755037],[-71.4776365, 42.4754209],[-71.4777943, 42.4753834],[-71.4778197, 42.4754421]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4661095, 42.4934788],[-71.4661443, 42.4934191],[-71.4662307, 42.4934466],[-71.46618410000001, 42.4935267],[-71.4660756, 42.4934921],[-71.46608740000001, 42.4934718],[-71.4661095, 42.4934788]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4680087, 42.4722247],[-71.4680079, 42.4721464],[-71.4681592, 42.4721455],[-71.4681604, 42.4722577],[-71.4680602, 42.4722582],[-71.4680599, 42.4722244],[-71.4680087, 42.4722247]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4751089, 42.4738645],[-71.4751138, 42.4738124],[-71.47520299999999, 42.4738171],[-71.47518839999999, 42.4739695],[-71.475061, 42.4739629],[-71.4750706, 42.4738625],[-71.4751089, 42.4738645]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47598170000001, 42.4753664],[-71.47606759999999, 42.4754514],[-71.47599049999999, 42.4754941],[-71.4759821, 42.4754858],[-71.4759505, 42.4755034],[-71.4758729, 42.4754267],[-71.47598170000001, 42.4753664]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4750805, 42.4878246],[-71.4750351, 42.4878569],[-71.4750629, 42.4878784],[-71.4750343, 42.4878987],[-71.4749626, 42.4878434],[-71.4750368, 42.4877908],[-71.4750805, 42.4878246]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4693896, 42.4832661],[-71.4692541, 42.4832788],[-71.4692443, 42.4832217],[-71.46928819999999, 42.4832176],[-71.4692866, 42.4832081],[-71.4693781, 42.4831995],[-71.4693896, 42.4832661]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46635620000001, 42.4875919],[-71.4664375, 42.4876736],[-71.46632959999999, 42.4877424],[-71.4663201, 42.4877342],[-71.4662806, 42.4877585],[-71.4661906, 42.4876822],[-71.46635620000001, 42.4875919]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47822789999999, 42.4860079],[-71.4781589, 42.4860142],[-71.478167, 42.4860624],[-71.47805510000001, 42.4860727],[-71.4780358, 42.4859583],[-71.4782167, 42.4859416],[-71.47822789999999, 42.4860079]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4654706, 42.4833025],[-71.4654369, 42.4833348],[-71.4654524, 42.4833435],[-71.46536709999999, 42.4834278],[-71.4652765, 42.4833775],[-71.46539559999999, 42.4832609],[-71.4654706, 42.4833025]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697784, 42.4825997],[-71.4697068, 42.4826992],[-71.46973319999999, 42.4827096],[-71.4696738, 42.4827923],[-71.4695463, 42.482742],[-71.46967739999999, 42.4825598],[-71.4697784, 42.4825997]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46881310000001, 42.4855047],[-71.4687897, 42.4855335],[-71.4688039, 42.4855398],[-71.468726, 42.4856355],[-71.468637, 42.4856016],[-71.4687418, 42.4854729],[-71.46881310000001, 42.4855047]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.460641, 42.4847874],[-71.4606409, 42.4849914],[-71.4605511, 42.4849914],[-71.4605512, 42.4848994],[-71.4605286, 42.4848994],[-71.4605287, 42.4847873],[-71.460641, 42.4847874]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47006759999999, 42.4731577],[-71.4700431, 42.4731137],[-71.4702042, 42.473065],[-71.4702727, 42.4731885],[-71.4701733, 42.4732184],[-71.4701292, 42.473139],[-71.47006759999999, 42.4731577]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47518909999999, 42.4771735],[-71.4751006, 42.4771819],[-71.47510509999999, 42.477208],[-71.47504669999999, 42.4772135],[-71.4750363, 42.4771532],[-71.4751832, 42.4771393],[-71.47518909999999, 42.4771735]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4718249, 42.4896061],[-71.4718474, 42.4895876],[-71.4718891, 42.4896155],[-71.4717787, 42.4897061],[-71.47168480000001, 42.4896433],[-71.4717727, 42.4895712],[-71.4718249, 42.4896061]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4822968, 42.4733807],[-71.48228899999999, 42.473401],[-71.48232590000001, 42.4734087],[-71.4822964, 42.4734861],[-71.4821852, 42.4734628],[-71.48222250000001, 42.4733651],[-71.4822968, 42.4733807]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4589375, 42.4859165],[-71.45891450000001, 42.4858486],[-71.459013, 42.4858303],[-71.45916939999999, 42.4859235],[-71.45910360000001, 42.485984],[-71.45897789999999, 42.485909],[-71.4589375, 42.4859165]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752711, 42.4776857],[-71.4751608, 42.4777003],[-71.4751316, 42.4775793],[-71.4751765, 42.4775734],[-71.4751696, 42.4775451],[-71.475235, 42.4775365],[-71.4752711, 42.4776857]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"10","source:datetime":"2014-04-11T16:40:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4818532, 42.4749205],[-71.4818465, 42.4749429],[-71.4819182, 42.4749547],[-71.48190099999999, 42.4750118],[-71.48165349999999, 42.4749711],[-71.4816774, 42.4748915],[-71.4818532, 42.4749205]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697324, 42.4918298],[-71.4696854, 42.4919029],[-71.4696275, 42.4918824],[-71.46960660000001, 42.4919148],[-71.4695354, 42.4918897],[-71.4696033, 42.4917843],[-71.4697324, 42.4918298]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4709979, 42.4949869],[-71.4711729, 42.4950559],[-71.4711381, 42.4951042],[-71.4711028, 42.4950902],[-71.4710898, 42.4951082],[-71.4709501, 42.4950531],[-71.4709979, 42.4949869]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4625412, 42.473558],[-71.462574, 42.4736533],[-71.4625023, 42.4736668],[-71.4624979, 42.4736541],[-71.4623478, 42.4736824],[-71.4623193, 42.4735999],[-71.4625412, 42.473558]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4600748, 42.4837497],[-71.459971, 42.4836677],[-71.4601139, 42.4835592],[-71.4601969, 42.4836192],[-71.4601031, 42.4836945],[-71.460128, 42.4837161],[-71.4600748, 42.4837497]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4716281, 42.4851184],[-71.471613, 42.4851389],[-71.4716549, 42.4851558],[-71.4716042, 42.4852249],[-71.4714585, 42.4851663],[-71.4715243, 42.4850767],[-71.4716281, 42.4851184]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4784871, 42.4747959],[-71.4784947, 42.4748234],[-71.4785723, 42.4748117],[-71.4785928, 42.4748857],[-71.47845289999999, 42.4749069],[-71.47842489999999, 42.4748053],[-71.4784871, 42.4747959]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4770127, 42.4842258],[-71.47695760000001, 42.4842053],[-71.47702200000001, 42.4841105],[-71.4772019, 42.4841689],[-71.47716200000001, 42.4842368],[-71.4770343, 42.4841955],[-71.4770127, 42.4842258]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47038619999999, 42.4800538],[-71.47037330000001, 42.4800587],[-71.4704009, 42.480099],[-71.47028330000001, 42.4801431],[-71.4702193, 42.4800497],[-71.47034979999999, 42.4800007],[-71.47038619999999, 42.4800538]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4716009, 42.4954496],[-71.4715094, 42.4955464],[-71.47143680000001, 42.4955089],[-71.47146170000001, 42.4954826],[-71.4713356, 42.4954173],[-71.4714022, 42.4953467],[-71.4716009, 42.4954496]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4579578, 42.4859499],[-71.4579662, 42.4859351],[-71.4580359, 42.4859569],[-71.4579773, 42.4860593],[-71.4578885, 42.4860314],[-71.45793860000001, 42.4859439],[-71.4579578, 42.4859499]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4609418, 42.4902503],[-71.46073699999999, 42.4904144],[-71.4606694, 42.4903682],[-71.46076909999999, 42.4902883],[-71.4607535, 42.4902777],[-71.46085859999999, 42.4901935],[-71.4609418, 42.4902503]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46450849999999, 42.4807384],[-71.4643647, 42.4809473],[-71.46429019999999, 42.4809192],[-71.4643559, 42.4808238],[-71.4643291, 42.4808137],[-71.4644072, 42.4807002],[-71.46450849999999, 42.4807384]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697168, 42.4928775],[-71.46974350000001, 42.4928994],[-71.46984209999999, 42.4928334],[-71.4699178, 42.4928953],[-71.4697951, 42.4929774],[-71.4696927, 42.4928936],[-71.4697168, 42.4928775]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47566999999999, 42.4718878],[-71.4756852, 42.471871],[-71.4757585, 42.4719073],[-71.47570380000001, 42.4719679],[-71.4755546, 42.4718939],[-71.4755942, 42.4718502],[-71.47566999999999, 42.4718878]]]}}, +{"type":"Feature","properties":{"addr:street":"Main Street","addr:city":"Acton","building":"yes","addr:housenumber":"289","addr:state":"MA","area":"yes","name":"Gulf","amenity":"fuel","source:datetime":"2015-02-28T17:00:30Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.452189, 42.4756498],[-71.452155, 42.4756115],[-71.4522788, 42.4755513],[-71.4524098, 42.475699],[-71.4523105, 42.4757473],[-71.4522135, 42.4756379],[-71.452189, 42.4756498]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47703799999999, 42.4905809],[-71.4771339, 42.4904573],[-71.47722229999999, 42.4904948],[-71.47710429999999, 42.4906616],[-71.4769283, 42.4905868],[-71.4769589, 42.4905473],[-71.47703799999999, 42.4905809]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715494, 42.4728487],[-71.47151359999999, 42.4728586],[-71.47151789999999, 42.4728671],[-71.47145519999999, 42.4728845],[-71.4714086, 42.4727924],[-71.4715071, 42.4727651],[-71.4715494, 42.4728487]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.451536, 42.4850233],[-71.4515456, 42.4850183],[-71.451626, 42.4851022],[-71.4515384, 42.4851482],[-71.45142439999999, 42.4850292],[-71.4515023, 42.4849882],[-71.451536, 42.4850233]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4702219, 42.4910935],[-71.47016669999999, 42.491145],[-71.47021290000001, 42.4911722],[-71.4701524, 42.4912286],[-71.4700291, 42.4911561],[-71.4701448, 42.4910482],[-71.4702219, 42.4910935]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4669078, 42.4888228],[-71.4669894, 42.4888493],[-71.4669617, 42.488896],[-71.4669492, 42.4888919],[-71.46693860000001, 42.4889097],[-71.4668697, 42.4888873],[-71.4669078, 42.4888228]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.477801, 42.4885438],[-71.4777864, 42.4886196],[-71.4777431, 42.4886151],[-71.4777211, 42.488729],[-71.47760100000001, 42.4887163],[-71.47763759999999, 42.4885265],[-71.477801, 42.4885438]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47257070000001, 42.4864181],[-71.47243520000001, 42.4865665],[-71.4724061, 42.486552],[-71.472365, 42.486597],[-71.47227770000001, 42.4865533],[-71.4724544, 42.4863599],[-71.47257070000001, 42.4864181]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48022949999999, 42.4795632],[-71.4801961, 42.4795704],[-71.48022570000001, 42.4796446],[-71.4801004, 42.4796719],[-71.4800552, 42.4795583],[-71.4802138, 42.4795237],[-71.48022949999999, 42.4795632]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47595509999999, 42.4965175],[-71.4758949, 42.496693],[-71.4757695, 42.4966694],[-71.4757888, 42.4966134],[-71.47574059999999, 42.4966044],[-71.4757816, 42.4964849],[-71.47595509999999, 42.4965175]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4636146, 42.4773805],[-71.4636225, 42.477412],[-71.463638, 42.4774099],[-71.4636766, 42.4775649],[-71.46356609999999, 42.47758],[-71.46351970000001, 42.4773935],[-71.4636146, 42.4773805]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4716471, 42.4833096],[-71.4716178, 42.4833466],[-71.4715691, 42.4833255],[-71.471503, 42.4834089],[-71.47132190000001, 42.4833302],[-71.4714173, 42.4832098],[-71.4716471, 42.4833096]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4512472, 42.4882881],[-71.4511615, 42.4884845],[-71.451069, 42.4884624],[-71.4510934, 42.4884066],[-71.451018, 42.4883885],[-71.4510794, 42.488248],[-71.4512472, 42.4882881]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4691024, 42.484011],[-71.4690365, 42.4842116],[-71.4689377, 42.4841938],[-71.4689588, 42.4841293],[-71.4689474, 42.4841272],[-71.4689918, 42.4839911],[-71.4691024, 42.484011]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46402740000001, 42.4857722],[-71.4640445, 42.4857455],[-71.46410109999999, 42.4857653],[-71.4640306, 42.4858752],[-71.4639438, 42.4858447],[-71.4639971, 42.4857616],[-71.46402740000001, 42.4857722]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46981049999999, 42.4863868],[-71.4697693, 42.4864277],[-71.4697833, 42.4864354],[-71.4696459, 42.4865841],[-71.4695703, 42.486546],[-71.469739, 42.4863474],[-71.46981049999999, 42.4863868]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4745702, 42.4730471],[-71.47456889999999, 42.4730019],[-71.4747122, 42.4729997],[-71.4747138, 42.4730549],[-71.47460959999999, 42.4730565],[-71.4746093, 42.4730465],[-71.4745702, 42.4730471]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47560230000001, 42.4898524],[-71.4755476, 42.4899754],[-71.4754598, 42.489954],[-71.475475, 42.4899198],[-71.47540119999999, 42.4899018],[-71.47544069999999, 42.4898131],[-71.47560230000001, 42.4898524]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4555493, 42.4823774],[-71.4555503, 42.4823569],[-71.4555974, 42.4823581],[-71.4555938, 42.4824329],[-71.455512, 42.4824307],[-71.4555146, 42.4823765],[-71.4555493, 42.4823774]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"6","source:datetime":"2014-04-11T16:40:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4827344, 42.4754392],[-71.4827226, 42.4754472],[-71.4827851, 42.4754977],[-71.4827147, 42.4755454],[-71.4825975, 42.4754507],[-71.48267970000001, 42.475395],[-71.4827344, 42.4754392]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4626815, 42.4748825],[-71.4625597, 42.4748907],[-71.46256440000001, 42.4749282],[-71.4624931, 42.474933],[-71.462479, 42.4748193],[-71.462672, 42.4748062],[-71.4626815, 42.4748825]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4510148, 42.4895575],[-71.4510027, 42.4896566],[-71.4510264, 42.4896583],[-71.451013, 42.4897701],[-71.4508982, 42.4897623],[-71.4509238, 42.4895513],[-71.4510148, 42.4895575]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4709502, 42.4841167],[-71.4709343, 42.484118],[-71.47094679999999, 42.484204],[-71.4708567, 42.4842112],[-71.4708294, 42.4840242],[-71.4709355, 42.4840157],[-71.4709502, 42.4841167]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4722146, 42.4720556],[-71.47223870000001, 42.472055],[-71.4722374, 42.4720304],[-71.4722671, 42.4720296],[-71.47226809999999, 42.4720486],[-71.4724054, 42.4720447],[-71.472404, 42.472018],[-71.47243539999999, 42.4720171],[-71.4724365, 42.4720397],[-71.472511, 42.4720376],[-71.47251009999999, 42.4720204],[-71.4725747, 42.4720186],[-71.4725735, 42.4719943],[-71.47260799999999, 42.4719933],[-71.4726091, 42.4720153],[-71.47267960000001, 42.4720133],[-71.4726783, 42.4719885],[-71.4727518, 42.4719864],[-71.4727503, 42.4719568],[-71.4727887, 42.4719557],[-71.47279, 42.4719802],[-71.472936, 42.471976],[-71.4729347, 42.4719505],[-71.4729676, 42.4719496],[-71.4729688, 42.4719734],[-71.4730632, 42.4719707],[-71.4730646, 42.4719996],[-71.47310640000001, 42.4719984],[-71.4731089, 42.472047],[-71.473062, 42.4720483],[-71.473063, 42.4720668],[-71.47297469999999, 42.4720693],[-71.4729755, 42.4720852],[-71.47295699999999, 42.4720857],[-71.4729564, 42.4720732],[-71.47279349999999, 42.4720778],[-71.4727944, 42.4720962],[-71.4727681, 42.4720969],[-71.4727673, 42.4720826],[-71.47269110000001, 42.4720848],[-71.4726922, 42.4721072],[-71.47261570000001, 42.4721094],[-71.4726168, 42.4721312],[-71.4725967, 42.4721318],[-71.4725957, 42.4721122],[-71.4725179, 42.4721144],[-71.4725188, 42.4721316],[-71.47244550000001, 42.4721336],[-71.4724466, 42.4721543],[-71.4724265, 42.4721549],[-71.4724255, 42.4721365],[-71.4722754, 42.4721408],[-71.47227669999999, 42.4721657],[-71.47225659999999, 42.4721662],[-71.4722555, 42.4721437],[-71.4721842, 42.4721457],[-71.4721804, 42.4720723],[-71.4722154, 42.4720713],[-71.4722146, 42.4720556]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:53:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4721002, 42.4718245],[-71.4720683, 42.4718145],[-71.4720757, 42.4718016],[-71.4721045, 42.4718106],[-71.47220799999999, 42.4718115],[-71.472228, 42.471819],[-71.4722151, 42.471838],[-71.4722395, 42.4718471],[-71.4722269, 42.4718655],[-71.4722055, 42.4718575],[-71.4721919, 42.4718775],[-71.4721792, 42.4718727],[-71.47214169999999, 42.4719276],[-71.4721685, 42.4719376],[-71.4721562, 42.4719558],[-71.4721785, 42.4719641],[-71.4721649, 42.471984],[-71.472145, 42.4719766],[-71.4721315, 42.4719963],[-71.4721199, 42.4719919],[-71.4720998, 42.4720214],[-71.4720816, 42.4720146],[-71.4720538, 42.4720553],[-71.4719868, 42.4720303],[-71.4720367, 42.4719432],[-71.4720062, 42.4719336],[-71.47201339999999, 42.471921],[-71.4720401, 42.4719293],[-71.4721002, 42.4718245]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47130180000001, 42.475136],[-71.4712924, 42.4752556],[-71.47110929999999, 42.4752478],[-71.4711186, 42.4751281],[-71.47130180000001, 42.475136]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4821365, 42.4769698],[-71.482118, 42.4770081],[-71.48207069999999, 42.4769956],[-71.4820892, 42.4769573],[-71.4821365, 42.4769698]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4784063, 42.474888],[-71.478351, 42.4748955],[-71.47833540000001, 42.4748327],[-71.4783906, 42.4748251],[-71.4784063, 42.474888]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728063, 42.4904286],[-71.4727196, 42.4905959],[-71.4726211, 42.490568],[-71.47270779999999, 42.4904007],[-71.4728063, 42.4904286]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4571871, 42.4747657],[-71.4571411, 42.4748408],[-71.4568948, 42.4747581],[-71.4569407, 42.474683],[-71.4571871, 42.4747657]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4712067, 42.4902465],[-71.4710821, 42.4903524],[-71.4710044, 42.4903023],[-71.4711289, 42.4901964],[-71.4712067, 42.4902465]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46403789999999, 42.4902927],[-71.4640371, 42.4903221],[-71.4639506, 42.4903208],[-71.46395149999999, 42.4902913],[-71.46403789999999, 42.4902927]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4640431, 42.4869194],[-71.4640382, 42.4869815],[-71.463955, 42.4869779],[-71.46395990000001, 42.4869158],[-71.4640431, 42.4869194]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47275550000001, 42.4880286],[-71.4727194, 42.4880934],[-71.47260799999999, 42.4880594],[-71.4726441, 42.4879946],[-71.47275550000001, 42.4880286]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4755134, 42.4876159],[-71.4754987, 42.4876326],[-71.4753626, 42.4875668],[-71.47537730000001, 42.4875501],[-71.4755134, 42.4876159]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4570161, 42.4779568],[-71.4569856, 42.4779661],[-71.4569576, 42.4779162],[-71.4569881, 42.4779068],[-71.4570161, 42.4779568]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4779911, 42.490695],[-71.4779657, 42.4907596],[-71.47788250000001, 42.4907417],[-71.47790790000001, 42.4906771],[-71.4779911, 42.490695]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4624925, 42.4752249],[-71.46248850000001, 42.4752642],[-71.4624335, 42.4752611],[-71.4624376, 42.4752218],[-71.4624925, 42.4752249]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4708653, 42.4728807],[-71.4707804, 42.4729045],[-71.4707398, 42.4728252],[-71.4708248, 42.4728014],[-71.4708653, 42.4728807]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4784622, 42.4768155],[-71.4784045, 42.4769559],[-71.478241, 42.4769191],[-71.4782987, 42.4767787],[-71.4784622, 42.4768155]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4765403, 42.4856005],[-71.476518, 42.4857041],[-71.4764028, 42.4856905],[-71.4764251, 42.4855869],[-71.4765403, 42.4856005]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47545100000001, 42.4767281],[-71.47528459999999, 42.4767403],[-71.4752733, 42.4766555],[-71.4754396, 42.4766432],[-71.47545100000001, 42.4767281]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4579988, 42.4954286],[-71.4579416, 42.4954439],[-71.4579234, 42.4954068],[-71.45798069999999, 42.4953915],[-71.4579988, 42.4954286]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4573558, 42.4807882],[-71.457105, 42.4808283],[-71.45702609999999, 42.4805579],[-71.4572769, 42.4805178],[-71.4573558, 42.4807882]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4512474, 42.4922343],[-71.4512154, 42.4923268],[-71.4511009, 42.4923056],[-71.4511328, 42.4922132],[-71.4512474, 42.4922343]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4753613, 42.473807],[-71.4753525, 42.4738507],[-71.47528130000001, 42.4738428],[-71.4752902, 42.4737991],[-71.4753613, 42.473807]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4754379, 42.473826],[-71.4754362, 42.4738624],[-71.4753867, 42.4738611],[-71.47538849999999, 42.4738247],[-71.4754379, 42.473826]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4720944, 42.4784202],[-71.47204360000001, 42.4784478],[-71.47202160000001, 42.4784256],[-71.4720725, 42.478398],[-71.4720944, 42.4784202]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474773, 42.4796264],[-71.4747098, 42.4796365],[-71.47468480000001, 42.4795505],[-71.47474800000001, 42.4795404],[-71.474773, 42.4796264]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45531459999999, 42.4720114],[-71.45527749999999, 42.4720722],[-71.4551913, 42.4720433],[-71.4552284, 42.4719826],[-71.45531459999999, 42.4720114]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4557156, 42.4781554],[-71.4556554, 42.4781958],[-71.45560709999999, 42.4781564],[-71.4556673, 42.478116],[-71.4557156, 42.4781554]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47105980000001, 42.4959454],[-71.47104849999999, 42.4959826],[-71.470984, 42.4959718],[-71.4709953, 42.4959346],[-71.47105980000001, 42.4959454]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689496, 42.4953523],[-71.46891549999999, 42.4954222],[-71.46877859999999, 42.4953856],[-71.46881279999999, 42.4953157],[-71.4689496, 42.4953523]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4606615, 42.4774243],[-71.4606388, 42.4775073],[-71.4605747, 42.4774976],[-71.46059750000001, 42.4774147],[-71.4606615, 42.4774243]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474722, 42.4785912],[-71.47462280000001, 42.4785962],[-71.4746147, 42.4785074],[-71.4747139, 42.4785025],[-71.474722, 42.4785912]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4760356, 42.4724543],[-71.4759865, 42.4725094],[-71.4759057, 42.47247],[-71.4759548, 42.4724148],[-71.4760356, 42.4724543]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.479989, 42.4794568],[-71.4798376, 42.4794795],[-71.47981299999999, 42.4793895],[-71.4799644, 42.4793668],[-71.479989, 42.4794568]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4729481, 42.4890039],[-71.4728698, 42.4890348],[-71.47283179999999, 42.4889821],[-71.47291010000001, 42.4889512],[-71.4729481, 42.4890039]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4731883, 42.4896518],[-71.4731374, 42.4897394],[-71.473054, 42.4897128],[-71.4731049, 42.4896252],[-71.4731883, 42.4896518]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47013920000001, 42.4775216],[-71.47004, 42.477522],[-71.47003909999999, 42.4773832],[-71.4701383, 42.4773828],[-71.47013920000001, 42.4775216]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744049, 42.4734333],[-71.4744043, 42.4734981],[-71.4743311, 42.4734977],[-71.47433169999999, 42.4734329],[-71.4744049, 42.4734333]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45165590000001, 42.4722748],[-71.4516098, 42.4722928],[-71.4515876, 42.4722615],[-71.4516338, 42.4722435],[-71.45165590000001, 42.4722748]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4784571, 42.4727055],[-71.47844019999999, 42.4727898],[-71.4783276, 42.4727775],[-71.47834450000001, 42.4726931],[-71.4784571, 42.4727055]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695431, 42.4939217],[-71.46934400000001, 42.4940723],[-71.46927359999999, 42.4940213],[-71.4694727, 42.4938707],[-71.4695431, 42.4939217]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4513709, 42.4854353],[-71.4513425, 42.4854669],[-71.45130450000001, 42.4854482],[-71.4513329, 42.4854166],[-71.4513709, 42.4854353]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4830709, 42.4744444],[-71.4830101, 42.4745009],[-71.4828564, 42.4744103],[-71.4829172, 42.4743538],[-71.4830709, 42.4744444]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47081919999999, 42.4940339],[-71.470769, 42.4940726],[-71.470702, 42.4940251],[-71.4707521, 42.4939863],[-71.47081919999999, 42.4940339]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4804178, 42.4730135],[-71.48040279999999, 42.4730371],[-71.48034440000001, 42.4730168],[-71.4803594, 42.4729932],[-71.4804178, 42.4730135]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47709519999999, 42.4777489],[-71.47705240000001, 42.4778519],[-71.47693959999999, 42.4778261],[-71.4769824, 42.4777232],[-71.47709519999999, 42.4777489]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47698889999999, 42.4903343],[-71.4769369, 42.4904142],[-71.4767643, 42.4903527],[-71.4768163, 42.4902728],[-71.47698889999999, 42.4903343]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47544069999999, 42.4755978],[-71.4754361, 42.4756273],[-71.4753745, 42.4756221],[-71.4753791, 42.4755926],[-71.47544069999999, 42.4755978]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4731153, 42.4732572],[-71.4730127, 42.4732611],[-71.47300540000001, 42.473157],[-71.473108, 42.473153],[-71.4731153, 42.4732572]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4793272, 42.4742616],[-71.4792257, 42.4742722],[-71.4792058, 42.4741681],[-71.4793073, 42.4741574],[-71.4793272, 42.4742616]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4772837, 42.4866814],[-71.4771863, 42.4866941],[-71.4771709, 42.4866298],[-71.47726830000001, 42.486617],[-71.4772837, 42.4866814]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4790106, 42.472598],[-71.478998, 42.4726283],[-71.4789459, 42.4726164],[-71.4789585, 42.4725861],[-71.4790106, 42.472598]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4805879, 42.4731432],[-71.4805671, 42.4731994],[-71.4804786, 42.4731814],[-71.4804994, 42.4731253],[-71.4805879, 42.4731432]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4615065, 42.4826081],[-71.4614699, 42.4826678],[-71.46138089999999, 42.4826379],[-71.4614176, 42.4825782],[-71.4615065, 42.4826081]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4830546, 42.474573],[-71.48301480000001, 42.4746112],[-71.482986, 42.4745947],[-71.4830257, 42.4745565],[-71.4830546, 42.474573]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4525422, 42.4726265],[-71.4524806, 42.4727432],[-71.45237109999999, 42.4727115],[-71.4524327, 42.4725948],[-71.4525422, 42.4726265]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4756604, 42.4728074],[-71.475644, 42.4728299],[-71.47556590000001, 42.4727987],[-71.47558220000001, 42.4727762],[-71.4756604, 42.4728074]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4780277, 42.4918037],[-71.47797, 42.4918611],[-71.4778941, 42.4918193],[-71.4779518, 42.4917619],[-71.4780277, 42.4918037]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4731702, 42.4742383],[-71.4731669, 42.4743059],[-71.4730689, 42.4743033],[-71.4730722, 42.4742358],[-71.4731702, 42.4742383]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45740139999999, 42.4808238],[-71.4573536, 42.4808309],[-71.4573441, 42.4807956],[-71.4573919, 42.4807885],[-71.45740139999999, 42.4808238]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4820022, 42.4751611],[-71.4819415, 42.4751659],[-71.4819323, 42.4751035],[-71.481993, 42.4750987],[-71.4820022, 42.4751611]]]}}, +{"type":"Feature","properties":{"building":"shed","source:datetime":"2014-11-16T05:11:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696976, 42.4722878],[-71.46964370000001, 42.4722925],[-71.4696385, 42.47226],[-71.4696924, 42.4722553],[-71.4696976, 42.4722878]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47923369999999, 42.4806745],[-71.47921669999999, 42.4807061],[-71.479173, 42.4806932],[-71.47918989999999, 42.4806617],[-71.47923369999999, 42.4806745]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4600384, 42.4837608],[-71.46000530000001, 42.4837868],[-71.4599199, 42.4837148],[-71.4599662, 42.4836905],[-71.4600384, 42.4837608]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45271529999999, 42.4941921],[-71.4526058, 42.4942356],[-71.45255659999999, 42.4941677],[-71.4526661, 42.4941242],[-71.45271529999999, 42.4941921]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4783438, 42.4865236],[-71.4782998, 42.4865246],[-71.4782979, 42.4864765],[-71.4783419, 42.4864756],[-71.4783438, 42.4865236]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"19","addr:state":"MA","source:datetime":"2014-11-08T14:01:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4546794, 42.4927717],[-71.4545756, 42.4927765],[-71.4545619, 42.4926133],[-71.4546658, 42.4926086],[-71.4546794, 42.4927717]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48298149999999, 42.4768996],[-71.482955, 42.4769751],[-71.4827959, 42.4769445],[-71.4828225, 42.476869],[-71.48298149999999, 42.4768996]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4556225, 42.4843109],[-71.4555481, 42.4843259],[-71.45551810000001, 42.4842441],[-71.4555924, 42.4842291],[-71.4556225, 42.4843109]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4640959, 42.4858915],[-71.46405729999999, 42.4859435],[-71.4640079, 42.4859234],[-71.46404649999999, 42.4858714],[-71.4640959, 42.4858915]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4810042, 42.4751227],[-71.4809793, 42.4751942],[-71.4808515, 42.4751698],[-71.4808764, 42.4750983],[-71.4810042, 42.4751227]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47928450000001, 42.4719942],[-71.47925669999999, 42.4720607],[-71.4791833, 42.4720439],[-71.4792111, 42.4719773],[-71.47928450000001, 42.4719942]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4599024, 42.4756627],[-71.4598963, 42.4757359],[-71.4598067, 42.4757318],[-71.45981279999999, 42.4756586],[-71.4599024, 42.4756627]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45723460000001, 42.4776165],[-71.4572046, 42.4776395],[-71.45716950000001, 42.4776143],[-71.4571995, 42.4775913],[-71.45723460000001, 42.4776165]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4685597, 42.4860001],[-71.4683947, 42.486106],[-71.4683204, 42.4860425],[-71.4684853, 42.4859366],[-71.4685597, 42.4860001]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4697207, 42.4890887],[-71.4696828, 42.4891529],[-71.4695838, 42.4891209],[-71.4696218, 42.4890567],[-71.4697207, 42.4890887]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45810659999999, 42.4776122],[-71.4579871, 42.4776322],[-71.45796540000001, 42.4775613],[-71.458085, 42.4775412],[-71.45810659999999, 42.4776122]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4741499, 42.4737862],[-71.47414910000001, 42.4738504],[-71.4740472, 42.4738496],[-71.474048, 42.4737854],[-71.4741499, 42.4737862]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4635632, 42.4869138],[-71.4635274, 42.4869142],[-71.4635263, 42.4868667],[-71.4635621, 42.4868663],[-71.4635632, 42.4869138]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47740589999999, 42.4760166],[-71.4773843, 42.4760857],[-71.4773196, 42.4760747],[-71.4773411, 42.4760055],[-71.47740589999999, 42.4760166]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4806262, 42.4764366],[-71.48057679999999, 42.4765946],[-71.480468, 42.476576],[-71.4805175, 42.476418],[-71.4806262, 42.4764366]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4733818, 42.4753784],[-71.4733777, 42.4754469],[-71.4732027, 42.4754411],[-71.47320689999999, 42.4753726],[-71.4733818, 42.4753784]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46998960000001, 42.4742923],[-71.469953, 42.4742925],[-71.46995250000001, 42.4742471],[-71.46998910000001, 42.4742469],[-71.46998960000001, 42.4742923]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4741518, 42.4925721],[-71.4740006, 42.492581],[-71.4739877, 42.4924607],[-71.47413880000001, 42.4924518],[-71.4741518, 42.4925721]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4573795, 42.4809418],[-71.4573728, 42.4809646],[-71.4572987, 42.4809527],[-71.4573054, 42.4809298],[-71.4573795, 42.4809418]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4784392, 42.4868132],[-71.47837250000001, 42.4869013],[-71.4782809, 42.4868633],[-71.47834760000001, 42.4867752],[-71.4784392, 42.4868132]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4711387, 42.4896734],[-71.47096139999999, 42.4898316],[-71.4708851, 42.4897848],[-71.47106239999999, 42.4896266],[-71.4711387, 42.4896734]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46749699999999, 42.4939937],[-71.4673926, 42.4941314],[-71.4673123, 42.494098],[-71.4674167, 42.4939603],[-71.46749699999999, 42.4939937]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703281, 42.4955563],[-71.4703133, 42.4955886],[-71.4702697, 42.4955776],[-71.47028450000001, 42.4955453],[-71.4703281, 42.4955563]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4777246, 42.4756169],[-71.4776415, 42.4756301],[-71.4776231, 42.4755665],[-71.4777062, 42.4755533],[-71.4777246, 42.4756169]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"176","addr:state":"MA","source:datetime":"2015-01-20T23:47:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45694949999999, 42.4994699],[-71.4568556, 42.4995172],[-71.4567042, 42.4993526],[-71.4567981, 42.4993053],[-71.45694949999999, 42.4994699]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4762489, 42.4877318],[-71.4762411, 42.4877913],[-71.4760927, 42.4877807],[-71.4761006, 42.4877211],[-71.4762489, 42.4877318]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46950409999999, 42.4812472],[-71.4694538, 42.4813338],[-71.4693633, 42.481305],[-71.4694135, 42.4812184],[-71.46950409999999, 42.4812472]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47540480000001, 42.4733144],[-71.47538590000001, 42.4733922],[-71.47512020000001, 42.4733567],[-71.4751392, 42.4732789],[-71.47540480000001, 42.4733144]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4740942, 42.4741499],[-71.4740226, 42.4741503],[-71.47402219999999, 42.4741125],[-71.4740939, 42.4741121],[-71.4740942, 42.4741499]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4577578, 42.4953097],[-71.4577376, 42.4953368],[-71.4576885, 42.4953167],[-71.4577087, 42.4952896],[-71.4577578, 42.4953097]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4522948, 42.4808187],[-71.4522485, 42.4808271],[-71.4522365, 42.4807906],[-71.45228280000001, 42.4807822],[-71.4522948, 42.4808187]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4642475, 42.4864575],[-71.46417769999999, 42.4865064],[-71.4640871, 42.4864355],[-71.46415690000001, 42.4863866],[-71.4642475, 42.4864575]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47735110000001, 42.4925505],[-71.4773132, 42.4926808],[-71.4772112, 42.4926645],[-71.47724909999999, 42.4925343],[-71.47735110000001, 42.4925505]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4640598, 42.4902444],[-71.4640572, 42.4902791],[-71.4639531, 42.4902749],[-71.4639557, 42.4902401],[-71.4640598, 42.4902444]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46713870000001, 42.4887476],[-71.4671032, 42.4888055],[-71.4670291, 42.4887806],[-71.46706450000001, 42.4887227],[-71.46713870000001, 42.4887476]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4629051, 42.4726107],[-71.4629023, 42.472683],[-71.4627496, 42.4726798],[-71.46275230000001, 42.4726075],[-71.4629051, 42.4726107]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543195, 42.4799313],[-71.4543015, 42.4799495],[-71.4541818, 42.4798847],[-71.45419990000001, 42.4798664],[-71.4543195, 42.4799313]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45624220000001, 42.4729265],[-71.4561975, 42.472927],[-71.4561968, 42.4728923],[-71.4562414, 42.4728918],[-71.45624220000001, 42.4729265]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4670017, 42.4934768],[-71.46683849999999, 42.4935328],[-71.4667905, 42.4934562],[-71.4669537, 42.4934002],[-71.4670017, 42.4934768]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4843832, 42.4724226],[-71.4843468, 42.4724955],[-71.4842884, 42.4724795],[-71.4843248, 42.4724066],[-71.4843832, 42.4724226]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.461974, 42.4762845],[-71.46189099999999, 42.4762851],[-71.46189010000001, 42.4762147],[-71.46197309999999, 42.4762141],[-71.461974, 42.4762845]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703677, 42.481566],[-71.47032350000001, 42.4816432],[-71.4701142, 42.4815775],[-71.4701584, 42.4815003],[-71.4703677, 42.481566]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47035099999999, 42.4829654],[-71.470339, 42.483005],[-71.4702966, 42.482998],[-71.4703086, 42.4829584],[-71.47035099999999, 42.4829654]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4725964, 42.4832092],[-71.47257310000001, 42.4832417],[-71.4724999, 42.4832129],[-71.4725231, 42.4831804],[-71.4725964, 42.4832092]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46663359999999, 42.4931449],[-71.46647729999999, 42.4932329],[-71.4664128, 42.4931702],[-71.4665692, 42.4930822],[-71.46663359999999, 42.4931449]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4755672, 42.4719417],[-71.4755432, 42.4719705],[-71.4754701, 42.471937],[-71.47549410000001, 42.4719082],[-71.4755672, 42.4719417]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713399, 42.486153],[-71.4712785, 42.4862313],[-71.4711906, 42.4861936],[-71.47125200000001, 42.4861153],[-71.4713399, 42.486153]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48076500000001, 42.4759559],[-71.4807082, 42.476104],[-71.4805872, 42.4760786],[-71.480644, 42.4759305],[-71.48076500000001, 42.4759559]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4787786, 42.4808357],[-71.47876410000001, 42.4808819],[-71.4787137, 42.4808732],[-71.4787281, 42.480827],[-71.4787786, 42.4808357]]]}}, +{"type":"Feature","properties":{"building":"roof","source:datetime":"2015-02-28T17:00:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45220380000001, 42.4758634],[-71.45207360000001, 42.4759253],[-71.45196799999999, 42.4758035],[-71.45209819999999, 42.4757417],[-71.45220380000001, 42.4758634]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4764715, 42.4722476],[-71.4763951, 42.472256],[-71.4763854, 42.4722075],[-71.4764618, 42.4721992],[-71.4764715, 42.4722476]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4690415, 42.4726398],[-71.4690411, 42.4726696],[-71.4689855, 42.4726692],[-71.46898590000001, 42.4726394],[-71.4690415, 42.4726398]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687184, 42.4856709],[-71.4686615, 42.4857121],[-71.468599, 42.4856649],[-71.4686559, 42.4856237],[-71.4687184, 42.4856709]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47458760000001, 42.4782196],[-71.4744493, 42.4782365],[-71.4744338, 42.478167],[-71.4745721, 42.47815],[-71.47458760000001, 42.4782196]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4684326, 42.4952909],[-71.4684094, 42.4953646],[-71.4681502, 42.4953199],[-71.4681734, 42.4952462],[-71.4684326, 42.4952909]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45342290000001, 42.4825341],[-71.4531578, 42.4826353],[-71.4529562, 42.4823458],[-71.4532213, 42.4822447],[-71.45342290000001, 42.4825341]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4643317, 42.4912766],[-71.46430169999999, 42.491337],[-71.4641539, 42.4912967],[-71.464184, 42.4912363],[-71.4643317, 42.4912766]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4778173, 42.4908395],[-71.4777891, 42.4909019],[-71.4776342, 42.4908635],[-71.47766249999999, 42.4908012],[-71.4778173, 42.4908395]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4583947, 42.4724457],[-71.4583413, 42.4724469],[-71.45833930000001, 42.4723953],[-71.4583926, 42.4723941],[-71.4583947, 42.4724457]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4712778, 42.4724023],[-71.4711721, 42.4724258],[-71.47111200000001, 42.4722774],[-71.4712177, 42.472254],[-71.4712778, 42.4724023]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4598398, 42.4895425],[-71.45982770000001, 42.4896088],[-71.4597393, 42.4896],[-71.45975129999999, 42.4895337],[-71.4598398, 42.4895425]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4668971, 42.4772357],[-71.46678919999999, 42.4772386],[-71.466785, 42.4771532],[-71.4668929, 42.4771504],[-71.4668971, 42.4772357]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.472722, 42.473844],[-71.47267170000001, 42.4738483],[-71.4726668, 42.4738168],[-71.4727171, 42.4738125],[-71.472722, 42.473844]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47108110000001, 42.4857434],[-71.470996, 42.4858368],[-71.4709099, 42.4857939],[-71.4709951, 42.4857004],[-71.47108110000001, 42.4857434]]]}}, +{"type":"Feature","properties":{"building":"roof","source:datetime":"2014-11-16T04:49:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4726983, 42.4762861],[-71.4726952, 42.4763587],[-71.4724046, 42.476352],[-71.4724076, 42.4762794],[-71.4726983, 42.4762861]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759719, 42.4854434],[-71.475875, 42.4854509],[-71.47585599999999, 42.4853156],[-71.4759529, 42.4853082],[-71.4759719, 42.4854434]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47917990000001, 42.4911912],[-71.47909919999999, 42.4913545],[-71.4789865, 42.491324],[-71.4790672, 42.4911606],[-71.47917990000001, 42.4911912]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4784112, 42.4839453],[-71.4783322, 42.4839774],[-71.478246, 42.4838609],[-71.478325, 42.4838288],[-71.4784112, 42.4839453]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474752, 42.4916781],[-71.4747023, 42.4916949],[-71.4746695, 42.4916417],[-71.4747192, 42.4916249],[-71.474752, 42.4916781]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4721172, 42.4954477],[-71.4720615, 42.4955133],[-71.47194759999999, 42.4954603],[-71.4720033, 42.4953947],[-71.4721172, 42.4954477]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4518278, 42.485352],[-71.4517269, 42.4853899],[-71.45164389999999, 42.4852653],[-71.45174539999999, 42.4852283],[-71.4518278, 42.485352]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4726426, 42.4832824],[-71.4725959, 42.4833661],[-71.47250270000001, 42.4833376],[-71.47254940000001, 42.4832539],[-71.4726426, 42.4832824]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47656120000001, 42.4875706],[-71.4765196, 42.4876661],[-71.47642020000001, 42.4876424],[-71.4764618, 42.4875469],[-71.47656120000001, 42.4875706]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4708983, 42.4927801],[-71.470789, 42.4927901],[-71.47077659999999, 42.4927159],[-71.47088599999999, 42.492706],[-71.4708983, 42.4927801]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4510406, 42.4721693],[-71.45100189999999, 42.4721814],[-71.4509796, 42.4721419],[-71.45101819999999, 42.4721299],[-71.4510406, 42.4721693]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4710391, 42.4772216],[-71.4709721, 42.4772707],[-71.47084390000001, 42.4771747],[-71.47091090000001, 42.4771257],[-71.4710391, 42.4772216]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47334480000001, 42.4927675],[-71.4732271, 42.4927824],[-71.4731949, 42.4926435],[-71.47331269999999, 42.4926286],[-71.47334480000001, 42.4927675]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4761936, 42.4727479],[-71.4761743, 42.4728631],[-71.47585960000001, 42.4728343],[-71.4758789, 42.4727191],[-71.4761936, 42.4727479]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48496249999999, 42.4724418],[-71.48494359999999, 42.4724977],[-71.4848999, 42.4724896],[-71.48491869999999, 42.4724337],[-71.48496249999999, 42.4724418]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4743393, 42.4730905],[-71.4742518, 42.4730912],[-71.4742511, 42.4730493],[-71.4743387, 42.4730486],[-71.4743393, 42.4730905]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4780607, 42.4769568],[-71.4780396, 42.4770108],[-71.4779623, 42.4769943],[-71.4779834, 42.4769402],[-71.4780607, 42.4769568]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4699244, 42.4862879],[-71.4699104, 42.4863177],[-71.4698545, 42.4863032],[-71.4698686, 42.4862734],[-71.4699244, 42.4862879]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47516539999999, 42.476584],[-71.47489400000001, 42.4766113],[-71.47487340000001, 42.4764994],[-71.4751448, 42.4764721],[-71.47516539999999, 42.476584]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4619022, 42.4866931],[-71.461868, 42.4867293],[-71.461809, 42.4866986],[-71.4618433, 42.4866625],[-71.4619022, 42.4866931]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47470509999999, 42.4782291],[-71.4746138, 42.4782456],[-71.4745842, 42.4781557],[-71.4746755, 42.4781393],[-71.47470509999999, 42.4782291]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4699736, 42.4882068],[-71.4699059, 42.4883058],[-71.4698197, 42.4882735],[-71.4698874, 42.4881745],[-71.4699736, 42.4882068]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728674, 42.4779636],[-71.47281580000001, 42.4779811],[-71.4727908, 42.4779408],[-71.4728423, 42.4779232],[-71.4728674, 42.4779636]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757106, 42.4779974],[-71.4756166, 42.478014],[-71.47556539999999, 42.4778551],[-71.4756593, 42.4778385],[-71.4757106, 42.4779974]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4736942, 42.4781704],[-71.4736154, 42.4781944],[-71.4735945, 42.4781567],[-71.47367319999999, 42.4781327],[-71.4736942, 42.4781704]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47354559999999, 42.4930443],[-71.4734272, 42.4930526],[-71.4734127, 42.4929131],[-71.4735278, 42.4929051],[-71.47354559999999, 42.4930443]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4806225, 42.4735281],[-71.48060839999999, 42.4735665],[-71.4805283, 42.4735505],[-71.4805424, 42.473512],[-71.4806225, 42.4735281]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47653270000001, 42.4742421],[-71.4764605, 42.4742449],[-71.47645780000001, 42.4742068],[-71.47653010000001, 42.4742041],[-71.47653270000001, 42.4742421]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4517668, 42.4719287],[-71.4516998, 42.4719852],[-71.45154340000001, 42.4718837],[-71.45161040000001, 42.4718272],[-71.4517668, 42.4719287]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4778594, 42.4889376],[-71.4778383, 42.4890273],[-71.47774320000001, 42.489015],[-71.47776639999999, 42.488926],[-71.4778594, 42.4889376]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47329430000001, 42.4741683],[-71.4732147, 42.4741688],[-71.4732142, 42.4741288],[-71.47329379999999, 42.4741283],[-71.47329430000001, 42.4741683]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45791850000001, 42.4774419],[-71.45786889999999, 42.4775215],[-71.4577362, 42.4774762],[-71.4577859, 42.4773965],[-71.45791850000001, 42.4774419]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46038129999999, 42.472019],[-71.46028769999999, 42.4720235],[-71.4602838, 42.4719775],[-71.4603773, 42.4719731],[-71.46038129999999, 42.472019]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4550861, 42.4823075],[-71.4550852, 42.4823321],[-71.45496559999999, 42.4823298],[-71.4549664, 42.4823053],[-71.4550861, 42.4823075]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47209839999999, 42.4896934],[-71.4720623, 42.489726],[-71.4720291, 42.4897059],[-71.4720653, 42.4896733],[-71.47209839999999, 42.4896934]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.482062, 42.4749554],[-71.4820325, 42.4750358],[-71.4819313, 42.4750155],[-71.4819609, 42.474935],[-71.482062, 42.4749554]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4758788, 42.4733607],[-71.47585290000001, 42.4734759],[-71.4755565, 42.4734394],[-71.4755825, 42.4733241],[-71.4758788, 42.4733607]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.477687, 42.4877852],[-71.4774843, 42.4878004],[-71.47747080000001, 42.4877014],[-71.4776734, 42.4876862],[-71.477687, 42.4877852]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4760268, 42.4856856],[-71.47602000000001, 42.4857807],[-71.475919, 42.4857767],[-71.4759257, 42.4856817],[-71.4760268, 42.4856856]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4772488, 42.4886287],[-71.4772487, 42.4886648],[-71.4772062, 42.4886647],[-71.47720630000001, 42.4886286],[-71.4772488, 42.4886287]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.470755, 42.4821852],[-71.4706975, 42.482271],[-71.47051310000001, 42.4822032],[-71.4705706, 42.4821174],[-71.470755, 42.4821852]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687143, 42.492786],[-71.46861579999999, 42.4928488],[-71.46855360000001, 42.4927954],[-71.4686521, 42.4927326],[-71.4687143, 42.492786]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4758636, 42.4728511],[-71.47584569999999, 42.472959],[-71.4757218, 42.4729478],[-71.47573970000001, 42.4728399],[-71.4758636, 42.4728511]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47587799999999, 42.476525],[-71.4758779, 42.4765916],[-71.47578710000001, 42.4765915],[-71.47578729999999, 42.4765249],[-71.47587799999999, 42.476525]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.471982, 42.474973],[-71.4718457, 42.47501],[-71.47178599999999, 42.4748893],[-71.4719223, 42.4748523],[-71.471982, 42.474973]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695318, 42.4739855],[-71.46952210000001, 42.474123],[-71.4693815, 42.4741175],[-71.4693912, 42.47398],[-71.4695318, 42.4739855]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.469021, 42.4957375],[-71.4689769, 42.4957418],[-71.46896940000001, 42.4956997],[-71.4690135, 42.4956954],[-71.469021, 42.4957375]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4732705, 42.4755815],[-71.4731877, 42.4755839],[-71.4731843, 42.4755185],[-71.4732671, 42.4755161],[-71.4732705, 42.4755815]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47481999999999, 42.4874563],[-71.4747402, 42.4875309],[-71.4746045, 42.4874515],[-71.4746842, 42.4873768],[-71.47481999999999, 42.4874563]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45311049999999, 42.4968116],[-71.4530367, 42.4968728],[-71.4529543, 42.4968183],[-71.453028, 42.4967572],[-71.45311049999999, 42.4968116]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618329, 42.489206],[-71.4617425, 42.4892097],[-71.46173640000001, 42.4891297],[-71.4618268, 42.4891259],[-71.4618329, 42.489206]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:52:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47307290000001, 42.4915607],[-71.47302310000001, 42.4915989],[-71.472919, 42.4915245],[-71.4729689, 42.4914863],[-71.47307290000001, 42.4915607]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45836660000001, 42.4940136],[-71.4583477, 42.4940449],[-71.4582587, 42.4940154],[-71.4582776, 42.4939841],[-71.45836660000001, 42.4940136]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4732528, 42.4830515],[-71.4732143, 42.4831148],[-71.4731689, 42.4830997],[-71.47320740000001, 42.4830364],[-71.4732528, 42.4830515]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752447, 42.4791598],[-71.4750502, 42.4792183],[-71.4750051, 42.4791363],[-71.4751996, 42.4790777],[-71.4752447, 42.4791598]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47867359999999, 42.4769424],[-71.4786163, 42.4770204],[-71.4785237, 42.476983],[-71.4785811, 42.476905],[-71.47867359999999, 42.4769424]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4776246, 42.4912283],[-71.47760049999999, 42.4912926],[-71.47750019999999, 42.491272],[-71.4775243, 42.4912077],[-71.4776246, 42.4912283]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4718421, 42.4916416],[-71.4717079, 42.4916449],[-71.47170439999999, 42.4915651],[-71.4718385, 42.4915618],[-71.4718421, 42.4916416]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46648810000001, 42.4837111],[-71.466459, 42.4837445],[-71.4663844, 42.4837088],[-71.4664135, 42.4836754],[-71.46648810000001, 42.4837111]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4611366, 42.4726776],[-71.4611141, 42.4727289],[-71.46107019999999, 42.4727183],[-71.46109269999999, 42.472667],[-71.4611366, 42.4726776]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.451362, 42.4768954],[-71.45127119999999, 42.4769187],[-71.451223, 42.4768157],[-71.45131379999999, 42.4767924],[-71.451362, 42.4768954]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4686832, 42.4740715],[-71.46863829999999, 42.474079],[-71.4686255, 42.4740369],[-71.46867039999999, 42.4740294],[-71.4686832, 42.4740715]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4650784, 42.4851432],[-71.4650487, 42.4851674],[-71.4650019, 42.4851359],[-71.4650316, 42.4851117],[-71.4650784, 42.4851432]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45139210000001, 42.4769778],[-71.451334, 42.4769949],[-71.45130330000001, 42.4769378],[-71.4513614, 42.4769207],[-71.45139210000001, 42.4769778]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46132110000001, 42.485134],[-71.46121290000001, 42.4851947],[-71.46115589999999, 42.485139],[-71.46126409999999, 42.4850783],[-71.46132110000001, 42.485134]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4802676, 42.4721137],[-71.48021079999999, 42.4721834],[-71.4801061, 42.4721367],[-71.4801629, 42.4720669],[-71.4802676, 42.4721137]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4643329, 42.4793214],[-71.4642534, 42.4793807],[-71.4640926, 42.4792626],[-71.4641721, 42.4792033],[-71.4643329, 42.4793214]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47523169999999, 42.4736702],[-71.47521999999999, 42.4737553],[-71.4750827, 42.4737449],[-71.4750944, 42.4736598],[-71.47523169999999, 42.4736702]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4755715, 42.4875306],[-71.4755408, 42.4875522],[-71.4755054, 42.4875247],[-71.47553600000001, 42.487503],[-71.4755715, 42.4875306]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4709489, 42.4791465],[-71.4708782, 42.4791791],[-71.47083379999999, 42.4791264],[-71.4709045, 42.4790938],[-71.4709489, 42.4791465]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47070220000001, 42.4786809],[-71.4706077, 42.4787497],[-71.47055229999999, 42.4787079],[-71.4706468, 42.4786392],[-71.47070220000001, 42.4786809]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4745137, 42.4721749],[-71.47451150000001, 42.4722495],[-71.4743247, 42.4722465],[-71.47432689999999, 42.4721719],[-71.4745137, 42.4721749]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4558756, 42.4801719],[-71.4558246, 42.480241],[-71.45580270000001, 42.4802321],[-71.4558536, 42.480163],[-71.4558756, 42.4801719]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4726229, 42.4782108],[-71.4725263, 42.4782367],[-71.47251060000001, 42.4782048],[-71.4726072, 42.4781789],[-71.4726229, 42.4782108]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4690555, 42.4720622],[-71.4690532, 42.4721263],[-71.4689001, 42.4721233],[-71.4689024, 42.4720592],[-71.4690555, 42.4720622]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46116000000001, 42.4817793],[-71.46104920000001, 42.4817977],[-71.46098569999999, 42.4815884],[-71.4610965, 42.48157],[-71.46116000000001, 42.4817793]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4777076, 42.4908893],[-71.47768480000001, 42.4909315],[-71.4776081, 42.4909089],[-71.4776308, 42.4908667],[-71.4777076, 42.4908893]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.465067, 42.4819396],[-71.46494199999999, 42.4819685],[-71.4649034, 42.4818772],[-71.46502839999999, 42.4818483],[-71.465067, 42.4819396]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4734287, 42.4830823],[-71.473392, 42.4831455],[-71.47324260000001, 42.483098],[-71.4732793, 42.4830348],[-71.4734287, 42.4830823]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4716426, 42.4828018],[-71.4716087, 42.4828591],[-71.47153590000001, 42.4828354],[-71.4715698, 42.4827782],[-71.4716426, 42.4828018]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4544126, 42.4724257],[-71.4543961, 42.4724563],[-71.45433989999999, 42.4724398],[-71.4543563, 42.4724092],[-71.4544126, 42.4724257]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"17","addr:state":"MA","source:datetime":"2014-11-08T14:01:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4546174, 42.4930445],[-71.4546055, 42.4931539],[-71.4545014, 42.4931478],[-71.4545133, 42.4930383],[-71.4546174, 42.4930445]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47496169999999, 42.491343],[-71.47483680000001, 42.4913988],[-71.4747704, 42.4913174],[-71.474898, 42.4912648],[-71.47496169999999, 42.491343]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4781017, 42.4919523],[-71.478043, 42.4920078],[-71.4779118, 42.4919317],[-71.4779705, 42.4918762],[-71.4781017, 42.4919523]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.460291, 42.4725075],[-71.4601334, 42.4725197],[-71.4601229, 42.4724457],[-71.4602805, 42.4724334],[-71.460291, 42.4725075]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4742426, 42.4878897],[-71.4740852, 42.488007],[-71.47402080000001, 42.4879597],[-71.4741782, 42.4878424],[-71.4742426, 42.4878897]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.459481, 42.4864973],[-71.459295, 42.4865657],[-71.4592391, 42.4864825],[-71.4594251, 42.4864141],[-71.459481, 42.4864973]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4781503, 42.4864259],[-71.47803260000001, 42.4864298],[-71.47802609999999, 42.4863206],[-71.4781438, 42.4863168],[-71.4781503, 42.4864259]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4720203, 42.493365],[-71.471935, 42.493441],[-71.4717459, 42.4933248],[-71.4718312, 42.4932488],[-71.4720203, 42.493365]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4691546, 42.485675],[-71.46912500000001, 42.4857087],[-71.46908089999999, 42.4856875],[-71.4691105, 42.4856538],[-71.4691546, 42.485675]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.478438, 42.4920938],[-71.47838, 42.492148],[-71.478284, 42.4920916],[-71.478342, 42.4920375],[-71.478438, 42.4920938]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46959510000001, 42.4730812],[-71.4695517, 42.4731923],[-71.4694592, 42.4731725],[-71.4695025, 42.4730614],[-71.46959510000001, 42.4730812]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45539309999999, 42.4782153],[-71.4553743, 42.4782373],[-71.45531389999999, 42.4782091],[-71.45533260000001, 42.4781871],[-71.45539309999999, 42.4782153]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4755827, 42.4721172],[-71.4755306, 42.472177],[-71.47541099999999, 42.4721199],[-71.4754631, 42.4720601],[-71.4755827, 42.4721172]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4754832, 42.479723],[-71.4753497, 42.479776],[-71.47524319999999, 42.4796292],[-71.4753767, 42.4795761],[-71.4754832, 42.479723]]]}}, +{"type":"Feature","properties":{"addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"8","addr:state":"MA","source:datetime":"2014-11-08T14:01:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4537095, 42.4947847],[-71.4536076, 42.4947912],[-71.45359209999999, 42.4946572],[-71.453694, 42.4946507],[-71.4537095, 42.4947847]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4684584, 42.4863291],[-71.46843800000001, 42.4863525],[-71.46838870000001, 42.4863289],[-71.4684091, 42.4863056],[-71.4684584, 42.4863291]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.471953, 42.4774327],[-71.4718438, 42.477493],[-71.4717071, 42.4773573],[-71.47181620000001, 42.477297],[-71.471953, 42.4774327]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:43Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.470934, 42.4780939],[-71.4708354, 42.478109],[-71.4707976, 42.4779747],[-71.47089630000001, 42.4779595],[-71.470934, 42.4780939]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47558170000001, 42.4726331],[-71.47551609999999, 42.4727453],[-71.4754334, 42.4727187],[-71.475499, 42.4726065],[-71.47558170000001, 42.4726331]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Bulette Road","addr:city":"Acton","building":"yes","addr:housenumber":"19","addr:state":"MA","source:datetime":"2015-01-20T23:47:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47019520000001, 42.4987215],[-71.4701266, 42.4989161],[-71.47001210000001, 42.4988941],[-71.47008080000001, 42.4986994],[-71.47019520000001, 42.4987215]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4783518, 42.4920084],[-71.4783025, 42.4920595],[-71.4782087, 42.49201],[-71.47825810000001, 42.4919589],[-71.4783518, 42.4920084]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4804029, 42.474229],[-71.4803588, 42.4743179],[-71.4802009, 42.4742749],[-71.480245, 42.474186],[-71.4804029, 42.474229]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687459, 42.4878279],[-71.468728, 42.4878698],[-71.46868499999999, 42.4878598],[-71.4687029, 42.4878179],[-71.4687459, 42.4878279]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4556078, 42.4822573],[-71.45560159999999, 42.4823495],[-71.45555280000001, 42.4823477],[-71.4555589, 42.4822555],[-71.4556078, 42.4822573]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689853, 42.4931162],[-71.4689321, 42.4931639],[-71.4688721, 42.4931272],[-71.4689253, 42.4930795],[-71.4689853, 42.4931162]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4677411, 42.487337],[-71.46770100000001, 42.4873776],[-71.46766030000001, 42.4873556],[-71.4677003, 42.487315],[-71.4677411, 42.487337]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4576948, 42.4775498],[-71.4576191, 42.4775701],[-71.45760009999999, 42.4775312],[-71.4576758, 42.477511],[-71.4576948, 42.4775498]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:38Z"},"geometry": {"type":"Point","coordinates": [-71.4701711, 42.4718159]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46673730000001, 42.4936311],[-71.4666417, 42.4937395],[-71.46654940000001, 42.4936928],[-71.4666431, 42.4935835],[-71.46673730000001, 42.4936311]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4717892, 42.4828281],[-71.4717624, 42.4828628],[-71.47172019999999, 42.4828449],[-71.47174699999999, 42.4828102],[-71.4717892, 42.4828281]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4638307, 42.4727961],[-71.46382010000001, 42.4728716],[-71.4637381, 42.4728653],[-71.4637487, 42.4727898],[-71.4638307, 42.4727961]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4778342, 42.4881126],[-71.4777327, 42.4881232],[-71.47771969999999, 42.4880541],[-71.47782119999999, 42.4880436],[-71.4778342, 42.4881126]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4689755, 42.480995],[-71.46889590000001, 42.4810593],[-71.4688223, 42.4810094],[-71.46890190000001, 42.4809451],[-71.4689755, 42.480995]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:37Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4631818, 42.4991684],[-71.4631028, 42.4991701],[-71.4631016, 42.4991408],[-71.4631806, 42.4991391],[-71.4631818, 42.4991684]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45432769999999, 42.4728434],[-71.45431139999999, 42.4728884],[-71.4541729, 42.4728609],[-71.4541892, 42.4728159],[-71.45432769999999, 42.4728434]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45745460000001, 42.4808946],[-71.457442, 42.4809466],[-71.45737889999999, 42.4809382],[-71.4573915, 42.4808862],[-71.45745460000001, 42.4808946]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.453287, 42.4722351],[-71.4532463, 42.4722574],[-71.45321, 42.4722211],[-71.4532507, 42.4721988],[-71.453287, 42.4722351]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4706132, 42.4832488],[-71.4705549, 42.4832491],[-71.47055469999999, 42.4832198],[-71.470613, 42.4832195],[-71.4706132, 42.4832488]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46989960000001, 42.485255],[-71.46985789999999, 42.4853022],[-71.4697841, 42.4852665],[-71.4698259, 42.4852193],[-71.46989960000001, 42.485255]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4691981, 42.498084],[-71.46917790000001, 42.4981299],[-71.46911110000001, 42.4981139],[-71.46913120000001, 42.4980679],[-71.4691981, 42.498084]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4742018, 42.4753982],[-71.47428859999999, 42.4753982],[-71.47428859999999, 42.4754611],[-71.47420169999999, 42.4754611],[-71.4742018, 42.4753982]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4734066, 42.4743044],[-71.47339839999999, 42.4743758],[-71.4731894, 42.4743626],[-71.47319760000001, 42.4742912],[-71.4734066, 42.4743044]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744577, 42.4730055],[-71.4744556, 42.4730679],[-71.4743672, 42.4730663],[-71.47436930000001, 42.4730039],[-71.4744577, 42.4730055]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45654740000001, 42.4782944],[-71.45643029999999, 42.4783135],[-71.4563656, 42.4780958],[-71.4564827, 42.4780767],[-71.45654740000001, 42.4782944]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.458777, 42.4729207],[-71.4587222, 42.4729324],[-71.4587046, 42.4728872],[-71.45875940000001, 42.4728755],[-71.458777, 42.4729207]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4772775, 42.4922652],[-71.4772601, 42.4922982],[-71.4772088, 42.4922833],[-71.4772262, 42.4922504],[-71.4772775, 42.4922652]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4726313, 42.4776883],[-71.4725588, 42.4777094],[-71.4725273, 42.4776499],[-71.4725998, 42.4776288],[-71.4726313, 42.4776883]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45602270000001, 42.483756],[-71.4554871, 42.4838801],[-71.45520689999999, 42.4832182],[-71.455744, 42.4830974],[-71.45602270000001, 42.483756]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687995, 42.4891216],[-71.46875729999999, 42.4892037],[-71.46866319999999, 42.4891773],[-71.4687054, 42.4890951],[-71.4687995, 42.4891216]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46935190000001, 42.4812128],[-71.4693034, 42.4812875],[-71.469145, 42.4812311],[-71.4691935, 42.4811564],[-71.46935190000001, 42.4812128]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4692968, 42.4796747],[-71.4692391, 42.4797261],[-71.4691624, 42.479679],[-71.4692201, 42.4796276],[-71.4692968, 42.4796747]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Cherry Ridge Road","addr:city":"Acton","building":"yes","addr:housenumber":"8","source:datetime":"2014-04-11T16:40:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48243220000001, 42.4751741],[-71.48236300000001, 42.4752415],[-71.4821809, 42.4751392],[-71.4822501, 42.4750718],[-71.48243220000001, 42.4751741]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.457414, 42.4993651],[-71.45736479999999, 42.4993808],[-71.457346, 42.4993485],[-71.45739519999999, 42.4993329],[-71.457414, 42.4993651]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4743421, 42.4718845],[-71.4742586, 42.4718858],[-71.4742565, 42.4718091],[-71.47434010000001, 42.4718079],[-71.4743421, 42.4718845]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4732054, 42.4746468],[-71.47319760000001, 42.4747177],[-71.47310419999999, 42.4747121],[-71.47311209999999, 42.4746411],[-71.4732054, 42.4746468]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714893, 42.4757365],[-71.4714297, 42.4757387],[-71.4714237, 42.4756478],[-71.4714833, 42.4756456],[-71.4714893, 42.4757365]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4728874, 42.4885017],[-71.4728348, 42.4885181],[-71.4728161, 42.4884853],[-71.47286870000001, 42.4884689],[-71.4728874, 42.4885017]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4652334, 42.4860261],[-71.4651733, 42.4860689],[-71.4651078, 42.4860185],[-71.4651679, 42.4859757],[-71.4652334, 42.4860261]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.475616, 42.4744814],[-71.4756108, 42.4745734],[-71.47551730000001, 42.4745706],[-71.4755224, 42.4744785],[-71.475616, 42.4744814]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4771452, 42.4757972],[-71.4771109, 42.4758022],[-71.4770853, 42.4757062],[-71.4771197, 42.4757011],[-71.4771452, 42.4757972]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47526000000001, 42.4755134],[-71.47525640000001, 42.4756003],[-71.4751177, 42.4755972],[-71.4751213, 42.4755102],[-71.47526000000001, 42.4755134]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47292520000001, 42.4867642],[-71.47284999999999, 42.486826],[-71.4727884, 42.486785],[-71.4728635, 42.4867232],[-71.47292520000001, 42.4867642]]]}}, +{"type":"Feature","properties":{"building":"roof","source:datetime":"2015-02-28T17:00:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4533241, 42.4750784],[-71.45320649999999, 42.4751818],[-71.4531161, 42.4751254],[-71.4532336, 42.4750221],[-71.4533241, 42.4750784]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47422709999999, 42.4733605],[-71.4741222, 42.4733651],[-71.4741173, 42.4733031],[-71.47422210000001, 42.4732985],[-71.47422709999999, 42.4733605]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4638118, 42.4720479],[-71.46374969999999, 42.4720992],[-71.4636826, 42.4720546],[-71.4637446, 42.4720034],[-71.4638118, 42.4720479]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4686322, 42.4846661],[-71.46861490000001, 42.4847014],[-71.4685753, 42.4846908],[-71.46859259999999, 42.4846555],[-71.4686322, 42.4846661]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47405089999999, 42.4940935],[-71.4740132, 42.4942029],[-71.4738984, 42.4941812],[-71.4739361, 42.4940718],[-71.47405089999999, 42.4940935]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4786524, 42.4773485],[-71.4785957, 42.4773634],[-71.4785826, 42.4773359],[-71.4786393, 42.477321],[-71.4786524, 42.4773485]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4694724, 42.4926251],[-71.4693765, 42.4927539],[-71.4692936, 42.4927201],[-71.4693896, 42.4925913],[-71.4694724, 42.4926251]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4781346, 42.490088],[-71.47811710000001, 42.4901603],[-71.4779893, 42.4901433],[-71.4780068, 42.490071],[-71.4781346, 42.490088]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4679003, 42.4942464],[-71.46781489999999, 42.4943457],[-71.4677245, 42.4943031],[-71.46780990000001, 42.4942038],[-71.4679003, 42.4942464]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4609004, 42.4733089],[-71.46087989999999, 42.473347],[-71.4608391, 42.4733349],[-71.46085960000001, 42.4732969],[-71.4609004, 42.4733089]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4512908, 42.4770964],[-71.4511578, 42.4771334],[-71.4511292, 42.4770772],[-71.4512623, 42.4770402],[-71.4512908, 42.4770964]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4796624, 42.4799155],[-71.47965960000001, 42.4799806],[-71.4795393, 42.4799777],[-71.4795421, 42.4799126],[-71.4796624, 42.4799155]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4702798, 42.4807954],[-71.4701912, 42.4807987],[-71.470187, 42.4807365],[-71.47027559999999, 42.4807332],[-71.4702798, 42.4807954]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4648153, 42.4826153],[-71.4648124, 42.4827057],[-71.4647016, 42.4827037],[-71.4647046, 42.4826133],[-71.4648153, 42.4826153]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4688982, 42.4931997],[-71.4688103, 42.4932731],[-71.46873100000001, 42.4932211],[-71.4688189, 42.4931477],[-71.4688982, 42.4931997]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47761730000001, 42.4869244],[-71.4775137, 42.4869348],[-71.477493, 42.4868214],[-71.4775966, 42.4868111],[-71.47761730000001, 42.4869244]]]}}, +{"type":"Feature","properties":{"building":"garage","source:datetime":"2014-11-16T05:11:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4755299, 42.4844058],[-71.4754356, 42.4844068],[-71.4754344, 42.4843396],[-71.4755286, 42.4843387],[-71.4755299, 42.4844058]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4704747, 42.4771333],[-71.4703167, 42.477206],[-71.4702662, 42.4771459],[-71.4704242, 42.4770732],[-71.4704747, 42.4771333]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752945, 42.4724877],[-71.4752173, 42.4724882],[-71.47521690000001, 42.4724523],[-71.4752941, 42.4724518],[-71.4752945, 42.4724877]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4785445, 42.474152],[-71.4784329, 42.4741941],[-71.47838779999999, 42.4741285],[-71.4784994, 42.4740865],[-71.4785445, 42.474152]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4671599, 42.4900322],[-71.4671132, 42.4901059],[-71.4669337, 42.4900437],[-71.4669803, 42.48997],[-71.4671599, 42.4900322]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4752481, 42.4769813],[-71.4750713, 42.4770014],[-71.4750583, 42.4769391],[-71.47523510000001, 42.4769189],[-71.4752481, 42.4769813]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4761015, 42.4866235],[-71.4759693, 42.486759],[-71.4758741, 42.4867082],[-71.47600629999999, 42.4865726],[-71.4761015, 42.4866235]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47352309999999, 42.490063],[-71.47350400000001, 42.4900991],[-71.4734641, 42.4900876],[-71.4734832, 42.4900515],[-71.47352309999999, 42.490063]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4790304, 42.4917288],[-71.4789969, 42.4917774],[-71.4789548, 42.4917615],[-71.4789882, 42.4917129],[-71.4790304, 42.4917288]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4818157, 42.4753572],[-71.48166879999999, 42.4753687],[-71.4816565, 42.4752824],[-71.4818034, 42.475271],[-71.4818157, 42.4753572]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4533388, 42.4758241],[-71.4533026, 42.4758754],[-71.45326230000001, 42.4758599],[-71.4532985, 42.4758085],[-71.4533388, 42.4758241]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4642679, 42.4865765],[-71.46421340000001, 42.4866312],[-71.4641402, 42.4865912],[-71.4641948, 42.4865365],[-71.4642679, 42.4865765]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.479302, 42.4718976],[-71.4792818, 42.471942],[-71.4792422, 42.4719321],[-71.4792624, 42.4718877],[-71.479302, 42.4718976]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46644619999999, 42.4946421],[-71.46638059999999, 42.4947053],[-71.4663503, 42.494688],[-71.4664159, 42.4946249],[-71.46644619999999, 42.4946421]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.467381, 42.4871062],[-71.4672764, 42.4872793],[-71.46718250000001, 42.4872482],[-71.46728709999999, 42.4870751],[-71.467381, 42.4871062]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4710343, 42.4776408],[-71.47094800000001, 42.4776714],[-71.4708669, 42.4775464],[-71.4709532, 42.4775157],[-71.4710343, 42.4776408]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4599171, 42.4721885],[-71.4598155, 42.4721959],[-71.4598051, 42.4721178],[-71.4599067, 42.4721104],[-71.4599171, 42.4721885]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695998, 42.484368],[-71.469544, 42.4844992],[-71.46944569999999, 42.4844763],[-71.4695016, 42.4843451],[-71.4695998, 42.484368]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4758334, 42.4745031],[-71.47582559999999, 42.4745913],[-71.47573010000001, 42.4745867],[-71.4757379, 42.4744985],[-71.4758334, 42.4745031]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48303799999999, 42.4761521],[-71.483024, 42.4762342],[-71.48284, 42.4762171],[-71.48285389999999, 42.476135],[-71.48303799999999, 42.4761521]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4841729, 42.4768092],[-71.4841254, 42.4769218],[-71.4840097, 42.476895],[-71.4840571, 42.4767825],[-71.4841729, 42.4768092]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4666107, 42.4904966],[-71.4665765, 42.490558],[-71.4665028, 42.4905356],[-71.466537, 42.4904741],[-71.4666107, 42.4904966]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.471953, 42.4740708],[-71.4718324, 42.4740811],[-71.4718239, 42.4740265],[-71.47194450000001, 42.4740163],[-71.471953, 42.4740708]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47029310000001, 42.476132],[-71.4702875, 42.4762665],[-71.47020120000001, 42.4762646],[-71.47020689999999, 42.4761301],[-71.47029310000001, 42.476132]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713532, 42.4972204],[-71.47123670000001, 42.497231],[-71.4712165, 42.4971101],[-71.4713331, 42.4970994],[-71.4713532, 42.4972204]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4709623, 42.4867022],[-71.4709057, 42.4868185],[-71.47081110000001, 42.4867933],[-71.47086779999999, 42.486677],[-71.4709623, 42.4867022]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4846094, 42.4723937],[-71.4845636, 42.4724771],[-71.48443399999999, 42.4724381],[-71.4844799, 42.4723547],[-71.4846094, 42.4723937]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45235630000001, 42.4723576],[-71.4522633, 42.4723922],[-71.4521731, 42.4722594],[-71.4522661, 42.4722248],[-71.45235630000001, 42.4723576]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47515, 42.4735239],[-71.4751304, 42.4736315],[-71.474975, 42.473616],[-71.4749946, 42.4735083],[-71.47515, 42.4735239]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4771573, 42.4766992],[-71.4771411, 42.4767757],[-71.4770505, 42.4767652],[-71.47706669999999, 42.4766887],[-71.4771573, 42.4766992]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46600429999999, 42.4778826],[-71.4658907, 42.4779045],[-71.4658175, 42.4776966],[-71.46593110000001, 42.4776747],[-71.46600429999999, 42.4778826]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4601109, 42.4900031],[-71.46004240000001, 42.490048],[-71.4599746, 42.4899912],[-71.46004309999999, 42.4899464],[-71.4601109, 42.4900031]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4691402, 42.4962918],[-71.4691388, 42.4963643],[-71.4685193, 42.4963582],[-71.46852060000001, 42.4962857],[-71.4691402, 42.4962918]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4729505, 42.4964292],[-71.47290820000001, 42.4964738],[-71.4728535, 42.4964453],[-71.4728959, 42.4964008],[-71.4729505, 42.4964292]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4724544, 42.4757367],[-71.4723802, 42.4757375],[-71.47237920000001, 42.4756805],[-71.47245340000001, 42.4756797],[-71.4724544, 42.4757367]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.472077, 42.47839],[-71.47201370000001, 42.4784202],[-71.4719678, 42.4783676],[-71.4720311, 42.4783374],[-71.472077, 42.47839]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4761831, 42.476687],[-71.4760825, 42.4766878],[-71.4760816, 42.4766247],[-71.4761823, 42.476624],[-71.4761831, 42.476687]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4696081, 42.4807827],[-71.46957039999999, 42.4808188],[-71.4695366, 42.4807995],[-71.4695743, 42.4807634],[-71.4696081, 42.4807827]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474101, 42.476298],[-71.4740956, 42.4764397],[-71.4739502, 42.4764366],[-71.4739557, 42.4762949],[-71.474101, 42.476298]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47311980000001, 42.4897626],[-71.4730964, 42.4898017],[-71.4730201, 42.4897768],[-71.47304339999999, 42.4897377],[-71.47311980000001, 42.4897626]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47180729999999, 42.4898709],[-71.4717891, 42.4898877],[-71.47173239999999, 42.4898541],[-71.47175059999999, 42.4898373],[-71.47180729999999, 42.4898709]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4764765, 42.4853482],[-71.4763708, 42.4853599],[-71.47633930000001, 42.4852036],[-71.476445, 42.4851919],[-71.4764765, 42.4853482]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4760588, 42.4911968],[-71.4760474, 42.4912771],[-71.47587420000001, 42.4912668],[-71.475886, 42.4911834],[-71.4760588, 42.4911968]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4771401, 42.489545],[-71.47711339999999, 42.4896076],[-71.4770017, 42.4895815],[-71.4770283, 42.4895189],[-71.4771401, 42.489545]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47542369999999, 42.4736972],[-71.47541510000001, 42.4737563],[-71.47534, 42.4737502],[-71.4753487, 42.4736912],[-71.47542369999999, 42.4736972]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4750619, 42.4756394],[-71.4750603, 42.475677],[-71.47498299999999, 42.4756751],[-71.47498469999999, 42.4756376],[-71.4750619, 42.4756394]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4620145, 42.4718631],[-71.461968, 42.4719839],[-71.461879, 42.4719651],[-71.46192550000001, 42.4718443],[-71.4620145, 42.4718631]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4766644, 42.4751227],[-71.4766225, 42.4751465],[-71.476557, 42.4750836],[-71.4765988, 42.4750597],[-71.4766644, 42.4751227]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4698223, 42.4962178],[-71.46964920000001, 42.4962475],[-71.46964149999999, 42.4961631],[-71.4698147, 42.4961345],[-71.4698223, 42.4962178]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47418070000001, 42.4886826],[-71.4740955, 42.4887581],[-71.4740024, 42.4887005],[-71.4740876, 42.488625],[-71.47418070000001, 42.4886826]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474, 42.4938248],[-71.4738914, 42.4938271],[-71.47388669999999, 42.4937064],[-71.4739972, 42.493704],[-71.474, 42.4938248]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46185509999999, 42.4829979],[-71.46181850000001, 42.4831022],[-71.4617184, 42.483083],[-71.461755, 42.4829787],[-71.46185509999999, 42.4829979]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4737064, 42.476245],[-71.47370119999999, 42.4763816],[-71.47348580000001, 42.4763771],[-71.473491, 42.4762405],[-71.4737064, 42.476245]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47152370000001, 42.4764575],[-71.4715229, 42.4765294],[-71.4714353, 42.4765289],[-71.47143610000001, 42.4764569],[-71.47152370000001, 42.4764575]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4839843, 42.4746911],[-71.4839154, 42.4747458],[-71.4837553, 42.4746353],[-71.48382410000001, 42.4745806],[-71.4839843, 42.4746911]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47566740000001, 42.4789469],[-71.47557089999999, 42.4789676],[-71.47550769999999, 42.4788065],[-71.47560420000001, 42.4787858],[-71.47566740000001, 42.4789469]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:08Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47504720000001, 42.4901894],[-71.4750037, 42.4901907],[-71.4750019, 42.4901448],[-71.4750449, 42.4901436],[-71.47504720000001, 42.4901894]]]}}, +{"type":"Feature","properties":{"addr:street":"Massachusetts Avenue","addr:city":"Acton","operator":"Sunoco","building":"yes","addr:housenumber":"421","addr:state":"MA","area":"yes","name":"Sunoco","amenity":"fuel","source:datetime":"2015-02-28T17:00:28Z","addr:postcode":"01720"},"geometry": {"type":"Polygon","coordinates": [[[-71.45322280000001, 42.4752198],[-71.4531561, 42.4752774],[-71.4529996, 42.4751783],[-71.4530663, 42.4751207],[-71.45322280000001, 42.4752198]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.455501, 42.4747618],[-71.455493, 42.4748637],[-71.45541160000001, 42.4748602],[-71.4554196, 42.4747583],[-71.455501, 42.4747618]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45516019999999, 42.4913612],[-71.4551471, 42.4914559],[-71.45493639999999, 42.4914399],[-71.4549495, 42.4913452],[-71.45516019999999, 42.4913612]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:07Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4788607, 42.4740629],[-71.4788121, 42.4740714],[-71.47879, 42.4740024],[-71.4788386, 42.4739939],[-71.4788607, 42.4740629]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47015399999999, 42.4920754],[-71.4701, 42.49213],[-71.4699232, 42.4920342],[-71.46997709999999, 42.4919796],[-71.47015399999999, 42.4920754]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46375949999999, 42.472479],[-71.46375879999999, 42.4725131],[-71.463706, 42.4725126],[-71.46370659999999, 42.4724785],[-71.46375949999999, 42.472479]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4742538, 42.4732034],[-71.47421660000001, 42.4732301],[-71.4741865, 42.4732072],[-71.4742236, 42.4731805],[-71.4742538, 42.4732034]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759475, 42.4901351],[-71.475866, 42.4903288],[-71.4757728, 42.4903073],[-71.47585429999999, 42.4901136],[-71.4759475, 42.4901351]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4725045, 42.4897393],[-71.4724209, 42.4897968],[-71.47230020000001, 42.4897008],[-71.4723838, 42.4896433],[-71.4725045, 42.4897393]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:02Z"},"geometry": {"type":"Point","coordinates": [-71.4784294, 42.4718073]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47176949999999, 42.4954501],[-71.4717019, 42.4955313],[-71.4716171, 42.4954926],[-71.4716847, 42.4954115],[-71.47176949999999, 42.4954501]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4678224, 42.4888805],[-71.4677899, 42.4889215],[-71.46771390000001, 42.4888884],[-71.4677465, 42.4888474],[-71.4678224, 42.4888805]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4694791, 42.4857376],[-71.4693346, 42.4859099],[-71.46925, 42.485871],[-71.4693944, 42.4856987],[-71.4694791, 42.4857376]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695512, 42.473385],[-71.4695231, 42.4734202],[-71.469455, 42.4733905],[-71.46948310000001, 42.4733553],[-71.4695512, 42.473385]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:51:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46822880000001, 42.4722962],[-71.4681506, 42.4723027],[-71.4681458, 42.4722711],[-71.46822400000001, 42.4722646],[-71.46822880000001, 42.4722962]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4744469, 42.4757063],[-71.4744405, 42.4758202],[-71.4742162, 42.4758133],[-71.4742226, 42.4756994],[-71.4744469, 42.4757063]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47646930000001, 42.4727695],[-71.47633930000001, 42.4728501],[-71.476314, 42.4728278],[-71.47644409999999, 42.4727472],[-71.47646930000001, 42.4727695]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:59Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4715355, 42.4779516],[-71.47148900000001, 42.4779922],[-71.47141929999999, 42.4779486],[-71.4714658, 42.477908],[-71.4715355, 42.4779516]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4589096, 42.475013],[-71.4588238, 42.4750164],[-71.45881900000001, 42.4749503],[-71.4589048, 42.4749469],[-71.4589096, 42.475013]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4763487, 42.4865027],[-71.47630119999999, 42.4865604],[-71.47621959999999, 42.4865236],[-71.4762671, 42.4864659],[-71.4763487, 42.4865027]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46188239999999, 42.487071],[-71.46182779999999, 42.487128],[-71.46175390000001, 42.4870893],[-71.4618085, 42.4870323],[-71.46188239999999, 42.487071]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:57Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4703121, 42.4954996],[-71.4702925, 42.4955356],[-71.4702492, 42.4955228],[-71.4702688, 42.4954867],[-71.4703121, 42.4954996]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4684548, 42.4724614],[-71.46844590000001, 42.4724977],[-71.4683835, 42.4724894],[-71.4683923, 42.4724531],[-71.4684548, 42.4724614]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47781519999999, 42.4891049],[-71.47780090000001, 42.4891725],[-71.4777072, 42.4891616],[-71.4777215, 42.4890941],[-71.47781519999999, 42.4891049]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:56Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.459969, 42.476345],[-71.4599212, 42.4763835],[-71.4598568, 42.4763397],[-71.4599047, 42.4763012],[-71.459969, 42.476345]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.476904, 42.4759598],[-71.4768888, 42.4760404],[-71.47684219999999, 42.4760356],[-71.4768574, 42.475955],[-71.476904, 42.4759598]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46359649999999, 42.4995075],[-71.4635555, 42.4995203],[-71.4635246, 42.4994737],[-71.4635656, 42.4994609],[-71.46359649999999, 42.4995075]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45592430000001, 42.495882],[-71.4558765, 42.4959131],[-71.45584479999999, 42.4958864],[-71.4558926, 42.4958553],[-71.45592430000001, 42.495882]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4747, 42.4824945],[-71.4746691, 42.4825578],[-71.4745892, 42.4825364],[-71.4746202, 42.4824731],[-71.4747, 42.4824945]]]}}, +{"type":"Feature","properties":{"building":"garage","source:datetime":"2014-11-16T05:11:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47665309999999, 42.4842774],[-71.47656259999999, 42.4842922],[-71.47654110000001, 42.4842202],[-71.4766316, 42.4842054],[-71.47665309999999, 42.4842774]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:55Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4694458, 42.4904708],[-71.4694179, 42.4905082],[-71.4693784, 42.4904921],[-71.46940619999999, 42.4904547],[-71.4694458, 42.4904708]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48324359999999, 42.473934],[-71.4831822, 42.4739904],[-71.48308609999999, 42.4739331],[-71.48314739999999, 42.4738767],[-71.48324359999999, 42.473934]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48395240000001, 42.4772931],[-71.4839423, 42.4773182],[-71.4838826, 42.4773049],[-71.4838927, 42.4772798],[-71.48395240000001, 42.4772931]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4619831, 42.4769978],[-71.46178879999999, 42.4771223],[-71.4617128, 42.4770574],[-71.4619071, 42.4769328],[-71.4619831, 42.4769978]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47640060000001, 42.4839336],[-71.47627439999999, 42.4839511],[-71.4762571, 42.4838827],[-71.47638329999999, 42.4838652],[-71.47640060000001, 42.4839336]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45664840000001, 42.4991527],[-71.4565995, 42.4991747],[-71.4565734, 42.4991427],[-71.45662230000001, 42.4991208],[-71.45664840000001, 42.4991527]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4740393, 42.4746946],[-71.4739541, 42.4746947],[-71.4739541, 42.4746563],[-71.4740393, 42.4746562],[-71.4740393, 42.4746946]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.484217, 42.4763257],[-71.48416829999999, 42.4764887],[-71.4840662, 42.476472],[-71.484115, 42.476309],[-71.484217, 42.4763257]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4698831, 42.4887202],[-71.4698631, 42.4887464],[-71.4698085, 42.4887235],[-71.46982850000001, 42.4886973],[-71.4698831, 42.4887202]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46326259999999, 42.4744249],[-71.4631808, 42.4745572],[-71.4630745, 42.4745212],[-71.46315629999999, 42.4743888],[-71.46326259999999, 42.4744249]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.474602, 42.475733],[-71.47458709999999, 42.4760563],[-71.4744601, 42.4760531],[-71.474475, 42.4757298],[-71.474602, 42.475733]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4570576, 42.4807923],[-71.4570313, 42.4808207],[-71.45699140000001, 42.4808005],[-71.45701769999999, 42.4807721],[-71.4570576, 42.4807923]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45677190000001, 42.4787056],[-71.4567198, 42.4787131],[-71.4567074, 42.4786658],[-71.4567595, 42.4786583],[-71.45677190000001, 42.4787056]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4745422, 42.4770709],[-71.4745236, 42.4769746],[-71.4745405, 42.4769729],[-71.4745329, 42.4769318],[-71.4745114, 42.4769339],[-71.4745016, 42.476881],[-71.4745168, 42.4768795],[-71.4745101, 42.4768427],[-71.4744912, 42.4768447],[-71.47449279999999, 42.4768535],[-71.4744331, 42.4768596],[-71.47443579999999, 42.4768746],[-71.4743047, 42.4768878],[-71.47430230000001, 42.4768749],[-71.47424289999999, 42.4768809],[-71.47422709999999, 42.4767946],[-71.4742627, 42.476791],[-71.47427759999999, 42.4767875],[-71.4742893, 42.4767909],[-71.474296, 42.4767981],[-71.47440899999999, 42.4767866],[-71.47440589999999, 42.4767732],[-71.47453779999999, 42.4767595],[-71.47453489999999, 42.4767439],[-71.4746155, 42.4767355],[-71.4746336, 42.4768308],[-71.4746112, 42.4768331],[-71.47462779999999, 42.4769204],[-71.4746504, 42.476918],[-71.4746591, 42.4769636],[-71.4746385, 42.4769657],[-71.4746557, 42.477056],[-71.474678, 42.4770538],[-71.4746868, 42.4771017],[-71.474571, 42.4771134],[-71.4745628, 42.4770688],[-71.4745422, 42.4770709]]]}}, +{"type":"Feature","properties":{"building":"school","name":"Douglas School","source:datetime":"2014-11-12T02:24:09Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714518, 42.4819844],[-71.4713325, 42.4819418],[-71.47150619999999, 42.4816753],[-71.471585, 42.4817034],[-71.4716379, 42.4816224],[-71.4711903, 42.4814626],[-71.4713422, 42.4812295],[-71.4714359, 42.4812629],[-71.47145810000001, 42.4812288],[-71.4713655, 42.4811957],[-71.4714154, 42.4811192],[-71.4714819, 42.481143],[-71.47149140000001, 42.4811283],[-71.471515, 42.4811367],[-71.47150929999999, 42.4811456],[-71.4715444, 42.4811581],[-71.4715369, 42.4811696],[-71.47163140000001, 42.4812033],[-71.47158450000001, 42.4812753],[-71.4714826, 42.4812389],[-71.47145879999999, 42.4812753],[-71.4719845, 42.481463],[-71.4718355, 42.4816917],[-71.47172999999999, 42.481654],[-71.4716717, 42.4817434],[-71.47190740000001, 42.4818276],[-71.4718374, 42.481935],[-71.47194570000001, 42.4819737],[-71.4718802, 42.4820741],[-71.4718292, 42.4820559],[-71.4718153, 42.4820772],[-71.4718633, 42.4820943],[-71.4717972, 42.4821957],[-71.47161180000001, 42.4821295],[-71.47164189999999, 42.4820833],[-71.4714354, 42.4820096],[-71.4714518, 42.4819844]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.480019, 42.4804745],[-71.48001240000001, 42.4804883],[-71.47998149999999, 42.4804803],[-71.47998560000001, 42.4804717],[-71.4799391, 42.4804597],[-71.4799502, 42.480436],[-71.4799088, 42.4804253],[-71.47993219999999, 42.4803759],[-71.4799625, 42.4803838],[-71.4799723, 42.4803632],[-71.48000159999999, 42.4803708],[-71.4800091, 42.4803651],[-71.48001979999999, 42.4803644],[-71.4800285, 42.4803691],[-71.4800305, 42.4803785],[-71.48004280000001, 42.480381],[-71.480039, 42.4803922],[-71.4801137, 42.4804162],[-71.4801231, 42.4804048],[-71.48017230000001, 42.4804196],[-71.48015580000001, 42.4804434],[-71.4801831, 42.4804532],[-71.4801662, 42.48048],[-71.4801966, 42.4804885],[-71.4801763, 42.4805313],[-71.4801214, 42.4805171],[-71.4801257, 42.4805081],[-71.4800802, 42.4804963],[-71.4800747, 42.4805078],[-71.4800461, 42.4805004],[-71.4800541, 42.4804836],[-71.480019, 42.4804745]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Newtown Road","addr:city":"Acton","building":"yes","addr:housenumber":"226","addr:state":"MA","source:datetime":"2015-01-20T23:47:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4623474, 42.4994871],[-71.462379, 42.4994729],[-71.46240709999999, 42.4994804],[-71.46244160000001, 42.4994645],[-71.462457, 42.4994828],[-71.46250360000001, 42.4994613],[-71.4624792, 42.4994323],[-71.4625343, 42.4994069],[-71.4625611, 42.4994387],[-71.4625989, 42.4994213],[-71.46257439999999, 42.4993921],[-71.4625904, 42.4993847],[-71.4625843, 42.4993774],[-71.4626406, 42.4993514],[-71.4626512, 42.499364],[-71.46266970000001, 42.4993557],[-71.4627358, 42.4994419],[-71.4626501, 42.4994779],[-71.462638, 42.4994622],[-71.4626068, 42.4994753],[-71.4626184, 42.4994904],[-71.46260580000001, 42.4994957],[-71.46262, 42.4995142],[-71.4625983, 42.4995233],[-71.46260789999999, 42.4995358],[-71.46251289999999, 42.4995757],[-71.4625063, 42.4995671],[-71.46241019999999, 42.4996075],[-71.46233770000001, 42.4995128],[-71.4623474, 42.4994871]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45271459999999, 42.4761795],[-71.4526724, 42.476129],[-71.4526443, 42.476142],[-71.4526097, 42.4761006],[-71.45263, 42.4760913],[-71.4526106, 42.4760681],[-71.4525877, 42.4760786],[-71.4525447, 42.4760273],[-71.4527499, 42.4759332],[-71.4528172, 42.4760136],[-71.4528061, 42.4760187],[-71.4528358, 42.4760541],[-71.45276680000001, 42.4760858],[-71.45281009999999, 42.4761374],[-71.45288119999999, 42.4761048],[-71.4529086, 42.4761375],[-71.4529185, 42.4761329],[-71.4529893, 42.4762175],[-71.45294699999999, 42.4762369],[-71.4529531, 42.4762442],[-71.4529189, 42.4762599],[-71.4529096, 42.4762489],[-71.4527889, 42.4763043],[-71.4527461, 42.4762532],[-71.45276149999999, 42.4762461],[-71.45274379999999, 42.4762249],[-71.4527307, 42.4762309],[-71.4526951, 42.4761884],[-71.45271459999999, 42.4761795]]]}}, +{"type":"Feature","properties":{"addr:country":"US","gnis:state_id":"25","addr:street":"Arlington Street","gnis:county_id":"017","religion":"christian","addr:city":"Acton","ele":"66","building":"yes","gnis:created":"01/15/2003","addr:housenumber":"89","addr:state":"MA","gnis:feature_id":"1974523","name":"Saint Elizabeth of Hungary Church","amenity":"place_of_worship","source:datetime":"2015-01-23T22:30:06Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4629966, 42.4873648],[-71.4628934, 42.4873072],[-71.4629194, 42.4872855],[-71.4628135, 42.4871373],[-71.4630458, 42.4871516],[-71.4630544, 42.4871219],[-71.4631631, 42.4871749],[-71.4631531, 42.4871861],[-71.4633118, 42.4872636],[-71.46332270000001, 42.4872514],[-71.4635273, 42.4873513],[-71.4634573, 42.4873986],[-71.46351919999999, 42.4874851],[-71.4633855, 42.4874747],[-71.463362, 42.4875408],[-71.4633007, 42.4875109],[-71.4632478, 42.4875687],[-71.46335860000001, 42.4876198],[-71.46328010000001, 42.487713],[-71.4632153, 42.487683],[-71.4632258, 42.4876705],[-71.46306920000001, 42.4875982],[-71.4630897, 42.4875449],[-71.4631438, 42.4875142],[-71.4631921, 42.487459],[-71.4631547, 42.4874411],[-71.46316849999999, 42.4874253],[-71.46301, 42.4873494],[-71.4629966, 42.4873648]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:40Z"},"geometry": {"type":"LineString","coordinates": [[-71.4724174, 42.4718067],[-71.47252450000001, 42.4718415],[-71.47254119999999, 42.4718466],[-71.4725356, 42.4718567],[-71.4725189, 42.4718516],[-71.47248930000001, 42.4719051],[-71.4724028, 42.4718788],[-71.472418, 42.4718513],[-71.4723968, 42.4718449],[-71.4724085, 42.4718237],[-71.4723675, 42.4718112]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4590273, 42.4752024],[-71.45903, 42.4751875],[-71.45898769999999, 42.4751834],[-71.4589902, 42.4751692],[-71.45895299999999, 42.4751656],[-71.4589495, 42.4751854],[-71.4588299, 42.4751738],[-71.45884270000001, 42.4751014],[-71.45886489999999, 42.4751036],[-71.45887159999999, 42.4750654],[-71.4589606, 42.4750741],[-71.4589588, 42.4750841],[-71.45903869999999, 42.4750918],[-71.4590443, 42.4750605],[-71.4591295, 42.4750687],[-71.459124, 42.4750999],[-71.4592021, 42.4751075],[-71.45920479999999, 42.4750926],[-71.45929529999999, 42.4751013],[-71.4592868, 42.4751496],[-71.45931349999999, 42.4751522],[-71.4593019, 42.4752178],[-71.4591491, 42.475203],[-71.459152, 42.4751868],[-71.4591175, 42.4751834],[-71.4591152, 42.4751963],[-71.4590769, 42.4751926],[-71.45907440000001, 42.4752069],[-71.4590273, 42.4752024]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.473918, 42.4788611],[-71.47392859999999, 42.4788546],[-71.4739466, 42.4788542],[-71.4740444, 42.478826],[-71.47405139999999, 42.4788393],[-71.47412319999999, 42.4788187],[-71.4740772, 42.478731],[-71.4740917, 42.4787269],[-71.4740743, 42.4786938],[-71.4740632, 42.4786969],[-71.4740132, 42.4786017],[-71.4741154, 42.4785723],[-71.47416459999999, 42.4786662],[-71.4741781, 42.4786623],[-71.47420150000001, 42.4787069],[-71.4741894, 42.4787104],[-71.47423259999999, 42.4787928],[-71.47417590000001, 42.478809],[-71.4742135, 42.4788809],[-71.47409690000001, 42.4789144],[-71.47408969999999, 42.4789006],[-71.47397530000001, 42.4789334],[-71.47398130000001, 42.4789449],[-71.47382090000001, 42.4789908],[-71.4737734, 42.4789],[-71.47386899999999, 42.4788727],[-71.4738739, 42.4788821],[-71.4739136, 42.4788707],[-71.473918, 42.4788611]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47872719999999, 42.4814456],[-71.478758, 42.4814882],[-71.478781, 42.4814791],[-71.4787968, 42.4815008],[-71.4789464, 42.4814414],[-71.47897519999999, 42.4814039],[-71.479005, 42.4814193],[-71.47904629999999, 42.4813695],[-71.4789937, 42.4813456],[-71.479073, 42.4812501],[-71.47911499999999, 42.4812692],[-71.4791539, 42.4812225],[-71.4792183, 42.4812518],[-71.4792102, 42.4812616],[-71.47924, 42.4812751],[-71.4792128, 42.4813078],[-71.47918009999999, 42.4812929],[-71.47914280000001, 42.4813377],[-71.479161, 42.481346],[-71.479039, 42.4814928],[-71.4790252, 42.4814865],[-71.47883229999999, 42.4815656],[-71.47882490000001, 42.4815566],[-71.4787834, 42.4815748],[-71.4787662, 42.4815526],[-71.47871259999999, 42.4815753],[-71.4786428, 42.4814792],[-71.47872719999999, 42.4814456]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4735702, 42.4784015],[-71.4735641, 42.4783898],[-71.47361189999999, 42.4783762],[-71.4736166, 42.4783852],[-71.4737375, 42.4783507],[-71.47372350000001, 42.4783239],[-71.4738378, 42.4782913],[-71.47384769999999, 42.4783103],[-71.474, 42.4782669],[-71.47401929999999, 42.4783039],[-71.4739799, 42.4783151],[-71.47400519999999, 42.4783636],[-71.47388580000001, 42.4783977],[-71.4738613, 42.4783507],[-71.47375270000001, 42.4783817],[-71.4737477, 42.4783721],[-71.4737308, 42.4783769],[-71.47374379999999, 42.4784018],[-71.47371269999999, 42.4784106],[-71.47372059999999, 42.4784258],[-71.473665, 42.4784417],[-71.47367389999999, 42.4784587],[-71.4736123, 42.4784763],[-71.4736063, 42.4784648],[-71.4735553, 42.4784794],[-71.473522, 42.4784153],[-71.4735702, 42.4784015]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:35Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4687412, 42.4959162],[-71.468754, 42.4958932],[-71.4688, 42.4959072],[-71.468824, 42.4958639],[-71.468858, 42.4958742],[-71.4688656, 42.4958605],[-71.4689664, 42.4958911],[-71.46893470000001, 42.4959482],[-71.46895139999999, 42.4959533],[-71.4689322, 42.4959901],[-71.4686516, 42.49591],[-71.46855170000001, 42.4959197],[-71.46852029999999, 42.4959408],[-71.4685221, 42.4959607],[-71.4685351, 42.4959607],[-71.4685424, 42.4960733],[-71.4684445, 42.4960768],[-71.4684369, 42.4959608],[-71.4684955, 42.4959587],[-71.46849349999999, 42.495928],[-71.4685068, 42.4959253],[-71.4685459, 42.4959004],[-71.4685379, 42.4958653],[-71.4686421, 42.4958523],[-71.468653, 42.4958894],[-71.4687412, 42.4959162]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4739103, 42.4731012],[-71.47390969999999, 42.4730927],[-71.4739555, 42.4730909],[-71.4739562, 42.4730993],[-71.4739707, 42.4730987],[-71.4739718, 42.4731146],[-71.474126, 42.4731084],[-71.4741284, 42.4731403],[-71.4741034, 42.4731414],[-71.4741068, 42.4731864],[-71.4740407, 42.4731891],[-71.47403920000001, 42.4731693],[-71.4740278, 42.4731698],[-71.47402839999999, 42.4731781],[-71.4739938, 42.4731796],[-71.4739932, 42.4731712],[-71.47398130000001, 42.4731716],[-71.4739829, 42.4731922],[-71.4739245, 42.4731946],[-71.4739235, 42.4731813],[-71.4739014, 42.4731822],[-71.4739026, 42.4731989],[-71.473821, 42.4732022],[-71.4738138, 42.4731051],[-71.4739103, 42.4731012]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4615984, 42.4753604],[-71.4615505, 42.475359],[-71.461551, 42.4753502],[-71.46151209999999, 42.475349],[-71.46151159999999, 42.4753579],[-71.46145439999999, 42.4753562],[-71.4614548, 42.4753475],[-71.46136970000001, 42.475345],[-71.46137520000001, 42.4752385],[-71.4615099, 42.4752424],[-71.4615106, 42.4752273],[-71.4615324, 42.4752279],[-71.46153529999999, 42.4751732],[-71.4616097, 42.4751754],[-71.4616067, 42.4752333],[-71.4616669, 42.475235],[-71.4616659, 42.4752543],[-71.46178070000001, 42.4752577],[-71.46176730000001, 42.475354],[-71.4616929, 42.4753483],[-71.46169039999999, 42.4753666],[-71.46163749999999, 42.4753626],[-71.46163869999999, 42.4753541],[-71.46159969999999, 42.4753511],[-71.4615984, 42.4753604]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4743979, 42.4742767],[-71.4743979, 42.4742069],[-71.4743632, 42.4742069],[-71.4743632, 42.4741899],[-71.474333, 42.4741898],[-71.4743331, 42.4741491],[-71.4743704, 42.4741491],[-71.4743704, 42.4741343],[-71.47435400000001, 42.4741343],[-71.47435400000001, 42.4740705],[-71.474447, 42.4740705],[-71.474447, 42.4741335],[-71.47448919999999, 42.4741335],[-71.47448919999999, 42.474213],[-71.4744606, 42.474213],[-71.4744606, 42.4742296],[-71.474498, 42.4742296],[-71.4744979, 42.4743071],[-71.4745104, 42.4743071],[-71.47451030000001, 42.4743846],[-71.4743805, 42.4743846],[-71.4743805, 42.4743403],[-71.47432070000001, 42.4743403],[-71.47432070000001, 42.4742767],[-71.4743979, 42.4742767]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4592662, 42.4749838],[-71.4592684, 42.4749345],[-71.45921389999999, 42.4749332],[-71.459217, 42.4748656],[-71.4592418, 42.4748662],[-71.45924429999999, 42.4748104],[-71.45920390000001, 42.4748094],[-71.45920719999999, 42.4747372],[-71.45922179999999, 42.4747375],[-71.4592246, 42.4746762],[-71.45932740000001, 42.4746788],[-71.4593246, 42.4747388],[-71.45935540000001, 42.4747396],[-71.4593512, 42.4748322],[-71.459383, 42.474833],[-71.4593812, 42.4748728],[-71.45940589999999, 42.4748734],[-71.4594029, 42.4749398],[-71.45938339999999, 42.4749393],[-71.4593813, 42.4749855],[-71.4594108, 42.4749862],[-71.45940760000001, 42.4750577],[-71.4592504, 42.4750538],[-71.4592536, 42.4749835],[-71.4592662, 42.4749838]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46136009999999, 42.485389],[-71.46134410000001, 42.4853731],[-71.4613226, 42.4853849],[-71.46129500000001, 42.4853575],[-71.46134309999999, 42.485331],[-71.4612874, 42.485275],[-71.46140370000001, 42.4852117],[-71.46145780000001, 42.4852662],[-71.4614385, 42.4852767],[-71.4614816, 42.48532],[-71.4615045, 42.4853076],[-71.4615409, 42.4853442],[-71.46152240000001, 42.4853543],[-71.4615627, 42.4853948],[-71.46157839999999, 42.4853963],[-71.4615924, 42.4854111],[-71.461589, 42.4854265],[-71.4615671, 42.4854396],[-71.46154780000001, 42.485434],[-71.461483, 42.4854709],[-71.4614483, 42.4854364],[-71.4613765, 42.485476],[-71.4613143, 42.4854143],[-71.46136009999999, 42.485389]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46350870000001, 42.4787744],[-71.4634214, 42.4786928],[-71.463489, 42.4786499],[-71.46355250000001, 42.4787048],[-71.4635636, 42.4786978],[-71.4635675, 42.478689],[-71.46357519999999, 42.4786818],[-71.4635938, 42.4786753],[-71.4636062, 42.4786755],[-71.46361779999999, 42.4786789],[-71.4636271, 42.4786851],[-71.463633, 42.4786932],[-71.46363359999999, 42.4787084],[-71.4636227, 42.4787213],[-71.4636095, 42.4787268],[-71.4635787, 42.4787287],[-71.4636673, 42.478811],[-71.4636335, 42.4788319],[-71.46364029999999, 42.478839],[-71.4636264, 42.4788468],[-71.4636172, 42.4788395],[-71.4635873, 42.4788601],[-71.463493, 42.4787853],[-71.46350870000001, 42.4787744]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45830840000001, 42.4736275],[-71.4584583, 42.47358],[-71.45851469999999, 42.4736845],[-71.4583476, 42.4737339],[-71.4583408, 42.4737212],[-71.4583111, 42.47373],[-71.4583157, 42.4737386],[-71.4582649, 42.4737536],[-71.4582602, 42.4737451],[-71.45823369999999, 42.4737529],[-71.4582405, 42.4737655],[-71.4582135, 42.4737735],[-71.4582183, 42.4737825],[-71.45819160000001, 42.4737904],[-71.4581862, 42.4737805],[-71.45814970000001, 42.4737913],[-71.4581683, 42.4738257],[-71.458072, 42.4738542],[-71.45800629999999, 42.4737326],[-71.4581499, 42.4736794],[-71.4581443, 42.4736692],[-71.4583014, 42.473617],[-71.45830840000001, 42.4736275]]]}}, +{"type":"Feature","properties":{"building":"school","name":"Gates School","source:datetime":"2014-11-12T02:24:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4739355, 42.4798069],[-71.4740987, 42.4797807],[-71.474037, 42.4795707],[-71.47422570000001, 42.4795404],[-71.4742136, 42.4794991],[-71.474339, 42.4794789],[-71.4744034, 42.4796937],[-71.47458109999999, 42.4796651],[-71.4746533, 42.479915],[-71.4744213, 42.4799523],[-71.47447270000001, 42.4801272],[-71.47412490000001, 42.4801832],[-71.47407870000001, 42.4800262],[-71.47401600000001, 42.4800363],[-71.4740474, 42.4801432],[-71.473675, 42.4802031],[-71.4736681, 42.4801798],[-71.4734389, 42.4802167],[-71.4733571, 42.4799383],[-71.47360639999999, 42.4798982],[-71.47355589999999, 42.4797264],[-71.4738957, 42.4796717],[-71.4739355, 42.4798069]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4577426, 42.4738901],[-71.45770469999999, 42.4739015],[-71.45768750000001, 42.4738699],[-71.4577187, 42.4738605],[-71.4577079, 42.4738408],[-71.4576725, 42.4738514],[-71.45763719999999, 42.4737867],[-71.4576476, 42.4737836],[-71.45764130000001, 42.4737722],[-71.45765969999999, 42.4737666],[-71.457645, 42.4737396],[-71.4577802, 42.4736991],[-71.4578107, 42.4737549],[-71.4578301, 42.4737491],[-71.4578954, 42.4738685],[-71.45788400000001, 42.4738719],[-71.45791370000001, 42.4739261],[-71.4577864, 42.4739642],[-71.45777630000001, 42.4739459],[-71.4577326, 42.473959],[-71.4577139, 42.4739248],[-71.4577549, 42.4739125],[-71.4577426, 42.4738901]]]}}, +{"type":"Feature","properties":{"addr:street":"Arlington Street","addr:city":"Acton","building":"yes","addr:housenumber":"18","addr:state":"MA","source:datetime":"2014-11-08T13:55:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4556012, 42.4956558],[-71.4556544, 42.4955951],[-71.4556978, 42.4956159],[-71.4557119, 42.4955998],[-71.4556834, 42.495586],[-71.4557307, 42.495532],[-71.45578020000001, 42.4955558],[-71.45580150000001, 42.4955314],[-71.4558552, 42.4955572],[-71.45583600000001, 42.4955791],[-71.45587209999999, 42.4955964],[-71.4558112, 42.4956659],[-71.45587829999999, 42.495698],[-71.4558224, 42.4957618],[-71.4557597, 42.4957317],[-71.4557259, 42.4957703],[-71.45571630000001, 42.4957657],[-71.45569140000001, 42.4957941],[-71.4556178, 42.4957588],[-71.45564349999999, 42.4957295],[-71.4556108, 42.4957139],[-71.4556438, 42.4956762],[-71.4556012, 42.4956558]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46474329999999, 42.4810864],[-71.4647339, 42.4811],[-71.46464640000001, 42.4810663],[-71.4646989, 42.4809948],[-71.464776, 42.4810258],[-71.4648048, 42.4810208],[-71.4648002, 42.4810061],[-71.46499319999999, 42.4809729],[-71.4650237, 42.4810699],[-71.4649938, 42.4810751],[-71.4650042, 42.4811082],[-71.46498389999999, 42.4811117],[-71.464977, 42.4810896],[-71.4649233, 42.4810989],[-71.4649296, 42.4811189],[-71.4649004, 42.4811239],[-71.4648938, 42.4811031],[-71.4648344, 42.4811133],[-71.4648411, 42.4811346],[-71.46481249999999, 42.4811395],[-71.46479309999999, 42.4810778],[-71.46474329999999, 42.4810864]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47246490000001, 42.4964113],[-71.4724348, 42.496398],[-71.4724835, 42.4963377],[-71.4725229, 42.4963551],[-71.4725488, 42.496323],[-71.47257209999999, 42.4963333],[-71.4726301, 42.4962604],[-71.4727233, 42.4963067],[-71.47266190000001, 42.4963745],[-71.4726436, 42.4963653],[-71.47261450000001, 42.4963973],[-71.47270140000001, 42.4964405],[-71.47267429999999, 42.4964703],[-71.4726649, 42.4964657],[-71.47259870000001, 42.4965386],[-71.472622, 42.4965969],[-71.472562, 42.4966096],[-71.4725395, 42.4965607],[-71.47255370000001, 42.4965555],[-71.4725292, 42.4964931],[-71.47244000000001, 42.4964422],[-71.47246490000001, 42.4964113]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4700907, 42.4758745],[-71.4700909, 42.4758938],[-71.4700078, 42.4758943],[-71.47000749999999, 42.4758663],[-71.4699796, 42.4758665],[-71.4699786, 42.4757788],[-71.4700228, 42.4757786],[-71.47002209999999, 42.4757104],[-71.4701037, 42.4757099],[-71.47010450000001, 42.475781],[-71.4701931, 42.4757805],[-71.4701924, 42.475718],[-71.4702731, 42.4757175],[-71.4702739, 42.4757916],[-71.47032969999999, 42.4757913],[-71.4703306, 42.4758782],[-71.47026200000001, 42.4758786],[-71.47026219999999, 42.4758973],[-71.47018, 42.4758977],[-71.4701798, 42.475874],[-71.4700907, 42.4758745]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45866150000001, 42.4735933],[-71.4586418, 42.4735628],[-71.4586652, 42.4735546],[-71.4586549, 42.4735385],[-71.4586333, 42.4735461],[-71.45855760000001, 42.4734286],[-71.4585728, 42.4734232],[-71.45856000000001, 42.4734035],[-71.4587011, 42.4733537],[-71.45874980000001, 42.4734293],[-71.4587769, 42.4734197],[-71.45878999999999, 42.4734401],[-71.4587607, 42.4734504],[-71.45881060000001, 42.4735279],[-71.45883739999999, 42.4735184],[-71.45885579999999, 42.473547],[-71.4588294, 42.4735563],[-71.4588567, 42.4735986],[-71.4587159, 42.4736483],[-71.45867699999999, 42.4735879],[-71.45866150000001, 42.4735933]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45357, 42.4756336],[-71.4534736, 42.4755755],[-71.45377670000001, 42.4752999],[-71.4538713, 42.475357],[-71.45384919999999, 42.4753771],[-71.4539051, 42.4754107],[-71.45387580000001, 42.4754373],[-71.4538202, 42.4754037],[-71.4537717, 42.4754478],[-71.4538288, 42.4754823],[-71.45380230000001, 42.4755064],[-71.4537459, 42.4754725],[-71.4536975, 42.4755165],[-71.4537526, 42.4755497],[-71.4537279, 42.4755721],[-71.45367520000001, 42.4755404],[-71.45362489999999, 42.4755862],[-71.4536796, 42.4756192],[-71.453653, 42.4756433],[-71.45359670000001, 42.4756094],[-71.45357, 42.4756336]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47916240000001, 42.4745716],[-71.47915860000001, 42.4745441],[-71.47914660000001, 42.474545],[-71.4791405, 42.4745],[-71.47927060000001, 42.4744902],[-71.4792681, 42.4744717],[-71.4793797, 42.4744364],[-71.4794041, 42.4744802],[-71.4793777, 42.4744873],[-71.47938360000001, 42.4744992],[-71.47937210000001, 42.4745023],[-71.479389, 42.4745365],[-71.4793123, 42.4745573],[-71.47930789999999, 42.4745485],[-71.4792272, 42.4745611],[-71.4792297, 42.474585],[-71.47924260000001, 42.4745841],[-71.47924740000001, 42.4746574],[-71.4791441, 42.4746651],[-71.4791316, 42.4745739],[-71.47916240000001, 42.4745716]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45883000000001, 42.4749085],[-71.458828, 42.474871],[-71.4588469, 42.4748705],[-71.45884239999999, 42.474786],[-71.4588818, 42.4747849],[-71.45888050000001, 42.474761],[-71.4588438, 42.474762],[-71.45883670000001, 42.4746288],[-71.4589179, 42.4746264],[-71.45891949999999, 42.4746569],[-71.45899, 42.4746548],[-71.458994, 42.4747302],[-71.45897119999999, 42.4747309],[-71.458974, 42.474784],[-71.458997, 42.4747833],[-71.45900260000001, 42.4748575],[-71.4589879, 42.4748577],[-71.4589926, 42.4749151],[-71.458888, 42.4749182],[-71.45888739999999, 42.4749069],[-71.45883000000001, 42.4749085]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4543091, 42.4807478],[-71.45431259999999, 42.4806984],[-71.45440139999999, 42.4807018],[-71.45440429999999, 42.4806616],[-71.4544903, 42.4806649],[-71.45449139999999, 42.4806486],[-71.4545517, 42.4806509],[-71.4545506, 42.4806674],[-71.4545985, 42.4806692],[-71.4546004, 42.4806412],[-71.4547493, 42.480647],[-71.45474659999999, 42.4806969],[-71.45479690000001, 42.4806952],[-71.45479589999999, 42.4807301],[-71.4547446, 42.4807294],[-71.4547374, 42.4808163],[-71.45440600000001, 42.4808035],[-71.4544016, 42.4808659],[-71.4541339, 42.4808555],[-71.4541419, 42.4807414],[-71.4543091, 42.4807478]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4594654, 42.4894656],[-71.4594364, 42.489438],[-71.4594273, 42.4894431],[-71.4593842, 42.489402],[-71.4594159, 42.4893838],[-71.4594014, 42.48937],[-71.45945639999999, 42.4893384],[-71.4594476, 42.4893284],[-71.4594488, 42.4893188],[-71.459457, 42.4893113],[-71.4594695, 42.4893084],[-71.45948199999999, 42.4893111],[-71.4594914, 42.4893208],[-71.4594918, 42.4893279],[-71.45947750000001, 42.4893368],[-71.4596402, 42.4894921],[-71.4595499, 42.4895439],[-71.45952080000001, 42.4895161],[-71.4594512, 42.489556],[-71.4593974, 42.4895047],[-71.4594654, 42.4894656]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:18Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4542476, 42.4753403],[-71.4542445, 42.4753784],[-71.45431809999999, 42.4753817],[-71.4543157, 42.4754099],[-71.4542419, 42.4754066],[-71.45423719999999, 42.475463],[-71.45430090000001, 42.4754659],[-71.4542981, 42.4754998],[-71.4542252, 42.4754965],[-71.4542206, 42.4755526],[-71.4542879, 42.4755557],[-71.4542855, 42.4755843],[-71.4542113, 42.4755809],[-71.45420660000001, 42.475638],[-71.4542733, 42.475641],[-71.4542708, 42.4756715],[-71.4542, 42.4756683],[-71.45419750000001, 42.4756987],[-71.45407179999999, 42.4756872],[-71.4541304, 42.475335],[-71.4542476, 42.4753403]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:17Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4537369, 42.4759915],[-71.4536772, 42.47591],[-71.45409669999999, 42.4757415],[-71.4541542, 42.47582],[-71.4541195, 42.4758339],[-71.4541543, 42.4758813],[-71.45411989999999, 42.4758952],[-71.45408689999999, 42.4758501],[-71.4540149, 42.475879],[-71.45404910000001, 42.4759256],[-71.4540169, 42.4759385],[-71.4539824, 42.4758916],[-71.4539146, 42.4759188],[-71.4539463, 42.4759621],[-71.4539087, 42.4759772],[-71.4538774, 42.4759346],[-71.4538048, 42.4759637],[-71.45383579999999, 42.476006],[-71.4538033, 42.4760191],[-71.45377259999999, 42.4759771],[-71.4537369, 42.4759915]]]}}, +{"type":"Feature","properties":{"religion":"jewish","building":"yes","amenity":"place_of_worship","source:datetime":"2013-09-05T23:20:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4563088, 42.4726316],[-71.4562916, 42.4725537],[-71.4562524, 42.4725561],[-71.45624770000001, 42.4725148],[-71.45627589999999, 42.472513],[-71.4562621, 42.4723925],[-71.4563002, 42.4723901],[-71.45632879999999, 42.4723641],[-71.45631849999999, 42.4723578],[-71.4564, 42.4722683],[-71.4565875, 42.4723619],[-71.4565329, 42.4724219],[-71.4565381, 42.4724614],[-71.4565844, 42.4724581],[-71.4565869, 42.4724768],[-71.4565893, 42.4724951],[-71.4565542, 42.4724977],[-71.45656, 42.472539],[-71.4567282, 42.472625],[-71.4566068, 42.4727551],[-71.45639199999999, 42.4726453],[-71.4563558, 42.4726268],[-71.4563088, 42.4726316]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46892130000001, 42.4817225],[-71.4689827, 42.481611],[-71.46891669999999, 42.4815906],[-71.468954, 42.4815333],[-71.46908759999999, 42.481581],[-71.4690664, 42.4816137],[-71.46916210000001, 42.4816478],[-71.4691725, 42.4816318],[-71.4692885, 42.4816732],[-71.4692474, 42.4817363],[-71.4691289, 42.4817005],[-71.4691423, 42.4816762],[-71.4690475, 42.4816475],[-71.4690008, 42.4817321],[-71.4690344, 42.4817423],[-71.4690233, 42.4817624],[-71.46900359999999, 42.4817565],[-71.4689886, 42.4817838],[-71.4689194, 42.4817629],[-71.4689387, 42.4817278],[-71.46892130000001, 42.4817225]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4775609, 42.4751177],[-71.477529, 42.475041],[-71.4775767, 42.4750301],[-71.4775679, 42.475009],[-71.4776183, 42.4749975],[-71.4776107, 42.4749792],[-71.4776368, 42.4749733],[-71.47763329999999, 42.4749649],[-71.4776877, 42.4749524],[-71.4776991, 42.4749798],[-71.47777309999999, 42.4749629],[-71.4777989, 42.4750248],[-71.4777289, 42.4750408],[-71.477734, 42.475053],[-71.47766470000001, 42.4750688],[-71.477678, 42.4751007],[-71.4776608, 42.4751046],[-71.4776672, 42.4751202],[-71.47760580000001, 42.4751342],[-71.4775956, 42.4751098],[-71.4775609, 42.4751177]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4757319, 42.4849108],[-71.4757007, 42.4849133],[-71.4756967, 42.4848863],[-71.4757274, 42.4848843],[-71.475724, 42.484866],[-71.4758102, 42.4848562],[-71.47583280000001, 42.4849573],[-71.47584620000001, 42.4849556],[-71.4758563, 42.485001],[-71.4757606, 42.4850127],[-71.475787, 42.4850245],[-71.4757809, 42.4850342],[-71.4758516, 42.4850674],[-71.47581839999999, 42.4851033],[-71.4757279, 42.4850593],[-71.4757223, 42.4850218],[-71.4756998, 42.4850236],[-71.47569470000001, 42.4849887],[-71.47574280000001, 42.4849848],[-71.4757319, 42.4849108]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:11Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.460319, 42.4751418],[-71.4603109, 42.4751861],[-71.46013859999999, 42.4751688],[-71.460183, 42.4749259],[-71.46020470000001, 42.474917],[-71.4602332, 42.4748656],[-71.4602381, 42.4748243],[-71.46043090000001, 42.4748369],[-71.4604248, 42.474888],[-71.4605035, 42.4748932],[-71.46051199999999, 42.4748219],[-71.4608568, 42.4748522],[-71.4608329, 42.4750013],[-71.4608865, 42.475006],[-71.4608521, 42.4752186],[-71.4606266, 42.475196],[-71.46062310000001, 42.4752152],[-71.4604417, 42.475197],[-71.4604494, 42.4751549],[-71.460319, 42.4751418]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4764914, 42.4915682],[-71.4764928, 42.4915549],[-71.4766395, 42.4915637],[-71.4766454, 42.4915101],[-71.4767191, 42.4915145],[-71.47671750000001, 42.4915284],[-71.4767666, 42.4915313],[-71.4767614, 42.4915783],[-71.4768057, 42.4915809],[-71.47680130000001, 42.4916211],[-71.4767415, 42.4916175],[-71.4767428, 42.4916057],[-71.4766372, 42.4915993],[-71.4766362, 42.4916086],[-71.4765799, 42.4916053],[-71.4765771, 42.4916311],[-71.4764563, 42.4916238],[-71.4764626, 42.4915665],[-71.4764914, 42.4915682]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4678552, 42.4876382],[-71.4678212, 42.4876196],[-71.46784630000001, 42.4875946],[-71.4678337, 42.4875876],[-71.467843, 42.4875783],[-71.4678164, 42.4875637],[-71.46784719999999, 42.4875329],[-71.4678856, 42.487554],[-71.4679165, 42.4875231],[-71.4679874, 42.4875621],[-71.4679119, 42.4876374],[-71.46795, 42.4876582],[-71.4679308, 42.4876773],[-71.4679416, 42.4876833],[-71.46789680000001, 42.487728],[-71.46788669999999, 42.4877225],[-71.4678705, 42.4877387],[-71.4677956, 42.4876976],[-71.4678552, 42.4876382]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713151, 42.4959685],[-71.47131589999999, 42.4959599],[-71.4714638, 42.4959674],[-71.4714572, 42.4960398],[-71.4714931, 42.4960416],[-71.471497, 42.4960434],[-71.4715116, 42.496056],[-71.4715154, 42.4960724],[-71.4715119, 42.4960821],[-71.4715013, 42.4960928],[-71.4714855, 42.4960993],[-71.4714676, 42.4961004],[-71.4714469, 42.4960939],[-71.4714334, 42.4960802],[-71.4713893, 42.4960792],[-71.4713904, 42.4960509],[-71.4712399, 42.4960477],[-71.471243, 42.495967],[-71.4713151, 42.4959685]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4833154, 42.4749197],[-71.4833103, 42.4749093],[-71.4832555, 42.474924],[-71.4832512, 42.4749152],[-71.4832155, 42.4749248],[-71.4831911, 42.4748751],[-71.4832024, 42.4748721],[-71.4831731, 42.4748126],[-71.48319530000001, 42.4748066],[-71.4831568, 42.4747282],[-71.4832548, 42.4747018],[-71.4832808, 42.4747547],[-71.4832651, 42.4747589],[-71.48327569999999, 42.4747805],[-71.48330060000001, 42.4747738],[-71.4833205, 42.4748142],[-71.48339350000001, 42.4747946],[-71.48343869999999, 42.4748865],[-71.4833154, 42.4749197]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45413310000001, 42.4732987],[-71.45409789999999, 42.4732904],[-71.4540787, 42.4733353],[-71.4540092, 42.4733189],[-71.454037, 42.4732542],[-71.45413840000001, 42.4732781],[-71.4541488, 42.4732537],[-71.4541371, 42.473251],[-71.4541571, 42.4732046],[-71.4542021, 42.4732153],[-71.4541827, 42.4732604],[-71.4542596, 42.4732741],[-71.4542541, 42.4732906],[-71.45427770000001, 42.4732962],[-71.45426070000001, 42.4733303],[-71.4542395, 42.4733253],[-71.45423169999999, 42.4733433],[-71.45412469999999, 42.4733181],[-71.45413310000001, 42.4732987]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45473939999999, 42.4911038],[-71.45474849999999, 42.4910062],[-71.4547924, 42.4910084],[-71.4547944, 42.4909877],[-71.454849, 42.4909905],[-71.4548467, 42.4910161],[-71.45493430000001, 42.4910206],[-71.4549299, 42.4910678],[-71.4549111, 42.4910669],[-71.4549073, 42.4911076],[-71.45492640000001, 42.4911086],[-71.4549233, 42.4911423],[-71.45497039999999, 42.4911447],[-71.4549653, 42.4911992],[-71.454801, 42.4911908],[-71.4548027, 42.4911728],[-71.45466879999999, 42.4911659],[-71.4546749, 42.4911005],[-71.45473939999999, 42.4911038]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4765082, 42.4757775],[-71.4765247, 42.4757922],[-71.47660620000001, 42.4757531],[-71.476619, 42.4757653],[-71.4767719, 42.4757005],[-71.4767284, 42.4756511],[-71.47685629999999, 42.4756013],[-71.47692429999999, 42.4756926],[-71.4767827, 42.4757423],[-71.4767927, 42.4757579],[-71.47664930000001, 42.4758163],[-71.476641, 42.4758073],[-71.4765371, 42.4758612],[-71.4765196, 42.4758433],[-71.476485, 42.475861],[-71.4765153, 42.4758891],[-71.4764607, 42.4759171],[-71.4763932, 42.4758395],[-71.4765082, 42.4757775]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:02Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4743404, 42.4882952],[-71.47441329999999, 42.4882288],[-71.4743947, 42.4882176],[-71.474418, 42.4881964],[-71.47443699999999, 42.4882078],[-71.47446429999999, 42.488183],[-71.47447339999999, 42.4881885],[-71.4744918, 42.4881717],[-71.4745463, 42.4882045],[-71.4745271, 42.488222],[-71.47453590000001, 42.4882273],[-71.4744774, 42.4882806],[-71.4745238, 42.4883085],[-71.47445690000001, 42.4883694],[-71.4744312, 42.4883539],[-71.4744142, 42.4883694],[-71.47439079999999, 42.4883553],[-71.4744105, 42.4883373],[-71.4743404, 42.4882952]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:01Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4655152, 42.4772954],[-71.465515, 42.4773229],[-71.46545690000001, 42.477322],[-71.465456, 42.4772343],[-71.4654269, 42.4772332],[-71.46543010000001, 42.4771687],[-71.46550089999999, 42.4771702],[-71.4655023, 42.4771858],[-71.4655338, 42.4771854],[-71.46553470000001, 42.4772031],[-71.4656092, 42.4772028],[-71.4656051, 42.47743],[-71.4656987, 42.4774309],[-71.4656961, 42.477573],[-71.4655819, 42.4775719],[-71.4655838, 42.4774676],[-71.46552610000001, 42.477467],[-71.46552920000001, 42.4772952],[-71.4655152, 42.4772954]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:50:00Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4759766, 42.4888899],[-71.4759847, 42.4888707],[-71.4759727, 42.4888679],[-71.4759877, 42.4888323],[-71.47601640000001, 42.4888389],[-71.476034, 42.4887971],[-71.47596969999999, 42.4887823],[-71.4759859, 42.4887438],[-71.4760455, 42.4887575],[-71.4760552, 42.4887346],[-71.47618660000001, 42.4887649],[-71.47615519999999, 42.4888396],[-71.4761439, 42.488837],[-71.47612049999999, 42.4888925],[-71.4761069, 42.4888893],[-71.4760704, 42.4889761],[-71.47589929999999, 42.4889367],[-71.47592400000001, 42.4888778],[-71.4759766, 42.4888899]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.462827, 42.4787432],[-71.4628225, 42.4787341],[-71.46280059999999, 42.47874],[-71.46279180000001, 42.4787221],[-71.4628132, 42.4787163],[-71.4627907, 42.4786707],[-71.4627502, 42.4786817],[-71.46271590000001, 42.4786123],[-71.462762, 42.4785998],[-71.4627557, 42.4785872],[-71.46287359999999, 42.4785552],[-71.46291600000001, 42.478641],[-71.4628982, 42.4786458],[-71.4629317, 42.4787137],[-71.46290810000001, 42.4787201],[-71.4629262, 42.4787568],[-71.4628644, 42.4787735],[-71.46284679999999, 42.4787379],[-71.462827, 42.4787432]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46252440000001, 42.4845212],[-71.46253129999999, 42.4844539],[-71.46255290000001, 42.4844551],[-71.46255859999999, 42.4843998],[-71.4625772, 42.4844008],[-71.4625747, 42.4844252],[-71.4626622, 42.4844301],[-71.46265870000001, 42.4844649],[-71.4626727, 42.4844657],[-71.4626684, 42.4845073],[-71.4626547, 42.4845065],[-71.46265099999999, 42.4845422],[-71.4626379, 42.4845415],[-71.4626274, 42.4846447],[-71.46253299999999, 42.4846393],[-71.46253900000001, 42.4845804],[-71.46248509999999, 42.4845774],[-71.462491, 42.4845193],[-71.46252440000001, 42.4845212]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:54Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4792992, 42.4803542],[-71.4792083, 42.4803299],[-71.47924310000001, 42.4802586],[-71.47926769999999, 42.4802651],[-71.4792836, 42.4802326],[-71.4793336, 42.4802459],[-71.4793428, 42.480227],[-71.4793865, 42.4802387],[-71.4794143, 42.4801816],[-71.4794632, 42.4801947],[-71.4794303, 42.4802621],[-71.4795149, 42.4802847],[-71.4795077, 42.4802994],[-71.4795402, 42.4803081],[-71.479495, 42.4804007],[-71.4794078, 42.4803774],[-71.47941280000001, 42.4803671],[-71.4793067, 42.4803388],[-71.4792992, 42.4803542]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:53Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4735167, 42.4915629],[-71.47348, 42.4915226],[-71.4735684, 42.4914785],[-71.4735852, 42.491497],[-71.4736138, 42.4914827],[-71.47362409999999, 42.4914941],[-71.4736865, 42.4914629],[-71.4737374, 42.4915187],[-71.4736499, 42.4915624],[-71.4736664, 42.4915805],[-71.4736352, 42.4915961],[-71.4736432, 42.491605],[-71.4736106, 42.4916212],[-71.4735987, 42.4916081],[-71.4735731, 42.4916209],[-71.4735823, 42.491631],[-71.4735416, 42.4916513],[-71.4734784, 42.4915819],[-71.4735167, 42.4915629]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:52Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45846779999999, 42.4725324],[-71.4584598, 42.4724612],[-71.4584333, 42.4724629],[-71.4584282, 42.4724175],[-71.45847190000001, 42.4724148],[-71.4584687, 42.4723862],[-71.4584845, 42.4723852],[-71.4584792, 42.4723388],[-71.4585769, 42.4723328],[-71.4585819, 42.4723765],[-71.4585958, 42.4723756],[-71.4586047, 42.4724545],[-71.4585718, 42.4724566],[-71.45857359999999, 42.4724727],[-71.458612, 42.4724703],[-71.4586193, 42.4725354],[-71.4585265, 42.4725411],[-71.4585251, 42.4725289],[-71.45846779999999, 42.4725324]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46108889999999, 42.4848066],[-71.4610981, 42.4847654],[-71.4610835, 42.4847636],[-71.461091, 42.4847299],[-71.461108, 42.484732],[-71.4611172, 42.4846909],[-71.4611286, 42.4846923],[-71.4611508, 42.4845843],[-71.4612889, 42.4846012],[-71.4612681, 42.4846943],[-71.4613099, 42.4846994],[-71.4612948, 42.4847668],[-71.4612285, 42.4847586],[-71.46121239999999, 42.4848307],[-71.46119590000001, 42.4848287],[-71.4611941, 42.4848371],[-71.4611692, 42.4848341],[-71.461173, 42.4848169],[-71.46108889999999, 42.4848066]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45165040000001, 42.4886907],[-71.45166089999999, 42.4886526],[-71.4516739, 42.4886546],[-71.4516898, 42.488597],[-71.45162120000001, 42.4885867],[-71.45163169999999, 42.4885485],[-71.451786, 42.4885718],[-71.4517707, 42.4886274],[-71.45179280000001, 42.4886308],[-71.4517967, 42.4886167],[-71.4518333, 42.4886224],[-71.4518161, 42.4886848],[-71.45176429999999, 42.4886767],[-71.4517402, 42.4887641],[-71.4516338, 42.488748],[-71.45164320000001, 42.4887139],[-71.4516271, 42.4887115],[-71.45163359999999, 42.4886882],[-71.45165040000001, 42.4886907]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:51Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4779406, 42.4767966],[-71.4779442, 42.476784],[-71.4779583, 42.4767718],[-71.4779702, 42.4767678],[-71.47798330000001, 42.4767669],[-71.477996, 42.4767692],[-71.4780059, 42.4767737],[-71.4780244, 42.4767591],[-71.478039, 42.4767537],[-71.478055, 42.4767513],[-71.47807659999999, 42.4767532],[-71.4781219, 42.476764],[-71.478086, 42.4768774],[-71.4779875, 42.4768604],[-71.4779762, 42.4768961],[-71.4779005, 42.476883],[-71.4779284, 42.4767945],[-71.4779406, 42.4767966]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:50Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4714682, 42.4738587],[-71.4713974, 42.4738217],[-71.4714494, 42.4737671],[-71.47152029999999, 42.4738041],[-71.4715109, 42.473814],[-71.47156080000001, 42.47384],[-71.4716004, 42.4738188],[-71.4716172, 42.4738414],[-71.47164220000001, 42.4738285],[-71.471694, 42.4738834],[-71.47165870000001, 42.4739017],[-71.47168449999999, 42.4739318],[-71.471575, 42.4739831],[-71.4715042, 42.4739003],[-71.47153, 42.4738882],[-71.471509, 42.473862],[-71.4714797, 42.4738467],[-71.4714682, 42.4738587]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Massachusetts Avenue","addr:city":"Acton","building":"yes","addr:housenumber":"603","addr:state":"MA","source:datetime":"2014-11-18T02:34:26Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4758633, 42.4764022],[-71.47580069999999, 42.4763992],[-71.4758058, 42.4763399],[-71.475785, 42.4763389],[-71.47578609999999, 42.4763265],[-71.47577579999999, 42.4763175],[-71.47577250000001, 42.476309],[-71.4757748, 42.4762974],[-71.47578420000001, 42.4762879],[-71.47580379999999, 42.4762822],[-71.47589790000001, 42.4762877],[-71.4758923, 42.4763401],[-71.4759779, 42.4763441],[-71.4759724, 42.4764084],[-71.4759076, 42.4764053],[-71.47590889999999, 42.4763903],[-71.4758645, 42.4763882],[-71.4758633, 42.4764022]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4596956, 42.4831183],[-71.4595843, 42.4830622],[-71.45961389999999, 42.48303],[-71.4595514, 42.4829985],[-71.45955240000001, 42.4830167],[-71.4594168, 42.4830226],[-71.4594092, 42.4829267],[-71.4595439, 42.4829208],[-71.4596567, 42.4829801],[-71.4596641, 42.4829723],[-71.4596986, 42.4829905],[-71.459726, 42.4829619],[-71.45978909999999, 42.4829951],[-71.45975780000001, 42.4830291],[-71.4598574, 42.4830793],[-71.4597947, 42.4831474],[-71.4597088, 42.483104],[-71.4596956, 42.4831183]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:49Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45197039999999, 42.4950921],[-71.4519559, 42.4950107],[-71.45191149999999, 42.495015],[-71.4519007, 42.4949546],[-71.45210950000001, 42.4949342],[-71.45211949999999, 42.4949906],[-71.4520741, 42.494995],[-71.45208390000001, 42.4950498],[-71.4521087, 42.4950474],[-71.45212050000001, 42.4950534],[-71.45212650000001, 42.4950631],[-71.4521249, 42.4950736],[-71.4521163, 42.4950821],[-71.45208909999999, 42.4950842],[-71.4521031, 42.4951489],[-71.4520107, 42.4951579],[-71.4519985, 42.4950894],[-71.45197039999999, 42.4950921]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47328090000001, 42.4733423],[-71.4732807, 42.4733283],[-71.4732104, 42.4733315],[-71.4732053, 42.4732697],[-71.4732879, 42.473266],[-71.4732867, 42.4732523],[-71.4733509, 42.4732494],[-71.47335169999999, 42.4732591],[-71.4733774, 42.473258],[-71.4733937, 42.473249],[-71.4734101, 42.4732472],[-71.4734237, 42.4732562],[-71.4734251, 42.4733418],[-71.4733617, 42.4733424],[-71.47336199999999, 42.4733609],[-71.47329430000001, 42.4733615],[-71.4732939, 42.4733422],[-71.47328090000001, 42.4733423]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4508084, 42.4904825],[-71.45079920000001, 42.4904802],[-71.4507835, 42.4904719],[-71.4507734, 42.4904596],[-71.4507706, 42.4904491],[-71.4507735, 42.4904349],[-71.45078719999999, 42.4904202],[-71.45080249999999, 42.4904149],[-71.45079680000001, 42.4904055],[-71.4509266, 42.4903738],[-71.4509219, 42.4903633],[-71.45106699999999, 42.490326],[-71.4511152, 42.4904268],[-71.4509719, 42.4904645],[-71.450968, 42.4904561],[-71.45083440000001, 42.4904913],[-71.4508277, 42.4904775],[-71.4508084, 42.4904825]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:48Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4776569, 42.4910833],[-71.4776636, 42.4910903],[-71.4776663, 42.4911014],[-71.4776614, 42.4911121],[-71.4776506, 42.4911195],[-71.4776519, 42.4911332],[-71.47764290000001, 42.4911489],[-71.4776245, 42.4911592],[-71.4776016, 42.4911613],[-71.47758829999999, 42.4911584],[-71.47757679999999, 42.4911525],[-71.4775683, 42.4911443],[-71.4775636, 42.4911343],[-71.47750569999999, 42.4911223],[-71.47751839999999, 42.4910947],[-71.4775065, 42.4910909],[-71.4775214, 42.4910536],[-71.4776569, 42.4910833]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48025130000001, 42.480537],[-71.4803951, 42.4805371],[-71.4803951, 42.480552],[-71.4804316, 42.4805521],[-71.4804316, 42.4806116],[-71.48037720000001, 42.4806115],[-71.48037720000001, 42.4806759],[-71.48039559999999, 42.4806759],[-71.4803955, 42.480729],[-71.4803521, 42.480729],[-71.4803521, 42.4807546],[-71.4802486, 42.4807545],[-71.4802487, 42.4806581],[-71.4802712, 42.4806581],[-71.4802713, 42.4806017],[-71.48025130000001, 42.4806017],[-71.48025130000001, 42.480537]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4749704, 42.4753995],[-71.4750841, 42.4754059],[-71.4750828, 42.475419],[-71.4751285, 42.4754216],[-71.4751314, 42.475394],[-71.47520059999999, 42.4753979],[-71.4751926, 42.475475],[-71.47515180000001, 42.4754727],[-71.47515009999999, 42.4754895],[-71.4750817, 42.4754856],[-71.4750805, 42.4754965],[-71.4750226, 42.4754932],[-71.47502419999999, 42.4754779],[-71.4749328, 42.4754727],[-71.4749388, 42.4754152],[-71.4749686, 42.4754169],[-71.4749704, 42.4753995]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:46Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48309709999999, 42.4724824],[-71.48315789999999, 42.472461],[-71.4831502, 42.4724489],[-71.4831894, 42.4724351],[-71.4831969, 42.4724468],[-71.4832399, 42.4724316],[-71.4832464, 42.4724417],[-71.4833326, 42.4724113],[-71.48337669999999, 42.4724799],[-71.4831629, 42.4725551],[-71.4831832, 42.4725867],[-71.48313760000001, 42.4726028],[-71.48311769999999, 42.4725719],[-71.4831032, 42.472577],[-71.48305740000001, 42.4725057],[-71.483102, 42.47249],[-71.48309709999999, 42.4724824]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4770293, 42.490083],[-71.4770564, 42.4900807],[-71.4770504, 42.4900424],[-71.4770949, 42.4900386],[-71.4771002, 42.4900724],[-71.47712679999999, 42.4900701],[-71.4771329, 42.490109],[-71.4772026, 42.490103],[-71.47720080000001, 42.4900912],[-71.4773009, 42.4900827],[-71.47732360000001, 42.4902278],[-71.4772198, 42.4902367],[-71.4772064, 42.4901511],[-71.4771338, 42.4901574],[-71.4771353, 42.490167],[-71.4770437, 42.4901748],[-71.4770293, 42.490083]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.462352, 42.4809786],[-71.46243699999999, 42.4809398],[-71.4624893, 42.4810025],[-71.46247839999999, 42.4810075],[-71.46248660000001, 42.4810173],[-71.462458, 42.4810304],[-71.462468, 42.4810425],[-71.46247769999999, 42.481038],[-71.4624871, 42.4810492],[-71.4624688, 42.4810576],[-71.4624918, 42.4810853],[-71.46237859999999, 42.481137],[-71.4623023, 42.4810454],[-71.4622904, 42.4810509],[-71.46222040000001, 42.4809669],[-71.46230869999999, 42.4809266],[-71.462352, 42.4809786]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:45Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4641197, 42.4819554],[-71.4640936, 42.4819487],[-71.4641007, 42.4819335],[-71.46407670000001, 42.4819274],[-71.46409970000001, 42.4818778],[-71.4642664, 42.4819201],[-71.4642355, 42.4819867],[-71.4641884, 42.4819748],[-71.46416429999999, 42.4820266],[-71.46412170000001, 42.4820157],[-71.46408529999999, 42.482094],[-71.4640054, 42.4820737],[-71.4640445, 42.4819894],[-71.46406279999999, 42.4819941],[-71.4640741, 42.4819697],[-71.4641089, 42.4819786],[-71.4641197, 42.4819554]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:42Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46649770000001, 42.4896956],[-71.466516, 42.4896709],[-71.46649909999999, 42.489664],[-71.4665243, 42.48963],[-71.4665454, 42.4896385],[-71.4665557, 42.4896246],[-71.46663820000001, 42.4896581],[-71.46660919999999, 42.4896973],[-71.4666374, 42.4897087],[-71.4665869, 42.4897768],[-71.4665486, 42.4897613],[-71.46652469999999, 42.4897935],[-71.46637, 42.4897307],[-71.466391, 42.4897024],[-71.4664235, 42.4897156],[-71.466452, 42.4896771],[-71.46649770000001, 42.4896956]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:41Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.450737, 42.493966],[-71.4507524, 42.493974],[-71.4507667, 42.4939589],[-71.45080919999999, 42.493981],[-71.4507936, 42.4939974],[-71.45081450000001, 42.4940083],[-71.4508294, 42.4939927],[-71.4508652, 42.4940107],[-71.4508051, 42.494074],[-71.4507868, 42.4940652],[-71.4507152, 42.4941405],[-71.4506176, 42.4940897],[-71.4506672, 42.4940375],[-71.45065030000001, 42.4940285],[-71.4506796, 42.4939978],[-71.4506976, 42.4940074],[-71.450737, 42.493966]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45252549999999, 42.4946118],[-71.4525371, 42.4946115],[-71.45253099999999, 42.4944961],[-71.4526223, 42.4944935],[-71.452626, 42.4945628],[-71.4526851, 42.4945611],[-71.45268729999999, 42.4946032],[-71.45263919999999, 42.4946046],[-71.4526432, 42.4946794],[-71.45267560000001, 42.4946785],[-71.4526778, 42.4947193],[-71.452533, 42.4947235],[-71.4525319, 42.494703],[-71.45248719999999, 42.4947043],[-71.45248340000001, 42.494632],[-71.4525265, 42.4946307],[-71.45252549999999, 42.4946118]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4720609, 42.4950655],[-71.4721131, 42.4950861],[-71.472092, 42.4951154],[-71.4721483, 42.4951376],[-71.4721411, 42.4951477],[-71.47219250000001, 42.495168],[-71.4721617, 42.4952108],[-71.4721969, 42.4952246],[-71.4721206, 42.4953306],[-71.4720591, 42.4953064],[-71.4720671, 42.4952951],[-71.4719764, 42.4952593],[-71.4720143, 42.4952067],[-71.4720445, 42.4952186],[-71.4720841, 42.4951636],[-71.47201099999999, 42.4951348],[-71.4720609, 42.4950655]]]}}, +{"type":"Feature","properties":{"addr:country":"US","gnis:state_id":"25","addr:street":"Newtown Road","gnis:county_id":"017","religion":"christian","addr:city":"Acton","ele":"80","building":"yes","gnis:created":"01/15/2003","addr:housenumber":"164","addr:state":"MA","gnis:feature_id":"1974359","name":"Good Shepherd Church","amenity":"place_of_worship","source:datetime":"2014-11-08T14:01:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4551787, 42.4974398],[-71.45551140000001, 42.4973887],[-71.4555268, 42.4974436],[-71.4556125, 42.497442],[-71.45560070000001, 42.4976633],[-71.45554730000001, 42.4976617],[-71.4555464, 42.4976795],[-71.4554933, 42.497678],[-71.4555026, 42.4975042],[-71.45525840000001, 42.4975398],[-71.4552896, 42.4976824],[-71.4551184, 42.4977039],[-71.455112, 42.4976762],[-71.4550828, 42.4976798],[-71.45501830000001, 42.4974471],[-71.455174, 42.4974232],[-71.4551787, 42.4974398]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4713383, 42.4874189],[-71.4714109, 42.487447],[-71.471391, 42.4874752],[-71.47140949999999, 42.4874823],[-71.47143989999999, 42.4874392],[-71.47149690000001, 42.487464],[-71.4714749, 42.4874952],[-71.4715832, 42.4875347],[-71.47154089999999, 42.4875947],[-71.4715054, 42.487581],[-71.4714934, 42.487598],[-71.47145810000001, 42.4875844],[-71.4714681, 42.4875702],[-71.47142839999999, 42.4875549],[-71.4714455, 42.4875306],[-71.47129940000001, 42.4874742],[-71.4713383, 42.4874189]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:39Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695754, 42.4878043],[-71.4696691, 42.487849],[-71.4696258, 42.4878988],[-71.4696053, 42.487889],[-71.4695961, 42.4878995],[-71.4696208, 42.4879113],[-71.4695849, 42.4879526],[-71.4695952, 42.4879575],[-71.469543, 42.4880175],[-71.4694513, 42.4879737],[-71.4694727, 42.4879491],[-71.4694318, 42.4879296],[-71.4694949, 42.4878572],[-71.4695378, 42.4878777],[-71.46956609999999, 42.4878452],[-71.4695475, 42.4878363],[-71.4695754, 42.4878043]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4643257, 42.4843748],[-71.4643586, 42.4843749],[-71.4643585, 42.4843873],[-71.4644215, 42.4843875],[-71.4644215, 42.4843959],[-71.4645098, 42.4843962],[-71.46450919999999, 42.4845045],[-71.46445060000001, 42.4845043],[-71.4644504, 42.4845329],[-71.4643962, 42.4845327],[-71.4643963, 42.4845172],[-71.4643616, 42.4845171],[-71.46436180000001, 42.4844747],[-71.4642674, 42.4844744],[-71.4642679, 42.4843832],[-71.4643256, 42.4843834],[-71.4643257, 42.4843748]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:38Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46903140000001, 42.4863579],[-71.4691059, 42.4863794],[-71.4691497, 42.4862961],[-71.4692431, 42.486323],[-71.46919920000001, 42.4864064],[-71.4691708, 42.4863982],[-71.469122, 42.486491],[-71.4690442, 42.4864686],[-71.46904910000001, 42.4864592],[-71.4690087, 42.4864476],[-71.4690148, 42.4864361],[-71.46899019999999, 42.486429],[-71.4690018, 42.4864069],[-71.4690208, 42.4864123],[-71.4690279, 42.4863988],[-71.46901219999999, 42.4863943],[-71.46903140000001, 42.4863579]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:36Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4634063, 42.480151],[-71.4634169, 42.4801403],[-71.46345169999999, 42.4801591],[-71.46348279999999, 42.4801277],[-71.4635438, 42.4801607],[-71.4635181, 42.4801867],[-71.46360780000001, 42.4802353],[-71.4635797, 42.4802637],[-71.4635913, 42.4802701],[-71.463584, 42.4802775],[-71.46357190000001, 42.4802709],[-71.4635408, 42.4803023],[-71.4634343, 42.4802445],[-71.46344209999999, 42.4802367],[-71.4633311, 42.4801766],[-71.4633738, 42.4801334],[-71.4634063, 42.480151]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46453339999999, 42.4780998],[-71.464551, 42.478088],[-71.4646085, 42.4781351],[-71.4645865, 42.4781498],[-71.4646127, 42.4781713],[-71.4646518, 42.4781451],[-71.46469430000001, 42.4781811],[-71.4646537, 42.4782057],[-71.46471099999999, 42.4782575],[-71.4646358, 42.4783078],[-71.4645651, 42.4782499],[-71.46457599999999, 42.4782426],[-71.464521, 42.4781976],[-71.46451089999999, 42.4782043],[-71.46442330000001, 42.4781326],[-71.46450590000001, 42.4780773],[-71.46453339999999, 42.4780998]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4632412, 42.4783573],[-71.46333060000001, 42.4783462],[-71.4633326, 42.4783548],[-71.4633443, 42.4783533],[-71.4633474, 42.478367],[-71.463334, 42.4783686],[-71.4633426, 42.4784067],[-71.46336340000001, 42.4784041],[-71.4633712, 42.4784385],[-71.4633548, 42.4784406],[-71.46337130000001, 42.4785129],[-71.4632936, 42.4785225],[-71.46328629999999, 42.4784904],[-71.46325419999999, 42.4784944],[-71.4632326, 42.4783992],[-71.4632502, 42.478397],[-71.4632412, 42.4783573]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:34Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4772146, 42.4750229],[-71.4772936, 42.4751516],[-71.4772655, 42.4751611],[-71.47727709999999, 42.47518],[-71.47722640000001, 42.4751971],[-71.47721730000001, 42.4751823],[-71.4770912, 42.4752247],[-71.4771068, 42.4752501],[-71.4769658, 42.4752975],[-71.47692360000001, 42.4752287],[-71.476972, 42.4752124],[-71.4769632, 42.4751981],[-71.47702649999999, 42.4751768],[-71.4770097, 42.4751494],[-71.4771563, 42.4751001],[-71.477127, 42.4750524],[-71.4772146, 42.4750229]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:33Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4772406, 42.4860104],[-71.47731810000001, 42.4859959],[-71.4773077, 42.4859654],[-71.4773747, 42.4859529],[-71.4773632, 42.4859192],[-71.4774881, 42.4858959],[-71.4775121, 42.4859662],[-71.4774151, 42.4859843],[-71.4774475, 42.4860793],[-71.47747320000001, 42.4860745],[-71.4774932, 42.4861332],[-71.4774168, 42.4861475],[-71.4774094, 42.486126],[-71.4773588, 42.4861354],[-71.4773339, 42.4860626],[-71.47726299999999, 42.4860759],[-71.4772406, 42.4860104]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:32Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4724659, 42.4884909],[-71.4724981, 42.488449],[-71.4724788, 42.4884408],[-71.47251110000001, 42.4883989],[-71.4725437, 42.4884126],[-71.47253139999999, 42.4884286],[-71.4725656, 42.488443],[-71.4725799, 42.4884244],[-71.472764, 42.4885019],[-71.4727175, 42.4885624],[-71.47267669999999, 42.4885452],[-71.47266020000001, 42.4885666],[-71.4725705, 42.4885289],[-71.4725457, 42.4885612],[-71.4724882, 42.488537],[-71.4725095, 42.4885092],[-71.4724659, 42.4884909]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:32Z"},"geometry": {"type":"LineString","coordinates": [[-71.48318380000001, 42.471864],[-71.4831496, 42.471853],[-71.48314379999999, 42.4718626],[-71.48287139999999, 42.471883],[-71.4827363, 42.4718393],[-71.4827413, 42.4718309],[-71.4826915, 42.4718148]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:31Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4695971, 42.4950403],[-71.46964060000001, 42.4950509],[-71.4696242, 42.4950878],[-71.46961140000001, 42.4950847],[-71.46960319999999, 42.495103],[-71.4695749, 42.4950961],[-71.4695614, 42.4951263],[-71.4696024, 42.4951363],[-71.46955130000001, 42.4952512],[-71.4694612, 42.4952292],[-71.46950940000001, 42.4951208],[-71.4694581, 42.4951083],[-71.46948039999999, 42.4950582],[-71.46952659999999, 42.4950694],[-71.46954049999999, 42.4950381],[-71.4695925, 42.4950508],[-71.4695971, 42.4950403]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:30Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46323150000001, 42.4823665],[-71.4632632, 42.4822026],[-71.4633281, 42.4822094],[-71.46331859999999, 42.4822584],[-71.4634179, 42.4822689],[-71.4634116, 42.4823014],[-71.46349480000001, 42.4823102],[-71.4634823, 42.4823748],[-71.4633943, 42.4823655],[-71.4633851, 42.4824132],[-71.4633211, 42.4824064],[-71.463324, 42.4823911],[-71.4632677, 42.4823852],[-71.4632651, 42.4823987],[-71.463207, 42.4823926],[-71.4632124, 42.4823645],[-71.46323150000001, 42.4823665]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4627052, 42.4753322],[-71.4627071, 42.4753196],[-71.46276279999999, 42.475324],[-71.4627644, 42.4753135],[-71.4628331, 42.4753189],[-71.4628297, 42.4753427],[-71.4628424, 42.4753437],[-71.46283200000001, 42.4754159],[-71.46270869999999, 42.4754061],[-71.4627064, 42.4754216],[-71.4626389, 42.4754163],[-71.4626402, 42.4754075],[-71.46260409999999, 42.4754047],[-71.4626054, 42.4753959],[-71.4625278, 42.4753898],[-71.462538, 42.475319],[-71.4627052, 42.4753322]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:29Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4798158, 42.481096],[-71.4799105, 42.4810935],[-71.4799111, 42.4811055],[-71.4800352, 42.4811022],[-71.48003439999999, 42.4810861],[-71.4801226, 42.4810838],[-71.4801265, 42.4811646],[-71.4800991, 42.4811654],[-71.4801008, 42.4812014],[-71.4799453, 42.4812055],[-71.47994610000001, 42.4812236],[-71.47990780000001, 42.4812246],[-71.47990710000001, 42.4812092],[-71.47985540000001, 42.4812106],[-71.47985439999999, 42.4811893],[-71.4798203, 42.4811902],[-71.4798158, 42.481096]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:28Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4716464, 42.4925177],[-71.4716325, 42.4925087],[-71.47170920000001, 42.4924444],[-71.471782, 42.4924919],[-71.4716988, 42.4925616],[-71.4717089, 42.4925682],[-71.47162969999999, 42.4926346],[-71.4716506, 42.4926483],[-71.4715901, 42.4926991],[-71.4714922, 42.4926351],[-71.471515, 42.492616],[-71.4714922, 42.4926012],[-71.4715255, 42.4925733],[-71.4715467, 42.4925871],[-71.4715976, 42.4925444],[-71.4716071, 42.4925506],[-71.4716464, 42.4925177]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4801277, 42.4745849],[-71.48002169999999, 42.4745425],[-71.4800322, 42.474528],[-71.4799751, 42.4745052],[-71.48000930000001, 42.4744583],[-71.47999849999999, 42.474454],[-71.48001910000001, 42.4744256],[-71.4800324, 42.4744309],[-71.4800651, 42.4743861],[-71.4801326, 42.4744131],[-71.4800832, 42.4744808],[-71.48020889999999, 42.474531],[-71.480177, 42.4745746],[-71.4801882, 42.4745791],[-71.4801655, 42.4746102],[-71.48012180000001, 42.4745928],[-71.4801277, 42.4745849]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:27Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4608107, 42.4761422],[-71.46086510000001, 42.4762369],[-71.4607906, 42.4762604],[-71.46079640000001, 42.4762705],[-71.4607196, 42.4762947],[-71.4607336, 42.476319],[-71.4606148, 42.4763565],[-71.46057519999999, 42.4762877],[-71.46060180000001, 42.4762794],[-71.4605937, 42.4762654],[-71.46062910000001, 42.4762542],[-71.4606024, 42.4762077],[-71.4606642, 42.4761882],[-71.4606694, 42.4761972],[-71.46073629999999, 42.4761761],[-71.4607312, 42.4761673],[-71.4608107, 42.4761422]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:25Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4825239, 42.4741548],[-71.48251089999999, 42.4741669],[-71.4824618, 42.4741381],[-71.4825086, 42.4740944],[-71.48254799999999, 42.4741176],[-71.48258079999999, 42.4740871],[-71.482693, 42.474153],[-71.4826705, 42.474174],[-71.4826948, 42.4741882],[-71.4827049, 42.4741788],[-71.48278620000001, 42.4742266],[-71.4827293, 42.4742797],[-71.48269620000001, 42.4742602],[-71.4826771, 42.4742781],[-71.48260209999999, 42.474234],[-71.482624, 42.4742136],[-71.4825239, 42.4741548]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48348369999999, 42.4762825],[-71.4835386, 42.4761671],[-71.48362659999999, 42.4761901],[-71.4836069, 42.4762315],[-71.4836249, 42.4762362],[-71.48363000000001, 42.4762255],[-71.483643, 42.4762289],[-71.4836371, 42.4762412],[-71.48384540000001, 42.4762954],[-71.4838024, 42.4763858],[-71.4837326, 42.4763676],[-71.4837395, 42.4763532],[-71.4836414, 42.4763277],[-71.4836311, 42.4763493],[-71.4835882, 42.4763381],[-71.4836002, 42.4763129],[-71.48348369999999, 42.4762825]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:24Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4674679, 42.4859744],[-71.46748289999999, 42.4859631],[-71.467493, 42.4859704],[-71.46750369999999, 42.4859623],[-71.46753699999999, 42.4859864],[-71.4675893, 42.4859473],[-71.4676539, 42.4859939],[-71.4675774, 42.4860565],[-71.4676001, 42.4860712],[-71.4675474, 42.4861157],[-71.4675319, 42.4861051],[-71.46747790000001, 42.4861455],[-71.46740440000001, 42.4860911],[-71.467457, 42.4860513],[-71.46739959999999, 42.4860097],[-71.4674569, 42.4859664],[-71.4674679, 42.4859744]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"3B","addr:state":"MA","source:datetime":"2014-11-08T14:01:44Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.454742, 42.4961292],[-71.4548111, 42.496074],[-71.4548307, 42.4960875],[-71.4548566, 42.4960668],[-71.4549373, 42.4961222],[-71.4548592, 42.4961845],[-71.45490390000001, 42.4962152],[-71.4548593, 42.4962508],[-71.45481340000001, 42.4962193],[-71.4547989, 42.4962309],[-71.45478060000001, 42.4962183],[-71.4547281, 42.4962602],[-71.4546328, 42.4961948],[-71.45469610000001, 42.4961443],[-71.45472700000001, 42.4961655],[-71.4547584, 42.4961404],[-71.454742, 42.4961292]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4838452, 42.4753427],[-71.48395739999999, 42.475321],[-71.4839987, 42.4754379],[-71.48407950000001, 42.4754222],[-71.4841139, 42.4755196],[-71.4840729, 42.4755275],[-71.484078, 42.4755418],[-71.48404669999999, 42.4755478],[-71.484042, 42.4755345],[-71.4840038, 42.4755419],[-71.4840189, 42.4755846],[-71.4839435, 42.4755992],[-71.4839046, 42.4754894],[-71.4838792, 42.4754944],[-71.4838546, 42.4754246],[-71.48387289999999, 42.4754211],[-71.4838452, 42.4753427]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:23Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4748171, 42.4909147],[-71.47497559999999, 42.490852],[-71.475027, 42.4909233],[-71.4749413, 42.4909572],[-71.47493540000001, 42.4909491],[-71.4748991, 42.4909634],[-71.4749113, 42.4909804],[-71.47487529999999, 42.4909946],[-71.4748661, 42.4909818],[-71.4748268, 42.4909973],[-71.4748168, 42.4909833],[-71.47472689999999, 42.4910188],[-71.4746882, 42.4909652],[-71.4747311, 42.4909483],[-71.4747115, 42.4909211],[-71.47479730000001, 42.4908872],[-71.4748171, 42.4909147]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:22Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.479499, 42.478247],[-71.47963540000001, 42.4782089],[-71.47966649999999, 42.4782697],[-71.4796552, 42.4782729],[-71.4796722, 42.4783062],[-71.4796319, 42.4783175],[-71.4796159, 42.4782862],[-71.4795704, 42.4782989],[-71.4795515, 42.478262],[-71.4795137, 42.4782726],[-71.4795388, 42.4783219],[-71.4794332, 42.4783514],[-71.4793584, 42.4782051],[-71.4794445, 42.4781811],[-71.47946349999999, 42.4782184],[-71.4794818, 42.4782133],[-71.479499, 42.478247]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:21Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47456680000001, 42.4910165],[-71.47460049999999, 42.4910509],[-71.47455480000001, 42.4910754],[-71.47458020000001, 42.4911014],[-71.4745175, 42.491135],[-71.4745288, 42.4911466],[-71.47448009999999, 42.4911726],[-71.4744364, 42.4911279],[-71.4743914, 42.491152],[-71.4744106, 42.4911717],[-71.474341, 42.491209],[-71.4742947, 42.4911616],[-71.47436039999999, 42.4911265],[-71.47433340000001, 42.4910989],[-71.47439300000001, 42.491067],[-71.47442030000001, 42.491095],[-71.47456680000001, 42.4910165]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4751749, 42.4718245],[-71.4752383, 42.4718247],[-71.4752381, 42.4718639],[-71.4752044, 42.4718638],[-71.47520400000001, 42.4719401],[-71.4752271, 42.4719402],[-71.4752269, 42.471981],[-71.4751661, 42.4719809],[-71.4751662, 42.4719601],[-71.4750897, 42.4719599],[-71.4750899, 42.471921],[-71.47504790000001, 42.4719209],[-71.4750481, 42.471879],[-71.475093, 42.4718791],[-71.4750931, 42.4718449],[-71.4751748, 42.4718451],[-71.4751749, 42.4718245]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46979760000001, 42.4965072],[-71.46971449999999, 42.496525],[-71.4696568, 42.4963972],[-71.46975279999999, 42.4963756],[-71.4697595, 42.4963927],[-71.4699554, 42.4963507],[-71.4699809, 42.496416],[-71.4701309, 42.4963838],[-71.47016859999999, 42.4964802],[-71.469982, 42.4965201],[-71.4699918, 42.4965451],[-71.4699633, 42.4965512],[-71.469955, 42.4965298],[-71.46989550000001, 42.4965425],[-71.4698891, 42.4965261],[-71.46981150000001, 42.4965427],[-71.46979760000001, 42.4965072]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:20Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4765046, 42.4891536],[-71.47648719999999, 42.489149],[-71.4764959, 42.4891309],[-71.4765173, 42.4891366],[-71.4765348, 42.4891002],[-71.4766149, 42.4891213],[-71.4765732, 42.489208],[-71.4765551, 42.4892032],[-71.4765397, 42.4892353],[-71.4765505, 42.4892382],[-71.4765328, 42.4892751],[-71.4764515, 42.4892537],[-71.47646810000001, 42.4892192],[-71.4764948, 42.4892262],[-71.47650969999999, 42.4891952],[-71.4764874, 42.4891893],[-71.4765046, 42.4891536]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47733340000001, 42.4921054],[-71.47739350000001, 42.4920433],[-71.47744179999999, 42.4920689],[-71.47745519999999, 42.492055],[-71.47748730000001, 42.492072],[-71.4774766, 42.492083],[-71.4775518, 42.4921228],[-71.477526, 42.4921495],[-71.47759979999999, 42.4921887],[-71.4775362, 42.4922544],[-71.4774572, 42.4922125],[-71.4774863, 42.4921824],[-71.477411, 42.4921424],[-71.4773869, 42.4921672],[-71.47733150000001, 42.4921378],[-71.4773529, 42.4921158],[-71.47733340000001, 42.4921054]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46926879999999, 42.4932671],[-71.4692507, 42.4932575],[-71.4693053, 42.4932007],[-71.4693827, 42.4932416],[-71.4693276, 42.4932988],[-71.4693099, 42.4932895],[-71.4692771, 42.4933236],[-71.4693351, 42.4933542],[-71.4692594, 42.4934329],[-71.4692108, 42.4934073],[-71.4691644, 42.4934555],[-71.46908519999999, 42.4934138],[-71.4691318, 42.4933654],[-71.46917790000001, 42.4933898],[-71.4692494, 42.4933155],[-71.4692313, 42.493306],[-71.46926879999999, 42.4932671]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:19Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4699892, 42.4861145],[-71.4700117, 42.4861212],[-71.4700337, 42.4860808],[-71.4700952, 42.4860992],[-71.4700737, 42.4861386],[-71.4700864, 42.4861424],[-71.47004459999999, 42.4862189],[-71.47006500000001, 42.486225],[-71.47004250000001, 42.4862662],[-71.47002689999999, 42.4862615],[-71.4700004, 42.48631],[-71.46991800000001, 42.4862843],[-71.46995, 42.4862257],[-71.469962, 42.4862293],[-71.4699767, 42.4862024],[-71.4699462, 42.4861933],[-71.4699892, 42.4861145]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Spruce Street","addr:city":"Acton","building":"yes","addr:housenumber":"10","addr:state":"MA","source:datetime":"2015-01-23T23:15:58Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.47262600000001, 42.4766345],[-71.47260009999999, 42.4765858],[-71.4726423, 42.4765734],[-71.4726189, 42.4765295],[-71.4727379, 42.4764947],[-71.4727633, 42.4765424],[-71.4728479, 42.4765176],[-71.47287350000001, 42.4765656],[-71.4728681, 42.4765828],[-71.47283470000001, 42.4765924],[-71.4728259, 42.4765766],[-71.47278489999999, 42.4765886],[-71.4728045, 42.4766294],[-71.4726883, 42.4766633],[-71.4726666, 42.4766226],[-71.47262600000001, 42.4766345]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4618029, 42.482038],[-71.46175959999999, 42.4820237],[-71.461708, 42.482003],[-71.46177590000001, 42.48191],[-71.4618567, 42.4819424],[-71.4618782, 42.481913],[-71.4619666, 42.4819484],[-71.46195539999999, 42.4819636],[-71.46203749999999, 42.4819965],[-71.4620207, 42.4820194],[-71.4620817, 42.4820438],[-71.4621283, 42.482083],[-71.4620615, 42.4821218],[-71.46200279999999, 42.4820767],[-71.46181869999999, 42.4820154],[-71.4618029, 42.482038]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:16Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4626099, 42.4829352],[-71.4626677, 42.4829557],[-71.46267, 42.4829345],[-71.46280369999999, 42.4829424],[-71.4628019, 42.4829594],[-71.4628534, 42.4829625],[-71.4628495, 42.4829988],[-71.4628617, 42.4829995],[-71.46286069999999, 42.4830086],[-71.4628469, 42.4830078],[-71.4628395, 42.4830759],[-71.4627819, 42.4830725],[-71.4627866, 42.4830286],[-71.4626605, 42.4830211],[-71.4625783, 42.4830008],[-71.4626099, 42.4829352]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.48064479999999, 42.4729201],[-71.4804895, 42.4728628],[-71.4805415, 42.4727857],[-71.48058090000001, 42.4728003],[-71.480587, 42.4727913],[-71.4806382, 42.4728102],[-71.4806315, 42.4728202],[-71.4807028, 42.4728465],[-71.4807096, 42.4728536],[-71.48072639999999, 42.4728533],[-71.4807629, 42.4728877],[-71.48077240000001, 42.4729986],[-71.4806758, 42.4729997],[-71.4806703, 42.4729152],[-71.4806535, 42.4729073],[-71.48064479999999, 42.4729201]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:15Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4630403, 42.4740494],[-71.46304360000001, 42.4739686],[-71.4630279, 42.4739019],[-71.4631212, 42.4738902],[-71.4631361, 42.4739908],[-71.4631601, 42.4739919],[-71.463157, 42.4741356],[-71.46311439999999, 42.4741351],[-71.46311439999999, 42.474189],[-71.4630985, 42.4741998],[-71.4630571, 42.4742007],[-71.46304240000001, 42.4741873],[-71.4630406, 42.4741696],[-71.4629985, 42.4741519],[-71.4630006, 42.4740663],[-71.4630403, 42.4740494]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4645474, 42.4795068],[-71.464642, 42.479402],[-71.4646236, 42.4793929],[-71.4646508, 42.4793627],[-71.4647525, 42.479413],[-71.46473020000001, 42.4794377],[-71.4647453, 42.4794452],[-71.464669, 42.4795299],[-71.464652, 42.4795215],[-71.4646193, 42.4795579],[-71.4645848, 42.4795408],[-71.46454060000001, 42.4795898],[-71.4644614, 42.4795507],[-71.46451519999999, 42.4794909],[-71.4645474, 42.4795068]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:14Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46342540000001, 42.4730771],[-71.4634248, 42.4730579],[-71.4634594, 42.4730574],[-71.46345820000001, 42.4730129],[-71.46354719999999, 42.4730115],[-71.4635487, 42.4730682],[-71.46359959999999, 42.4730674],[-71.4636009, 42.4731139],[-71.46354460000001, 42.4731148],[-71.4635473, 42.4732093],[-71.46343830000001, 42.473211],[-71.4634355, 42.4731134],[-71.4634595, 42.473113],[-71.4634585, 42.4730766],[-71.46342540000001, 42.4730771]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:13Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45146029999999, 42.4876259],[-71.4514489, 42.4876163],[-71.45144000000001, 42.4876025],[-71.4513637, 42.4875807],[-71.4513966, 42.4875161],[-71.4516314, 42.4875815],[-71.4516201, 42.4876038],[-71.4516361, 42.4876082],[-71.4516191, 42.4876417],[-71.4516038, 42.4876374],[-71.4515806, 42.487683],[-71.451487, 42.4876569],[-71.4514965, 42.4876381],[-71.4514798, 42.4876348],[-71.45146029999999, 42.4876259]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4790431, 42.4920133],[-71.4789811, 42.4919885],[-71.4789897, 42.4919767],[-71.4789454, 42.4919589],[-71.4789564, 42.4919439],[-71.478888, 42.4919165],[-71.4788781, 42.4919302],[-71.4787487, 42.4918784],[-71.47882250000001, 42.4917776],[-71.47913079999999, 42.4919011],[-71.4791458, 42.4918806],[-71.4792199, 42.4919103],[-71.4791318, 42.4920307],[-71.4790534, 42.4919993],[-71.4790431, 42.4920133]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4609105, 42.4800499],[-71.4608832, 42.4800087],[-71.4609368, 42.4799892],[-71.4609107, 42.4799498],[-71.46092280000001, 42.4799454],[-71.46087009999999, 42.4798659],[-71.4609675, 42.4798305],[-71.461029, 42.4799234],[-71.4610163, 42.479928],[-71.4610417, 42.4799663],[-71.4610303, 42.4799704],[-71.4610673, 42.4800262],[-71.4609809, 42.4800576],[-71.4609631, 42.4800308],[-71.4609105, 42.4800499]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.478983, 42.476907],[-71.47900799999999, 42.4768319],[-71.4790536, 42.4768402],[-71.4790591, 42.4768238],[-71.4790924, 42.4768298],[-71.47908889999999, 42.4768404],[-71.47914729999999, 42.4768511],[-71.4791345, 42.4768895],[-71.4791749, 42.4768969],[-71.479161, 42.4769386],[-71.4790562, 42.4769195],[-71.479049, 42.476941],[-71.47899580000001, 42.4769313],[-71.4790027, 42.4769105],[-71.478983, 42.476907]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:12Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4622657, 42.4786826],[-71.46227260000001, 42.4785699],[-71.46228549999999, 42.4785704],[-71.4622925, 42.4784574],[-71.4623825, 42.4784605],[-71.4623788, 42.4785207],[-71.46243749999999, 42.4785227],[-71.46243459999999, 42.4785695],[-71.4623826, 42.4785677],[-71.46237530000001, 42.4786858],[-71.4623307, 42.4786842],[-71.4623301, 42.4786932],[-71.46231160000001, 42.4786926],[-71.46231210000001, 42.4786842],[-71.4622657, 42.4786826]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4840574, 42.4758337],[-71.48406319999999, 42.4758104],[-71.48402969999999, 42.4757475],[-71.48411179999999, 42.4757206],[-71.48413119999999, 42.4757563],[-71.48413840000001, 42.4757867],[-71.48413909999999, 42.4758027],[-71.4841287, 42.4758777],[-71.4841918, 42.4758891],[-71.4841693, 42.4759793],[-71.484004, 42.4759567],[-71.4840195, 42.4758943],[-71.4839885, 42.4758901],[-71.4840044, 42.4758264],[-71.4840574, 42.4758337]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.46061760000001, 42.4792572],[-71.4606114, 42.4792222],[-71.4605667, 42.4792266],[-71.4605602, 42.4791902],[-71.4606036, 42.479186],[-71.4605811, 42.4790597],[-71.4606886, 42.4790492],[-71.46070640000001, 42.4791487],[-71.4606934, 42.47915],[-71.4606989, 42.4791812],[-71.4607383, 42.4791774],[-71.4607627, 42.479314],[-71.4606758, 42.4793224],[-71.4606634, 42.4792528],[-71.46061760000001, 42.4792572]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Lincoln Drive","addr:city":"Acton","building":"yes","addr:housenumber":"1","addr:state":"MA","source:datetime":"2014-11-08T14:01:40Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4541856, 42.4963805],[-71.4540788, 42.4963258],[-71.4541266, 42.4962748],[-71.4541632, 42.4962936],[-71.4541846, 42.4962707],[-71.4541621, 42.4962591],[-71.4542312, 42.4961853],[-71.4543379, 42.49624],[-71.454302, 42.4962783],[-71.4543494, 42.4963026],[-71.4543101, 42.4963446],[-71.4542993, 42.4963391],[-71.45425969999999, 42.4963814],[-71.4542091, 42.4963554],[-71.4541856, 42.4963805]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4627206, 42.4783565],[-71.462705, 42.4782633],[-71.46264069999999, 42.4782692],[-71.46263159999999, 42.4782146],[-71.46269909999999, 42.4782084],[-71.462701, 42.47822],[-71.462784, 42.4782124],[-71.46279, 42.4782482],[-71.4628159, 42.4782458],[-71.4628329, 42.4783472],[-71.46278580000001, 42.4783515],[-71.4627873, 42.4783602],[-71.46277019999999, 42.4783618],[-71.4627686, 42.4783521],[-71.4627206, 42.4783565]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:10Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4641819, 42.4799635],[-71.46413029999999, 42.4799341],[-71.4641596, 42.4799059],[-71.46414900000001, 42.4798999],[-71.46421309999999, 42.4798384],[-71.4643318, 42.4799062],[-71.4643057, 42.4799313],[-71.4644124, 42.4799923],[-71.464349, 42.4800532],[-71.46428210000001, 42.480015],[-71.4642948, 42.4800028],[-71.4642477, 42.4799759],[-71.4642576, 42.4799664],[-71.4642082, 42.4799382],[-71.4641819, 42.4799635]]]}}, +{"type":"Feature","properties":{"addr:country":"US","addr:street":"Bulette Road","addr:city":"Acton","building":"yes","addr:housenumber":"16","addr:state":"MA","source:datetime":"2015-01-20T23:47:47Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.468914, 42.4990161],[-71.4689561, 42.4990395],[-71.46907229999999, 42.4989111],[-71.4691503, 42.4989497],[-71.4690985, 42.499007],[-71.4691128, 42.4990141],[-71.46908759999999, 42.4990402],[-71.4691048, 42.4990493],[-71.469082, 42.499073],[-71.4690676, 42.4990654],[-71.469044, 42.4990899],[-71.46902780000001, 42.4990809],[-71.468996, 42.4991121],[-71.4688814, 42.4990482],[-71.468914, 42.4990161]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:05Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4691322, 42.489444],[-71.4691631, 42.4893841],[-71.46918549999999, 42.4893905],[-71.4691942, 42.4893737],[-71.4692288, 42.4893834],[-71.4692336, 42.4893741],[-71.4692914, 42.4893904],[-71.4692855, 42.4894018],[-71.4693256, 42.4894132],[-71.469291, 42.4894802],[-71.4692567, 42.4894705],[-71.4692266, 42.489529],[-71.4691304, 42.4895018],[-71.46915660000001, 42.4894509],[-71.4691322, 42.489444]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:04Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.4673434, 42.4893001],[-71.467398, 42.489228],[-71.46742399999999, 42.4892388],[-71.4674541, 42.4891991],[-71.4675398, 42.4892347],[-71.46751209999999, 42.4892713],[-71.4675455, 42.4892852],[-71.4675137, 42.4893273],[-71.4675361, 42.4893366],[-71.4675118, 42.4893687],[-71.4674522, 42.4893438],[-71.4674236, 42.4893815],[-71.4673574, 42.489354],[-71.4673852, 42.4893174],[-71.4673434, 42.4893001]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:03Z"},"geometry": {"type":"Polygon","coordinates": [[[-71.45173870000001, 42.4756196],[-71.4517311, 42.4756095],[-71.451705, 42.4756203],[-71.45164149999999, 42.4755355],[-71.45166159999999, 42.4755273],[-71.4516476, 42.4755087],[-71.4518053, 42.4754439],[-71.4518155, 42.4754576],[-71.45188210000001, 42.4754303],[-71.4519547, 42.4755272],[-71.45189120000001, 42.4755541],[-71.4519233, 42.4755969],[-71.45181839999999, 42.4756392],[-71.45178850000001, 42.4755992],[-71.45173870000001, 42.4756196]]]}}, +{"type":"Feature","properties":{"building":"yes","source:datetime":"2012-12-15T01:49:03Z"},"geometry": {"type":"LineString","coordinates": [[-71.45132959999999, 42.4718556],[-71.45126209999999, 42.4718288],[-71.45124920000001, 42.4718465],[-71.4511704, 42.4718152],[-71.451165, 42.4718228],[-71.4511092, 42.4718006]]}}, +{"type":"Feature","properties":{"leisure":"pitch","sport":"soccer","source:datetime":"2014-03-31T02:11:12Z"},"geometry": {"type":"GeometryCollection","geometries": [{"type":"Polygon","coordinates": [[[-71.45388819999999, 42.4795498],[-71.453664, 42.4786716],[-71.4529775, 42.4787485],[-71.4532458, 42.4796408],[-71.45388819999999, 42.4795498]]]},{"type":"Polygon","coordinates": [[[-71.4534657, 42.479716],[-71.45350860000001, 42.4799692],[-71.45341209999999, 42.4802264],[-71.45334769999999, 42.4802976],[-71.45251089999999, 42.4804638],[-71.45232849999999, 42.4803253],[-71.4515614, 42.4792966],[-71.45129849999999, 42.4788021],[-71.4513951, 42.4786161],[-71.4515882, 42.4785212],[-71.45191010000001, 42.4784658],[-71.4521246, 42.4785093],[-71.45262889999999, 42.4784144],[-71.4528005, 42.4786715],[-71.4529615, 42.4786953],[-71.4529775, 42.4787485],[-71.4532458, 42.4796408],[-71.4534657, 42.479716]]]}]}}, +{"type":"Feature","properties":{"restriction":"only_right_turn","source:datetime":"2013-12-27T16:52:14Z"},"geometry": {"type":"GeometryCollection","geometries": [{"type":"LineString","coordinates": [[-71.471169, 42.476039],[-71.47060999999999, 42.476026],[-71.470069, 42.476008],[-71.4693, 42.476],[-71.468417, 42.475973],[-71.4674, 42.475924],[-71.466774, 42.475887],[-71.46657399999999, 42.475874],[-71.46595499999999, 42.475833],[-71.464855, 42.475769],[-71.464628, 42.475754],[-71.464232, 42.475728],[-71.464027, 42.475715],[-71.463813, 42.475701],[-71.46355699999999, 42.475685],[-71.46274699999999, 42.475637],[-71.462166, 42.475595],[-71.461583, 42.475549],[-71.46156000000001, 42.475547],[-71.46087199999999, 42.475495],[-71.460329, 42.475444],[-71.459784, 42.475404],[-71.45902700000001, 42.475341],[-71.458575, 42.475317],[-71.458125, 42.47528],[-71.457649, 42.475227],[-71.45643699999999, 42.475097],[-71.455956, 42.475058],[-71.455736, 42.475038],[-71.455125, 42.474991],[-71.454544, 42.474945],[-71.45376400000001, 42.474867],[-71.453422, 42.474833],[-71.4533126, 42.4748231],[-71.4529064, 42.4747864],[-71.452382, 42.474739],[-71.4519975, 42.4746955],[-71.45177200000001, 42.47467],[-71.45102079999999, 42.4745852],[-71.4507292, 42.4745521]]}]}}, +{"type":"Feature","properties":{"access":"customers","amenity":"parking","source:datetime":"2013-09-05T23:31:03Z"},"geometry": {"type":"GeometryCollection","geometries": [{"type":"Polygon","coordinates": [[[-71.4568448, 42.4728027],[-71.45699639999999, 42.4733137],[-71.4569516, 42.47337],[-71.45637139999999, 42.4734652],[-71.4563223, 42.4732545],[-71.4564002, 42.4732401],[-71.4563191, 42.4728709],[-71.4568448, 42.4728027]]]},{"type":"Polygon","coordinates": [[[-71.458037, 42.4726415],[-71.4575736, 42.4728008],[-71.4570123, 42.4729314],[-71.4568775, 42.472468],[-71.457249, 42.4723966],[-71.4575103, 42.4723088],[-71.45763599999999, 42.4723809],[-71.4576871, 42.4723646],[-71.45772789999999, 42.472435],[-71.4578412, 42.472484],[-71.45788, 42.472531],[-71.4579371, 42.4725116],[-71.458037, 42.4726415]]]}]}}, +{"type":"Feature","properties":{"route":"train","operator":"MBTA","from":"Boston","to":"Fitchburg","name":"Fitchburg/South Acton Line","wikipedia":"en:Fitchburg Line","source:datetime":"2015-01-17T09:07:09Z"},"geometry": {"type":"GeometryCollection","geometries": [{"type":"LineString","coordinates": [[-71.4723865, 42.4741861],[-71.4729382, 42.4755277],[-71.4731836, 42.4761161],[-71.4735768, 42.476953],[-71.47366580000001, 42.4771424],[-71.4737729, 42.4773528],[-71.4739924, 42.4777675],[-71.4741705, 42.4781018],[-71.4743694, 42.4784668],[-71.47452319999999, 42.4787413],[-71.4746524, 42.4789678],[-71.4747873, 42.4791912],[-71.4750717, 42.479638],[-71.47522669999999, 42.4798678],[-71.47538779999999, 42.4800953],[-71.47572390000001, 42.4805507],[-71.475887, 42.48076],[-71.4760552, 42.4809652],[-71.476398, 42.4813732],[-71.4767958, 42.4818263]]},{"type":"LineString","coordinates": [[-71.4767958, 42.4818263],[-71.4768308, 42.481868]]},{"type":"LineString","coordinates": [[-71.4768308, 42.481868],[-71.4773763, 42.4824917],[-71.4777867, 42.4829708],[-71.47883710000001, 42.4841644],[-71.48021060000001, 42.4857329],[-71.4826288, 42.4884724],[-71.4832468, 42.4893585],[-71.4836931, 42.4900737]]},{"type":"LineString","coordinates": [[-71.483643, 42.4900853],[-71.4831913, 42.4893697],[-71.4827786, 42.4887981],[-71.4824691, 42.4884095],[-71.48166879999999, 42.4874821],[-71.480191, 42.4857878],[-71.4795859, 42.4850928],[-71.4789322, 42.4843396],[-71.4785584, 42.4839148],[-71.4781448, 42.4834583],[-71.4777005, 42.4829524],[-71.47734869999999, 42.4825336],[-71.47678430000001, 42.4818866]]},{"type":"LineString","coordinates": [[-71.47678430000001, 42.4818866],[-71.47675169999999, 42.4818489]]},{"type":"LineString","coordinates": [[-71.47675169999999, 42.4818489],[-71.4766403, 42.4817241],[-71.4761862, 42.4811912],[-71.4757664, 42.4806872],[-71.47536119999999, 42.4801363],[-71.4751122, 42.4797799],[-71.4748584, 42.4794],[-71.47459480000001, 42.4789644],[-71.47434579999999, 42.4785377],[-71.4741456, 42.4781579],[-71.47396500000001, 42.477814],[-71.473738, 42.4773946],[-71.4735269, 42.4769731],[-71.4731367, 42.4761148],[-71.4729078, 42.4755957],[-71.4727718, 42.4752635],[-71.472589, 42.4748089],[-71.4724148, 42.4743748],[-71.4723001, 42.4741084],[-71.4721471, 42.4737228],[-71.4720047, 42.4733968],[-71.47182189999999, 42.4729862],[-71.4716732, 42.4726413],[-71.47142460000001, 42.472052]]}]}}, +{"type":"Feature","properties":{"network":"US:MA","route":"road","ref":"111","source:datetime":"2014-11-16T04:49:05Z"},"geometry": {"type":"GeometryCollection","geometries": [{"type":"LineString","coordinates": [[-71.48470279999999, 42.4776146],[-71.484374, 42.4775573],[-71.484247, 42.4775328],[-71.483932, 42.477468],[-71.483215, 42.477342],[-71.482809, 42.477269],[-71.481606, 42.477075],[-71.480662, 42.47692],[-71.48063399999999, 42.476915],[-71.480047, 42.47681],[-71.479876, 42.476779],[-71.479152, 42.476649],[-71.478718, 42.476567],[-71.478571, 42.476532],[-71.47794, 42.47643],[-71.47782100000001, 42.476411],[-71.47716200000001, 42.476296],[-71.476832, 42.476249],[-71.476586, 42.476224],[-71.47653800000001, 42.476218],[-71.47628899999999, 42.476202],[-71.475927, 42.476182],[-71.475728, 42.476172],[-71.475656, 42.476168],[-71.475516, 42.476168]]},{"type":"LineString","coordinates": [[-71.471169, 42.476039],[-71.47060999999999, 42.476026],[-71.470069, 42.476008],[-71.4693, 42.476],[-71.468417, 42.475973],[-71.4674, 42.475924],[-71.466774, 42.475887],[-71.46657399999999, 42.475874],[-71.46595499999999, 42.475833],[-71.464855, 42.475769],[-71.464628, 42.475754],[-71.464232, 42.475728],[-71.464027, 42.475715],[-71.463813, 42.475701],[-71.46355699999999, 42.475685],[-71.46274699999999, 42.475637],[-71.462166, 42.475595],[-71.461583, 42.475549],[-71.46156000000001, 42.475547],[-71.46087199999999, 42.475495],[-71.460329, 42.475444],[-71.459784, 42.475404],[-71.45902700000001, 42.475341],[-71.458575, 42.475317],[-71.458125, 42.47528],[-71.457649, 42.475227],[-71.45643699999999, 42.475097],[-71.455956, 42.475058],[-71.455736, 42.475038],[-71.455125, 42.474991],[-71.454544, 42.474945],[-71.45376400000001, 42.474867],[-71.453422, 42.474833],[-71.4533126, 42.4748231],[-71.4529064, 42.4747864],[-71.452382, 42.474739],[-71.4519975, 42.4746955],[-71.45177200000001, 42.47467],[-71.45102079999999, 42.4745852],[-71.4507292, 42.4745521]]},{"type":"LineString","coordinates": [[-71.475516, 42.476168],[-71.475253, 42.476168],[-71.4747, 42.476152],[-71.474113, 42.476134],[-71.4735378, 42.4761234],[-71.4733988, 42.4761203],[-71.4731836, 42.4761161],[-71.4731367, 42.4761148],[-71.472905, 42.4761084],[-71.4727378, 42.4761035],[-71.4725096, 42.476097],[-71.472272, 42.4760871],[-71.4717129, 42.476061],[-71.471169, 42.476039]]}]}}, +{"type":"Feature","properties":{"network":"US:MA","route":"road","ref":"27","source:datetime":"2014-12-28T17:30:10Z"},"geometry": {"type":"GeometryCollection","geometries": [{"type":"LineString","coordinates": [[-71.454933, 42.47211],[-71.454685, 42.472744],[-71.45440600000001, 42.473444],[-71.454379, 42.473514],[-71.45427599999999, 42.473791],[-71.454223, 42.4739293],[-71.454142, 42.474141],[-71.45404619999999, 42.4743868],[-71.454013, 42.474472],[-71.45395190000001, 42.4745841],[-71.45391600000001, 42.47465],[-71.45376400000001, 42.474867],[-71.45370699999999, 42.474949],[-71.45355000000001, 42.475126],[-71.45348180000001, 42.4751887],[-71.45332500000001, 42.475333],[-71.45325939999999, 42.4753895],[-71.453158, 42.475477],[-71.45307010000001, 42.4755322],[-71.45294, 42.475614],[-71.45269999999999, 42.475752],[-71.4526365, 42.4757833],[-71.452298, 42.47595],[-71.451885, 42.476142],[-71.45151, 42.476316],[-71.451054, 42.476524],[-71.4504781, 42.4768382]]}]}}, +{"type":"Feature","properties":{"network":"US:MA","route":"road","name":"MA 2","ref":"2","wikipedia":"en:Massachusetts_Route_2","source:datetime":"2015-02-11T20:48:24Z"},"geometry": {"type":"GeometryCollection","geometries": [{"type":"Point","coordinates": [-71.47388599999999, 42.499658]},{"type":"LineString","coordinates": [[-71.47388599999999, 42.499658],[-71.473079, 42.499215],[-71.47229799999999, 42.498785],[-71.471473, 42.498327],[-71.470473, 42.497765],[-71.469497, 42.497225],[-71.468554, 42.4967],[-71.46686200000001, 42.495767],[-71.466165, 42.495372],[-71.464862, 42.494655],[-71.464234, 42.494282],[-71.463595, 42.493876],[-71.46316400000001, 42.493586],[-71.462649, 42.493217],[-71.462234, 42.492906],[-71.461742, 42.492497],[-71.46133500000001, 42.492156],[-71.460553, 42.491485],[-71.45985400000001, 42.490891],[-71.459344, 42.490462],[-71.458995, 42.490164],[-71.458555, 42.489784],[-71.45798000000001, 42.489296],[-71.457196, 42.488637],[-71.456475, 42.488013],[-71.455793, 42.487433],[-71.455427, 42.487092],[-71.45501400000001, 42.486672],[-71.454505, 42.486138],[-71.454109, 42.485684],[-71.453726, 42.485205],[-71.453361, 42.484737],[-71.453159, 42.484437],[-71.452375, 42.483292],[-71.452072, 42.482857],[-71.451275, 42.4817]]},{"type":"LineString","coordinates": [[-71.451162, 42.481768],[-71.451677, 42.482521],[-71.45221100000001, 42.483311],[-71.452674, 42.483976],[-71.453112, 42.484615],[-71.453709, 42.485424],[-71.45424800000001, 42.486048],[-71.45447900000001, 42.486302],[-71.454959, 42.48681],[-71.455355, 42.487191],[-71.455716, 42.487525],[-71.456264, 42.488004],[-71.456968, 42.488599],[-71.45759099999999, 42.489133],[-71.458277, 42.489708],[-71.458726, 42.490103],[-71.459723, 42.490951],[-71.46038799999999, 42.49151],[-71.46131200000001, 42.492301],[-71.46155899999999, 42.492519],[-71.462324, 42.49313],[-71.462806, 42.493482],[-71.46300100000001, 42.493622],[-71.463294, 42.493827],[-71.46367600000001, 42.494072],[-71.46419400000001, 42.494396],[-71.464653, 42.494672],[-71.465383, 42.49508],[-71.466667, 42.495789],[-71.468586, 42.496858],[-71.46968, 42.497463],[-71.47036799999999, 42.497847],[-71.471147, 42.498279],[-71.472568, 42.499073],[-71.472742, 42.499168],[-71.472939, 42.499278],[-71.473395, 42.499534]]}]}}] +} \ No newline at end of file diff --git a/vendor/github.com/tidwall/geojson/test_files/simple_points.geojson b/vendor/github.com/tidwall/geojson/test_files/simple_points.geojson new file mode 100644 index 00000000..ea66377e --- /dev/null +++ b/vendor/github.com/tidwall/geojson/test_files/simple_points.geojson @@ -0,0 +1,77 @@ +{ + "type":"FeatureCollection", + "features":[ + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]},"properties":null,"id":1234}, + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3]},"properties":null,"id":1234}, + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3,4]},"id":"hello"}, + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4]}}, + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4,5,6]}}, + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4,5,6,7,8]}}, + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2],"bbox":[1,2,3,4,5,6,7,8]}}, + {"type":"Feature","geometry":{"type":"Point","coordinates":[1,2]}}, + {"type":"Feature","geometry":{"type":"MultiPoint","coordinates":[]}}, + {"type":"Feature","geometry":{"type":"MultiPoint","coordinates":[[1,2]]}}, + {"type":"Feature","geometry":{"type":"MultiPoint","coordinates":[[1,2,3]]}}, + {"type":"Feature","geometry":{"type":"MultiPoint","coordinates":[[1,2,3,4]]}}, + {"type":"Feature","geometry":{"type":"MultiPoint","coordinates":[[1,2,3,4],[1,2],[1,2,3]]}}, + {"type":"Feature","geometry":{"type":"MultiPoint","coordinates":[[1,2,3,4],[1,2],[1,2,3]],"bbox":[1,2,3,4]},"bbox":[2,3,4,5]}, + {"type":"Feature","geometry":{"type":"LineString","coordinates":[]}}, + {"type":"Feature","geometry":{"type":"LineString","coordinates":[[1,2]]}}, + {"type":"Feature","geometry":{"type":"LineString","coordinates":[[1,2,3]]}}, + {"type":"Feature","geometry":{"type":"LineString","coordinates":[[1,2,3,4]]}}, + {"type":"Feature","geometry":{"type":"LineString","coordinates":[[1,2,3,4],[1,2,0,0],[1,2,3,0]]}}, + {"type":"Feature","geometry":{"type":"LineString","coordinates":[[1,2,3,4],[1,2,0,0],[1,2,3,0]],"bbox":[1,2,3,4]},"bbox":[2,3,4,5]}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[]}}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[]]}}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[1,2]]]}}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[1,2,3]]]}}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[1,2,3,4]]]}}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[1,2,3,4],[1,2,0,0],[1,2,3,0]]]}}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[1,2,3,4],[1,2,0,0],[1,2,3,0]]],"bbox":[1,2,3,4]},"bbox":[2,3,4,5]}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[1,2]],[]]}}, + {"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[1,2,3]],[[1,2,3],[4,5,6]]]}}, + {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]]}}, + {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]],"bbox":[1,2,3,4]}}, + {"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]]],"bbox":[1,2,3,4]}}, + {"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]]]}}, + {"type":"Feature","geometry":{"type":"GeometryCollection","geometries":[]}}, + {"type":"Feature","geometry":{"type":"GeometryCollection","geometries":[],"bbox":[1,2,3,4,5,6]}}, + {"type":"Feature","geometry":{"type":"GeometryCollection","geometries":[ + {"type":"Point","coordinates":[1,2]}, + {"type":"Point","coordinates":[1,2,3]}, + {"type":"Point","coordinates":[1,2,3,4]}, + {"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4]}, + {"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4,5,6]}, + {"type":"Point","coordinates":[1,2,3],"bbox":[1,2,3,4,5,6,7,8]}, + {"type":"Point","coordinates":[1,2],"bbox":[1,2,3,4,5,6,7,8]}, + {"type":"Point","coordinates":[1,2]}, + {"type":"MultiPoint","coordinates":[]}, + {"type":"MultiPoint","coordinates":[[1,2]]}, + {"type":"MultiPoint","coordinates":[[1,2,3]]}, + {"type":"MultiPoint","coordinates":[[1,2,3,4]]}, + {"type":"MultiPoint","coordinates":[[1,2,3,4],[1,2],[1,2,3]]}, + {"type":"MultiPoint","coordinates":[[1,2,3,4],[1,2],[1,2,3]],"bbox":[1,2,3,4]}, + {"type":"LineString","coordinates":[]}, + {"type":"LineString","coordinates":[[1,2]]}, + {"type":"LineString","coordinates":[[1,2,3]]}, + {"type":"LineString","coordinates":[[1,2,3,4]]}, + {"type":"LineString","coordinates":[[1,2,3,4],[1,2,0,0],[1,2,3,0]]}, + {"type":"LineString","coordinates":[[1,2,3,4],[1,2,0,0],[1,2,3,0]],"bbox":[1,2,3,4]}, + {"type":"MultiLineString","coordinates":[]}, + {"type":"MultiLineString","coordinates":[[]]}, + {"type":"MultiLineString","coordinates":[[[1,2]]]}, + {"type":"MultiLineString","coordinates":[[[1,2,3]]]}, + {"type":"MultiLineString","coordinates":[[[1,2,3,4]]]}, + {"type":"MultiLineString","coordinates":[[[1,2,3,4],[1,2,0,0],[1,2,3,0]]]}, + {"type":"MultiLineString","coordinates":[[[1,2,3,4],[1,2,0,0],[1,2,3,0]]],"bbox":[1,2,3,4]}, + {"type":"MultiLineString","coordinates":[[[1,2]],[]]}, + {"type":"MultiLineString","coordinates":[[[1,2,3]],[[1,2,3],[4,5,6]]]}, + {"type":"Polygon","coordinates":[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]]}, + {"type":"Polygon","coordinates":[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]],"bbox":[1,2,3,4]}, + {"type":"MultiPolygon","coordinates":[[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]]],"bbox":[1,2,3,4]}, + {"type":"MultiPolygon","coordinates":[[[[1,2,3],[1,2,3],[4,5,6],[1,2,3]]]]}, + {"type":"GeometryCollection","geometries":[]}, + {"type":"GeometryCollection","geometries":[],"bbox":[1,2,3,4,5,6]} + ]}} + ] +} \ No newline at end of file diff --git a/vendor/github.com/tidwall/pretty/.travis.yml b/vendor/github.com/tidwall/pretty/.travis.yml new file mode 100644 index 00000000..4f2ee4d9 --- /dev/null +++ b/vendor/github.com/tidwall/pretty/.travis.yml @@ -0,0 +1 @@ +language: go diff --git a/vendor/github.com/tidwall/pretty/LICENSE b/vendor/github.com/tidwall/pretty/LICENSE new file mode 100644 index 00000000..993b83f2 --- /dev/null +++ b/vendor/github.com/tidwall/pretty/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/tidwall/pretty/README.md b/vendor/github.com/tidwall/pretty/README.md new file mode 100644 index 00000000..d2b8864d --- /dev/null +++ b/vendor/github.com/tidwall/pretty/README.md @@ -0,0 +1,124 @@ +# Pretty +[![Build Status](https://img.shields.io/travis/tidwall/pretty.svg?style=flat-square)](https://travis-ci.org/tidwall/prettty) +[![Coverage Status](https://img.shields.io/badge/coverage-100%25-brightgreen.svg?style=flat-square)](http://gocover.io/github.com/tidwall/pretty) +[![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/tidwall/pretty) + + +Pretty is a Go package that provides [fast](#performance) methods for formatting JSON for human readability, or to compact JSON for smaller payloads. + +Getting Started +=============== + +## Installing + +To start using Pretty, install Go and run `go get`: + +```sh +$ go get -u github.com/tidwall/pretty +``` + +This will retrieve the library. + +## Pretty + +Using this example: + +```json +{"name": {"first":"Tom","last":"Anderson"}, "age":37, +"children": ["Sara","Alex","Jack"], +"fav.movie": "Deer Hunter", "friends": [ + {"first": "Janet", "last": "Murphy", "age": 44} + ]} +``` + +The following code: +```go +result = pretty.Pretty(example) +``` + +Will format the json to: + +```json +{ + "name": { + "first": "Tom", + "last": "Anderson" + }, + "age": 37, + "children": ["Sara", "Alex", "Jack"], + "fav.movie": "Deer Hunter", + "friends": [ + { + "first": "Janet", + "last": "Murphy", + "age": 44 + } + ] +} +``` + +## Color + +Color will colorize the json for outputing to the screen. + +```json +result = pretty.Color(json, nil) +``` + +Will add color to the result for printing to the terminal. +The second param is used for a customizing the style, and passing nil will use the default `pretty.TerminalStyle`. + +## Ugly + +The following code: +```go +result = pretty.Ugly(example) +``` + +Will format the json to: + +```json +{"name":{"first":"Tom","last":"Anderson"},"age":37,"children":["Sara","Alex","Jack"],"fav.movie":"Deer Hunter","friends":[{"first":"Janet","last":"Murphy","age":44}]}``` +``` + + +## Customized output + +There's a `PrettyOptions(json, opts)` function which allows for customizing the output with the following options: + +```go +type Options struct { + // Width is an max column width for single line arrays + // Default is 80 + Width int + // Prefix is a prefix for all lines + // Default is an empty string + Prefix string + // Indent is the nested indentation + // Default is two spaces + Indent string + // SortKeys will sort the keys alphabetically + // Default is false + SortKeys bool +} +``` +## Performance + +Benchmarks of Pretty alongside the builtin `encoding/json` Indent/Compact methods. +``` +BenchmarkPretty-8 1000000 1283 ns/op 720 B/op 2 allocs/op +BenchmarkUgly-8 3000000 426 ns/op 240 B/op 1 allocs/op +BenchmarkUglyInPlace-8 5000000 340 ns/op 0 B/op 0 allocs/op +BenchmarkJSONIndent-8 300000 4628 ns/op 1069 B/op 4 allocs/op +BenchmarkJSONCompact-8 1000000 2469 ns/op 758 B/op 4 allocs/op +``` + +*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.* + +## Contact +Josh Baker [@tidwall](http://twitter.com/tidwall) + +## License + +Pretty source code is available under the MIT [License](/LICENSE). + diff --git a/vendor/github.com/tidwall/pretty/pretty.go b/vendor/github.com/tidwall/pretty/pretty.go new file mode 100644 index 00000000..5b615bc7 --- /dev/null +++ b/vendor/github.com/tidwall/pretty/pretty.go @@ -0,0 +1,432 @@ +package pretty + +import ( + "sort" +) + +// Options is Pretty options +type Options struct { + // Width is an max column width for single line arrays + // Default is 80 + Width int + // Prefix is a prefix for all lines + // Default is an empty string + Prefix string + // Indent is the nested indentation + // Default is two spaces + Indent string + // SortKeys will sort the keys alphabetically + // Default is false + SortKeys bool +} + +// DefaultOptions is the default options for pretty formats. +var DefaultOptions = &Options{Width: 80, Prefix: "", Indent: " ", SortKeys: false} + +// Pretty converts the input json into a more human readable format where each +// element is on it's own line with clear indentation. +func Pretty(json []byte) []byte { return PrettyOptions(json, nil) } + +// PrettyOptions is like Pretty but with customized options. +func PrettyOptions(json []byte, opts *Options) []byte { + if opts == nil { + opts = DefaultOptions + } + buf := make([]byte, 0, len(json)) + if len(opts.Prefix) != 0 { + buf = append(buf, opts.Prefix...) + } + buf, _, _, _ = appendPrettyAny(buf, json, 0, true, + opts.Width, opts.Prefix, opts.Indent, opts.SortKeys, + 0, 0, -1) + if len(buf) > 0 { + buf = append(buf, '\n') + } + return buf +} + +// Ugly removes insignificant space characters from the input json byte slice +// and returns the compacted result. +func Ugly(json []byte) []byte { + buf := make([]byte, 0, len(json)) + return ugly(buf, json) +} + +// UglyInPlace removes insignificant space characters from the input json +// byte slice and returns the compacted result. This method reuses the +// input json buffer to avoid allocations. Do not use the original bytes +// slice upon return. +func UglyInPlace(json []byte) []byte { return ugly(json, json) } + +func ugly(dst, src []byte) []byte { + dst = dst[:0] + for i := 0; i < len(src); i++ { + if src[i] > ' ' { + dst = append(dst, src[i]) + if src[i] == '"' { + for i = i + 1; i < len(src); i++ { + dst = append(dst, src[i]) + if src[i] == '"' { + j := i - 1 + for ; ; j-- { + if src[j] != '\\' { + break + } + } + if (j-i)%2 != 0 { + break + } + } + } + } + } + } + return dst +} + +func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) { + for ; i < len(json); i++ { + if json[i] <= ' ' { + continue + } + if json[i] == '"' { + return appendPrettyString(buf, json, i, nl) + } + if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { + return appendPrettyNumber(buf, json, i, nl) + } + if json[i] == '{' { + return appendPrettyObject(buf, json, i, '{', '}', pretty, width, prefix, indent, sortkeys, tabs, nl, max) + } + if json[i] == '[' { + return appendPrettyObject(buf, json, i, '[', ']', pretty, width, prefix, indent, sortkeys, tabs, nl, max) + } + switch json[i] { + case 't': + return append(buf, 't', 'r', 'u', 'e'), i + 4, nl, true + case 'f': + return append(buf, 'f', 'a', 'l', 's', 'e'), i + 5, nl, true + case 'n': + return append(buf, 'n', 'u', 'l', 'l'), i + 4, nl, true + } + } + return buf, i, nl, true +} + +type pair struct { + kstart, kend int + vstart, vend int +} + +type byKey struct { + sorted bool + json []byte + pairs []pair +} + +func (arr *byKey) Len() int { + return len(arr.pairs) +} +func (arr *byKey) Less(i, j int) bool { + key1 := arr.json[arr.pairs[i].kstart+1 : arr.pairs[i].kend-1] + key2 := arr.json[arr.pairs[j].kstart+1 : arr.pairs[j].kend-1] + return string(key1) < string(key2) +} +func (arr *byKey) Swap(i, j int) { + arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i] + arr.sorted = true +} + +func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) { + var ok bool + if width > 0 { + if pretty && open == '[' && max == -1 { + // here we try to create a single line array + max := width - (len(buf) - nl) + if max > 3 { + s1, s2 := len(buf), i + buf, i, _, ok = appendPrettyObject(buf, json, i, '[', ']', false, width, prefix, "", sortkeys, 0, 0, max) + if ok && len(buf)-s1 <= max { + return buf, i, nl, true + } + buf = buf[:s1] + i = s2 + } + } else if max != -1 && open == '{' { + return buf, i, nl, false + } + } + buf = append(buf, open) + i++ + var pairs []pair + if open == '{' && sortkeys { + pairs = make([]pair, 0, 8) + } + var n int + for ; i < len(json); i++ { + if json[i] <= ' ' { + continue + } + if json[i] == close { + if pretty { + if open == '{' && sortkeys { + buf = sortPairs(json, buf, pairs) + } + if n > 0 { + nl = len(buf) + buf = append(buf, '\n') + } + if buf[len(buf)-1] != open { + buf = appendTabs(buf, prefix, indent, tabs) + } + } + buf = append(buf, close) + return buf, i + 1, nl, open != '{' + } + if open == '[' || json[i] == '"' { + if n > 0 { + buf = append(buf, ',') + if width != -1 { + buf = append(buf, ' ') + } + } + var p pair + if pretty { + nl = len(buf) + buf = append(buf, '\n') + if open == '{' && sortkeys { + p.kstart = i + p.vstart = len(buf) + } + buf = appendTabs(buf, prefix, indent, tabs+1) + } + if open == '{' { + buf, i, nl, _ = appendPrettyString(buf, json, i, nl) + if sortkeys { + p.kend = i + } + buf = append(buf, ':') + if pretty { + buf = append(buf, ' ') + } + } + buf, i, nl, ok = appendPrettyAny(buf, json, i, pretty, width, prefix, indent, sortkeys, tabs+1, nl, max) + if max != -1 && !ok { + return buf, i, nl, false + } + if pretty && open == '{' && sortkeys { + p.vend = len(buf) + if p.kstart > p.kend || p.vstart > p.vend { + // bad data. disable sorting + sortkeys = false + } else { + pairs = append(pairs, p) + } + } + i-- + n++ + } + } + return buf, i, nl, open != '{' +} +func sortPairs(json, buf []byte, pairs []pair) []byte { + if len(pairs) == 0 { + return buf + } + vstart := pairs[0].vstart + vend := pairs[len(pairs)-1].vend + arr := byKey{false, json, pairs} + sort.Sort(&arr) + if !arr.sorted { + return buf + } + nbuf := make([]byte, 0, vend-vstart) + for i, p := range pairs { + nbuf = append(nbuf, buf[p.vstart:p.vend]...) + if i < len(pairs)-1 { + nbuf = append(nbuf, ',') + nbuf = append(nbuf, '\n') + } + } + return append(buf[:vstart], nbuf...) +} + +func appendPrettyString(buf, json []byte, i, nl int) ([]byte, int, int, bool) { + s := i + i++ + for ; i < len(json); i++ { + if json[i] == '"' { + var sc int + for j := i - 1; j > s; j-- { + if json[j] == '\\' { + sc++ + } else { + break + } + } + if sc%2 == 1 { + continue + } + i++ + break + } + } + return append(buf, json[s:i]...), i, nl, true +} + +func appendPrettyNumber(buf, json []byte, i, nl int) ([]byte, int, int, bool) { + s := i + i++ + for ; i < len(json); i++ { + if json[i] <= ' ' || json[i] == ',' || json[i] == ':' || json[i] == ']' || json[i] == '}' { + break + } + } + return append(buf, json[s:i]...), i, nl, true +} + +func appendTabs(buf []byte, prefix, indent string, tabs int) []byte { + if len(prefix) != 0 { + buf = append(buf, prefix...) + } + if len(indent) == 2 && indent[0] == ' ' && indent[1] == ' ' { + for i := 0; i < tabs; i++ { + buf = append(buf, ' ', ' ') + } + } else { + for i := 0; i < tabs; i++ { + buf = append(buf, indent...) + } + } + return buf +} + +// Style is the color style +type Style struct { + Key, String, Number [2]string + True, False, Null [2]string + Append func(dst []byte, c byte) []byte +} + +func hexp(p byte) byte { + switch { + case p < 10: + return p + '0' + default: + return (p - 10) + 'a' + } +} + +// TerminalStyle is for terminals +var TerminalStyle = &Style{ + Key: [2]string{"\x1B[94m", "\x1B[0m"}, + String: [2]string{"\x1B[92m", "\x1B[0m"}, + Number: [2]string{"\x1B[93m", "\x1B[0m"}, + True: [2]string{"\x1B[96m", "\x1B[0m"}, + False: [2]string{"\x1B[96m", "\x1B[0m"}, + Null: [2]string{"\x1B[91m", "\x1B[0m"}, + Append: func(dst []byte, c byte) []byte { + if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') { + dst = append(dst, "\\u00"...) + dst = append(dst, hexp((c>>4)&0xF)) + return append(dst, hexp((c)&0xF)) + } + return append(dst, c) + }, +} + +// Color will colorize the json. The style parma is used for customizing +// the colors. Passing nil to the style param will use the default +// TerminalStyle. +func Color(src []byte, style *Style) []byte { + if style == nil { + style = TerminalStyle + } + apnd := style.Append + if apnd == nil { + apnd = func(dst []byte, c byte) []byte { + return append(dst, c) + } + } + type stackt struct { + kind byte + key bool + } + var dst []byte + var stack []stackt + for i := 0; i < len(src); i++ { + if src[i] == '"' { + key := len(stack) > 0 && stack[len(stack)-1].key + if key { + dst = append(dst, style.Key[0]...) + } else { + dst = append(dst, style.String[0]...) + } + dst = apnd(dst, '"') + for i = i + 1; i < len(src); i++ { + dst = apnd(dst, src[i]) + if src[i] == '"' { + j := i - 1 + for ; ; j-- { + if src[j] != '\\' { + break + } + } + if (j-i)%2 != 0 { + break + } + } + } + if key { + dst = append(dst, style.Key[1]...) + } else { + dst = append(dst, style.String[1]...) + } + } else if src[i] == '{' || src[i] == '[' { + stack = append(stack, stackt{src[i], src[i] == '{'}) + dst = apnd(dst, src[i]) + } else if (src[i] == '}' || src[i] == ']') && len(stack) > 0 { + stack = stack[:len(stack)-1] + dst = apnd(dst, src[i]) + } else if (src[i] == ':' || src[i] == ',') && len(stack) > 0 && stack[len(stack)-1].kind == '{' { + stack[len(stack)-1].key = !stack[len(stack)-1].key + dst = apnd(dst, src[i]) + } else { + var kind byte + if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' { + kind = '0' + dst = append(dst, style.Number[0]...) + } else if src[i] == 't' { + kind = 't' + dst = append(dst, style.True[0]...) + } else if src[i] == 'f' { + kind = 'f' + dst = append(dst, style.False[0]...) + } else if src[i] == 'n' { + kind = 'n' + dst = append(dst, style.Null[0]...) + } else { + dst = apnd(dst, src[i]) + } + if kind != 0 { + for ; i < len(src); i++ { + if src[i] <= ' ' || src[i] == ',' || src[i] == ':' || src[i] == ']' || src[i] == '}' { + i-- + break + } + dst = apnd(dst, src[i]) + } + if kind == '0' { + dst = append(dst, style.Number[1]...) + } else if kind == 't' { + dst = append(dst, style.True[1]...) + } else if kind == 'f' { + dst = append(dst, style.False[1]...) + } else if kind == 'n' { + dst = append(dst, style.Null[1]...) + } + } + } + } + return dst +} diff --git a/vendor/github.com/tidwall/pretty/pretty_test.go b/vendor/github.com/tidwall/pretty/pretty_test.go new file mode 100644 index 00000000..422ef0bf --- /dev/null +++ b/vendor/github.com/tidwall/pretty/pretty_test.go @@ -0,0 +1,432 @@ +package pretty + +import ( + "bytes" + gojson "encoding/json" + "fmt" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func j(json interface{}) string { + var v interface{} + if err := gojson.Unmarshal([]byte(fmt.Sprintf("%s", json)), &v); err != nil { + fmt.Printf(">>%s<<\n", json) + panic(err) + } + data, err := gojson.Marshal(v) + if err != nil { + panic(err) + } + return string(data) +} + +var example1 = []byte(` +{ + "name": { + "last": "Sanders", + "first": "Janet" + }, + "children": [ + "Andy", "Carol", "Mike" + ], + "values": [ + 10.10, true, false, null, "hello", {} + ], + "values2": {}, + "values3": [], + "deep": {"deep":{"deep":[1,2,3,4,5]}} +} +`) + +var example2 = `[ 0, 10, 10.10, true, false, null, "hello \" "]` + +func TestPretty(t *testing.T) { + pretty := Pretty(Ugly(Pretty([]byte(example1)))) + assert.Equal(t, j(pretty), j(pretty)) + assert.Equal(t, j(example1), j(pretty)) + pretty = Pretty(Ugly(Pretty([]byte(example2)))) + assert.Equal(t, j(pretty), j(pretty)) + assert.Equal(t, j(example2), j(pretty)) + pretty = Pretty([]byte(" ")) + assert.Equal(t, "", string(pretty)) + opts := *DefaultOptions + opts.SortKeys = true + pretty = PrettyOptions(Ugly(Pretty([]byte(example2))), &opts) + assert.Equal(t, j(pretty), j(pretty)) + assert.Equal(t, j(example2), j(pretty)) +} + +func TestUgly(t *testing.T) { + ugly := Ugly([]byte(example1)) + var buf bytes.Buffer + err := gojson.Compact(&buf, []byte(example1)) + assert.Equal(t, nil, err) + assert.Equal(t, buf.Bytes(), ugly) + ugly = UglyInPlace(ugly) + assert.Equal(t, nil, err) + assert.Equal(t, buf.Bytes(), ugly) +} + +func TestRandom(t *testing.T) { + rand.Seed(time.Now().UnixNano()) + for i := 0; i < 100000; i++ { + b := make([]byte, 1024) + rand.Read(b) + Pretty(b) + Ugly(b) + } +} +func TestBig(t *testing.T) { + json := `[ + { + "_id": "58d19e070f4898817162964a", + "index": "", + "guid": "65d46c3e-9d3a-4bfe-bab2-252f36a53c6b", + "isActive": false, + "balance": "$1,064.00", + "picture": "http://placehold.it/32x32", + "age": 37, + "eyeColor": "brown", + "name": "Chan Orr", + "gender": "male", + "company": "SURETECH", + "email": "chanorr@suretech.com", + "phone": "+1 (808) 496-3754", + "address": "792 Bushwick Place, Glenbrook, Vermont, 9893", + "about": "Amet consequat eu enim laboris cillum ad laboris in quis laboris reprehenderit. Eu deserunt occaecat dolore eu veniam non dolore et magna ex incididunt. Ea dolor laboris ex officia culpa laborum amet adipisicing laboris tempor magna elit mollit ad. Tempor ex aliqua mollit enim laboris sunt fugiat. Sint sunt ex est non dolore consectetur culpa ullamco id dolor nulla labore. Sunt duis fugiat cupidatat sunt deserunt qui aute elit consequat sint cupidatat. Consequat ullamco aliqua nulla velit tempor aute.\r\n", + "registered": "2014-08-04T04:09:10 +07:00", + "latitude": 80.707807, + "longitude": 18.857548, + "tags": [ + "consectetur", + "est", + "cupidatat", + "nisi", + "incididunt", + "aliqua", + "ullamco" + ], + "friends": [ + { + "id": 0, + "name": "Little Edwards" + }, + { + "id": 1, + "name": "Gay Johns" + }, + { + "id": 2, + "name": "Hoover Noble" + } + ], + "greeting": "Hello, Chan Orr! You have 3 unread messages.", + "favoriteFruit": "banana" + }, + { + "_id": "58d19e07c2119248f8fa11ff", + "index": "", + "guid": "b362f0a0-d1ed-4b94-9d6b-213712620a20", + "isActive": false, + "balance": "$1,321.26", + "picture": "http://placehold.it/32x32", + "age": 28, + "eyeColor": "blue", + "name": "Molly Hyde", + "gender": "female", + "company": "QUALITEX", + "email": "mollyhyde@qualitex.com", + "phone": "+1 (849) 455-2934", + "address": "440 Visitation Place, Bridgetown, Palau, 5053", + "about": "Ipsum reprehenderit nulla est nostrud ad incididunt officia in commodo id esse id. Ullamco ullamco commodo mollit ut id cupidatat veniam nostrud minim duis qui sit. Occaecat esse nostrud velit qui non dolor proident. Ipsum ipsum anim non mollit minim voluptate amet irure in. Sunt commodo occaecat aute ullamco sunt fugiat laboris culpa Lorem anim. Aliquip tempor excepteur labore aute deserunt consectetur incididunt aute eu est ullamco consectetur excepteur. Sunt sint consequat cupidatat nisi exercitation minim enim occaecat esse ex amet ex non.\r\n", + "registered": "2014-09-12T08:51:11 +07:00", + "latitude": 15.867177, + "longitude": 165.862595, + "tags": [ + "enim", + "sint", + "elit", + "laborum", + "elit", + "cupidatat", + "ipsum" + ], + "friends": [ + { + "id": 0, + "name": "Holmes Hurley" + }, + { + "id": 1, + "name": "Rhoda Spencer" + }, + { + "id": 2, + "name": "Tommie Gallegos" + } + ], + "greeting": "Hello, Molly Hyde! You have 10 unread messages.", + "favoriteFruit": "banana" + }, + { + "_id": "58d19e07fc27eedd9159d710", + "index": "", + "guid": "1d343fd3-44f7-4246-a5e6-a9297afb3146", + "isActive": false, + "balance": "$1,459.65", + "picture": "http://placehold.it/32x32", + "age": 26, + "eyeColor": "brown", + "name": "Jaime Kennedy", + "gender": "female", + "company": "RECRITUBE", + "email": "jaimekennedy@recritube.com", + "phone": "+1 (983) 483-3522", + "address": "997 Vanderveer Street, Alamo, Marshall Islands, 4767", + "about": "Qui consequat veniam ex enim excepteur aliqua dolor duis Lorem deserunt. Lorem occaecat laboris quis nisi Lorem aute exercitation consectetur officia velit aliqua aliquip commodo. Tempor irure ad ipsum aliquip. Incididunt mollit aute cillum non magna duis officia anim laboris deserunt voluptate.\r\n", + "registered": "2015-08-31T06:51:25 +07:00", + "latitude": -7.486839, + "longitude": 57.659287, + "tags": [ + "veniam", + "aliqua", + "aute", + "amet", + "laborum", + "quis", + "sint" + ], + "friends": [ + { + "id": 0, + "name": "Brown Christensen" + }, + { + "id": 1, + "name": "Robyn Whitehead" + }, + { + "id": 2, + "name": "Dolly Weaver" + } + ], + "greeting": "Hello, Jaime Kennedy! You have 3 unread messages.", + "favoriteFruit": "banana" + }, + { + "_id": "58d19e0783c362da4b71240d", + "index": "", + "guid": "dbe60229-60d2-4879-82f3-d9aca0baaf6f", + "isActive": false, + "balance": "$3,221.63", + "picture": "http://placehold.it/32x32", + "age": 32, + "eyeColor": "green", + "name": "Cherie Vinson", + "gender": "female", + "company": "SLAX", + "email": "cherievinson@slax.com", + "phone": "+1 (905) 474-3132", + "address": "563 Macdougal Street, Navarre, New York, 8733", + "about": "Ad laborum et magna quis veniam duis magna consectetur mollit in minim non officia aliquip. Ullamco dolor qui consectetur adipisicing. Incididunt ad ad incididunt duis velit laboris. Reprehenderit ullamco magna quis exercitation excepteur nisi labore pariatur laborum consequat eu laboris amet velit. Et dolore aliqua proident sunt dolore incididunt dolore fugiat ipsum tempor occaecat.\r\n", + "registered": "2015-03-19T08:48:47 +07:00", + "latitude": -56.480034, + "longitude": -59.894094, + "tags": [ + "irure", + "commodo", + "quis", + "cillum", + "quis", + "nulla", + "irure" + ], + "friends": [ + { + "id": 0, + "name": "Danielle Mullins" + }, + { + "id": 1, + "name": "Maxine Peters" + }, + { + "id": 2, + "name": "Francine James" + } + ], + "greeting": "Hello, Cherie Vinson! You have 1 unread messages.", + "favoriteFruit": "apple" + }, + { + "_id": "58d19e07b8f1ea8e3451870d", + "index": "", + "guid": "91fd9527-770c-4006-a0ed-64ca0d819199", + "isActive": true, + "balance": "$2,387.38", + "picture": "http://placehold.it/32x32", + "age": 37, + "eyeColor": "blue", + "name": "Glenna Hanson", + "gender": "female", + "company": "ACUMENTOR", + "email": "glennahanson@acumentor.com", + "phone": "+1 (965) 564-3926", + "address": "323 Seigel Street, Rosedale, Florida, 2700", + "about": "Commodo id ex velit nulla incididunt occaecat aliquip ullamco consequat est. Esse officia adipisicing magna et et incididunt sit deserunt ex mollit id. Laborum proident sit sit duis proident cillum irure aliquip et commodo.\r\n", + "registered": "2014-06-29T02:48:04 +07:00", + "latitude": -6.141759, + "longitude": 155.991532, + "tags": [ + "amet", + "pariatur", + "culpa", + "eu", + "commodo", + "magna", + "excepteur" + ], + "friends": [ + { + "id": 0, + "name": "Blanchard Blackburn" + }, + { + "id": 1, + "name": "Ayers Guy" + }, + { + "id": 2, + "name": "Powers Salinas" + } + ], + "greeting": "Hello, Glenna Hanson! You have 4 unread messages.", + "favoriteFruit": "strawberry" + }, + { + "_id": "58d19e07f1ad063dac8b72dc", + "index": "", + "guid": "9b8c6cef-cfcd-4e6d-85e4-fe2e6920ec31", + "isActive": true, + "balance": "$1,828.58", + "picture": "http://placehold.it/32x32", + "age": 29, + "eyeColor": "green", + "name": "Hays Shields", + "gender": "male", + "company": "ISOLOGICA", + "email": "haysshields@isologica.com", + "phone": "+1 (882) 469-3201", + "address": "574 Columbus Place, Singer, Georgia, 8716", + "about": "Consectetur et adipisicing ad quis incididunt qui labore et ex elit esse. Ad elit officia ullamco dolor reprehenderit. Sunt nisi ullamco mollit incididunt consectetur nostrud anim adipisicing ullamco aliqua eiusmod ad. Et excepteur voluptate adipisicing velit id quis duis Lorem id deserunt esse irure Lorem. Est irure sint Lorem aliqua adipisicing velit irure Lorem. Ex in culpa laborum nostrud esse eu laboris velit. Anim excepteur ex ipsum amet nostrud cillum.\r\n", + "registered": "2014-02-10T07:17:14 +07:00", + "latitude": -66.354543, + "longitude": 138.400461, + "tags": [ + "mollit", + "labore", + "id", + "labore", + "dolor", + "in", + "elit" + ], + "friends": [ + { + "id": 0, + "name": "Mendoza Craig" + }, + { + "id": 1, + "name": "Rowena Carey" + }, + { + "id": 2, + "name": "Barry Francis" + } + ], + "greeting": "Hello, Hays Shields! You have 10 unread messages.", + "favoriteFruit": "strawberry" + } +]` + + opts := *DefaultOptions + opts.SortKeys = true + jsonb := PrettyOptions(Ugly([]byte(json)), &opts) + assert.Equal(t, j(jsonb), j(json)) +} + +func TestColor(t *testing.T) { + res := Color(Pretty([]byte(` +{"hello":"world","what":123, +"arr":["1","2",1,2,true,false,null], +"obj":{"key1":null,"ar`+"\x1B[36m"+`Cyanr2":[1,2,3,"123","456"]}} + `)), nil) + if string(res) != `{ + "hello": "world", + "what": 123, + "arr": ["1", "2", 1, 2, true, false, null], + "obj": { + "key1": null, + "ar\u001b[36mCyanr2": [1, 2, 3, "123", "456"] + } +} +` { + t.Fatal("invalid output") + } +} + +func BenchmarkPretty(t *testing.B) { + t.ReportAllocs() + t.ResetTimer() + for i := 0; i < t.N; i++ { + Pretty(example1) + } +} + +func BenchmarkPrettySortKeys(t *testing.B) { + opts := *DefaultOptions + opts.SortKeys = true + t.ReportAllocs() + t.ResetTimer() + for i := 0; i < t.N; i++ { + PrettyOptions(example1, &opts) + } +} +func BenchmarkUgly(t *testing.B) { + t.ReportAllocs() + t.ResetTimer() + for i := 0; i < t.N; i++ { + Ugly(example1) + } +} + +func BenchmarkUglyInPlace(t *testing.B) { + example2 := []byte(string(example1)) + t.ReportAllocs() + t.ResetTimer() + for i := 0; i < t.N; i++ { + UglyInPlace(example2) + } +} +func BenchmarkJSONIndent(t *testing.B) { + var dst bytes.Buffer + t.ReportAllocs() + t.ResetTimer() + for i := 0; i < t.N; i++ { + gojson.Indent(&dst, example1, "", " ") + } +} + +func BenchmarkJSONCompact(t *testing.B) { + var dst bytes.Buffer + t.ReportAllocs() + t.ResetTimer() + for i := 0; i < t.N; i++ { + gojson.Compact(&dst, example1) + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json deleted file mode 100644 index a84f8d2c..00000000 --- a/vendor/vendor.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "comment": "", - "ignore": "", - "package": [ - { - "checksumSHA1": "6i5WXEn6aAYiWSNCH0TsCyAhgv4=", - "path": "github.com/nats-io/gnatsd/conf", - "revision": "6608e9ac3be979dcb0614b772cc86a87b71acaa3", - "revisionTime": "2018-07-05T16:41:09Z" - }, - { - "checksumSHA1": "+BI6v9+uBu6jwqNPS0PbPvepW2Y=", - "path": "github.com/nats-io/gnatsd/logger", - "revision": "6608e9ac3be979dcb0614b772cc86a87b71acaa3", - "revisionTime": "2018-07-05T16:41:09Z" - }, - { - "checksumSHA1": "mklbylVp2WB0G8kkDmBaRFBqLqs=", - "path": "github.com/nats-io/gnatsd/server", - "revision": "6608e9ac3be979dcb0614b772cc86a87b71acaa3", - "revisionTime": "2018-07-05T16:41:09Z" - }, - { - "checksumSHA1": "P5raehO2Vgq7FKicqSdeRYoUJlo=", - "path": "github.com/nats-io/gnatsd/server/pse", - "revision": "6608e9ac3be979dcb0614b772cc86a87b71acaa3", - "revisionTime": "2018-07-05T16:41:09Z" - }, - { - "checksumSHA1": "7q/nF5pQYkUKwXkXM1H4yO7XgHU=", - "path": "github.com/nats-io/gnatsd/test", - "revision": "6608e9ac3be979dcb0614b772cc86a87b71acaa3", - "revisionTime": "2018-07-05T16:41:09Z" - }, - { - "checksumSHA1": "GfQ4LMqxsTdxY+4VjBPzejbMI6Q=", - "path": "github.com/nats-io/gnatsd/util", - "revision": "6608e9ac3be979dcb0614b772cc86a87b71acaa3", - "revisionTime": "2018-07-05T16:41:09Z" - }, - { - "checksumSHA1": "RNbZJ1hCzyADhvkfy0TwbJ6d3MM=", - "path": "github.com/nats-io/go-nats", - "revision": "ff578ff05d4180a259da6d729af7b55285d24b78", - "revisionTime": "2018-07-25T00:55:40Z" - }, - { - "checksumSHA1": "C/TsNyFOKX2bTW3U9O3m5mQhJo8=", - "path": "github.com/nats-io/go-nats/encoders/builtin", - "revision": "ff578ff05d4180a259da6d729af7b55285d24b78", - "revisionTime": "2018-07-25T00:55:40Z" - }, - { - "checksumSHA1": "pG50W6cz2QHdf4/oW8sQQzPF7Wo=", - "path": "github.com/nats-io/go-nats/encoders/protobuf", - "revision": "ff578ff05d4180a259da6d729af7b55285d24b78", - "revisionTime": "2018-07-25T00:55:40Z" - }, - { - "checksumSHA1": "wXnHPa+satCH17fbk9XxK7MrxXw=", - "path": "github.com/nats-io/go-nats/encoders/protobuf/testdata", - "revision": "ff578ff05d4180a259da6d729af7b55285d24b78", - "revisionTime": "2018-07-25T00:55:40Z" - }, - { - "checksumSHA1": "mBZQa+wv/u+0qA1ceh6MrWBEgxs=", - "path": "github.com/nats-io/go-nats/util", - "revision": "ff578ff05d4180a259da6d729af7b55285d24b78", - "revisionTime": "2018-07-25T00:55:40Z" - }, - { - "checksumSHA1": "ufhg4R5aa96zJxjSy0JfliML97Y=", - "path": "github.com/nats-io/nuid", - "revision": "3024a71c3cbe30667286099921591e6fcc328230", - "revisionTime": "2018-07-12T04:49:59Z" - } - ], - "rootPath": "github.com/tidwall/tile38" -}