// Locate returns the point and uncertainty associated with a location as // interpreted by the geolocation service. Uncertainty indicates the maximum // uncertainty (in meters) accepted for the point (with 0 any uncertainty // will be accepted). func (s *service) Locate(l *geography.Location, locality string, uncertainty uint) (geography.Georeference, error) { ls, err := s.List(l, locality, uncertainty) if err != nil { return geography.Georeference{Point: geography.InvalidPoint()}, err } if len(ls) == 1 { return ls[0], nil } if len(ls) == 0 { return geography.Georeference{Point: geography.InvalidPoint()}, geography.ErrNotInDB } u := uncertainty if u == 0 { u = 200000 } // set a mid point var sLon, sLat float64 unc := uint(0) for _, p := range ls { sLon += p.Point.Lon sLat += p.Point.Lat if unc < p.Uncertainty { unc = p.Uncertainty } } den := float64(len(ls)) lon, lat := sLon/den, sLat/den if !(geography.IsLon(lon) && geography.IsLat(lat)) { return geography.Georeference{Point: geography.InvalidPoint()}, geography.ErrAmbiguous } max := uint(0) for _, p := range ls { d := p.Point.Distance(lon, lat) if d > max { max = d if (d + unc) > u { break } } } p := ls[0] p.Uncertainty = max + unc if (p.Uncertainty > u) || (!p.IsValid()) { return geography.Georeference{Point: geography.InvalidPoint()}, geography.ErrAmbiguous } p.Point = geography.Point{lon, lat} return p, nil }
func spGrefProc(c *cmdapp.Command, tax *jdh.Taxon, gzt geography.Gazetter) { defer func() { l := getTaxDesc(c, localDB, tax.Id, true) spGrefNav(c, l, gzt) l = getTaxDesc(c, localDB, tax.Id, false) spGrefNav(c, l, gzt) }() if len(tax.Id) == 0 { return } vals := new(jdh.Values) vals.Add(jdh.SpeTaxon, tax.Id) if !addFlag { vals.Add(jdh.SpeGeoref, "true") } l := speList(c, localDB, vals) defer l.Close() for { spe := &jdh.Specimen{} if err := l.Scan(spe); err != nil { if err == io.EOF { break } fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr(err)) os.Exit(1) } if len(spe.Georef.Validation) > 0 { continue } if !spe.Geography.IsValid() { fmt.Fprintf(os.Stdout, "%s: location without valid country\n", spe.Id) continue } u := uint(uncertFlag) if u == 0 { if u = spe.Georef.Uncertainty; u == 0 { // 200 km is the maximum validation u = 200000 } } if !spe.Georef.IsValid() { if !addFlag { fmt.Fprintf(os.Stdout, "%s: invalid georeference\n", spe.Id) continue } p, err := gzt.Locate(&spe.Geography, spe.Locality, uint(uncertFlag)) if err != nil { fmt.Fprintf(os.Stdout, "%s: unable to add: %v\n", spe.Id, err) if verboseFlag { if err == geography.ErrAmbiguous { pts, err := gzt.List(&spe.Geography, spe.Locality, uint(uncertFlag)) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr(err)) continue } for _, p := range pts { fmt.Fprintf(os.Stderr, "\t%.5f %.5f\t%d\n", p.Point.Lon, p.Point.Lat, p.Uncertainty) } } } continue } vals := new(jdh.Values) vals.Add(jdh.KeyId, spe.Id) vals.Add(jdh.GeoLonLat, strconv.FormatFloat(p.Point.Lon, 'g', -1, 64)+","+strconv.FormatFloat(p.Point.Lat, 'g', -1, 64)) vals.Add(jdh.GeoUncertainty, strconv.FormatInt(int64(p.Uncertainty), 10)) vals.Add(jdh.GeoSource, p.Source) vals.Add(jdh.GeoValidation, p.Validation) localDB.Exec(jdh.Set, jdh.Specimens, vals) continue } pts, err := gzt.List(&spe.Geography, spe.Locality, u) if err != nil { fmt.Fprintf(os.Stdout, "%s: %v\n", spe.Id, err) continue } if len(pts) == 0 { fmt.Fprintf(os.Stdout, "%s: location not found\n", spe.Id) continue } lon, lat := spe.Georef.Point.Lon, spe.Georef.Point.Lat val := false gr := geography.Georeference{ Point: geography.InvalidPoint(), Uncertainty: geography.EarthRadius * 10, // a distance large enough } for _, p := range pts { d := p.Point.Distance(lon, lat) if d <= u { val = true if (d + p.Uncertainty) < gr.Uncertainty { gr = p gr.Uncertainty += d } } } if val { vals := new(jdh.Values) vals.Add(jdh.KeyId, spe.Id) if spe.Georef.Uncertainty == 0 { vals.Add(jdh.GeoUncertainty, strconv.FormatInt(int64(gr.Uncertainty), 10)) } vals.Add(jdh.GeoValidation, gr.Validation) localDB.Exec(jdh.Set, jdh.Specimens, vals) continue } if !corrFlag { fmt.Fprintf(os.Stdout, "%s: location not found\n", spe.Id) if verboseFlag { fmt.Fprintf(os.Stderr, "\t%.5f %.5f\t\t[current georeference]\n", lon, lat) for _, p := range pts { fmt.Fprintf(os.Stderr, "\t%.5f %.5f\t%d\t%d\n", p.Point.Lon, p.Point.Lat, p.Uncertainty, p.Point.Distance(lon, lat)) } } continue } gr = geography.Georeference{ Point: geography.InvalidPoint(), Uncertainty: geography.EarthRadius * 10, // a distance large enough } val = false for _, p := range pts { // invert lon-lat lt, ln := lon, lat if geography.IsLon(ln) && geography.IsLat(lt) { d := p.Point.Distance(lon, lat) if d <= u { val = true gr = p gr.Point = geography.Point{Lon: ln, Lat: lt} gr.Uncertainty += d break } } // lon with wrong sign ln, lt = -lon, lat if geography.IsLon(ln) && geography.IsLat(lt) { d := p.Point.Distance(lon, lat) if d <= u { val = true gr = p gr.Point = geography.Point{Lon: ln, Lat: lt} gr.Uncertainty += d break } } // lat with wrong sing ln, lt = lon, -lat if geography.IsLon(ln) && geography.IsLat(lt) { d := p.Point.Distance(lon, lat) if d <= u { val = true gr = p gr.Point = geography.Point{Lon: ln, Lat: lt} gr.Uncertainty += d break } } // invert lon-lat, wrong sings lt, ln = -lon, -lat if geography.IsLon(ln) && geography.IsLat(lt) { d := p.Point.Distance(lon, lat) if d <= u { val = true gr = p gr.Point = geography.Point{Lon: ln, Lat: lt} gr.Uncertainty += d break } } // invert lon-lat, lon with wrong sing lt, ln = lon, -lat if geography.IsLon(ln) && geography.IsLat(lt) { d := p.Point.Distance(lon, lat) if d <= u { val = true gr = p gr.Point = geography.Point{Lon: ln, Lat: lt} gr.Uncertainty += d break } } // invert lon-lat, lat with wrong sing lt, ln = -lon, lat if geography.IsLon(ln) && geography.IsLat(lt) { d := p.Point.Distance(lon, lat) if d <= u { val = true gr = p gr.Point = geography.Point{Lon: ln, Lat: lt} gr.Uncertainty += d break } } } if val { vals := new(jdh.Values) vals.Add(jdh.KeyId, spe.Id) vals.Add(jdh.GeoLonLat, strconv.FormatFloat(gr.Point.Lon, 'g', -1, 64)+","+strconv.FormatFloat(gr.Point.Lat, 'g', -1, 64)) vals.Add(jdh.GeoUncertainty, strconv.FormatInt(int64(gr.Uncertainty), 10)) vals.Add(jdh.GeoSource, gr.Source) vals.Add(jdh.GeoValidation, gr.Validation) localDB.Exec(jdh.Set, jdh.Specimens, vals) continue } fmt.Fprintf(os.Stdout, "%s: not corrected\n", spe.Id) if verboseFlag { fmt.Fprintf(os.Stderr, "\t%.5f %.5f\t\t[current georeference]\n", lon, lat) for _, p := range pts { fmt.Fprintf(os.Stderr, "\t%.5f %.5f\t%d\t%d\n", p.Point.Lon, p.Point.Lat, p.Uncertainty, p.Point.Distance(lon, lat)) } } } }