Giter VIP home page Giter VIP logo

orb's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

orb's Issues

Why have wkb.Scanner() and wkb.Value() functions?

I've noticed that the different geometric types don't have Scan() and Value() directly implemented on them, but rather they are implemented by their wrappers.

This is fine if you are able to first call wkb.Scanner() and wkb.Value() before calling the database functions. However, if you are using an orm-type library that automatically calls Scan() and Value() behind the scenes, such as gorm, then this library won't work.

I'm proposing keeping the scanner and valuer wrappers for those who need them, but also implementing Scan() and Value() directly onto each type, for those who don't have the luxury of wrapping their fields prior.

Issue when retrieving postgis geometry

I've spend a couple of hours trying to figure out what doesn't work with my use-case (postgreSQL, postGIS, go-pg and orb)

I have a complicated SELECT query (2 joins and 1 pivot) that I will simplify for this thread. The problem occurs during the Scan of a polygon

p := orb.Polygon{}
_, err := b.Database.Query(wkb.Scanner(&p), `SELECT ST_AsBinary(jobs.region,'NDR') FROM jobs LIMIT 1`, id)

The returned error:

Expected
          <internal.Error>: {
              s: "wkb: invalid data",
          }
      to be nil

I dig in the code and found that the error occurs here:

orb/encoding/wkb/wkb.go

Lines 218 to 244 in 5af0ae2

func readByteOrderType(r io.Reader) (binary.ByteOrder, uint32, error) {
var bom = make([]byte, 1)
// the bom is the first byte
if _, err := r.Read(bom); err != nil {
return nil, 0, err
}
var byteOrder binary.ByteOrder
if bom[0] == 0 {
byteOrder = binary.BigEndian
} else if bom[0] == 1 {
byteOrder = binary.LittleEndian
} else {
return nil, 0, ErrNotWKB
}
// the type which is 4 bytes
var typ uint32
err := binary.Read(r, byteOrder, &typ)
if err != nil {
return nil, 0, err
}
return byteOrder, typ, err
}

I added some logs to print the actual data and I got this:

\x01030000000200000005000000000000000000000000000000000000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000005000000000000000000f03f000000000000f03f000000000000f03f0000000000000040000000000000004000000000000000400000000000000040000000000000f03f000000000000f03f000000000000f03f

which seems to be a perfect WKB apart from the leading \x. When I use a online service like https://rodic.fr/blog/online-conversion-between-geometric-formats/ to convert the data it gives exactly what I inserted in the database --> POLYGON((0 0,10 0,10 10,0 10,0 0),(1 1,1 2,2 2,2 1,1 1))

Any ideas ?

Properties inside a FeatureCollection

Feature request for adding a Properties map inside the FeatureCollection type so that various metadata can be saved inside while passing the "model" around within the program.
https://godoc.org/github.com/paulmach/orb/geojson#FeatureCollection

I understand the GeoJSON standard considers it a "may" feature, but a richer/more expressive in-memory representative would be pretty handy. Also, the marshaller should also serialize it consistent with the Feature implementation, so JavaScript code (e.g in a Mapbox GL JS HTML page) can also access it easily.

Thanks
Siddharth

Problems with UnmarshallingFeatureCollection with "GeometryCollection"

I have some problems with "GeometryCollection"s. I want to unmarshall "FeatureCollections" with "GeometryCollection" from GeoJSON.

I simplified the imported GeoJSON for testing purposes

{
  "type": "FeatureCollection",
  "name": "other_relations",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "osm_id": "175387"
      },
      "geometry": {
        "type": "GeometryCollection",
        "geometries": [
          {
            "type": "Point",
            "coordinates": [
              6.2169758,
              49.6411581
            ] } ] } } ]}

I get the error: geojson: invalid geometry

I digged a little bit:
It seems that Geometry is not unmarshalled well.

orb/geojson/geometry.go

Lines 105 to 113 in f23a705

