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 }