2019-12-20 11:30:58 +01:00
|
|
|
package maps
|
|
|
|
|
|
|
|
import (
|
2019-12-20 12:04:26 +01:00
|
|
|
"errors"
|
2019-12-20 11:30:58 +01:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/photoprism/photoprism/internal/maps/osm"
|
2019-12-31 07:16:11 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/maps/places"
|
2020-06-05 16:49:32 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/s2"
|
2019-12-20 11:30:58 +01:00
|
|
|
)
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
/* TODO
|
|
|
|
|
|
|
|
(SELECT pl.loc_label as album_name, pl.loc_country, YEAR(ph.taken_at) as taken_year, round(count(ph.id)) as photo_count FROM photos ph
|
|
|
|
JOIN places pl ON ph.place_id = pl.id AND pl.id <> 1
|
|
|
|
GROUP BY album_name, taken_year HAVING photo_count > 5) UNION (
|
|
|
|
SELECT c.country_name AS album_name, pl.loc_country, YEAR(ph.taken_at) as taken_year, round(count(ph.id)) as photo_count FROM photos ph
|
|
|
|
JOIN places pl ON ph.place_id = pl.id AND pl.id <> 1
|
|
|
|
JOIN countries c ON c.id = pl.loc_country
|
|
|
|
GROUP BY album_name, taken_year
|
|
|
|
HAVING photo_count > 10)
|
|
|
|
ORDER BY loc_country, album_name, taken_year;
|
|
|
|
|
2019-12-28 20:39:51 +01:00
|
|
|
*/
|
2019-12-28 20:24:20 +01:00
|
|
|
|
2019-12-20 11:30:58 +01:00
|
|
|
// Photo location
|
|
|
|
type Location struct {
|
2019-12-31 01:34:27 +01:00
|
|
|
ID string
|
2019-12-28 12:28:06 +01:00
|
|
|
LocName string
|
|
|
|
LocCategory string
|
2019-12-28 20:24:20 +01:00
|
|
|
LocLabel string
|
2019-12-28 12:28:06 +01:00
|
|
|
LocCity string
|
|
|
|
LocState string
|
|
|
|
LocCountry string
|
|
|
|
LocSource string
|
2020-04-28 19:41:06 +02:00
|
|
|
LocKeywords []string
|
2019-12-20 12:04:26 +01:00
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
type LocationSource interface {
|
2019-12-31 07:16:11 +01:00
|
|
|
CellID() string
|
2019-12-20 23:05:44 +01:00
|
|
|
CountryCode() string
|
|
|
|
Category() string
|
2019-12-27 05:18:52 +01:00
|
|
|
Name() string
|
2019-12-20 12:20:35 +01:00
|
|
|
City() string
|
|
|
|
State() string
|
|
|
|
Source() string
|
2020-04-28 19:41:06 +02:00
|
|
|
Keywords() []string
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
2020-04-28 19:41:06 +02:00
|
|
|
func NewLocation(id, name, category, label, city, state, country, source string, keywords []string) *Location {
|
2019-12-20 20:23:16 +01:00
|
|
|
result := &Location{
|
2020-01-19 13:27:15 +01:00
|
|
|
ID: id,
|
|
|
|
LocName: name,
|
|
|
|
LocCategory: category,
|
|
|
|
LocLabel: label,
|
|
|
|
LocCity: city,
|
|
|
|
LocState: state,
|
|
|
|
LocCountry: country,
|
|
|
|
LocSource: source,
|
2020-04-28 19:41:06 +02:00
|
|
|
LocKeywords: keywords,
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-01-11 01:59:43 +01:00
|
|
|
func (l *Location) QueryApi(api string) error {
|
2020-01-06 06:59:35 +01:00
|
|
|
switch api {
|
|
|
|
case "osm":
|
|
|
|
return l.QueryOSM()
|
|
|
|
case "places":
|
|
|
|
return l.QueryPlaces()
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.New("maps: reverse lookup disabled")
|
|
|
|
}
|
|
|
|
|
2019-12-31 07:16:11 +01:00
|
|
|
func (l *Location) QueryPlaces() error {
|
|
|
|
s, err := places.FindLocation(l.ID)
|
2019-12-20 12:04:26 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-31 07:16:11 +01:00
|
|
|
l.LocSource = s.Source()
|
|
|
|
l.LocName = s.Name()
|
|
|
|
l.LocCity = s.City()
|
|
|
|
l.LocState = s.State()
|
|
|
|
l.LocCountry = s.CountryCode()
|
|
|
|
l.LocCategory = s.Category()
|
|
|
|
l.LocLabel = s.Label()
|
2020-04-28 19:41:06 +02:00
|
|
|
l.LocKeywords = s.Keywords()
|
2019-12-31 07:16:11 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Location) QueryOSM() error {
|
|
|
|
s, err := osm.FindLocation(l.ID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return l.Assign(s)
|
2019-12-20 12:04:26 +01:00
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
func (l *Location) Assign(s LocationSource) error {
|
|
|
|
l.LocSource = s.Source()
|
2019-12-20 20:23:16 +01:00
|
|
|
|
2019-12-31 07:16:11 +01:00
|
|
|
l.ID = s.CellID()
|
2019-12-20 11:30:58 +01:00
|
|
|
|
|
|
|
if l.Unknown() {
|
2019-12-20 23:05:44 +01:00
|
|
|
l.LocCategory = "unknown"
|
2019-12-20 12:04:26 +01:00
|
|
|
return errors.New("maps: unknown location")
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
2019-12-27 05:18:52 +01:00
|
|
|
l.LocName = s.Name()
|
2019-12-20 12:20:35 +01:00
|
|
|
l.LocCity = s.City()
|
|
|
|
l.LocState = s.State()
|
2019-12-27 05:18:52 +01:00
|
|
|
l.LocCountry = s.CountryCode()
|
2019-12-20 23:05:44 +01:00
|
|
|
l.LocCategory = s.Category()
|
2019-12-28 20:24:20 +01:00
|
|
|
l.LocLabel = l.label()
|
2020-04-28 19:41:06 +02:00
|
|
|
l.LocKeywords = s.Keywords()
|
2019-12-20 12:04:26 +01:00
|
|
|
|
|
|
|
return nil
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Location) Unknown() bool {
|
2019-12-31 07:16:11 +01:00
|
|
|
return l.ID == ""
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
func (l *Location) label() string {
|
2019-12-20 11:30:58 +01:00
|
|
|
if l.Unknown() {
|
|
|
|
return "Unknown"
|
|
|
|
}
|
|
|
|
|
2019-12-20 20:23:16 +01:00
|
|
|
var countryName = l.CountryName()
|
2019-12-20 11:30:58 +01:00
|
|
|
var loc []string
|
2019-12-20 20:23:16 +01:00
|
|
|
|
2019-12-20 11:30:58 +01:00
|
|
|
shortCountry := len([]rune(countryName)) <= 20
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
if l.LocCity != "" {
|
2019-12-20 12:04:26 +01:00
|
|
|
loc = append(loc, l.LocCity)
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
2019-12-20 12:04:26 +01:00
|
|
|
if shortCountry && l.LocState != "" && l.LocCity != l.LocState {
|
|
|
|
loc = append(loc, l.LocState)
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if countryName != "" {
|
|
|
|
loc = append(loc, countryName)
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Join(loc[:], ", ")
|
|
|
|
}
|
2019-12-20 20:23:16 +01:00
|
|
|
|
2020-04-28 19:41:06 +02:00
|
|
|
func (l Location) S2Token() string {
|
|
|
|
return l.ID
|
|
|
|
}
|
|
|
|
|
2020-06-05 16:49:32 +02:00
|
|
|
func (l Location) PrefixedToken() string {
|
|
|
|
return s2.Prefix(l.ID)
|
|
|
|
}
|
|
|
|
|
2019-12-27 05:18:52 +01:00
|
|
|
func (l Location) Name() string {
|
|
|
|
return l.LocName
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
2019-12-28 12:28:06 +01:00
|
|
|
func (l Location) Category() string {
|
|
|
|
return l.LocCategory
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
func (l Location) Label() string {
|
|
|
|
return l.LocLabel
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
2019-12-28 12:28:06 +01:00
|
|
|
func (l Location) City() string {
|
|
|
|
return l.LocCity
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
2019-12-28 12:28:06 +01:00
|
|
|
func (l Location) State() string {
|
|
|
|
return l.LocState
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l Location) CountryCode() string {
|
2019-12-27 05:18:52 +01:00
|
|
|
return l.LocCountry
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l Location) CountryName() string {
|
2019-12-27 05:18:52 +01:00
|
|
|
return CountryNames[l.LocCountry]
|
2019-12-20 20:23:16 +01:00
|
|
|
}
|
2019-12-28 12:28:06 +01:00
|
|
|
|
|
|
|
func (l Location) Source() string {
|
|
|
|
return l.LocSource
|
|
|
|
}
|
2020-04-28 19:41:06 +02:00
|
|
|
|
|
|
|
func (l Location) Keywords() []string {
|
|
|
|
return l.LocKeywords
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l Location) KeywordString() string {
|
|
|
|
return strings.Join(l.LocKeywords, ", ")
|
|
|
|
}
|