// Searches the R-Tree to find the place corresponding to the given lat,lng // First uses the R-Tree which contains only the bounding boxes of the // entities, and then refine the result using the actual geometry of the entity. func reverseGeocode(rt *rtreego.Rtree, lat, lng, precision float64) (*GeoData, error) { var results []rtreego.Spatial // First search the R-Tree rpt := rtreego.Point{lng, lat} results = rt.SearchIntersect(rpt.ToRect(precision / 2.)) if len(results) == 0 || results[0] == nil { nn := rt.NearestNeighbor(rpt) if nn == nil { return nil, ErrNoMatchFound } else { results = []rtreego.Spatial{nn} } } // Then check with the actual geometry of the entity mindist := math.MaxFloat64 var argmindist *GeoData gpt, _ := geos.NewPoint(geos.NewCoord(lng, lat)) for _, res := range results { if res == nil { break } obj, _ := res.(SpatialData) geod := obj.GetData() inside, _ := geod.Geom.Contains(gpt) if inside { return geod, nil } dist, _ := gpt.Distance(geod.Geom) if dist < mindist { mindist = dist argmindist = geod } } if mindist < precision { return argmindist, nil } return nil, ErrNoMatchFound }
// Loads a CSV file from https://github.com/delight-im/FreeGeoDB // The expected CSV columns are: // 0 : id // 1 : coordinates_wkt // 2 : sovereign // 3 : name // 4 : formal // 5 : economy_level // 6 : income_level // 7 : iso_alpha2 // 8 : iso_alpha3 // 9 : iso_numeric3 // 10 : continent // 11 : subregion func load_freegeodb_countries_csv(rt *rtreego.Rtree, fname string) (int, error) { log.Println("Loading", fname, "...") f, err := os.Open(fname) if err != nil { return 0, err } defer f.Close() df, err := Decompressor(f) if err != nil { return 0, err } r := csv.NewReader(df) r.Comma = ',' _, err = r.Read() // Don't read header if err != nil { return 0, err } line := 0 loaded_objects := 0 for { cols, err := r.Read() if err == io.EOF { break } else if err != nil { return loaded_objects, err } line++ if len(cols) != 12 { return loaded_objects, fmt.Errorf("Invalid column format in", fname, cols) } if len(cols[7]) != 2 || len(cols[8]) != 3 || len(cols[3]) == 0 { continue } geodb_id, err := strconv.ParseInt(cols[0], 10, 64) if err != nil { log.Fatal("Error parsing", cols, line, err) } geom, err := geos.FromWKT(cols[1]) if err != nil { log.Fatal("Error parsing", cols, line, err) } rect, err := RtreeBboxg(geom, 1e-5) if err != nil { log.Fatal("Error getting bbox", cols, line, err) } obj := GeoObj{rect, &GeoData{ Id: geodb_id, CountryName: cols[3], CountryCode_2: cols[7], CountryCode_3: cols[8], Type: "country", Geom: geom}} rt.Insert(&obj) loaded_objects++ } return loaded_objects, nil }
// Loads a CSV file from http://download.gisgraphy.com/openstreetmap/csv/cities/ // The expected CSV columns are: // 0 : Node type; N|W|R (in uppercase), wheter it is a Node, Way or Relation in the openstreetmap Model // 1 : id; The openstreetmap id // 2 : name; the default name of the city // 3 : countrycode; The iso3166-2 country code (2 letters) // 4 : postcode; The postcode / zipcode / ons code / municipality code / ... // 5 : population; How many people lives in that city // 6 : location; The middle location of the city in HEXEWKB // 7 : shape; The delimitation of the city in HEXEWKB // 8 : type; the type of city ('city', 'village', 'town', 'hamlet', ...) // 9 : is_in ; where the cities is located (generally the fully qualified administrative division) // 10 : alternatenames; the names of the city in other languages func load_gisgraphy_cities_csv(rt *rtreego.Rtree, fname string) (int, error) { log.Println("Loading", fname, "...") f, err := os.Open(fname) if err != nil { return 0, err } defer f.Close() df, err := Decompressor(f) if err != nil { log.Fatal(err) } var r *csv.Reader if strings.Contains(fname, ".tar.") { // Handles uncompressed files downloaded from gisgraphy.com tf := tar.NewReader(df) for { hdr, err := tf.Next() if err == io.EOF { log.Fatal("Couldn't find CSV file in " + fname) } else if err != nil { log.Fatal(err) } if strings.HasSuffix(hdr.Name, ".txt") { r = csv.NewReader(tf) break } } } else { // Handles unzipped files r = csv.NewReader(df) } r.Comma = '\t' line := 0 loaded_objects := 0 for { cols, err := r.Read() if err == io.EOF { break } else if err != nil { return loaded_objects, err } line++ if len(cols[7]) == 0 { continue } osm_id, err := strconv.ParseInt(cols[1], 10, 64) if err != nil { log.Fatal("Error parsing", cols, line, err) } geom, err := geos.FromHex(cols[7]) if err != nil { log.Fatal("Error parsing", cols, line, err) } rect, err := RtreeBboxg(geom, 1e-5) if err != nil { log.Fatal("Error getting bbox", cols, line, err) } obj := GeoObj{rect, &GeoData{ Id: osm_id, City: cols[2], CountryCode_2: cols[3], Type: "city", Geom: geom}} rt.Insert(&obj) loaded_objects++ } return loaded_objects, nil }