forked from TrueCloudLab/frostfs-locode-db
194 lines
4.5 KiB
Go
194 lines
4.5 KiB
Go
|
package locodecolumn
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
|
||
|
"git.frostfs.info/TrueCloudLab/frostfs-locode-db/pkg/locode"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
minutesDigits = 2
|
||
|
hemisphereSymbols = 1
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
latDegDigits = 2
|
||
|
lngDegDigits = 3
|
||
|
)
|
||
|
|
||
|
type coordinateCode struct {
|
||
|
degDigits int
|
||
|
value []uint8
|
||
|
}
|
||
|
|
||
|
// LongitudeCode represents the value of the longitude
|
||
|
// of the location conforming to UN/LOCODE specification.
|
||
|
type LongitudeCode coordinateCode
|
||
|
|
||
|
// LongitudeHemisphere represents the hemisphere of the earth
|
||
|
// // along the Greenwich meridian.
|
||
|
type LongitudeHemisphere [hemisphereSymbols]uint8
|
||
|
|
||
|
// LatitudeCode represents the value of the latitude
|
||
|
// of the location conforming to UN/LOCODE specification.
|
||
|
type LatitudeCode coordinateCode
|
||
|
|
||
|
// LatitudeHemisphere represents the hemisphere of the earth
|
||
|
// along the equator.
|
||
|
type LatitudeHemisphere [hemisphereSymbols]uint8
|
||
|
|
||
|
func coordinateFromString(s string, degDigits int, hemisphereAlphabet []uint8) (*coordinateCode, error) {
|
||
|
if len(s) != degDigits+minutesDigits+hemisphereSymbols {
|
||
|
return nil, locode.ErrInvalidString
|
||
|
}
|
||
|
|
||
|
for i := range s[:degDigits+minutesDigits] {
|
||
|
if !isDigit(s[i]) {
|
||
|
return nil, locode.ErrInvalidString
|
||
|
}
|
||
|
}
|
||
|
|
||
|
loop:
|
||
|
for _, sym := range s[degDigits+minutesDigits:] {
|
||
|
for j := range hemisphereAlphabet {
|
||
|
if hemisphereAlphabet[j] == uint8(sym) {
|
||
|
continue loop
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil, locode.ErrInvalidString
|
||
|
}
|
||
|
|
||
|
return &coordinateCode{
|
||
|
degDigits: degDigits,
|
||
|
value: []uint8(s),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// LongitudeFromString parses a string and returns the location's longitude.
|
||
|
func LongitudeFromString(s string) (*LongitudeCode, error) {
|
||
|
cc, err := coordinateFromString(s, lngDegDigits, []uint8{'W', 'E'})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return (*LongitudeCode)(cc), nil
|
||
|
}
|
||
|
|
||
|
// LatitudeFromString parses a string and returns the location's latitude.
|
||
|
func LatitudeFromString(s string) (*LatitudeCode, error) {
|
||
|
cc, err := coordinateFromString(s, latDegDigits, []uint8{'N', 'S'})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return (*LatitudeCode)(cc), nil
|
||
|
}
|
||
|
|
||
|
func (cc *coordinateCode) degrees() []uint8 {
|
||
|
return cc.value[:cc.degDigits]
|
||
|
}
|
||
|
|
||
|
// Degrees returns the longitude's degrees.
|
||
|
func (lc *LongitudeCode) Degrees() (l [lngDegDigits]uint8) {
|
||
|
copy(l[:], (*coordinateCode)(lc).degrees())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Degrees returns the latitude's degrees.
|
||
|
func (lc *LatitudeCode) Degrees() (l [latDegDigits]uint8) {
|
||
|
copy(l[:], (*coordinateCode)(lc).degrees())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (cc *coordinateCode) minutes() (mnt [minutesDigits]uint8) {
|
||
|
for i := 0; i < minutesDigits; i++ {
|
||
|
mnt[i] = cc.value[cc.degDigits+i]
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Minutes returns the longitude's minutes.
|
||
|
func (lc *LongitudeCode) Minutes() [minutesDigits]uint8 {
|
||
|
return (*coordinateCode)(lc).minutes()
|
||
|
}
|
||
|
|
||
|
// Minutes returns the latitude's minutes.
|
||
|
func (lc *LatitudeCode) Minutes() [minutesDigits]uint8 {
|
||
|
return (*coordinateCode)(lc).minutes()
|
||
|
}
|
||
|
|
||
|
// Hemisphere returns the longitude's hemisphere code.
|
||
|
func (lc *LongitudeCode) Hemisphere() LongitudeHemisphere {
|
||
|
return (*coordinateCode)(lc).hemisphere()
|
||
|
}
|
||
|
|
||
|
// Hemisphere returns the latitude's hemisphere code.
|
||
|
func (lc *LatitudeCode) Hemisphere() LatitudeHemisphere {
|
||
|
return (*coordinateCode)(lc).hemisphere()
|
||
|
}
|
||
|
|
||
|
func (cc *coordinateCode) hemisphere() (h [hemisphereSymbols]uint8) {
|
||
|
for i := 0; i < hemisphereSymbols; i++ {
|
||
|
h[i] = cc.value[cc.degDigits+minutesDigits+i]
|
||
|
}
|
||
|
|
||
|
return h
|
||
|
}
|
||
|
|
||
|
// North returns true for the northern hemisphere.
|
||
|
func (h LatitudeHemisphere) North() bool {
|
||
|
return h[0] == 'N'
|
||
|
}
|
||
|
|
||
|
// East returns true for the eastern hemisphere.
|
||
|
func (h LongitudeHemisphere) East() bool {
|
||
|
return h[0] == 'E'
|
||
|
}
|
||
|
|
||
|
// Coordinates represents the coordinates of the location from UN/LOCODE table.
|
||
|
type Coordinates struct {
|
||
|
lat *LatitudeCode
|
||
|
|
||
|
lng *LongitudeCode
|
||
|
}
|
||
|
|
||
|
// Latitude returns the location's latitude.
|
||
|
func (c *Coordinates) Latitude() *LatitudeCode {
|
||
|
return c.lat
|
||
|
}
|
||
|
|
||
|
// Longitude returns the location's longitude.
|
||
|
func (c *Coordinates) Longitude() *LongitudeCode {
|
||
|
return c.lng
|
||
|
}
|
||
|
|
||
|
// CoordinatesFromString parses a string and returns the location's coordinates.
|
||
|
func CoordinatesFromString(s string) (*Coordinates, error) {
|
||
|
if len(s) == 0 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
strs := strings.Split(s, " ")
|
||
|
if len(strs) != 2 {
|
||
|
return nil, locode.ErrInvalidString
|
||
|
}
|
||
|
|
||
|
lat, err := LatitudeFromString(strs[0])
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("could not parse latitude: %w", err)
|
||
|
}
|
||
|
|
||
|
lng, err := LongitudeFromString(strs[1])
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("could not parse longitude: %w", err)
|
||
|
}
|
||
|
|
||
|
return &Coordinates{
|
||
|
lat: lat,
|
||
|
lng: lng,
|
||
|
}, nil
|
||
|
}
|