func (g *Geometry) UnmarshalJSON(data []byte) error {
jg := &jsonGeometry{}
err := json.Unmarshal(data, &jg)
if err != nil {
return err
}
switch jg.Type {
case "Point":

The type for the g *geometry is not defined for a jq jsonGeometry.

orb/geojson/feature.go

Lines 81 to 83 in f23a705

if jf.Geometry == nil || jf.Geometry.Coordinates == nil {
return ErrInvalidGeometry
}

jf.Geometry.Coordinates: A GeometryCollection has no coordinates. it has geometries. So the result will always be false.

orb/geojson/feature.go

Lines 85 to 91 in f23a705

*f = Feature{
ID: jf.ID,
Type: jf.Type,
Properties: jf.Properties,
BBox: jf.BBox,
Geometry: jf.Geometry.Coordinates,
}

Geometry: jf.Geometry.Coordinates: A GeometryCollection has geometries

At that point I stopped digging. I am not sure how to proceed to solve that issue?

Render mbtiles

Hey there, just a quick question:

Get we generate mbtiles with this project and use those with the klokantech/tileserver-gl?
If yes, wich source is recommended? Can we use the .osm.pbf file?

Thanks in advance

Panic when reading geojson with null geometries in UnmarshallJSON()

I am writing a program using SA2 geometries from the Australian Statistical Standard Geography which I have converted into geojson with the below code snippet.

curl http://www.ausstats.abs.gov.au/ausstats/subscriber.nsf/0/A09309ACB3FA50B8CA257FED0013D420/\$File/1270055001_sa2_2016_aust_shape.zip -o ../shapefiles/sa2_2016.zip

unzip ../shapefiles/sa2_2016.zip
unzip ../shapefiles/ste_2016.zip
ogr2ogr -f Geojson /SA2_2016_AUST.geojson SA2_2016_AUST.shp

When I try to run my code

SA2b, _ := ioutil.ReadFile(filename)
SA2, _ := geojson.UnmarshalFeatureCollection(SA2b)

I receive the following panic error

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x66961f]

goroutine 1 [running]:
encoding/json.(*decodeState).unmarshal.func1(0xc4200c5c18)
	/usr/local/go/src/encoding/json/decode.go:175 +0xd4
panic(0x6e71c0, 0x8eabd0)
	/usr/local/go/src/runtime/panic.go:502 +0x229
github.com/paulmach/orb/geojson.(*Feature).UnmarshalJSON(0xc42069b0e0, 0xc4304cdd1f, 0x1d1, 0x783ae33, 0x0, 0x7f318f62d840)
	/media/fpmpdrive/fpmp/goyulo/src/github.com/paulmach/orb/geojson/feature.go:86 +0x21f
encoding/json.(*decodeState).object(0xc4200ba240, 0x6efdc0, 0xc4200dfa00, 0x196)
	/usr/local/go/src/encoding/json/decode.go:626 +0x1c9d
encoding/json.(*decodeState).value(0xc4200ba240, 0x6efdc0, 0xc4200dfa00, 0x196)
	/usr/local/go/src/encoding/json/decode.go:408 +0x2d3
encoding/json.(*decodeState).array(0xc4200ba240, 0x6c8120, 0xc42530c0a8, 0x197)
	/usr/local/go/src/encoding/json/decode.go:583 +0x1d0
encoding/json.(*decodeState).value(0xc4200ba240, 0x6c8120, 0xc42530c0a8, 0x197)
	/usr/local/go/src/encoding/json/decode.go:405 +0x266
encoding/json.(*decodeState).object(0xc4200ba240, 0x6e83e0, 0xc42530c080, 0x16)
	/usr/local/go/src/encoding/json/decode.go:776 +0x132d
encoding/json.(*decodeState).value(0xc4200ba240, 0x6e83e0, 0xc42530c080, 0x16)
	/usr/local/go/src/encoding/json/decode.go:408 +0x2d3
encoding/json.(*decodeState).unmarshal(0xc4200ba240, 0x6e83e0, 0xc42530c080, 0x0, 0x0)
	/usr/local/go/src/encoding/json/decode.go:189 +0x1e7
encoding/json.Unmarshal(0xc42bd76000, 0xbf92952, 0xbf92b52, 0x6e83e0, 0xc42530c080, 0xbf92952, 0xbf92b52)
	/usr/local/go/src/encoding/json/decode.go:108 +0x148
github.com/paulmach/orb/geojson.UnmarshalFeatureCollection(0xc42bd76000, 0xbf92952, 0xbf92b52, 0xbf92952, 0xbf92b52, 0x0)
	/media/fpmpdrive/fpmp/goyulo/src/github.com/paulmach/orb/geojson/feature_collection.go:58 +0x6e
main.main()
	/media/fpmpdrive/fpmp/goyulo/src/yuloserver/main.go:61 +0x225
exit status 2

This was apparently caused missing geometries in the file (for statistical geographical classifications with no true spatial elements) and subsequently a pointer error when UnmarshallJSON. I fixed this by removing features will missing geometries in Python and resaving the file.

This scenario may well happen again to other users, especially if other data providers include features without spatial elements. Do you think it wise to put a check in to see if the geometry is present, or provide a specific error message?

polygons are inconsistent with geojson standard

According to geojson specification, polygon can contain closed Linear rings with 4 or more coordinates.
https://tools.ietf.org/html/rfc7946#section-3.1.6

image (2)

However, the version 0.1.6 allows to create polygons with only 3 coordinates which is incosistent with the specification.

	poly := orb.Polygon{
		{
			{48.01300048828125, -21.32305807356671},
			{48.01300048828125, -21.32305807356671},
			{48.01300048828125, -21.32305807356671},
		},
	}
	fmt.Println(poly)

Compressed mvt data is not handled

Some packages such as Tegola generates mvt tiles which are already compressed in gzip format, when I try to unmarshal these tiles with orb I get a generic EOF exception and that con lead to big wastes of developer time in debugging therefore I believe that a detection of compressed tiles (reading the first bytes of the input is enough to detect the compression) and a clear exception can be really helpful to developers which uses this package, even an implicit decompression feature can be implemented but I believe that leaving the eventual decompression of the tiles to another library while keeping this library focused towards parsing and processing of geospatial data is more respectful of the single responsibility principle.

In any case I'd be happy to contribute to the library implementing the detection and the eventual implicit decompression of compressed vector tiles.

"cilp" is unstable running!

Hello, Paul Mach
This project is very good and it gives me a lot of help at work. I am currently cutting Geojson using "clip". I see an unstable situation with "clip": the result of calculating the output in the same data is not.
Codes:

        fc,err:=geojson.UnmarshalFeatureCollection(b)
	mset:=maptile.Set{}
	for i:=range fc.Features{
		ms:=tilecover.Geometry(fc.Features[i].Geometry,11)
		for k,_:=range ms{
			mset[k]=true
			cps:=clip.Geometry(k.Bound().Bound(),fc.Features[i].Geometry)
			if cps!=nil{
				fmt.Println(k,len(cps.(orb.Polygon)[0]))
			}else
                        {
				fmt.Println(k,"clip error")
			}

		}
	}
	fmt.Println(len(mset))

After running multiple times, sometimes output:
{1684 850 11} 5
{1685 851 11} 7
{1685 850 11} 9
3
Sometimes output:
{1684 850 11} 5
{1685 851 11} 7
{1685 850 11} error
3

thank you very much!
GeoJson:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              116.22367858886719,
              29.165353121242656
            ],
            [
              116.18453979492186,
              29.136568744954467
            ],
            [
              116.16874694824217,
              29.11917427664377
            ],
            [
              116.19483947753906,
              29.07837571206272
            ],
            [
              116.20101928710936,
              29.045965338037213
            ],
            [
              116.21406555175781,
              29.019549354428978
            ],
            [
              116.24015808105469,
              29.003936732742975
            ],
            [
              116.32530212402342,
              29.010542360621983
            ],
            [
              116.36032104492186,
              29.02555358063341
            ],
            [
              116.36581420898438,
              29.062771988976138
            ],
            [
              116.3294219970703,
              29.09997696628295
            ],
            [
              116.26281738281249,
              29.162954731306638
            ],
            [
              116.22367858886719,
              29.165353121242656
            ]
          ]
        ]
      }
    }
  ]
}

