...
1 package exif
2
3 import (
4 "errors"
5 "fmt"
6 "time"
7
8 "github.com/dsoprea/go-logging"
9 "github.com/golang/geo/s2"
10
11 "github.com/dsoprea/go-exif/v3/common"
12 )
13
14 var (
15
16
17 ErrGpsCoordinatesNotValid = errors.New("GPS coordinates not valid")
18 )
19
20
21 type GpsDegrees struct {
22
23
24 Orientation byte
25
26
27
28 Degrees float64
29
30
31
32 Minutes float64
33
34
35
36 Seconds float64
37 }
38
39
40
41
42 func NewGpsDegreesFromRationals(refValue string, rawCoordinate []exifcommon.Rational) (gd GpsDegrees, err error) {
43 defer func() {
44 if state := recover(); state != nil {
45 err = log.Wrap(state.(error))
46 }
47 }()
48
49 if len(rawCoordinate) != 3 {
50 log.Panicf("new GpsDegrees struct requires a raw-coordinate with exactly three rationals")
51 }
52
53 gd = GpsDegrees{
54 Orientation: refValue[0],
55 Degrees: float64(rawCoordinate[0].Numerator) / float64(rawCoordinate[0].Denominator),
56 Minutes: float64(rawCoordinate[1].Numerator) / float64(rawCoordinate[1].Denominator),
57 Seconds: float64(rawCoordinate[2].Numerator) / float64(rawCoordinate[2].Denominator),
58 }
59
60 return gd, nil
61 }
62
63
64 func (d GpsDegrees) String() string {
65 return fmt.Sprintf("Degrees<O=[%s] D=(%g) M=(%g) S=(%g)>", string([]byte{d.Orientation}), d.Degrees, d.Minutes, d.Seconds)
66 }
67
68
69
70 func (d GpsDegrees) Decimal() float64 {
71 decimal := float64(d.Degrees) + float64(d.Minutes)/60.0 + float64(d.Seconds)/3600.0
72
73 if d.Orientation == 'S' || d.Orientation == 'W' {
74 return -decimal
75 }
76
77 return decimal
78 }
79
80
81
82
83 func (d GpsDegrees) Raw() []exifcommon.Rational {
84 return []exifcommon.Rational{
85 {Numerator: uint32(d.Degrees), Denominator: 1},
86 {Numerator: uint32(d.Minutes), Denominator: 1},
87 {Numerator: uint32(d.Seconds), Denominator: 1},
88 }
89 }
90
91
92 type GpsInfo struct {
93 Latitude, Longitude GpsDegrees
94 Altitude int
95 Timestamp time.Time
96 }
97
98
99 func (gi *GpsInfo) String() string {
100 return fmt.Sprintf("GpsInfo<LAT=(%.05f) LON=(%.05f) ALT=(%d) TIME=[%s]>",
101 gi.Latitude.Decimal(), gi.Longitude.Decimal(), gi.Altitude, gi.Timestamp)
102 }
103
104
105 func (gi *GpsInfo) S2CellId() s2.CellID {
106 latitude := gi.Latitude.Decimal()
107 longitude := gi.Longitude.Decimal()
108
109 ll := s2.LatLngFromDegrees(latitude, longitude)
110 cellId := s2.CellIDFromLatLng(ll)
111
112 if cellId.IsValid() == false {
113 panic(ErrGpsCoordinatesNotValid)
114 }
115
116 return cellId
117 }
118
View as plain text