2013-10-21 16:59:31 +04:00
|
|
|
package geojson
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2014-05-21 11:15:32 +04:00
|
|
|
"fmt"
|
2013-10-21 16:59:31 +04:00
|
|
|
"io"
|
2015-03-02 11:22:08 +03:00
|
|
|
|
|
|
|
"github.com/omniscale/imposm3/logging"
|
2013-10-21 16:59:31 +04:00
|
|
|
)
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
var log = logging.NewLogger("geojson")
|
|
|
|
|
2013-10-21 16:59:31 +04:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
type Point struct {
|
|
|
|
Long float64
|
|
|
|
Lat float64
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03: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
|
2015-03-02 11:22:08 +03:00
|
|
|
p.Long, ok = coords[0].(float64)
|
2013-10-21 16:59:31 +04:00
|
|
|
if !ok {
|
|
|
|
return p, errors.New("invalid lon")
|
|
|
|
}
|
2015-03-02 11:22:08 +03:00
|
|
|
p.Lat, ok = coords[1].(float64)
|
2013-10-21 16:59:31 +04:00
|
|
|
if !ok {
|
|
|
|
return p, errors.New("invalid lat")
|
|
|
|
}
|
2013-12-02 17:13:31 +04:00
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
if p.Long >= 180.0 || p.Long <= -180.0 || p.Lat >= 90.0 || p.Lat <= -90.0 {
|
|
|
|
log.Warn("coordinates outside of world boundary. non-4326?")
|
2013-12-02 17:13:31 +04:00
|
|
|
}
|
2015-03-02 11:22:08 +03:00
|
|
|
|
2013-10-21 16:59:31 +04:00
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
type LineString []Point
|
2013-10-21 16:59:31 +04:00
|
|
|
|
2015-03-02 11:22:08 +03: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
|
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
type Polygon []LineString
|
2013-10-21 16:59:31 +04:00
|
|
|
|
2014-05-21 11:15:32 +04:00
|
|
|
type polygonFeature struct {
|
2015-03-02 11:22:08 +03:00
|
|
|
polygon Polygon
|
2014-05-21 11:15:32 +04:00
|
|
|
properties map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Feature struct {
|
2015-03-02 11:22:08 +03:00
|
|
|
Polygon Polygon
|
2014-05-21 11:15:32 +04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03: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 {
|
2015-03-02 11:22:08 +03: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
|
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
func newMultiPolygonFeaturesFromCoords(coords []interface{}) ([]Feature, error) {
|
|
|
|
features := []Feature{}
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
for _, part := range coords {
|
|
|
|
polyCoords, ok := part.([]interface{})
|
|
|
|
if !ok {
|
2014-05-21 11:15:32 +04:00
|
|
|
return features, errors.New("multipolygon polygon not a list")
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
poly, err := newPolygonFromCoords(polyCoords)
|
|
|
|
if err != nil {
|
2014-05-21 11:15:32 +04:00
|
|
|
return features, err
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
2015-03-02 11:22:08 +03:00
|
|
|
features = append(features, Feature{poly, nil})
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
2014-05-21 11:15:32 +04:00
|
|
|
return features, nil
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
// ParseGeoJSON parses geojson from reader and returns []Feature in WGS84 and
|
|
|
|
// another []Feature tranformed in targetSRID.
|
|
|
|
func ParseGeoJSON(r io.Reader) ([]Feature, error) {
|
2013-10-21 17:33:47 +04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-05-21 11:15:32 +04:00
|
|
|
polygons, err := constructPolygonFeatures(obj)
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
return polygons, nil
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
|
2015-03-02 11:22:08 +03:00
|
|
|
func constructPolygonFeatures(obj *object) ([]Feature, 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)
|
2015-03-02 11:22:08 +03:00
|
|
|
return []Feature{{poly, nil}}, err
|
2013-10-21 16:59:31 +04:00
|
|
|
case "MultiPolygon":
|
2014-05-21 11:15:32 +04:00
|
|
|
poly, err := newMultiPolygonFeaturesFromCoords(obj.Coordinates)
|
2013-10-21 16:59:31 +04:00
|
|
|
return poly, err
|
|
|
|
case "Feature":
|
2014-05-21 11:15:32 +04:00
|
|
|
features, err := constructPolygonFeatures(obj.Geometry)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
properties := stringProperties(obj.Properties)
|
|
|
|
for i, _ := range features {
|
2015-03-02 11:22:08 +03:00
|
|
|
features[i].Properties = properties
|
2014-05-21 11:15:32 +04:00
|
|
|
}
|
|
|
|
return features, err
|
2013-10-21 16:59:31 +04:00
|
|
|
case "FeatureCollection":
|
2015-03-02 11:22:08 +03:00
|
|
|
features := make([]Feature, 0)
|
2013-10-21 16:59:31 +04:00
|
|
|
|
|
|
|
for _, obj := range obj.Features {
|
2014-05-21 11:15:32 +04:00
|
|
|
f, err := constructPolygonFeatures(&obj)
|
2013-10-21 16:59:31 +04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-05-21 11:15:32 +04:00
|
|
|
features = append(features, f...)
|
2013-10-21 16:59:31 +04:00
|
|
|
}
|
|
|
|
return features, nil
|
|
|
|
default:
|
|
|
|
return nil, errors.New("unknown type: " + obj.Type)
|
|
|
|
}
|
|
|
|
}
|