API question: Quadtree + Pointer interop with GeoJson types

So my heart a skipped a beat or two when I noticed that there is a now a QuadTree implementation. THANK YOU! :)

I am reading a tens of thousands of hexagons in a city from a single GeoJSON file. Naturally, I upon using UnmarshalFeatureCollection, I end up with a bunch of Polygon feature (not Ring, even though they suffice for my use case)

Now, if I start from the problem statement: "How do I use this Quadtree implementation to find which hexagon (if any) do these million points lie in", I initialize a QuadTree and then notice that I must have an orb.Pointer, which none of the geojson types implement out-of-the-box.

Therefore, the sequence of steps to initialize a QuadTree and uniquely identify the hexagon where an input Point lies must be as follows. Does this sound right?

  1. Parse GeoJSON FeatureCollection using UnmarshalFeatureCollection

  2. Create a wrapper struct type that (a) implements orb.Pointer perhaps by calculating centroid of Lat and Lng vertices of the wrapped geojson.Polygon, (b) encapsulates a geojson.Polygon , and (c) keeps track of index of geojson.Polygon in the geojson.FeatureCollection slice from (1) above.

  3. Iterate through the FeatureCollection and make and Add() the wrapped struct type to the QuadTree

  4. Use Find() to identify the hexagon given a orb.Point to search, type assert the returned orb.Pointer to the wrapper type and extract index from (2)(c) to lookup the index number from the geojson.FeatureCollection slice.

The two questions I am wondering about :
a) Should key types on package geojson have a stock implementation of orb.Pointer ?
b) Should there be more a natural way to process the return value of Find() to the collection of items fed into the QuadTree?

Thanks,
Siddharth

How to close clipped polygon?

Hi,
I am using polygon clipping to render map tiles with areas that are overlapping it but I am ending up only with points in the tile that belong to the overlapping area.
invert

See the triangles - basically they are this way only because I am closing the line in drawing library, otherwise there would be just lines on the edges of the tiles, fithout any fill.

And so I wonder how I should "close" the result so it covers the proper area in the tile? In this example the 2nd from the left is missing the bottom right corner point and the third is missing bottom right and top right conres in order to be properly closed.

Problem in connecting with go-pg/orm

Thanks for this nice piece of work.
I want to query a row by go-pg rom. It gives back the following panic

`panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x8fd469]

goroutine 1 [running]:
github.com/go-pg/pg/orm.(*Field).ScanValue(0xc0000c50e0, 0xa97ae0, 0xc0001bb200, 0x199, 0xb6f400, 0xc00012c0e0, 0x12, 0x0, 0x0)
/home/davood/go/src/github.com/go-pg/pg/orm/field.go:86 +0xb9
github.com/go-pg/pg/orm.(*structTableModel).scanColumn(0xc0002be0b0, 0x6, 0xc000249788, 0x3, 0xb6f400, 0xc00012c0e0, 0x12, 0xc000193aa8, 0x950f78, 0xc00012c0e0)
/home/davood/go/src/github.com/go-pg/pg/orm/model_table_struct.go:261 +0x14e
github.com/go-pg/pg/orm.(*structTableModel).ScanColumn(0xc0002be0b0, 0x6, 0xc000249788, 0x3, 0xb6f400, 0xc00012c0e0, 0x12, 0x0, 0x0)
/home/davood/go/src/github.com/go-pg/pg/orm/model_table_struct.go:219 +0x8e
github.com/go-pg/pg.readDataRow(0xc00012c0e0, 0xb698a0, 0xc0002be0b0, 0xc000081b00, 0xf, 0x10, 0x10, 0x0)
/home/davood/go/src/github.com/go-pg/pg/messages.go:760 +0x149
github.com/go-pg/pg.readSimpleQueryData(0xc00012c0e0, 0xa9cbc0, 0xc0002be0b0, 0x0, 0x93c480, 0xbefa5d71b665a1f1)
/home/davood/go/src/github.com/go-pg/pg/messages.go:813 +0x260
github.com/go-pg/pg.(*DB).simpleQueryData.func2(0xc00012c0e0, 0x0, 0x0)
/home/davood/go/src/github.com/go-pg/pg/db.go:511 +0x53
github.com/go-pg/pg/internal/pool.(*Conn).WithReader(0xc0001e2f00, 0x0, 0xc000193ca8, 0x0, 0x0)
/home/davood/go/src/github.com/go-pg/pg/internal/pool/conn.go:90 +0x4c
github.com/go-pg/pg.(*DB).simpleQueryData(0xc00026bef0, 0xc0001e2f00, 0xa9cbc0, 0xc0002be0b0, 0xa47fa0, 0xc000269ca0, 0xc00009f530, 0x1, 0x1, 0x96e4a0, ...)
/home/davood/go/src/github.com/go-pg/pg/db.go:510 +0x187
github.com/go-pg/pg.(*DB).Query(0xc00026bef0, 0xa9cbc0, 0xc0002be0b0, 0xa47fa0, 0xc000269ca0, 0xc00009f530, 0x1, 0x1, 0xc000193e88, 0x40ad9f, ...)
/home/davood/go/src/github.com/go-pg/pg/db.go:257 +0x170
github.com/go-pg/pg/orm.(*Query).query(0xc000224900, 0xb6fdc0, 0xc0002be0b0, 0xa47fa0, 0xc000269ca0, 0x0, 0x0, 0x0, 0xb70340)
/home/davood/go/src/github.com/go-pg/pg/orm/query.go:676 +0x250
github.com/go-pg/pg/orm.(*Query).Select(0xc000224900, 0x0, 0x0, 0x0, 0xc000224900, 0xa41e80)`

My User struct is something like this:

`package model

import "github.com/paulmach/orb"

type Users struct {
Id
FirstName string
MiddleName string
LastName string
UserName string sql:",notnull, unique"
Password string sql:",notnull"
Loc orb.Point sql:",type:Point"
}
`Would you pls help how should I figure it out?

From Polygon to Tile - and back again

I am trying to use your code to use your code to split a Geometry ( Polygon ) into smaller roughly equally sized Polygons.

My plan was to convert the polygon to tiles, divide the tiles as many sets as I need, and then go back to a polygon finding the edges of the new shapes.

Going from polygon to tiles, and splitting them works fine, but going back seems not to be as easy. I have merged the new polygons, and could perhaps use the centre point of each tiles as edges of the new polygon - but I am not sure about the importance of the ordering of the points.

Any tips ?

Mapbox Vector Tile Unmarshal Failed: proto: wrong wireType = 7 for field Layers

I downloaded the sample mvt from Mapbox to try the encoding/mvt library, but error occurred when unmarshal it.

The mvt file comes from:

curl https://api.mapbox.com/v4/mapbox.mapbox-streets-v6/9/150/194.mvt\?access_token\=YOUR_MAPBOX_ACCESS_TOKEN  >3.mvt

My code looks like:

		content, err := ioutil.ReadFile("3.mvt")
		if err != nil {
			log.Fatal(err)
		}

		layers, err := mvt.Unmarshal(content)
		if err != nil {
			log.Fatal(err)
		}

Then I got error 2020/02/03 11:00:35 proto: wrong wireType = 7 for field Layers.

But use the tool mapbox/vt2geojson I can get unmarshalled geojson successfully:

vt2geojson https://api.mapbox.com/v4/mapbox.mapbox-streets-v6/9/150/194.mvt\?access_token\=YOUR_MAPBOX_ACCESS_TOKEN >3.json

Anything wrong with the encoding/mvt library?

I have uploaded both the .mvt and .json in the 3.zip, FYI.

geojson.Geometry does not implement orb.Geometry (missing Bound method)

For me it would make sense to be able to cast a geojson feature orb.Geometry to a geojson.Geometry?

var f *geojson.Feature
g := f.Geometry.(geojson.Geometry)

geojson.Geometry does not implement orb.Geometry missing

func (g Geometry) Bound() orb.Bound {
         // todo
	return orb.Bound{}
}

func (g Geometry) Dimensions() int {
	return 2
}

