150 lines
4.9 KiB
Go
150 lines
4.9 KiB
Go
package types
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/mb0/wkt"
|
|
"github.com/pkg/errors"
|
|
"github.com/twpayne/go-geom"
|
|
)
|
|
|
|
// A Region holds information about a geographical region, including its ID, name & shape.
|
|
type Region struct {
|
|
ID ID `json:"id"` // Identifier of the region
|
|
Name string `json:"name"` // Name of the region
|
|
Status string `json:"status"` // Status of the dataset
|
|
|
|
// Shape of the region.
|
|
// You can use it to check if a particular coordinate is within that MultiPolygon
|
|
Shape *geom.MultiPolygon `json:"shape"`
|
|
|
|
DatasetCreation time.Time `json:"dataset_creation"` // When was the DataSet created ?
|
|
LastLoaded time.Time `json:"last_loaded"` // When was it last loaded at navitia.io's end ?
|
|
ProductionStart time.Time `json:"production_start"` // When did production start ?
|
|
ProductionEnd time.Time `json:"production_end"` // When did or when will it stop ?
|
|
|
|
// An error in the dataset.
|
|
// This comes from the server, not from this package.
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
// jsonRegion define the JSON implementation of Region types
|
|
// We define some of the value as pointers to the real values,
|
|
// allowing us to bypass copying in cases where we don't need to process the data.
|
|
type jsonRegion struct {
|
|
ID *ID `json:"id"`
|
|
Name *string `json:"name"`
|
|
Status *string `json:"status"`
|
|
|
|
// This is mind-fuckery of the highest level.
|
|
// While EVERY other geojson value returned by navitia is in standard format, THIS ONE, for NO GOOD REASON is coded in wkt...
|
|
// See (http://en.wikipedia.org/wiki/Well-known_text).
|
|
Shape string `json:"shape"`
|
|
|
|
DatasetCreation string `json:"dataset_created_at"`
|
|
LastLoaded string `json:"last_load_at"`
|
|
|
|
ProductionStart string `json:"start_production_date"`
|
|
ProductionEnd string `json:"end_production_date"`
|
|
|
|
Error *string `json:"error"`
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaller for a Region
|
|
func (r *Region) UnmarshalJSON(b []byte) error {
|
|
// First let's create the analogous structure
|
|
data := &jsonRegion{
|
|
ID: &r.ID,
|
|
Name: &r.Name,
|
|
Status: &r.Status,
|
|
Error: &r.Error,
|
|
}
|
|
|
|
// Now unmarshall the raw data into the analogous structure
|
|
err := json.Unmarshal(b, data)
|
|
if err != nil {
|
|
return fmt.Errorf("error while unmarshalling Region: %w", err)
|
|
}
|
|
|
|
// Let's create the error generator
|
|
gen := unmarshalErrorMaker{"Region", b}
|
|
|
|
// Now let's process the values
|
|
// First the times
|
|
r.DatasetCreation, err = parseDateTime(data.DatasetCreation)
|
|
if err != nil {
|
|
return gen.err(err, "DatasetCreation", "dataset_created_at", data.DatasetCreation, "Error while parsing datetime")
|
|
}
|
|
r.LastLoaded, err = parseDateTime(data.LastLoaded)
|
|
if err != nil {
|
|
return gen.err(err, "LastLoaded", "last_load_at", data.LastLoaded, "Error while parsing datetime")
|
|
}
|
|
r.ProductionStart, err = parseDateTime(data.ProductionStart)
|
|
if err != nil {
|
|
return gen.err(err, "ProductionStart", "start_production_date", data.ProductionStart, "Error while parsing datetime")
|
|
}
|
|
r.ProductionEnd, err = parseDateTime(data.ProductionEnd)
|
|
if err != nil {
|
|
return gen.err(err, "ProductionEnd", "end_production_date", data.ProductionEnd, "Error while parsing datetime")
|
|
}
|
|
|
|
// And now let's have some FUN, deal with the "shape" key.
|
|
// First, let's check if the string isn't empty, cause that would be so awesome...
|
|
if data.Shape != "" {
|
|
// Parse the MKT
|
|
out, err := wkt.Parse([]byte(data.Shape))
|
|
if err != nil {
|
|
return gen.err(err, "Shape", "shape", out, "error in wkt.Parse")
|
|
}
|
|
|
|
// Now, out should be a wkt.MultiPolygon
|
|
wktmp, ok := out.(*wkt.MultiPolygon)
|
|
if !ok {
|
|
return gen.err(nil, "Shape", "shape", out, "expected out to be of type wkt.MultiPolygon, but it isn't !")
|
|
}
|
|
|
|
// Call our funny little function to convert that to a geom format
|
|
mp, err := convertWktMPtoGeomMP(wktmp)
|
|
if err != nil {
|
|
return gen.err(err, "Shape", "shape", wktmp, "error while converting *wkt.MultiPolygon to *geom.MultiPolygon via convertWktMPtoGeomMP")
|
|
}
|
|
|
|
r.Shape = mp
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// convertWktMPtoGeomMP converts a wkt MultiPolygon to a geom MultiPolygon
|
|
func convertWktMPtoGeomMP(in *wkt.MultiPolygon) (*geom.MultiPolygon, error) {
|
|
// Now let's convert it to a geom format
|
|
// First let's create the geom.MultiPolygon
|
|
mp := geom.NewMultiPolygon(geom.XY)
|
|
|
|
// Then let's iterate through the polygons, and convert each of them from wkt.Coord to geom.Coord
|
|
multipolygonCoords := make([][][]geom.Coord, len(in.Polygons))
|
|
for i, k := range in.Polygons {
|
|
polygonCoords := make([][]geom.Coord, len(k))
|
|
for j, l := range k {
|
|
coords := make([]geom.Coord, len(l))
|
|
for n, m := range l {
|
|
coord := make(geom.Coord, 2)
|
|
coord[0] = m.X
|
|
coord[1] = m.Y
|
|
coords[n] = coord
|
|
}
|
|
polygonCoords[j] = coords
|
|
}
|
|
multipolygonCoords[i] = polygonCoords
|
|
}
|
|
|
|
// Now assign it !
|
|
mp, err := mp.SetCoords(multipolygonCoords)
|
|
if err != nil {
|
|
return mp, errors.Wrapf(err, "Error while setting coordinates")
|
|
}
|
|
return mp, err
|
|
}
|