gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

gps.go (3459B)


      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 	// ErrGpsCoordinatesNotValid means that some part of the geographic data was
     16 	// unparseable.
     17 	ErrGpsCoordinatesNotValid = errors.New("GPS coordinates not valid")
     18 )
     19 
     20 // GpsDegrees is a high-level struct representing geographic data.
     21 type GpsDegrees struct {
     22 	// Orientation describes the N/E/S/W direction that this position is
     23 	// relative to.
     24 	Orientation byte
     25 
     26 	// Degrees is a simple float representing the underlying rational degrees
     27 	// amount.
     28 	Degrees float64
     29 
     30 	// Minutes is a simple float representing the underlying rational minutes
     31 	// amount.
     32 	Minutes float64
     33 
     34 	// Seconds is a simple float representing the underlying ration seconds
     35 	// amount.
     36 	Seconds float64
     37 }
     38 
     39 // NewGpsDegreesFromRationals returns a GpsDegrees struct given the EXIF-encoded
     40 // information. The refValue is the N/E/S/W direction that this position is
     41 // relative to.
     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 // String provides returns a descriptive string.
     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 // Decimal calculates and returns the simplified float representation of the
     69 // component degrees.
     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 // Raw returns a Rational struct that can be used to *write* coordinates. In
     81 // practice, the denominator are typically (1) in the original EXIF data, and,
     82 // that being the case, this will best preserve precision.
     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 // GpsInfo encapsulates all of the geographic information in one place.
     92 type GpsInfo struct {
     93 	Latitude, Longitude GpsDegrees
     94 	Altitude            int
     95 	Timestamp           time.Time
     96 }
     97 
     98 // String returns a descriptive string.
     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 // S2CellId returns the cell-ID of the geographic location on the earth.
    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 }