func (g Geometry) GeoJSONType() string {
	return g.Type
}

// Still don't understand why this private thing XD?
func (g Geometry) private() {}

Add support for EWKB/EWKT

Hi! We're relying a lot at work on this package to handle geometries, and it works like a charm, but we have an issue when storing geometries inside postgis. We need to specify an SRID for the geometry, to obtain meaningful results with certain database operations, and we are currently converting back and forth between the geometries defined in go-geom and the geometries of orb, which is not ideal performance-wise. We still want to rely on orb for in-memory management of geometries, since it has lots of useful in-memory operations implemented.

If it's ok with you I'd like to try and submit a PR to add support for EWKB/EWKT serialization. I'm just not sure on how to structure it, and how to share code between the current WKB/WKT implementation, so I'm open to suggestions on this topic.

Would you be open to changing the signatures in maptile/tilecover ?

Specifically, changing the return values from maptile.Set to (maptile.Set, error)

My concern is that the /maptile/tilecover/polygon.go code triggers a panic if it encounters an invalid geometry and it would be helpful for the package to return an error that I can handle in my application code rather than all the scaffolding necessary to trap and recover from a panic.

I am happy to do the work to produce a PR but figured I would see whether this was interesting to you, first.

Error when inserting a point into MariaDB.

Hello! I have this problem. I populated the database using PointFromText and select queries work correctly. But I can't insert a new Point into the table.

_, err := tx.ExecContext(ctx, `
      INSERT INTO localities (
	      title,
	      coords
      )
      VALUES (?, ST_GeomFromWKB(?))
`,
      locality.Title,
      wkb.Value(locality.Coords),
)

Error 4079: Illegal parameter data type char for operation 'st_geometryfromwkb'

incorrect result from Quadkey()

As per bing maps, given tile XY coordinates of (3, 5) at level 3, the result should be a string “213”.

But Quadkey() from orb package for the same tile returns a uint64 of 39. Why are the results different?
func (t Tile) Quadkey() uint64

Wrong output from mysql

c1 := new(sql.RawBytes)
c2 := orb.Point{}
c3 := orb.Point{}
client.QueryRow("SELECT `ID`,`Point`,`PtrPoint` FROM `sqlike`.`spatial` WHERE `ID` = 1 LIMIT 1;").Scan(&c1, wkb.Scanner(&c2), wkb.Scanner(&c3))

This is the code snippet of my program, the expected value for c2 is Point [1 5], but it showed Point [7.291122019556398e-304 1.30509107131737e-309] instead. This problem occurs because MySQL returning the binary in format of SRID+WKB, every data retrieve back from MySQL we should truncate the first 4 bytes. You may refer to the below snippet.

	case *orb.Point:
                 // Most likely MySQL's SRID+WKB format.
		if len(data) == 25 {
			data = data[4:]
		}
		p, err := scanPoint(data)
		if err != nil {
			return err
		}

geojson.UnmarshalGeometry() fails to detect an invalid geojson

I have this geojson,

{
    "coordinates": [
        [
            [
                39.793632515714904,
                75.04223433164586
            ],
            null,
            [
                40.36804504271686,
                74.37994240330923
            ],
            [
                38.396554228796475,
                74.38108104787864
            ],
            [
                37.83643684788231,
                75.0431604849482
            ],
            [
                39.793632515714904,
                75.04223433164586
            ]
        ]
    ],
    "type": "Polygon",
    "srid": "epsg:4326"
}

which has a null in it. When I try to use the following code check if the above is good or not, my expectation is that geojson.UnmarshalGeometry will fail. But that is not happening.

        f, err := json.Marshal(_abovejson)
	if err != nil {
		return err
	}

	_footprint, err := geojson.UnmarshalGeometry(f)
	if err != nil {
		return err
	}

What am I missing?

ErrNotWKB not in effect

MSYQL column value: POINT(1 1)
i do not strip the first 4 bytes
after wkb process,get: POINT(7.291122019556398e-304 1.30509107131737e-309)
ErrNotWKB not in effect

Parsing of WKT

Would you interested in adding support for parsing WKT?

If so I'd be up to try that.

update geojson properties

Over at go.geojson, the library has PropertyBool, PropertyInt, etc, but the version of geojson in orb doesn't. Also one uses PropertyMustBool while the other uses MustBool, etc.

Could we get the geojson that's in orb to match what's in go.geojson?

Missing check if tile projected coordinates are in larger extent

In order to use the generated vector tiles as a source for mapbox all coordinates in a tile must not exceed a certain extent. (https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-vector)

x, y := mercator.ToPlanar(p[0], p[1], z)
return orb.Point{
math.Floor(x - minx),
math.Floor(y - miny),
}

Maybe the projection could also return if the point should be included or not?

Was this check left out by choice?

Incorrect centroids on geometries with holes

Observed

For geometries with holes, the planar.CentroidArea() function is returning incorrect centroids (the planar area returned is correct though).

To reproduce

Note that this bug has been observed on other geometries, but this is the one I've chosen as an example:

package main

import (
	"fmt"
	"github.com/paulmach/orb"
	"github.com/paulmach/orb/planar"
)

