2019-12-11 16:55:18 +01:00
|
|
|
package entity
|
2018-09-16 19:09:40 +02:00
|
|
|
|
2019-12-20 20:23:16 +01:00
|
|
|
import (
|
|
|
|
"strings"
|
2020-01-02 06:10:28 +01:00
|
|
|
"sync"
|
2019-12-20 20:23:16 +01:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
"github.com/photoprism/photoprism/internal/maps"
|
2019-12-31 07:16:11 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/s2"
|
2019-12-20 20:23:16 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/util"
|
|
|
|
)
|
2019-12-16 20:22:46 +01:00
|
|
|
|
2020-01-02 06:10:28 +01:00
|
|
|
var locationMutex = sync.Mutex{}
|
|
|
|
|
2018-11-06 19:02:03 +01:00
|
|
|
// Photo location
|
2018-09-16 19:09:40 +02:00
|
|
|
type Location struct {
|
2019-12-31 01:34:27 +01:00
|
|
|
ID string `gorm:"type:varbinary(16);primary_key;auto_increment:false;"`
|
|
|
|
PlaceID string `gorm:"type:varbinary(16);"`
|
2019-12-28 20:24:20 +01:00
|
|
|
Place *Place
|
2019-12-28 12:28:06 +01:00
|
|
|
LocName string `gorm:"type:varchar(100);"`
|
|
|
|
LocCategory string `gorm:"type:varchar(50);"`
|
|
|
|
LocSuburb string `gorm:"type:varchar(100);"`
|
|
|
|
LocSource string `gorm:"type:varbinary(16);"`
|
2019-12-21 17:24:29 +01:00
|
|
|
CreatedAt time.Time
|
|
|
|
UpdatedAt time.Time
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
2020-01-02 06:10:28 +01:00
|
|
|
// Locks locations for updates
|
|
|
|
func (Location) Lock() {
|
|
|
|
locationMutex.Lock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unlock locations for updates
|
|
|
|
func (Location) Unlock() {
|
|
|
|
locationMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
2019-12-20 20:23:16 +01:00
|
|
|
func NewLocation(lat, lng float64) *Location {
|
|
|
|
result := &Location{}
|
|
|
|
|
2019-12-31 07:16:11 +01:00
|
|
|
result.ID = s2.Token(lat, lng)
|
2019-12-20 20:23:16 +01:00
|
|
|
|
|
|
|
return result
|
2018-09-17 18:40:57 +02:00
|
|
|
}
|
2019-12-16 20:22:46 +01:00
|
|
|
|
2019-12-20 20:23:16 +01:00
|
|
|
func (m *Location) Find(db *gorm.DB) error {
|
2019-12-27 23:58:51 +01:00
|
|
|
if err := db.First(m, "id = ?", m.ID).Error; err == nil {
|
2019-12-28 20:24:20 +01:00
|
|
|
m.Place = FindPlace(m.PlaceID, db)
|
2019-12-28 12:28:06 +01:00
|
|
|
return nil
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
2019-12-28 12:28:06 +01:00
|
|
|
l := &maps.Location{
|
2020-01-02 04:08:33 +01:00
|
|
|
ID: m.ID,
|
2019-12-28 12:28:06 +01:00
|
|
|
}
|
|
|
|
|
2019-12-31 07:16:11 +01:00
|
|
|
if err := l.QueryPlaces(); err != nil {
|
2019-12-20 20:23:16 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
m.Place = FindPlaceByLabel(l.ID, l.LocLabel, db)
|
|
|
|
|
|
|
|
if m.Place.NoID() {
|
|
|
|
m.Place.ID = l.ID
|
|
|
|
m.Place.LocLabel = l.LocLabel
|
|
|
|
m.Place.LocCity = l.LocCity
|
|
|
|
m.Place.LocState = l.LocState
|
|
|
|
m.Place.LocCountry = l.LocCountry
|
|
|
|
}
|
|
|
|
|
2019-12-28 12:28:06 +01:00
|
|
|
m.LocName = l.LocName
|
|
|
|
m.LocCategory = l.LocCategory
|
|
|
|
m.LocSuburb = l.LocSuburb
|
|
|
|
m.LocSource = l.LocSource
|
|
|
|
|
2019-12-20 20:23:16 +01:00
|
|
|
if err := db.Create(m).Error; err != nil {
|
|
|
|
log.Errorf("location: %s", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) Keywords() []string {
|
|
|
|
result := []string{
|
2019-12-28 20:24:20 +01:00
|
|
|
strings.ToLower(m.City()),
|
|
|
|
strings.ToLower(m.Suburb()),
|
|
|
|
strings.ToLower(m.State()),
|
2019-12-20 20:23:16 +01:00
|
|
|
strings.ToLower(m.CountryName()),
|
2019-12-28 20:24:20 +01:00
|
|
|
strings.ToLower(m.Category()),
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
result = append(result, util.Keywords(m.Name())...)
|
|
|
|
result = append(result, util.Keywords(m.Label())...)
|
|
|
|
result = append(result, util.Keywords(m.Notes())...)
|
2019-12-20 20:23:16 +01:00
|
|
|
|
|
|
|
return result
|
2019-12-16 20:22:46 +01:00
|
|
|
}
|
2019-12-28 12:28:06 +01:00
|
|
|
|
|
|
|
func (m *Location) Unknown() bool {
|
2019-12-31 01:34:27 +01:00
|
|
|
return m.ID == ""
|
2019-12-28 12:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) Name() string {
|
|
|
|
return m.LocName
|
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
func (m *Location) NoName() bool {
|
|
|
|
return m.LocName == ""
|
|
|
|
}
|
|
|
|
|
2019-12-28 12:28:06 +01:00
|
|
|
func (m *Location) Category() string {
|
|
|
|
return m.LocCategory
|
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
func (m *Location) NoCategory() bool {
|
|
|
|
return m.LocCategory == ""
|
|
|
|
}
|
|
|
|
|
2019-12-28 12:28:06 +01:00
|
|
|
func (m *Location) Suburb() string {
|
|
|
|
return m.LocSuburb
|
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
func (m *Location) NoSuburb() bool {
|
|
|
|
return m.LocSuburb == ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) Label() string {
|
|
|
|
return m.Place.Label()
|
2019-12-28 12:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) City() string {
|
2019-12-28 20:24:20 +01:00
|
|
|
return m.Place.City()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) LongCity() bool {
|
|
|
|
return len(m.City()) > 16
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) NoCity() bool {
|
|
|
|
return m.City() == ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) CityContains(text string) bool {
|
|
|
|
return strings.Contains(text, m.City())
|
2019-12-28 12:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) State() string {
|
2019-12-28 20:24:20 +01:00
|
|
|
return m.Place.State()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) NoState() bool {
|
|
|
|
return m.Place.State() == ""
|
2019-12-28 12:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) CountryCode() string {
|
2019-12-28 20:24:20 +01:00
|
|
|
return m.Place.CountryCode()
|
2019-12-28 12:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) CountryName() string {
|
2019-12-28 20:24:20 +01:00
|
|
|
return m.Place.CountryName()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) Notes() string {
|
|
|
|
return m.Place.Notes()
|
2019-12-28 12:28:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Location) Source() string {
|
|
|
|
return m.LocSource
|
|
|
|
}
|