gpx-utils/pkg/openname/parse.go

168 lines
3.8 KiB
Go

package openname
import (
"bufio"
"encoding/csv"
"errors"
"io"
"strconv"
"github.com/dhconnelly/rtreego"
)
type Record struct {
ID string
NamesUri string
Name string
NameLang string
AltName string
AltNameLang string
Type string
LocalType string
GeomX float64
GeomY float64
MostDetailViewRes float64
LeastDetailViewRes float64
MbrXMin float64
MbrYMin float64
MbrXMax float64
MbrYMax float64
PostcodeDistrict string
PostcodeDistrictUri string
PopulatedPlace string
PopulatedPlaceUri string
PopulatedPlaceType string
DistrictBorough string
DistrictBoroughUri string
DistrictBoroughType string
CountyUnitary string
CountyUnitaryUri string
CountyUnitaryType string
Region string
RegionUri string
Country string
CountryUri string
RelativeSpatialObject string
SameAsDbpedia string
SameAsGeonames string
}
func (r *Record) Bounds() *rtreego.Rect {
p := rtreego.Point{r.MbrXMin, r.MbrYMin}
rect, err := rtreego.NewRect(p, []float64{r.MbrXMax - r.MbrXMin, r.MbrYMax - r.MbrYMin})
if err != nil {
panic(err)
}
return rect
}
func (r *Record) Area() float64 {
return (r.MbrXMax - r.MbrXMin) * (r.MbrYMax - r.MbrYMin)
}
type Scanner struct {
csvReader *csv.Reader
nextRecord *Record
err error
}
func NewScanner(r io.Reader) (*Scanner, error) {
br := bufio.NewReader(r)
err := skipBOM(br)
if err != nil {
return nil, err
}
return &Scanner{csvReader: csv.NewReader(br)}, nil
}
var BOM = [3]byte{0xef, 0xbb, 0xbf}
func skipBOM(br *bufio.Reader) error {
xs, err := br.Peek(3)
if err != nil {
return err
}
if xs[0] == BOM[0] && xs[1] == BOM[1] && xs[2] == BOM[2] {
br.Discard(3)
}
return nil
}
func (s *Scanner) Scan() bool {
rawRecord, err := s.csvReader.Read()
if err != nil {
if errors.Is(err, io.EOF) {
return false
}
s.err = err
return false
}
s.nextRecord, err = parseRecord(rawRecord)
if err != nil {
s.err = err
return false
}
return true
}
func (s *Scanner) Err() error {
return s.err
}
func (s *Scanner) Record() *Record {
return s.nextRecord
}
func parseRecord(xs []string) (*Record, error) {
if len(xs) != 34 {
return nil, csv.ErrFieldCount
}
record := Record{
ID: xs[0],
NamesUri: xs[1],
Name: xs[2],
NameLang: xs[3],
AltName: xs[4],
AltNameLang: xs[5],
Type: xs[6],
LocalType: xs[7],
GeomX: 0,
GeomY: 0,
MostDetailViewRes: 0,
LeastDetailViewRes: 0,
MbrXMin: 0,
MbrYMin: 0,
MbrXMax: 0,
MbrYMax: 0,
PostcodeDistrict: xs[16],
PostcodeDistrictUri: xs[17],
PopulatedPlace: xs[18],
PopulatedPlaceUri: xs[19],
PopulatedPlaceType: xs[20],
DistrictBorough: xs[21],
DistrictBoroughUri: xs[22],
DistrictBoroughType: xs[23],
CountyUnitary: xs[24],
CountyUnitaryUri: xs[25],
CountyUnitaryType: xs[26],
Region: xs[27],
RegionUri: xs[28],
Country: xs[29],
CountryUri: xs[30],
RelativeSpatialObject: xs[31],
SameAsDbpedia: xs[32],
SameAsGeonames: xs[33],
}
for i, p := range []*float64{&record.GeomX, &record.GeomY, &record.MostDetailViewRes, &record.LeastDetailViewRes, &record.MbrXMin, &record.MbrYMin, &record.MbrXMax, &record.MbrYMax} {
s := xs[i+8]
if s != "" {
var err error
*p, err = strconv.ParseFloat(s, 64)
if err != nil {
return nil, err
}
}
}
return &record, nil
}