func main() {
	geom := orb.Polygon{
		{
			{
				-102.2690493,
				40.9939916,
			},
			{
				-102.268951,
				40.9940453,
			},
			{
				-102.2690464,
				40.9941449,
			},
			{
				-102.2689287,
				40.9942091,
			},
			{
				-102.2688078,
				40.9940829,
			},
			{
				-102.2684138,
				40.9942979,
			},
			{
				-102.2683195,
				40.9941995,
			},
			{
				-102.2684496,
				40.9941286,
			},
			{
				-102.2683239,
				40.9939973,
			},
			{
				-102.2682875,
				40.9940171,
			},
			{
				-102.2682667,
				40.9939954,
			},
			{
				-102.2680722,
				40.9941015,
			},
			{
				-102.2679264,
				40.9939493,
			},
			{
				-102.2680433,
				40.9938855,
			},
			{
				-102.268059,
				40.9939019,
			},
			{
				-102.2682353,
				40.9938057,
			},
			{
				-102.2682124,
				40.9937818,
			},
			{
				-102.2682594,
				40.9937561,
			},
			{
				-102.2682808,
				40.9937785,
			},
			{
				-102.2683036,
				40.993766,
			},
			{
				-102.2683272,
				40.9937907,
			},
			{
				-102.2685813,
				40.9936521,
			},
			{
				-102.2688361,
				40.9939181,
			},
			{
				-102.2689632,
				40.9938488,
			},
			{
				-102.2690246,
				40.993913,
			},
			{
				-102.2689913,
				40.9939311,
			},
			{
				-102https://github.com/paulmach/orb/blob/master/planar/area.go#L197.2690493,
				40.9939916,
			},
		},
		{
			{
				-102.2687189,
				40.9939914,
			},
			{
				-102.2685563,
				40.9938227,
			},
			{
				-102.2684289,
				40.9938926,
			},
			{
				-102.2684666,
				40.9939317,
			},
			{
				-102.2684333,
				40.99395,
			},
			{
				-102.2685582,
				40.9940796,
			},
			{
				-102.2687189,
				40.9939914,
			},
		},
	}
	centroid, _ := planar.CentroidArea(geom)
	fmt.Println(centroid)
}

Running this code produces the following output:

[-115.88899555567677 46.45368396555677]

Expected

Using other tools (in this case, shapely) produces the following, which can be visually verified as being correct/expected:

[-102.2685248962348 40.99397171799407]

Possible causes

One bit of code I ran into while trying to debug is in the implementation for planar.CentroidArea(). I have read in some other implementations (namely libgeos) that they use the "signed" area, rather than the absolute value, as orb uses here. I'm not 100% sure that this is the cause, but wanted to ask about this logic.

Generate multiple tiles from same GeoJSON FeatureCollections

Okay, I'm kind of new to Go so sorry if this a dumb question, but I ran into some difficulties when creating more than just one mvt from multiple GeoJSON FeatureCollections.
I would argue that it's a fairly standard thing users would do, because no one will just generate a single tile and be done.

The example in the README looks like this:

// Start with a set of feature collections defining each layer in lon/lat (WGS84).
collections := map[string]*geojson.FeatureCollection{}

// Convert to a layers object and project to tile coordinates.
layers := mvt.NewLayers(collections)
layers.ProjectToTile(maptile.New(x, y, z))

// encoding using the Mapbox Vector Tile protobuf encoding.
data, err := layers.MarshalGzipped()

But if you want to create multiple tiles from the same layers this misses a crucial step. Before projecting the layers to a new tile you now have to project the layers back to WGS84 with layers.ProjectToWGS84 (took me way to long to figure that out so it would be nice to have a short paragraph in the README that explains that)
Is that the intended way or is there another way to generate multiple files from the same GeoJSON FeatureCollections?

If that is the intended way I want to illustrate, why this workflow is far from optimal:
The root of the problem is that the projection seems to work completely in-place, which means in the example above using ProjectToTile changes even the coordinates of the features in the collections-map. Imho projecting the Layers shouldn't change the original data or there should at least be a disclaimer that this is the case.

This "everything in-place" approach basically prevents any multi-threading when generating MVTs, which is imho ideal.

The other problem is that the projection is not 100% accurate (which is acceptable), but this means if I'm working with same Layers to generate multiple tiles the coordinates change ever so slightly every time I project from WGS84 to a tile and then back. So if I'm generating for example LODs 0 through 8 the coordinates on LOD 0 will be spot on but will be inaccurate on LOD 8, because at the point the first LOD 8 tile is being generated we already projected 43690 (2 * 21845) times and before the last time we projected 174761 times.

Supporting circles as a native data type

Hello,

Would you interested in adding support for circles as a native data type?

At least planar circles, not spherical caps. I know it is a curve, not a well defined polygon. It is also not supported by WKT.

But, it does help a use case I currently work with. And since I use Orb as my de-facto geometry library here, I'd like to implement the feature myself for Orb.

It could be a struct with a point inside or a []float64{} with 3 members. The latter might impede in supporting 3D points in future.

Quadtree KNearest not in sorted order

