paulmach / go.geo Goto Github PK
View Code? Open in Web Editor NEWGeometry/geography library in Go, DEPRECATED, use ->
Home Page: https://github.com/paulmach/orb
License: MIT License
Geometry/geography library in Go, DEPRECATED, use ->
Home Page: https://github.com/paulmach/orb
License: MIT License
I'm thinking this might be not accurate for large bounds.
Rather than having to use ToGeoJSON()
, is there a reason not to just have a built in custom MarshalJSON()
that defaults to GeoJSON? That would allow embedded geo fields to marshal properly to GeoJSON out of the box.
I created a point struct with a uuid and I noticed that k nearest returns the same point multiple times. Is this expected behavior?
code sample:
type MyPoint struct {
Lng float64
Lat float64
Id uuid.UUID
}
func (p MyPoint) String() string {
ju, err := json.MarshalIndent(p, "", " ")
if err != nil {
panic(err)
}
return string(ju)
}
func (p MyPoint) Point() orb.Point {
return orb.Point{p.Lng, p.Lat}
}
func run() error {
r := rand.New(rand.NewSource(42))
qt := quadtree.New(orb.Bound{Min: orb.Point{0, 0}, Max: orb.Point{1, 1}})
for i := 0; i < 1000; i++ {
qt.Add(MyPoint{r.Float64(), r.Float64(), uuid.New()})
}
nearest := qt.KNearest(nil, orb.Point{0.5, 0.5}, 5)
ju, err := json.MarshalIndent(nearest, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(ju))
return nil
}
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
output
[
{
"Lng": 0.4930591659434973,
"Lat": 0.5196585530161364,
"Id": "b1e5690a-1c7d-49f5-be4f-c389756af154"
},
{
"Lng": 0.4930591659434973,
"Lat": 0.5196585530161364,
"Id": "b1e5690a-1c7d-49f5-be4f-c389756af154"
},
{
"Lng": 0.4930591659434973,
"Lat": 0.5196585530161364,
"Id": "b1e5690a-1c7d-49f5-be4f-c389756af154"
},
{
"Lng": 0.5073640535317331,
"Lat": 0.478560836766942,
"Id": "691971a9-6097-4591-bd89-6791349025d0"
},
{
"Lng": 0.4930591659434973,
"Lat": 0.5196585530161364,
"Id": "b1e5690a-1c7d-49f5-be4f-c389756af154"
}
]
I have this javascript code which normalizes geographic coordinates to within the ranges of lat +-90
and lon +-180
(it unwinds multiple rotations around the globe).
This function is quite nice as it avoids floating point precision errors which can occur when using other methods.
Would you be interested in me porting the code to go.geo? If so what would you call the method? it's specific to geographic coordinates.
Maybe this could close #41 as it appears to be doing something similar?
Could be named something like point.NormalizeGeographicCoordinates()
should be able to use anti-meridian for correct calculation of distance
I'm pretty sure it's not correct for large bounds.
Line 36 in 3b59bf1
heya, is it currently possible to compute the mid point along a path (a point on the line string which is exactly 50% along the line)?
I had a look at the APIs and I guess I could use path.Distance()
to get the total distance, then I think I would need to walk the points to find the line segment which lies in the center, from there it's a task of interpolating along that segment to find the correct position.
is this all possible with the existing methods or would it require me to open a PR to add additional methods?
Currently I'm doing something like:
quadkey := geo.NewPoint(v.Lon, v.Lat).QuadkeyString(quadKeyLevel)
With v.Lon
and v.Lat
not necessarily being on a tile boundary. I'm not sure, based on the results, whether QuadKeyString will represent a tile, or if it'll be different based on different points with a tile at the same level.
I'm trying to select postgis multipoint to geo.PointSet - and scan fails. Checking code - seems like format actually returned by postgis is different from what's expected by geo.PointSet.
BTW for geo.Point WKB scan works fine.
Here is sample code demonstrating issue (manually setup postgresql + postgis server required for test):
package main
import (
"database/sql"
"testing"
_ "github.com/lib/pq"
geo "github.com/paulmach/go.geo"
"github.com/stretchr/testify/require"
)
func TestMultipointScan(t *testing.T) {
/*
select version() union select postgis_full_version();
version
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
POSTGIS="2.3.2 r15302" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.10.1, released 2013/08/26" LIBXML="2.9.1" LIBJSON="0.11.99" RASTER
PostgreSQL 9.6.2 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit
*/
db, err := sql.Open("postgres", "user=x password=x dbname=x sslmode=disable")
require.NoError(t, err)
const query = `
SELECT ST_AsBinary(ST_Collect(ST_GeomFromText('POINT(1 2)'), ST_GeomFromText('POINT(1 2)')))
`
var ps geo.PointSet
err = db.QueryRow(query).Scan(&ps)
require.NoError(t, err)
/*
go test -run TestMultipointScan -v
=== RUN TestMultipointScan
--- FAIL: TestMultipointScan (0.04s)
Error Trace: sample_test.go:23
Error: Received unexpected error:
sql: Scan error on column index 0: go.geo: invalid WKB data
*/
}
Hi, not an issue so much, as something I'm struggling with adapting.
I need the ability to have a number associated with a geo point, in order to reference back to what it relates to. I've customised the point type to have length of 3 instead of 2, with the 3rd float been the ID, so the idea was, when I ran a nearest command against the quad tree of points, I'd be able to find it's owner.
The creating of the point works, and the existing tests pass. But soon as the point is added to the quad tree, it appears to lose it's 3rd value, returning X and Y as expected, but the 3rd number is always 0.
Is there something I'm missing in the quad tree section, that is removing this 3rd column. Any help or advice on the best way to approach this would be much appreciated!
should be able to serialize points, lines, point sets and paths into WKB format. Unmarshalling is already supported.
hi @paulmach
I got bit by a gotcha today, which although is technically correct, could cause confusion for some users.
When using the PointSet.Centroid()
and PointSet.GeoCentroid()
methods on a polygon, the calculated result was incorrect, for instance [1 1, 1 -1, -1 -1, -1 1, 1, 1]
was not returning [0 0]
.
My error was that I failed to remove the last coordinate from the polygon (a duplicate of the first), which is a common convention to indicate that the polygon is closed (a convention in openstreetmap).
the fix was fairly simple:
isClosed = points.First().Equals(points.Last())
if isClosed {
points.RemoveAt(len(*points) - 1)
}
The current implementation of numPoints := float64(len(ps))
is technically correct, but could possibly also bite others like me who aren't careful about it's usage.
How would you feel about me changing these calculations in PointSet
to deduplicate the points before computing numPoints
? This would likely slow down the code and require some memory to do the deduplication.
Other options include:
Polygon
type which has polygon specific code[{"Location":[27.100918,93.691639]},{"Location":[27.10092,93.691639]}]
The above two points fail to cluster with a panic. I have a couple of more cases where it fails with a similar error.
I'm no math wiz, so reporting the issue.
panic: geo: latitude out of range, given 93.691639
goroutine 1 [running]:
github.com/paulmach/go%2egeo.MercatorScaleFactor(0x40576c43d03968d7, 0xc002415560)
/home/org/.go/pkg/mod/github.com/paulmach/[email protected]/projections.go:36 +0x112
github.com/paulmach/go.geo/clustering.geocluster(0xc002460ca0, 0x2, 0x2, 0x4034000000000000, 0xa55ca0, 0xc000000000, 0xc002a89260)
/home/org/.go/pkg/mod/github.com/paulmach/[email protected]/clustering/clustering.go:106 +0x106
github.com/paulmach/go.geo/clustering.ClusterGeoPointers(0xc000136e20, 0x2, 0x2, 0x4034000000000000, 0x2, 0x2, 0x0)
/home/org/.go/pkg/mod/github.com/paulmach/[email protected]/clustering/clustering.go:75 +0x1ab
clusterhelper.ComputeClusteringData(0xc000546000, 0xbc3, 0xd62, 0xc004251e58, 0x1, 0x1, 0x0, 0xc002415300)
/home/org/org/go-scripts/correct-data/cluster-helper/cluster-helper.go:43 +0x55d
main.main()
/home/org/org/go-scripts/correct-data/correct-data.go:105 +0x7f6
exit status 2
Hi there, any chance of implementing PointZ support?
Path.ToWKT
is hard-coded to output LINESTRING
. If i create a path that's closed, shouldn't it be a POLYGON
?
By following the example, it returned "\x010100000000000000000024c00000000000003e40" to go.geo causing "go.geo: incorrect geometry" error
Any idea what's wrong? thanks!
This library is beautiful work Paul, much gratitude.
I see no way to check if a Point
is within a hexagon using either go.geo
or using go.geojson
. Could such a helper function be added?
I have a Polygon-type Geometry
handy with [][][]float64
underlying data guaranteed to be closed and hexagonal, but there is no way to check whether a Point is in it.
The overall topic is segmenting a city into hundreds of hexagons and binning samples into each of them. Equivalent functionality in Turf.js is hexGrid()
for generating hexagons in a bounding box and then assigning points to bins with pointsWithinPolygon()
.
Thank you!
Siddharth
For example I used this and scanned it to a geo.PointSet
SET @g = 'POLYGON((50.866753 5.686455, 50.859819 5.708942, 50.851475 5.722675, 50.841611 5.720615, 50.834023 5.708427, 50.840744 5.689373, 50.858735 5.673923, 50.866753 5.686455))';
SELECT ST_AsBinary(PolyFromText(@g));
var p *geo.PointSet
row.Scan(&p)
// p == EMPTY
It's very likely that I'm way off on this. What am I doing wrong?
I'd like to be able to scan data from the multipolygon
column from the PostGIS database into Go app.
I was hoping I could simply define a struct where one of the fields is of type MultiPolygon
:
import(geo "github.com/paulmach/go.geo")
...
type Area struct {
ID uuid.UUID `gorm:"column:id" json:"id"`
Shape *geo.MultiPolygon `gorm:"column:shape" json:"shape"`
However, of course such a type is not yet defined.
Is there a way to actually read the MultiPolygon data from PostGIS?
Also it seems that we have to run the ST_AsBinary
function when fetching data from the database. Failing to do so results in the sql: Scan error on column index 3: go.geo: invalid WKB data
error.
Couldn't we always apply the ST_AsBinary
function? eg. when defining the struct? I'd say that the typical scenario is that the data is actually saved as WKB binary and the ST_AsBinary function simply changes:
shape | 01060...E614340
to
st_asbinary | \x01060...e614340
hey @paulmach I've been playing with an alternate irregular polygon centroid algorithm which I borrowed from https://github.com/manuelbieh/Geolib/blob/master/src/geolib.js#L404
in theory it should provide a better 'center of gravity' calc than than the existing PointSet.Centroid()
algo as it converts the coords to a spherical coordinate space before averaging the values.
what are your thoughts? is this something you'd be interested in merging at some point?
see: http://geojson.io/#id=gist:anonymous/ecd1ce4af92368aa4aea&map=3/-72.39/167.70
package main
import "fmt"
import "math"
import "github.com/paulmach/go.geo"
func main() {
// berlin := geo.NewPoint(13.408056,52.518611)
// moscow := geo.NewPoint(37.617778,55.751667)
// points := geo.PointSet{}
// points.Push(berlin).Push(moscow)
// rough new zealand polygon
// ref: http://geojson.io/#id=gist:anonymous/adc4993b4b85f55ea9ae
// points := geo.PointSet{}
// points.Push(geo.NewPoint(172.1298828125,-33.97980872872456))
// points.Push(geo.NewPoint(172.1298828125,-37.30027528134431))
// points.Push(geo.NewPoint(165.8974609375,-38.54816542304658))
// points.Push(geo.NewPoint(168.1728515625,-46.10370875598026))
// points.Push(geo.NewPoint(179.7802734375,-47.8721439688873))
// // points.Push(geo.NewPoint(-188.1298828125,-33.97980872872456))
// antarctica bbox
points := geo.PointSet{}
points.Push(geo.NewPoint(148.7109375,-84.83422451455142))
points.Push(geo.NewPoint(148.7109375,-76.51681887717322))
points.Push(geo.NewPoint(186.6796875,-76.51681887717322))
points.Push(geo.NewPoint(186.6796875,-84.83422451455142))
fmt.Println(points)
fmt.Println(points.Centroid())
fmt.Println(getCentroid(points))
}
func getCentroid(ps geo.PointSet) *geo.Point {
X := 0.0
Y := 0.0
Z := 0.0
var toRad = math.Pi / 180
var fromRad = 180 / math.Pi
for _, point := range ps {
var lon = point[0] * toRad
var lat = point[1] * toRad
X += math.Cos(lat) * math.Cos(lon)
Y += math.Cos(lat) * math.Sin(lon)
Z += math.Sin(lat)
}
numPoints := float64(len(ps))
X = X / numPoints
Y = Y / numPoints
Z = Z / numPoints
var lon = math.Atan2(Y, X)
var hyp = math.Sqrt(X * X + Y * Y)
var lat = math.Atan2(Z, hyp)
var centroid = geo.NewPoint(lon * fromRad, lat * fromRad)
return centroid;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.