read properties of geojson features
parent
85720d3be1
commit
593811a231
|
@ -3,6 +3,7 @@ package geojson
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"imposm3/geom/geos"
|
||||
"imposm3/proj"
|
||||
"io"
|
||||
|
@ -68,6 +69,24 @@ func newLineStringFromCoords(coords []interface{}) (lineString, error) {
|
|||
|
||||
type polygon []lineString
|
||||
|
||||
type polygonFeature struct {
|
||||
polygon polygon
|
||||
properties map[string]string
|
||||
}
|
||||
|
||||
type Feature struct {
|
||||
Geom *geos.Geom
|
||||
Properties map[string]string
|
||||
}
|
||||
|
||||
func stringProperties(properties map[string]interface{}) map[string]string {
|
||||
result := make(map[string]string, len(properties))
|
||||
for k, v := range properties {
|
||||
result[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func newPolygonFromCoords(coords []interface{}) (polygon, error) {
|
||||
poly := polygon{}
|
||||
|
||||
|
@ -85,24 +104,24 @@ func newPolygonFromCoords(coords []interface{}) (polygon, error) {
|
|||
return poly, nil
|
||||
}
|
||||
|
||||
func newMultiPolygonFromCoords(coords []interface{}) ([]polygon, error) {
|
||||
mp := []polygon{}
|
||||
func newMultiPolygonFeaturesFromCoords(coords []interface{}) ([]polygonFeature, error) {
|
||||
features := []polygonFeature{}
|
||||
|
||||
for _, part := range coords {
|
||||
polyCoords, ok := part.([]interface{})
|
||||
if !ok {
|
||||
return mp, errors.New("multipolygon polygon not a list")
|
||||
return features, errors.New("multipolygon polygon not a list")
|
||||
}
|
||||
poly, err := newPolygonFromCoords(polyCoords)
|
||||
if err != nil {
|
||||
return mp, err
|
||||
return features, err
|
||||
}
|
||||
mp = append(mp, poly)
|
||||
features = append(features, polygonFeature{poly, nil})
|
||||
}
|
||||
return mp, nil
|
||||
return features, nil
|
||||
}
|
||||
|
||||
func ParseGeoJson(r io.Reader) ([]*geos.Geom, error) {
|
||||
func ParseGeoJson(r io.Reader) ([]Feature, error) {
|
||||
decoder := json.NewDecoder(r)
|
||||
|
||||
obj := &object{}
|
||||
|
@ -111,7 +130,7 @@ func ParseGeoJson(r io.Reader) ([]*geos.Geom, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
polygons, err := constructPolygons(obj)
|
||||
polygons, err := constructPolygonFeatures(obj)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -119,20 +138,20 @@ func ParseGeoJson(r io.Reader) ([]*geos.Geom, error) {
|
|||
|
||||
g := geos.NewGeos()
|
||||
defer g.Finish()
|
||||
result := []*geos.Geom{}
|
||||
result := []Feature{}
|
||||
|
||||
for _, p := range polygons {
|
||||
geom, err := geosPolygon(g, p)
|
||||
geom, err := geosPolygon(g, p.polygon)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, geom)
|
||||
result = append(result, Feature{geom, p.properties})
|
||||
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func constructPolygons(obj *object) ([]polygon, error) {
|
||||
func constructPolygonFeatures(obj *object) ([]polygonFeature, error) {
|
||||
switch obj.Type {
|
||||
case "Point":
|
||||
return nil, errors.New("only polygon or MultiPolygon are supported")
|
||||
|
@ -140,22 +159,29 @@ func constructPolygons(obj *object) ([]polygon, error) {
|
|||
return nil, errors.New("only polygon or MultiPolygon are supported")
|
||||
case "Polygon":
|
||||
poly, err := newPolygonFromCoords(obj.Coordinates)
|
||||
return []polygon{poly}, err
|
||||
return []polygonFeature{{poly, nil}}, err
|
||||
case "MultiPolygon":
|
||||
poly, err := newMultiPolygonFromCoords(obj.Coordinates)
|
||||
poly, err := newMultiPolygonFeaturesFromCoords(obj.Coordinates)
|
||||
return poly, err
|
||||
case "Feature":
|
||||
geom, err := constructPolygons(obj.Geometry)
|
||||
return geom, err
|
||||
features, err := constructPolygonFeatures(obj.Geometry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
properties := stringProperties(obj.Properties)
|
||||
for i, _ := range features {
|
||||
features[i].properties = properties
|
||||
}
|
||||
return features, err
|
||||
case "FeatureCollection":
|
||||
features := make([]polygon, 0)
|
||||
features := make([]polygonFeature, 0)
|
||||
|
||||
for _, obj := range obj.Features {
|
||||
geom, err := constructPolygons(&obj)
|
||||
f, err := constructPolygonFeatures(&obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
features = append(features, geom...)
|
||||
features = append(features, f...)
|
||||
}
|
||||
return features, nil
|
||||
default:
|
||||
|
|
|
@ -8,49 +8,49 @@ import (
|
|||
|
||||
func TestParsePolygon(t *testing.T) {
|
||||
r := bytes.NewBufferString(`{"type": "Polygon", "coordinates": [[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]]]}`)
|
||||
geoms, err := ParseGeoJson(r)
|
||||
features, err := ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 1 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 1 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
|
||||
if math.Abs(geoms[0].Area()-1000000) > 0.00001 {
|
||||
t.Fatal(geoms[0].Area())
|
||||
if math.Abs(features[0].Geom.Area()-1000000) > 0.00001 {
|
||||
t.Fatal(features[0].Geom.Area())
|
||||
}
|
||||
|
||||
// ignore z values
|
||||
r = bytes.NewBufferString(`{"type": "Polygon", "coordinates": [[[1000, 1000, 1000], [2000, 1000, 1000], [2000, 2000, 1000], [1000, 2000, 1000], [1000, 1000, 1000]]]}`)
|
||||
geoms, err = ParseGeoJson(r)
|
||||
features, err = ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 1 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 1 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
|
||||
if math.Abs(geoms[0].Area()-1000000) > 0.00001 {
|
||||
t.Fatal(geoms[0].Area())
|
||||
if math.Abs(features[0].Geom.Area()-1000000) > 0.00001 {
|
||||
t.Fatal(features[0].Geom.Area())
|
||||
}
|
||||
|
||||
r = bytes.NewBufferString(`{"type": "Polygon", "coordinates": [[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]], [[500, 500], [600, 500], [600, 600], [500, 600], [500, 500]]]}`)
|
||||
geoms, err = ParseGeoJson(r)
|
||||
features, err = ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 1 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 1 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
|
||||
if math.Abs(geoms[0].Area()-990000) > 0.00001 {
|
||||
t.Fatal(geoms[0].Area())
|
||||
if math.Abs(features[0].Geom.Area()-990000) > 0.00001 {
|
||||
t.Fatal(features[0].Geom.Area())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,14 +60,14 @@ func TestParseMultiPolygon(t *testing.T) {
|
|||
[[[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 1000]]],
|
||||
[[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 1000]]]]
|
||||
}`)
|
||||
geoms, err := ParseGeoJson(r)
|
||||
features, err := ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 2 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 2 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,17 +75,17 @@ func TestParseFeature(t *testing.T) {
|
|||
r := bytes.NewBufferString(`{"type": "Feature", "geometry": {
|
||||
"type": "Polygon", "coordinates": [[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]]]
|
||||
}}`)
|
||||
geoms, err := ParseGeoJson(r)
|
||||
features, err := ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 1 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 1 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
if math.Abs(geoms[0].Area()-1000000) > 0.00001 {
|
||||
t.Fatal(geoms[0].Area())
|
||||
if math.Abs(features[0].Geom.Area()-1000000) > 0.00001 {
|
||||
t.Fatal(features[0].Geom.Area())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,40 +98,47 @@ func TestParseFeatureCollection(t *testing.T) {
|
|||
{"type": "Polygon", "coordinates": [[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]]]}
|
||||
}
|
||||
]}`)
|
||||
geoms, err := ParseGeoJson(r)
|
||||
features, err := ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 2 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 2 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
if math.Abs(geoms[0].Area()-1000000) > 0.00001 {
|
||||
t.Fatal(geoms[0].Area())
|
||||
if math.Abs(features[0].Geom.Area()-1000000) > 0.00001 {
|
||||
t.Fatal(features[0].Geom.Area())
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseGeoJson(t *testing.T) {
|
||||
r := bytes.NewBufferString(`{"type": "FeatureCollection", "features": [
|
||||
{"type": "Feature", "geometry":
|
||||
{"type": "Feature", "properties": {"foo": "bar", "baz": 42}, "geometry":
|
||||
{"type": "Polygon", "coordinates": [[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]]]}
|
||||
},
|
||||
{"type": "Feature", "geometry":
|
||||
{"type": "Polygon", "coordinates": [[[1000, 1000], [2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]]]}
|
||||
}
|
||||
]}`)
|
||||
geoms, err := ParseGeoJson(r)
|
||||
features, err := ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 2 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 2 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
if math.Abs(geoms[0].Area()-1000000) > 0.00001 {
|
||||
t.Fatal(geoms[0].Area())
|
||||
if v, ok := features[0].Properties["foo"]; !ok || v != "bar" {
|
||||
t.Errorf("foo != bar, but '%v'", v)
|
||||
}
|
||||
if v, ok := features[0].Properties["baz"]; !ok || v != "42" {
|
||||
t.Errorf("baz != 42, but '%v'", v)
|
||||
}
|
||||
|
||||
if math.Abs(features[0].Geom.Area()-1000000) > 0.00001 {
|
||||
t.Fatal(features[0].Geom.Area())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,16 +152,16 @@ func TestParseGeoJsonTransform(t *testing.T) {
|
|||
{"type": "Polygon", "coordinates": [[[9, 53], [10, 53], [10, 54], [9, 54], [9, 53]]]}
|
||||
}
|
||||
]}`)
|
||||
geoms, err := ParseGeoJson(r)
|
||||
features, err := ParseGeoJson(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(geoms) != 2 {
|
||||
t.Fatal(geoms)
|
||||
if len(features) != 2 {
|
||||
t.Fatal(features)
|
||||
}
|
||||
if math.Abs(geoms[0].Area()-20834374847.98027) > 0.01 {
|
||||
t.Fatal(geoms[0].Area())
|
||||
if math.Abs(features[0].Geom.Area()-20834374847.98027) > 0.01 {
|
||||
t.Fatal(features[0].Geom.Area())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,19 @@ func (this *Geos) IndexAdd(index *Index, geom *Geom) {
|
|||
index.geoms = append(index.geoms, IndexGeom{geom})
|
||||
}
|
||||
|
||||
// IndexQueryGeoms queries the index for intersections with geom.
|
||||
func (this *Geos) IndexQueryGeoms(index *Index, geom *Geom) []IndexGeom {
|
||||
hits := this.IndexQuery(index, geom)
|
||||
|
||||
var geoms []IndexGeom
|
||||
for _, idx := range hits {
|
||||
geoms = append(geoms, index.geoms[idx])
|
||||
}
|
||||
return geoms
|
||||
}
|
||||
|
||||
// IndexQuery queries the index for intersections with geom.
|
||||
func (this *Geos) IndexQuery(index *Index, geom *Geom) []IndexGeom {
|
||||
func (this *Geos) IndexQuery(index *Index, geom *Geom) []int {
|
||||
index.mu.Lock()
|
||||
defer index.mu.Unlock()
|
||||
var num C.uint32_t
|
||||
|
@ -58,9 +69,9 @@ func (this *Geos) IndexQuery(index *Index, geom *Geom) []IndexGeom {
|
|||
hits := (*[2 << 16]C.uint32_t)(unsafe.Pointer(r))[:num]
|
||||
defer C.free(unsafe.Pointer(r))
|
||||
|
||||
var geoms []IndexGeom
|
||||
for _, idx := range hits {
|
||||
geoms = append(geoms, index.geoms[idx])
|
||||
indices := make([]int, len(hits))
|
||||
for i := range hits {
|
||||
indices[i] = int(hits[i])
|
||||
}
|
||||
return geoms
|
||||
return indices
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error)
|
|||
}
|
||||
defer f.Close()
|
||||
|
||||
geoms, err := geojson.ParseGeoJson(f)
|
||||
features, err := geojson.ParseGeoJson(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -158,9 +158,9 @@ func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error)
|
|||
withBuffer = true
|
||||
}
|
||||
|
||||
for _, geom := range geoms {
|
||||
for _, feature := range features {
|
||||
if withBuffer {
|
||||
simplified := g.SimplifyPreserveTopology(geom, 1000)
|
||||
simplified := g.SimplifyPreserveTopology(feature.Geom, 1000)
|
||||
if simplified == nil {
|
||||
return nil, errors.New("couldn't simplify limitto")
|
||||
}
|
||||
|
@ -172,9 +172,9 @@ func NewFromGeoJsonWithBuffered(source string, buffer float64) (*Limiter, error)
|
|||
// buffered gets destroyed in UnionPolygons
|
||||
bufferedPolygons = append(bufferedPolygons, buffered)
|
||||
}
|
||||
polygons = append(polygons, geom)
|
||||
polygons = append(polygons, feature.Geom)
|
||||
|
||||
parts, err := SplitPolygonAtAutoGrid(g, geom)
|
||||
parts, err := SplitPolygonAtAutoGrid(g, feature.Geom)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -267,7 +267,7 @@ func (l *Limiter) Clip(geom *geos.Geom) ([]*geos.Geom, error) {
|
|||
l.geomPrepMu.Unlock()
|
||||
|
||||
// we have intersections, query index to get intersecting parts
|
||||
hits := g.IndexQuery(l.index, geom)
|
||||
hits := g.IndexQueryGeoms(l.index, geom)
|
||||
|
||||
geomType := g.Type(geom)
|
||||
|
||||
|
|
Loading…
Reference in New Issue