Inconsistent sorting of results slice with the KNearest function. The reduced test case below produces out of order results despite the recent enhancements in #67. Note that I am not passing the optional maxDistance attribute to KNearest.

Expected result: results slice in closest to farther ordering
Actual result: certain slice elements in unexpected ordering

package main

import (
	"testing"

	"github.com/paulmach/orb"
	"github.com/paulmach/orb/geo"
	"github.com/paulmach/orb/quadtree"
)

// place implements the orb.Pointer interface so that it may be added to a QuadTree
type place struct {
	Id       string // name of the city
	Location orb.Point
}

func (p place) Point() orb.Point {
	return p.Location
}

var destinations = []place{
	{"Portland (Oregon)", orb.Point{-122.6784, 45.5152}},
	{"Sydney", orb.Point{151.2093, -33.8688}},
	{"Jakarta", orb.Point{106.8456, -6.200000}},
	{"Tunis", orb.Point{10.181667, 36.806389}},
	{"Paris", orb.Point{2.3522, 48.8566}},
}

func TestQuadtreeKnn(t *testing.T) {
	qt := quadtree.New(orb.Bound{Min: orb.Point{-180, -90}, Max: orb.Point{180, 90}})
	for _, dest := range destinations {
		qt.Add(dest)
	}
	maxResults := 5

	// for the following cities, find the maxResults nearest neighbours
	fromCities := []place{
		{"London", orb.Point{-0.118092, 51.509865}},
	}

	for _, from := range fromCities {
		t.Run(from.Id, func(t *testing.T) {
			t.Logf("%s : %d nearest places to %v \n", from.Id, maxResults, from.Location)
			results := qt.KNearest(nil, from.Point(), maxResults)
			prevKm := 0.0
			for i, dest := range results {
				p := dest.(place)
				dKm := geo.DistanceHaversine(from.Location, p.Location) / 1000
				t.Logf("[%s] at sorted index %d : %s, %.0f km away (%+v)\n", from.Id, i, p.Id, dKm, p.Location)
				if prevKm > dKm {
					t.Errorf("FAIL: Out of order results, previous = %.0f km > this = %.0f km", prevKm, dKm)
				}
				prevKm = dKm
			}
		})
	}
}

Add NULL support to wkb

The package encoding/wkb seems to have issues if a null point is passed into a scanner.

Example:

package main

import (
	"database/sql"
	"log"

	_ "github.com/lib/pq"
	"github.com/paulmach/orb"
	"github.com/paulmach/orb/encoding/wkb"
)

func main() {
	db, _ := sql.Open("postgres", "postgres://localhost/writing?sslmode=disable")
	if err := db.Ping(); err != nil {
		log.Panic(err.Error())
	}

	row := db.QueryRow("SELECT ST_AsBinary(NULL::geometry)")
	var p orb.Point
	err := row.Scan(wkb.Scanner(&p))
	if err != nil {
		log.Panic(err.Error())
	}
}

Output:

$ go run main.go
2019/02/03 17:12:28 sql: Scan error on column index 0, name "st_asbinary": wkb: scan value must be []byte
panic: sql: Scan error on column index 0, name "st_asbinary": wkb: scan value must be []byte

goroutine 1 [running]:
log.Panic(0xc0000e9f58, 0x1, 0x1)
	/usr/local/Cellar/go/1.11.5/libexec/src/log/log.go:326 +0xc0
main.main()
	/Users/nat/tmp/example/main.go:22 +0x1f2
exit status 2

This is talking to a postgres 9.6 server.

Intersects

Hi, I've noticed that this library does not contain the Intersection and Intersects methods that were in your deprecated library geo. Are there plans for them to be implemented in this library as well?

Crash from maptile/tilecover/polygon.go

I am getting a crash from the library - it seems the intersections array can have an uneven number of elements.

panic: runtime error: index out of range [305] with length 305

goroutine 1 [running]:
github.com/paulmach/orb/maptile/tilecover.polygon(0xc000090810, 0xc0000a66c0, 0x1, 0x1, 0x19)
	/Users/tgilbert/code/go/src/github.com/paulmach/orb/maptile/tilecover/polygon.go:82 +0x445
github.com/paulmach/orb/maptile/tilecover.Polygon(...)
	/Users/tgilbert/code/go/src/github.com/paulmach/orb/maptile/tilecover/polygon.go:22
github.com/paulmach/orb/maptile/tilecover.Geometry(0x113a220, 0xc0000a6980, 0x19, 0xc0000a6980)
	/Users/tgilbert/code/go/src/github.com/paulmach/orb/maptile/tilecover/helpers.go:29 +0xf0
