2013-10-21 16:59:31 +04:00
|
|
|
package geojson
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"imposm3/geom/geos"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
|
|
|
type object struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
Features []object `json:"features"`
|
|
|
|
Geometry *object `json:"geometry"`
|
|
|
|
Coordinates []interface{} `json:"coordinates"`
|
|
|
|
Properties map[string]interface{} `json:"properties"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type geometry struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
Coordinates []interface{} `json:"coordinates"`
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
type point struct {
|
|
|
|
long float64
|
|
|
|
lat float64
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func newPointFromCoords(coords []interface{}) (point, error) {
|
|
|
|
p := point{}
|
2013-11-26 12:47:00 +04:00
|
|
|
if len(coords) != 2 && len(coords) != 3 {
|
|
|
|
return p, errors.New("point list length not 2 or 3")
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
var ok bool
|
2013-10-21 17:33:47 +04:00
|
|
|
p.long, ok = coords[0].(float64)
|
2013-10-21 16:59:31 +04:00
|
|
|
if !ok {
|
|
|
|
return p, errors.New("invalid lon")
|
|
|
|
}
|
2013-10-21 17:33:47 +04:00
|
|
|
p.lat, ok = coords[1].(float64)
|
2013-10-21 16:59:31 +04:00
|
|
|
if !ok {
|
|
|
|
return p, errors.New("invalid lat")
|
|
|
|
}
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
type lineString []point
|
2013-10-21 16:59:31 +04:00
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func newLineStringFromCoords(coords []interface{}) (lineString, error) {
|
|
|
|
ls := lineString{}
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
for _, part := range coords {
|
|
|
|
coord, ok := part.([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return ls, errors.New("point not a list")
|
|
|
|
}
|
|
|
|
p, err := newPointFromCoords(coord)
|
|
|
|
if err != nil {
|
|
|
|
return ls, err
|
|
|
|
}
|
2013-10-21 17:33:47 +04:00
|
|
|
ls = append(ls, p)
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
return ls, nil
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
type polygon []lineString
|
2013-10-21 16:59:31 +04:00
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func newPolygonFromCoords(coords []interface{}) (polygon, error) {
|
|
|
|
poly := polygon{}
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
for _, part := range coords {
|
|
|
|
lsCoords, ok := part.([]interface{})
|
|
|
|
if !ok {
|
2013-10-21 17:33:47 +04:00
|
|
|
return poly, errors.New("polygon lineString not a list")
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
ls, err := newLineStringFromCoords(lsCoords)
|
|
|
|
if err != nil {
|
|
|
|
return poly, err
|
|
|
|
}
|
2013-10-21 17:33:47 +04:00
|
|
|
poly = append(poly, ls)
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
return poly, nil
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func newMultiPolygonFromCoords(coords []interface{}) ([]polygon, error) {
|
|
|
|
mp := []polygon{}
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
for _, part := range coords {
|
|
|
|
polyCoords, ok := part.([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return mp, errors.New("multipolygon polygon not a list")
|
|
|
|
}
|
|
|
|
poly, err := newPolygonFromCoords(polyCoords)
|
|
|
|
if err != nil {
|
|
|
|
return mp, err
|
|
|
|
}
|
|
|
|
mp = append(mp, poly)
|
|
|
|
}
|
|
|
|
return mp, nil
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func ParseGeoJson(r io.Reader) ([]*geos.Geom, error) {
|
|
|
|
decoder := json.NewDecoder(r)
|
2013-10-21 16:59:31 +04:00
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
obj := &object{}
|
|
|
|
err := decoder.Decode(obj)
|
2013-10-21 16:59:31 +04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
polygons, err := constructPolygons(obj)
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
g := geos.NewGeos()
|
|
|
|
defer g.Finish()
|
|
|
|
result := []*geos.Geom{}
|
|
|
|
|
|
|
|
for _, p := range polygons {
|
|
|
|
geom, err := geosPolygon(g, p)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
result = append(result, geom)
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
}
|
2013-10-21 17:33:47 +04:00
|
|
|
return result, err
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func constructPolygons(obj *object) ([]polygon, error) {
|
2013-10-21 16:59:31 +04:00
|
|
|
switch obj.Type {
|
|
|
|
case "Point":
|
2013-10-21 17:33:47 +04:00
|
|
|
return nil, errors.New("only polygon or MultiPolygon are supported")
|
2013-10-21 16:59:31 +04:00
|
|
|
case "LineString":
|
2013-10-21 17:33:47 +04:00
|
|
|
return nil, errors.New("only polygon or MultiPolygon are supported")
|
2013-10-21 16:59:31 +04:00
|
|
|
case "Polygon":
|
|
|
|
poly, err := newPolygonFromCoords(obj.Coordinates)
|
2013-10-21 17:33:47 +04:00
|
|
|
return []polygon{poly}, err
|
2013-10-21 16:59:31 +04:00
|
|
|
case "MultiPolygon":
|
|
|
|
poly, err := newMultiPolygonFromCoords(obj.Coordinates)
|
|
|
|
return poly, err
|
|
|
|
case "Feature":
|
|
|
|
geom, err := constructPolygons(obj.Geometry)
|
|
|
|
return geom, err
|
|
|
|
case "FeatureCollection":
|
2013-10-21 17:33:47 +04:00
|
|
|
features := make([]polygon, 0)
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
for _, obj := range obj.Features {
|
|
|
|
geom, err := constructPolygons(&obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
features = append(features, geom...)
|
|
|
|
}
|
|
|
|
return features, nil
|
|
|
|
default:
|
|
|
|
return nil, errors.New("unknown type: " + obj.Type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type GeoJson struct {
|
|
|
|
object object
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewGeoJson(r io.Reader) (*GeoJson, error) {
|
|
|
|
result := &GeoJson{}
|
|
|
|
|
|
|
|
decoder := json.NewDecoder(r)
|
|
|
|
|
|
|
|
err := decoder.Decode(&result.object)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gj *GeoJson) Geoms() ([]*geos.Geom, error) {
|
|
|
|
|
|
|
|
polygons, err := constructPolygons(&gj.object)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
g := geos.NewGeos()
|
|
|
|
defer g.Finish()
|
|
|
|
result := []*geos.Geom{}
|
|
|
|
|
|
|
|
for _, p := range polygons {
|
|
|
|
geom, err := geosPolygon(g, p)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
result = append(result, geom)
|
|
|
|
|
|
|
|
}
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func geosRing(g *geos.Geos, ls lineString) (*geos.Geom, error) {
|
|
|
|
coordSeq, err := g.CreateCoordSeq(uint32(len(ls)), 2)
|
2013-10-21 16:59:31 +04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// coordSeq inherited by LinearRing, no destroy
|
2013-10-21 17:33:47 +04:00
|
|
|
for i, p := range ls {
|
|
|
|
err := coordSeq.SetXY(g, uint32(i), p.long, p.lat)
|
2013-10-21 16:59:31 +04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ring, err := coordSeq.AsLinearRing(g)
|
|
|
|
if err != nil {
|
|
|
|
// coordSeq gets Destroy by GEOS
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ring, nil
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
func geosPolygon(g *geos.Geos, polygon polygon) (*geos.Geom, error) {
|
|
|
|
if len(polygon) == 0 {
|
|
|
|
return nil, errors.New("empty polygon")
|
|
|
|
}
|
|
|
|
|
|
|
|
shell, err := geosRing(g, polygon[0])
|
2013-10-21 16:59:31 +04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
holes := make([]*geos.Geom, len(polygon)-1)
|
2013-10-21 16:59:31 +04:00
|
|
|
|
2013-10-21 17:33:47 +04:00
|
|
|
for i, ls := range polygon[1:] {
|
2013-10-21 16:59:31 +04:00
|
|
|
hole, err := geosRing(g, ls)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
holes[i] = hole
|
|
|
|
}
|
|
|
|
|
|
|
|
geom := g.Polygon(shell, holes)
|
|
|
|
if geom == nil {
|
|
|
|
g.Destroy(shell)
|
|
|
|
for _, hole := range holes {
|
|
|
|
g.Destroy(hole)
|
|
|
|
}
|
|
|
|
return nil, errors.New("unable to create polygon")
|
|
|
|
}
|
|
|
|
return geom, nil
|
|
|
|
}
|