idfm-api/types/region.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
}