geography/handlers/admin/memory_s2.go

120 lines
3.1 KiB
Go

package admin
import (
"errors"
"io"
"net/http"
"github.com/blevesearch/geo/s2"
"github.com/paulmach/orb"
"github.com/paulmach/orb/geojson"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
)
type AdminMemoryS2Index struct {
Layers []string
Indexes map[string]*s2.ShapeIndex
Documents map[string]map[string]*geojson.Feature
}
func NewAdminMemoryS2Index(cfg *viper.Viper) (*AdminMemoryS2Index, error) {
storage := &AdminMemoryS2Index{
Indexes: map[string]*s2.ShapeIndex{},
Documents: map[string]map[string]*geojson.Feature{},
}
layers := cfg.GetStringMapString("data.layers")
for layer, url := range layers {
storage.IndexLayer(layer, url)
}
return storage, nil
}
func (s *AdminMemoryS2Index) IndexLayer(layer, url string) error {
resp, err := http.Get(url)
if err != nil {
log.Error().Err(err).Msg("get request failed")
return err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Error().Err(err).Msg("cound not read response")
return err
}
data, err := geojson.UnmarshalFeatureCollection(body)
if err != nil {
log.Error().Err(err).Msg("could not read input data")
return err
}
s.Indexes[layer] = s2.NewShapeIndex()
s.Documents[layer] = map[string]*geojson.Feature{}
s.Layers = append(s.Layers, layer)
for _, feature := range data.Features {
if feature.Geometry.GeoJSONType() == "Polygon" {
poly, ok := feature.Geometry.(orb.Polygon)
if !ok {
log.Error().Str("name", feature.Properties.MustString("nom")).Msg("could not index layer")
break
}
s2Poly, err := orbToS2Polygon(&poly)
if err != nil {
log.Error().Err(err).Str("featurename", feature.Properties.MustString("nom")).Msg("could not convert polygon to S2")
break
}
s.Indexes[layer].Add(s2Poly)
s.Documents[layer][feature.Properties.MustString("code")] = feature
}
}
s.Indexes[layer].Build()
return nil
}
func (s *AdminMemoryS2Index) GeoSearch(feature *geojson.Feature) (map[string][]*geojson.Feature, error) {
// results := map[string][]*geojson.Feature
// point := feature.Point()
// for _, layer := s.Layers {
// query := s2.ContainsPointQuery{s.Indexes[layer], s2.VertexModelClosed}
// p := s2.PointFromLatLng(s2.LatLngFromDegrees(point.Lat(), point.Lon()))
// shapes := query.ContainingShapes(p)
// features := slices.Collect(func(yield func(*geojson.Feature)){
// for _, sh := range shapes {
// sh.
// if !yield()
// }
// })
// }
return nil, errors.New("unimplemented")
}
func orbToS2Polygon(poly *orb.Polygon) (*s2.Polygon, error) {
loops := []*s2.Loop{}
if poly == nil {
return nil, errors.New("polygon is nil")
}
for _, ring := range *poly {
points := []s2.Point{}
for _, point := range ring {
points = append(points, s2.PointFromLatLng(s2.LatLngFromDegrees(point.Lat(), point.Lon())))
}
loop := s2.LoopFromPoints(points)
loops = append(loops, loop)
}
polygon := s2.PolygonFromLoops(loops)
return polygon, nil
}
func (s *AdminMemoryS2Index) Find(layer, id string) (*geojson.Feature, error) {
result, ok := s.Documents["layer"][id]
if !ok || result == nil {
return nil, errors.New("admin not found")
}
return result, nil
}