Updated geojson package

This commit is contained in:
tidwall 2019-06-28 10:01:12 -07:00
parent 37d64f0466
commit 85b70e0d26
4 changed files with 90 additions and 43 deletions

6
Gopkg.lock generated
View File

@ -221,7 +221,8 @@
version = "v1.1.0" version = "v1.1.0"
[[projects]] [[projects]]
digest = "1:cdab3bce90a53a124ac3982719abde77d779e961d9c180e55c23fb74fc62563a" branch = "master"
digest = "1:16a87d3d8d3808f3206c4992d1fb50440bb7d5508fbe5bfab7d899b516c7f3b8"
name = "github.com/tidwall/geojson" name = "github.com/tidwall/geojson"
packages = [ packages = [
".", ".",
@ -229,8 +230,7 @@
"geometry", "geometry",
] ]
pruneopts = "" pruneopts = ""
revision = "eaf6e0a55a79c1e879bbbcc879a3176c720d99cd" revision = "ff8d554639ee72ffa650eca1e02f03ab51193c7f"
version = "v1.1.3"
[[projects]] [[projects]]
digest = "1:3ddca2bd5496c6922a2a9e636530e178a43c2a534ea6634211acdc7d10222794" digest = "1:3ddca2bd5496c6922a2a9e636530e178a43c2a534ea6634211acdc7d10222794"

View File