github.com/alexandrainst/agentlogic.(*Mission).GeneratePath(0xc0000cfad0, 0x11123d0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/Users/tgilbert/code/go/src/github.com/alexandrainst/agentlogic/mission.go:188 +0x9e
main.main()
	/Users/tgilbert/code/go/src/github.com/alexandrainst/agentlogic/examples/main.go:72 +0x9c5

Deep copy of FeatureCollection and Feature?

Paul,

Do you have any guidance on how to solve this use case?

Step 1: I have an input GeoJSON that has dozens of hexagonal Polygons for a given city. I unmarshal this and have a FeatureCollection or its wrapper struct initialized.

Step 2: After binning a million geo-tagged input data samples from say three mobile networks ("Sprint", "AT&T", "Verizon") into each of the above-mentioned hexagonal bins, I have some statistics for a given network for each of the grids (Features) with the city.

Step 3: I wish to now serialize three GeoJSONs (one per mobile network) with all of the information from Step (1) + stats from Step (2) inserted inside Feature->Properties->"QoS". With these GeoJSONs, I have easy ability to build three independent data sources for the mobile networks.

The problem: Since we use *FeatureCollection and *Feature everywhere, the only way to initialize a fresh copy of FeatureCollection and Feature that can be stuffed with an independent copy of "extra" data (such as Sprint's metrics for a hexagon without clobbering Verizon's stats) is to Marshal and Unmarshal to text repeatedly.

What am I missing? :)

Max points limit of 5000 when decoding WKB

Hi,

I am loading polygons that have many points, e.g. Swiss cantons have >10k points. This limit (maxPointsAlloc) just cuts everything to 5k, which would be fine if the cutoff didn't mess silently with the polygon - if there is only one Ring, there will be just the first 5k points, and if there are multiple Rings, decoding produces garbage data, and that is noticed only when actually looking at the data.

Also, it is not currently possible to change this limit, it is hard coded.

I would add a way to change the global limit, or add limit per decoder when constructing the object.

I would also add an error when the number is over the limit.

I am willing to invest some time to fix this, but I am asking here first in case there is some reason that this exists at all? It might as well be removed.

Enhancement: KNearest results in closest-first order

Enhancement request: It would be great if Quadtree.KNearest returned the results slice in distance-sorted order.

Current implementation returns it in a non-deterministic order, hence an additional Sort.Slice() appears to be required.

Results for haverstine seem slightly off.

I performed the calculations for haverstine distance between machida jp and shibuya jp.

The results I was expecting were around 26.416 km based on some web calculator... and with the following equation 26.409 km based on my personal calculator..

2(6371)asin(sqrt(sin(-0.001995/2)^2+cos(0.620364)cos(0.622358)sin(-0.004469/2)^2)

I received the following results.

Provided:

package main
import (
    "fmt"
    kdg "github.com/kellydunn/golang-geo"
    "github.com/paulmach/orb"
    "github.com/paulmach/orb/geo"
)
func main() {
    machida := orb.Point{139.445297, 35.544214}
    shibuya := orb.Point{139.70133, 35.658514}
    // 精度は割とよいけど完璧ではない
    fmt.Println("1. ", geo.DistanceHaversine(machida, shibuya))
    // 精度は低いと haversine より短いはず
    fmt.Println("2. ", geo.Distance(machida, shibuya))
    machida2 := kdg.NewPoint(35.544214, 139.445297)
    shibuya2 := kdg.NewPoint(35.658514, 139.70133)
    // 上記の haversine と同じはず
    fmt.Println("3. ", machida2.GreatCircleDistance(shibuya2))
}

I receive:

1. 26437.412389932888
2. 26437.42666610175
3. 26.40782948630022

I am assuming orb gives the output in meters.

While the output is close I have the following concerns.

  1. Even for the Haversine Formula, a difference of ~28m is fairly large?
  2. result 2 is larger than result 1, I would think this to be opposite?

Any clarification in the differences would be highly appreciated. (as I like this libraries implementation very much)

Addition key/value pairs for FeatureCollection

Currently I have this:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
...

I want to add some generator information:

{
  "type": "FeatureCollection",
  "generator": "printmaps",
  "timestamp": "2018-10-23T06:21:02Z",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
...

Question: Is this somehow possible?

GeoJSON : Elevation

Hello Paul,

Orb is a fantastic library and thank you for all of your hard work. It appears that geoJson supports and optional field in the coordinates specifying an elevation. Are you planning on supporting this in the future?

Thanks again for the great library.

mvt encoding with null properties crashes

If I try to create a mapbox vector tile from geojson that contains properties with null values it panics.

Example:

	func main(){
		rawJSON := []byte(`
		{ "type": "FeatureCollection",
		"features": [
		  { "type": "Feature",
			"geometry": {"type": "Point", "coordinates": [1, 1]},
			"properties": {"test": null}
		  }
		]
		}`)
		collections := map[string]*geojson.FeatureCollection{}
		fc, _ := geojson.UnmarshalFeatureCollection(rawJSON)

		collections["test"] = fc
		layers := mvt.NewLayers(collections)

		layers.ProjectToTile(maptile.New(0,0,0))

		data, err := mvt.Marshal(layers)

		if err != nil{
			fmt.Errorf("Error: %v\n", err)
		}
		base64 := base642.StdEncoding.EncodeToString(data)
		fmt.Printf("%s\n", base64)
	}

Error in README example

It seems ST_GeomFromWKB is missing in the README example:

db.Exec("INSERT INTO table (point_column) VALUES (ST_GeomFromWKB(?))", wkb.Value(p))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.