read properties of geojson features

master
Oliver Tonnhofer 2014-05-21 09:15:32 +02:00
parent 85720d3be1
commit 593811a231
4 changed files with 113 additions and 69 deletions

View File

@ -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:

View File

@ -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())
}
}

View File

@ -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
}

View File

@ -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)