@ -34,8 +34,8 @@ required = [
name = "github.com/tidwall/boxtree" name = "github.com/tidwall/boxtree"
[[constraint]] [[constraint]]
branch = "master"
name = "github.com/tidwall/geojson" name = "github.com/tidwall/geojson"
version = "1.1.3"
[[constraint]] [[constraint]]
name = "github.com/Shopify/sarama" name = "github.com/Shopify/sarama"

View File

@ -93,75 +93,60 @@ func BearingTo(latA, lonA, latB, lonB float64) float64 {
func RectFromCenter(lat, lon, meters float64) ( func RectFromCenter(lat, lon, meters float64) (
minLat, minLon, maxLat, maxLon float64, minLat, minLon, maxLat, maxLon float64,
) { ) {
// convert degrees to radians
// see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Latitude
lat *= radians lat *= radians
lon *= radians lon *= radians
r := meters / earthRadius // angular radius // Calculate ANGULAR RADIUS
// see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex
r := meters / earthRadius
// Calculate LATITUDE min and max
// see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Latitude
minLat = lat - r minLat = lat - r
maxLat = lat + r maxLat = lat + r
// Calculate LONGITUDE min and max
// see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Longitude
latT := math.Asin(math.Sin(lat) / math.Cos(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))) lonΔ := math.Acos((math.Cos(r) - math.Sin(latT)*math.Sin(lat)) / (math.Cos(latT) * math.Cos(lat)))
minLon = lon - lonΔ minLon = lon - lonΔ
maxLon = lon + lonΔ maxLon = lon + lonΔ
// Adjust for north poll // ADJUST mins and maxes for edge-of-map cases
// see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#PolesAnd180thMeridian
// adjust for NORTH POLE
if maxLat > math.Pi/2 { if maxLat > math.Pi/2 {
minLon = -math.Pi minLon = -math.Pi
maxLat = math.Pi / 2 maxLat = math.Pi / 2
maxLon = math.Pi maxLon = math.Pi
} }
// Adjust for south poll // adjust for SOUTH POLE
if minLat < -math.Pi/2 { if minLat < -math.Pi/2 {
minLat = -math.Pi / 2 minLat = -math.Pi / 2
minLon = -math.Pi minLon = -math.Pi
maxLon = math.Pi maxLon = math.Pi
} }
// Adjust for wraparound. Remove this if the commented-out condition below this block is added. /* adjust for WRAPAROUND
Creates a bounding box that wraps around the Earth like a belt, which
results in returning false positive candidates (candidates that are
farther away from the center than the distance of the search radius).
An alternative method, possibly to be implemented in the future, would be
to split the bounding box into two boxes. This would return fewer (or no)
false positives, but will require significant changes to the API's of
geoJSON and any of its dependents. */
if minLon < -math.Pi || maxLon > math.Pi { if minLon < -math.Pi || maxLon > math.Pi {
minLon = -math.Pi minLon = -math.Pi
maxLon = math.Pi maxLon = math.Pi
} }
/* // convert radians to degrees
// 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
}
*/
minLon = math.Mod(minLon+3*math.Pi, 2*math.Pi) - math.Pi // normalise to -180..+180°
maxLon = math.Mod(maxLon+3*math.Pi, 2*math.Pi) - math.Pi
minLat *= degrees minLat *= degrees
minLon *= degrees minLon *= degrees
maxLat *= degrees maxLat *= degrees

View File

@ -41,6 +41,68 @@ func TestGeoCalc(t *testing.T) {
if value2 != lonB { if value2 != lonB {
t.Fatalf("expected '%v', got '%v'", lonB, value2) t.Fatalf("expected '%v', got '%v'", lonB, value2)
} }
// RectFromCenter
// expected mins and maxes for wraparound and pole tests
expMinLat := -90.0
expMinLon := -180.0
expMaxLat := 90.0
expMaxLon := 180.0
dist1 := 1600000.0
wraparoundTests := []struct {
name string
lat, lon, searchRadius float64
}{
{name: "Wraparound E", lat: 0.0, lon: 179.0, searchRadius: dist1}, // at equator near 180th meridian East
{name: "Wraparound W", lat: 0.0, lon: -179.0, searchRadius: dist1}, // at equator near 180th meridian West
}
for _, tt := range wraparoundTests {
t.Run(tt.name, func(t *testing.T) {
_, minLon, _, maxLon := RectFromCenter(tt.lat, tt.lon, tt.searchRadius)
if !(minLon == expMinLon && maxLon == expMaxLon) {
t.Errorf("\nexpected minLon = '%v', maxLon = '%v'"+"\ngot minLon = '%v', maxLon = '%v'\n",
expMinLon, expMaxLon,
minLon, maxLon)
}
})
}
northPoleTests := []struct {
name string
lat, lon, searchRadius float64
}{
{name: "North Pole", lat: 89.0, lon: 90.0, searchRadius: dist1}, // near North Pole
{name: "North Pole: Tile38 iss422", lat: 13.0257553, lon: 77.6672509, searchRadius: 9000000.0},
}
for _, tt := range northPoleTests {
t.Run(tt.name, func(t *testing.T) {
_, minLon, maxLat, maxLon := RectFromCenter(tt.lat, tt.lon, tt.searchRadius)
if !(minLon == expMinLon && maxLat == expMaxLat && maxLon == expMaxLon) {
t.Errorf("\nexpected minLon = '%v', maxLat = '%v', maxLon = '%v'"+"\ngot minLon = '%v', maxLat = '%v', maxLon = '%v'",
expMinLon, expMaxLat, expMaxLon,
minLon, maxLat, maxLon)
}
})
}
southPoleTests := []struct {
name string
lat, lon, searchRadius float64
}{
{name: "South Pole", lat: -89.0, lon: 90.0, searchRadius: dist1}, // near South Pole
}
for _, tt := range southPoleTests {
t.Run(tt.name, func(t *testing.T) {
minLat, minLon, _, maxLon := RectFromCenter(tt.lat, tt.lon, tt.searchRadius)
if !(minLat == expMinLat && minLon == expMinLon && maxLon == expMaxLon) {
t.Errorf("\nexpected minLat = '%v', minLon = '%v', maxLon = '%v'"+"\ngot minLat = '%v', minLon = '%v', maxLon = '%v'",
expMinLat, expMinLon, expMaxLon,
minLat, minLon, maxLon)
}
})
}
} }
func TestHaversine(t *testing.T) { func TestHaversine(t *testing.T) {