Backend: Refactor location entity and indexer
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
e55bc8330c
commit
f3cf300590
|
@ -382,6 +382,7 @@ axolotl:
|
||||||
|
|
||||||
frog:
|
frog:
|
||||||
label: frog
|
label: frog
|
||||||
|
threshold: 0.3
|
||||||
categories:
|
categories:
|
||||||
- animal
|
- animal
|
||||||
|
|
||||||
|
@ -594,21 +595,18 @@ black grouse:
|
||||||
categories:
|
categories:
|
||||||
- animal
|
- animal
|
||||||
- bird
|
- bird
|
||||||
- chicken
|
|
||||||
|
|
||||||
ptarmigan:
|
ptarmigan:
|
||||||
threshold: 0.3
|
threshold: 0.3
|
||||||
categories:
|
categories:
|
||||||
- animal
|
- animal
|
||||||
- bird
|
- bird
|
||||||
- chicken
|
|
||||||
|
|
||||||
ruffed grouse:
|
ruffed grouse:
|
||||||
threshold: 0.3
|
threshold: 0.3
|
||||||
categories:
|
categories:
|
||||||
- animal
|
- animal
|
||||||
- bird
|
- bird
|
||||||
- chicken
|
|
||||||
|
|
||||||
prairie chicken:
|
prairie chicken:
|
||||||
threshold: 0.3
|
threshold: 0.3
|
||||||
|
@ -1318,7 +1316,7 @@ hyena:
|
||||||
|
|
||||||
fox:
|
fox:
|
||||||
label: fox
|
label: fox
|
||||||
threshold: 0.3
|
threshold: 0.5
|
||||||
categories:
|
categories:
|
||||||
- animal
|
- animal
|
||||||
|
|
||||||
|
@ -1414,7 +1412,7 @@ ant:
|
||||||
|
|
||||||
walking stick:
|
walking stick:
|
||||||
threshold: 0.3
|
threshold: 0.3
|
||||||
priority: -1
|
priority: -2
|
||||||
|
|
||||||
cockroach:
|
cockroach:
|
||||||
threshold: 0.3
|
threshold: 0.3
|
||||||
|
@ -1858,7 +1856,7 @@ airship:
|
||||||
ambulance:
|
ambulance:
|
||||||
label: van
|
label: van
|
||||||
categories:
|
categories:
|
||||||
- street
|
- road
|
||||||
- car
|
- car
|
||||||
- vehicle
|
- vehicle
|
||||||
|
|
||||||
|
@ -2008,7 +2006,7 @@ cab:
|
||||||
threshold: 0.6
|
threshold: 0.6
|
||||||
categories:
|
categories:
|
||||||
- car
|
- car
|
||||||
- street
|
- road
|
||||||
|
|
||||||
cannon:
|
cannon:
|
||||||
categories:
|
categories:
|
||||||
|
@ -2285,7 +2283,7 @@ freight car:
|
||||||
catgories:
|
catgories:
|
||||||
- train
|
- train
|
||||||
- vehicle
|
- vehicle
|
||||||
- street
|
- road
|
||||||
|
|
||||||
french horn:
|
french horn:
|
||||||
see: instrument
|
see: instrument
|
||||||
|
@ -2363,6 +2361,11 @@ hatchet:
|
||||||
|
|
||||||
home theater:
|
home theater:
|
||||||
label: screen
|
label: screen
|
||||||
|
threshold: 0.3
|
||||||
|
|
||||||
|
television:
|
||||||
|
label: screen
|
||||||
|
threshold: 0.3
|
||||||
|
|
||||||
honeycomb:
|
honeycomb:
|
||||||
priority: -1
|
priority: -1
|
||||||
|
@ -2481,12 +2484,12 @@ minibus:
|
||||||
categories:
|
categories:
|
||||||
- car
|
- car
|
||||||
- van
|
- van
|
||||||
- street
|
- road
|
||||||
|
|
||||||
minivan:
|
minivan:
|
||||||
label: van
|
label: van
|
||||||
categories:
|
categories:
|
||||||
- street
|
- road
|
||||||
- car
|
- car
|
||||||
- bus
|
- bus
|
||||||
|
|
||||||
|
@ -2538,7 +2541,7 @@ mountain tent:
|
||||||
moving van:
|
moving van:
|
||||||
label: van
|
label: van
|
||||||
categories:
|
categories:
|
||||||
- street
|
- road
|
||||||
- car
|
- car
|
||||||
- bus
|
- bus
|
||||||
|
|
||||||
|
@ -2583,13 +2586,16 @@ paddlewheel:
|
||||||
- sea
|
- sea
|
||||||
|
|
||||||
padlock:
|
padlock:
|
||||||
|
label: tool
|
||||||
|
threshold: 0.5
|
||||||
priority: -1
|
priority: -1
|
||||||
|
|
||||||
paintbrush:
|
paintbrush:
|
||||||
|
threshold: 0.3
|
||||||
categories:
|
categories:
|
||||||
- drawing
|
- drawing
|
||||||
- painting
|
- painting
|
||||||
- art
|
- exhibition
|
||||||
|
|
||||||
palace:
|
palace:
|
||||||
label: historic architecture
|
label: historic architecture
|
||||||
|
@ -2613,7 +2619,7 @@ parachute:
|
||||||
car:
|
car:
|
||||||
label: car
|
label: car
|
||||||
categories:
|
categories:
|
||||||
- street
|
- road
|
||||||
- vehicle
|
- vehicle
|
||||||
|
|
||||||
racer:
|
racer:
|
||||||
|
@ -2621,12 +2627,12 @@ racer:
|
||||||
threshold: 0.2
|
threshold: 0.2
|
||||||
categories:
|
categories:
|
||||||
- car
|
- car
|
||||||
- street
|
- road
|
||||||
|
|
||||||
passenger car:
|
passenger car:
|
||||||
see: car
|
see: car
|
||||||
categories:
|
categories:
|
||||||
- street
|
- road
|
||||||
- vehicle
|
- vehicle
|
||||||
|
|
||||||
beach wagon:
|
beach wagon:
|
||||||
|
@ -2691,7 +2697,7 @@ polaroid camera:
|
||||||
police van:
|
police van:
|
||||||
label: van
|
label: van
|
||||||
categories:
|
categories:
|
||||||
- street
|
- road
|
||||||
- car
|
- car
|
||||||
- bus
|
- bus
|
||||||
|
|
||||||
|
@ -2740,7 +2746,7 @@ recreational vehicle:
|
||||||
- bus
|
- bus
|
||||||
- van
|
- van
|
||||||
- vehicle
|
- vehicle
|
||||||
- street
|
- road
|
||||||
|
|
||||||
reflex camera:
|
reflex camera:
|
||||||
categories:
|
categories:
|
||||||
|
@ -2802,7 +2808,7 @@ school bus:
|
||||||
categories:
|
categories:
|
||||||
- bus
|
- bus
|
||||||
- van
|
- van
|
||||||
- street
|
- road
|
||||||
|
|
||||||
schooner:
|
schooner:
|
||||||
see: ship
|
see: ship
|
||||||
|
@ -2892,7 +2898,7 @@ sports car:
|
||||||
categories:
|
categories:
|
||||||
- car
|
- car
|
||||||
- vehicle
|
- vehicle
|
||||||
- street
|
- road
|
||||||
|
|
||||||
spotlight:
|
spotlight:
|
||||||
categories:
|
categories:
|
||||||
|
@ -2921,7 +2927,7 @@ streetcar:
|
||||||
categories:
|
categories:
|
||||||
- train
|
- train
|
||||||
- vehicle
|
- vehicle
|
||||||
- street
|
- road
|
||||||
|
|
||||||
studio couch:
|
studio couch:
|
||||||
label: couch
|
label: couch
|
||||||
|
@ -2998,7 +3004,7 @@ trolleybus:
|
||||||
label: bus
|
label: bus
|
||||||
categories:
|
categories:
|
||||||
- van
|
- van
|
||||||
- street
|
- road
|
||||||
|
|
||||||
trombone:
|
trombone:
|
||||||
see: instrument
|
see: instrument
|
||||||
|
@ -3298,6 +3304,7 @@ eggnog:
|
||||||
- alcohol
|
- alcohol
|
||||||
|
|
||||||
alp:
|
alp:
|
||||||
|
label: alpine hut
|
||||||
categories:
|
categories:
|
||||||
- landscape
|
- landscape
|
||||||
- mountains
|
- mountains
|
||||||
|
|
|
@ -53,8 +53,8 @@
|
||||||
:label="labels.country"
|
:label="labels.country"
|
||||||
flat solo hide-details
|
flat solo hide-details
|
||||||
color="secondary-dark"
|
color="secondary-dark"
|
||||||
item-value="LocCountryCode"
|
item-value="code"
|
||||||
item-text="LocCountry"
|
item-text="name"
|
||||||
v-model="filter.country"
|
v-model="filter.country"
|
||||||
:items="options.countries">
|
:items="options.countries">
|
||||||
</v-select>
|
</v-select>
|
||||||
|
@ -106,8 +106,8 @@
|
||||||
data() {
|
data() {
|
||||||
const cameras = [{ID: 0, CameraModel: this.$gettext('All Cameras')}].concat(this.$config.getValue('cameras'));
|
const cameras = [{ID: 0, CameraModel: this.$gettext('All Cameras')}].concat(this.$config.getValue('cameras'));
|
||||||
const countries = [{
|
const countries = [{
|
||||||
LocCountryCode: '',
|
code: '',
|
||||||
LocCountry: this.$gettext('All Countries')
|
name: this.$gettext('All Countries')
|
||||||
}].concat(this.$config.getValue('countries'));
|
}].concat(this.$config.getValue('countries'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
{{ photo.getCamera() }}
|
{{ photo.getCamera() }}
|
||||||
<br/>
|
<br/>
|
||||||
<v-icon size="14">location_on</v-icon>
|
<v-icon size="14">location_on</v-icon>
|
||||||
<span class="p-pointer" :title="photo.getFullLocation()"
|
<span class="p-pointer" :title="photo.LocationID"
|
||||||
@click.stop="openLocation(index)">{{ photo.getLocation() }}</span>
|
@click.stop="openLocation(index)">{{ photo.getLocation() }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td @click="openPhoto(props.index)" class="p-pointer">{{ props.item.PhotoTitle }}</td>
|
<td @click="openPhoto(props.index)" class="p-pointer">{{ props.item.PhotoTitle }}</td>
|
||||||
<td>{{ props.item.TakenAt | luxon:format('dd/MM/yyyy hh:mm:ss') }}</td>
|
<td>{{ props.item.TakenAt | luxon:format('dd/MM/yyyy hh:mm:ss') }}</td>
|
||||||
<td @click="openLocation(props.index)" class="p-pointer">{{ props.item.LocCountry }}</td>
|
<td @click="openLocation(props.index)" class="p-pointer">{{ props.item.getLocation() }}</td>
|
||||||
<td>{{ props.item.CameraMake }} {{ props.item.CameraModel }}</td>
|
<td>{{ props.item.CameraMake }} {{ props.item.CameraModel }}</td>
|
||||||
<td><v-btn icon small flat :ripple="false"
|
<td><v-btn icon small flat :ripple="false"
|
||||||
class="p-photo-like"
|
class="p-photo-like"
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
{text: '', value: '', align: 'center', sortable: false, class: 'p-col-select'},
|
{text: '', value: '', align: 'center', sortable: false, class: 'p-col-select'},
|
||||||
{text: this.$gettext('Title'), value: 'PhotoTitle'},
|
{text: this.$gettext('Title'), value: 'PhotoTitle'},
|
||||||
{text: this.$gettext('Taken At'), value: 'TakenAt'},
|
{text: this.$gettext('Taken At'), value: 'TakenAt'},
|
||||||
{text: this.$gettext('Country'), value: 'LocCountry'},
|
{text: this.$gettext('Location'), value: 'LocRegion'},
|
||||||
{text: this.$gettext('Camera'), value: 'CameraModel'},
|
{text: this.$gettext('Camera'), value: 'CameraModel'},
|
||||||
{text: this.$gettext('Favorite'), value: 'PhotoFavorite', align: 'left'},
|
{text: this.$gettext('Favorite'), value: 'PhotoFavorite', align: 'left'},
|
||||||
],
|
],
|
||||||
|
|
|
@ -53,8 +53,8 @@
|
||||||
:label="labels.country"
|
:label="labels.country"
|
||||||
flat solo hide-details
|
flat solo hide-details
|
||||||
color="secondary-dark"
|
color="secondary-dark"
|
||||||
item-value="LocCountryCode"
|
item-value="code"
|
||||||
item-text="LocCountry"
|
item-text="name"
|
||||||
v-model="filter.country"
|
v-model="filter.country"
|
||||||
:items="options.countries">
|
:items="options.countries">
|
||||||
</v-select>
|
</v-select>
|
||||||
|
@ -156,8 +156,8 @@
|
||||||
data() {
|
data() {
|
||||||
const cameras = [{ID: 0, CameraModel: this.$gettext('All Cameras')}].concat(this.$config.getValue('cameras'));
|
const cameras = [{ID: 0, CameraModel: this.$gettext('All Cameras')}].concat(this.$config.getValue('cameras'));
|
||||||
const countries = [{
|
const countries = [{
|
||||||
LocCountryCode: '',
|
code: '',
|
||||||
LocCountry: this.$gettext('All Countries')
|
name: this.$gettext('All Countries')
|
||||||
}].concat(this.$config.getValue('countries'));
|
}].concat(this.$config.getValue('countries'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Photo extends Abstract {
|
||||||
}
|
}
|
||||||
|
|
||||||
getGoogleMapsLink() {
|
getGoogleMapsLink() {
|
||||||
return "https://www.google.com/maps/place/" + this.PhotoLat + "," + this.PhotoLong;
|
return "https://www.google.com/maps/place/" + this.PhotoLat + "," + this.PhotoLng;
|
||||||
}
|
}
|
||||||
|
|
||||||
getThumbnailUrl(type) {
|
getThumbnailUrl(type) {
|
||||||
|
@ -101,71 +101,17 @@ class Photo extends Abstract {
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLocation() {
|
hasLocation() {
|
||||||
return this.PhotoLat !== 0 || this.PhotoLong !== 0;
|
return this.PhotoLat !== 0 || this.PhotoLng !== 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLocation() {
|
getLocation() {
|
||||||
const location = [];
|
if (this.LocRegion) {
|
||||||
|
return this.LocRegion
|
||||||
if (this.LocationID) {
|
|
||||||
if (this.LocName && !this.LocCity && !this.LocCounty) {
|
|
||||||
location.push(truncate(this.LocName, 20));
|
|
||||||
} else if (this.LocCity && this.LocCity.length < 20) {
|
|
||||||
location.push(this.LocCity);
|
|
||||||
} else if (this.LocCounty && this.LocCity.length < 20) {
|
|
||||||
location.push(this.LocCounty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.LocState && this.LocState !== this.LocCity) {
|
|
||||||
location.push(this.LocState);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.LocCountry) {
|
|
||||||
location.push(this.LocCountry);
|
|
||||||
}
|
|
||||||
} else if (this.CountryName) {
|
} else if (this.CountryName) {
|
||||||
location.push(this.CountryName);
|
return this.CountryName;
|
||||||
} else {
|
|
||||||
location.push("Unknown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return location.join(", ");
|
return "Unknown"
|
||||||
}
|
|
||||||
|
|
||||||
getFullLocation() {
|
|
||||||
const location = [];
|
|
||||||
|
|
||||||
if (this.LocationID) {
|
|
||||||
if (this.LocName) {
|
|
||||||
location.push(this.LocName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.LocCity) {
|
|
||||||
location.push(this.LocCity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.LocPostcode) {
|
|
||||||
location.push(this.LocPostcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.LocCounty) {
|
|
||||||
location.push(this.LocCounty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.LocState) {
|
|
||||||
location.push(this.LocState);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.LocCountry) {
|
|
||||||
location.push(this.LocCountry);
|
|
||||||
}
|
|
||||||
} else if (this.CountryName) {
|
|
||||||
location.push(this.CountryName);
|
|
||||||
} else {
|
|
||||||
location.push("Unknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
return location.join(", ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCamera() {
|
getCamera() {
|
||||||
|
|
|
@ -115,14 +115,10 @@
|
||||||
openLocation(index) {
|
openLocation(index) {
|
||||||
const photo = this.results[index];
|
const photo = this.results[index];
|
||||||
|
|
||||||
if (photo.PhotoLat && photo.PhotoLong) {
|
if (photo.PhotoLat && photo.PhotoLng) {
|
||||||
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), long: String(photo.PhotoLong)}});
|
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), lng: String(photo.PhotoLng)}});
|
||||||
} else if (photo.LocName) {
|
|
||||||
this.$router.push({name: "places", query: {q: photo.LocName}});
|
|
||||||
} else if (photo.LocCity) {
|
} else if (photo.LocCity) {
|
||||||
this.$router.push({name: "places", query: {q: photo.LocCity}});
|
this.$router.push({name: "places", query: {q: photo.LocCity}});
|
||||||
} else if (photo.LocCountry) {
|
|
||||||
this.$router.push({name: "places", query: {q: photo.LocCountry}});
|
|
||||||
} else {
|
} else {
|
||||||
this.$router.push({name: "places", query: {q: photo.CountryName}});
|
this.$router.push({name: "places", query: {q: photo.CountryName}});
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,14 +105,10 @@
|
||||||
openLocation(index) {
|
openLocation(index) {
|
||||||
const photo = this.results[index];
|
const photo = this.results[index];
|
||||||
|
|
||||||
if (photo.PhotoLat && photo.PhotoLong) {
|
if (photo.PhotoLat && photo.PhotoLng) {
|
||||||
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), long: String(photo.PhotoLong)}});
|
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), lng: String(photo.PhotoLng)}});
|
||||||
} else if (photo.LocName) {
|
|
||||||
this.$router.push({name: "places", query: {q: photo.LocName}});
|
|
||||||
} else if (photo.LocCity) {
|
} else if (photo.LocCity) {
|
||||||
this.$router.push({name: "places", query: {q: photo.LocCity}});
|
this.$router.push({name: "places", query: {q: photo.LocCity}});
|
||||||
} else if (photo.LocCountry) {
|
|
||||||
this.$router.push({name: "places", query: {q: photo.LocCountry}});
|
|
||||||
} else {
|
} else {
|
||||||
this.$router.push({name: "places", query: {q: photo.CountryName}});
|
this.$router.push({name: "places", query: {q: photo.CountryName}});
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
loading: false,
|
loading: false,
|
||||||
zoom: zoom,
|
zoom: zoom,
|
||||||
position: null,
|
position: null,
|
||||||
center: L.latLng(parseFloat(pos.lat), parseFloat(pos.long)),
|
center: L.latLng(parseFloat(pos.lat), parseFloat(pos.lng)),
|
||||||
url: 'https://{s}.tile.osm.org/{z}/{x}/{y}.png',
|
url: 'https://{s}.tile.osm.org/{z}/{x}/{y}.png',
|
||||||
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||||
options: {
|
options: {
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
query: {
|
query: {
|
||||||
q: q,
|
q: q,
|
||||||
lat: pos.lat,
|
lat: pos.lat,
|
||||||
long: pos.long,
|
lng: pos.lng,
|
||||||
dist: dist.toString(),
|
dist: dist.toString(),
|
||||||
zoom: zoom.toString(),
|
zoom: zoom.toString(),
|
||||||
},
|
},
|
||||||
|
@ -75,8 +75,8 @@
|
||||||
bounds: null,
|
bounds: null,
|
||||||
minLat: null,
|
minLat: null,
|
||||||
maxLat: null,
|
maxLat: null,
|
||||||
minLong: null,
|
minLng: null,
|
||||||
maxLong: null,
|
maxLng: null,
|
||||||
labels: {
|
labels: {
|
||||||
search: this.$gettext("Search"),
|
search: this.$gettext("Search"),
|
||||||
},
|
},
|
||||||
|
@ -128,12 +128,12 @@
|
||||||
},
|
},
|
||||||
onCenter(pos) {
|
onCenter(pos) {
|
||||||
const changed = Math.abs(this.query.lat - pos.lat) > 0.001 ||
|
const changed = Math.abs(this.query.lat - pos.lat) > 0.001 ||
|
||||||
Math.abs(this.query.long - pos.lng) > 0.001;
|
Math.abs(this.query.lng - pos.lng) > 0.001;
|
||||||
|
|
||||||
if(!changed) return;
|
if(!changed) return;
|
||||||
|
|
||||||
this.query.lat = pos.lat.toString();
|
this.query.lat = pos.lat.toString();
|
||||||
this.query.long = pos.lng.toString();
|
this.query.lng = pos.lng.toString();
|
||||||
|
|
||||||
this.search();
|
this.search();
|
||||||
},
|
},
|
||||||
|
@ -143,21 +143,21 @@
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
lat: pos.lat.toString(),
|
lat: pos.lat.toString(),
|
||||||
long: pos.long.toString(),
|
lng: pos.lng.toString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryLat = query['lat'];
|
const queryLat = query['lat'];
|
||||||
const queryLong = query['long'];
|
const queryLng = query['lng'];
|
||||||
|
|
||||||
let storedLat = window.localStorage.getItem("lat");
|
let storedLat = window.localStorage.getItem("lat");
|
||||||
let storedLong = window.localStorage.getItem("long");
|
let storedLng = window.localStorage.getItem("lng");
|
||||||
|
|
||||||
if (queryLat && queryLong) {
|
if (queryLat && queryLng) {
|
||||||
result.lat = queryLat;
|
result.lat = queryLat;
|
||||||
result.long = queryLong;
|
result.lng = queryLng;
|
||||||
} else if (storedLat && storedLong) {
|
} else if (storedLat && storedLng) {
|
||||||
result.lat = storedLat;
|
result.lat = storedLat;
|
||||||
result.long = storedLong;
|
result.lng = storedLng;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -183,23 +183,23 @@
|
||||||
},
|
},
|
||||||
formChange() {
|
formChange() {
|
||||||
this.query.lat = "";
|
this.query.lat = "";
|
||||||
this.query.long = "";
|
this.query.lng = "";
|
||||||
this.search();
|
this.search();
|
||||||
},
|
},
|
||||||
clearQuery() {
|
clearQuery() {
|
||||||
this.position = null;
|
this.position = null;
|
||||||
this.query.q = "";
|
this.query.q = "";
|
||||||
this.query.lat = "";
|
this.query.lat = "";
|
||||||
this.query.long = "";
|
this.query.lng = "";
|
||||||
this.search();
|
this.search();
|
||||||
},
|
},
|
||||||
resetBoundingBox() {
|
resetBoundingBox() {
|
||||||
this.minLat = null;
|
this.minLat = null;
|
||||||
this.maxLat = null;
|
this.maxLat = null;
|
||||||
this.minLong = null;
|
this.minLng = null;
|
||||||
this.maxLong = null;
|
this.maxLng = null;
|
||||||
},
|
},
|
||||||
fitBoundingBox(lat, long) {
|
fitBoundingBox(lat, lng) {
|
||||||
if (this.maxLat === null || lat > this.maxLat) {
|
if (this.maxLat === null || lat > this.maxLat) {
|
||||||
this.maxLat = lat;
|
this.maxLat = lat;
|
||||||
}
|
}
|
||||||
|
@ -208,12 +208,12 @@
|
||||||
this.minLat = lat;
|
this.minLat = lat;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.maxLong === null || long > this.maxLong) {
|
if (this.maxLng === null || lng > this.maxLng) {
|
||||||
this.maxLong = long;
|
this.maxLng = lng;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.minLong === null || long < this.minLong) {
|
if (this.minLng === null || lng < this.minLng) {
|
||||||
this.minLong = long;
|
this.minLng = lng;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateMap(results) {
|
updateMap(results) {
|
||||||
|
@ -239,7 +239,7 @@
|
||||||
iconSize: [50, 50],
|
iconSize: [50, 50],
|
||||||
className: 'leaflet-marker-photo',
|
className: 'leaflet-marker-photo',
|
||||||
}),
|
}),
|
||||||
location: L.latLng(result.PhotoLat, result.PhotoLong),
|
location: L.latLng(result.PhotoLat, result.PhotoLng),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,9 +257,9 @@
|
||||||
updateQuery() {
|
updateQuery() {
|
||||||
const query = Object(this.query);
|
const query = Object(this.query);
|
||||||
|
|
||||||
if (this.query.lat && this.query.long) {
|
if (this.query.lat && this.query.lng) {
|
||||||
window.localStorage.setItem("lat", this.query.lat.toString());
|
window.localStorage.setItem("lat", this.query.lat.toString());
|
||||||
window.localStorage.setItem("long", this.query.long.toString());
|
window.localStorage.setItem("lng", this.query.lng.toString());
|
||||||
} else {
|
} else {
|
||||||
this.position = null;
|
this.position = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ describe("model/photo", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get photo maps link", () => {
|
it("should get photo maps link", () => {
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", PhotoLat: 36.442881666666665, PhotoLong: 28.229493333333334};
|
const values = {ID: 5, PhotoTitle: "Crazy Cat", PhotoLat: 36.442881666666665, PhotoLng: 28.229493333333334};
|
||||||
const photo = new Photo(values);
|
const photo = new Photo(values);
|
||||||
const result = photo.getGoogleMapsLink();
|
const result = photo.getGoogleMapsLink();
|
||||||
assert.equal(result, "https://www.google.com/maps/place/36.442881666666665,28.229493333333334");
|
assert.equal(result, "https://www.google.com/maps/place/36.442881666666665,28.229493333333334");
|
||||||
|
@ -121,35 +121,35 @@ describe("model/photo", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should test whether photo has location", () => {
|
it("should test whether photo has location", () => {
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", PhotoLat: 36.442881666666665, PhotoLong: 28.229493333333334};
|
const values = {ID: 5, PhotoTitle: "Crazy Cat", PhotoLat: 36.442881666666665, PhotoLng: 28.229493333333334};
|
||||||
const photo = new Photo(values);
|
const photo = new Photo(values);
|
||||||
const result = photo.hasLocation();
|
const result = photo.hasLocation();
|
||||||
assert.equal(result, true);
|
assert.equal(result, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should test whether photo has location", () => {
|
it("should test whether photo has location", () => {
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", PhotoLat: 0, PhotoLong: 0};
|
const values = {ID: 5, PhotoTitle: "Crazy Cat", PhotoLat: 0, PhotoLng: 0};
|
||||||
const photo = new Photo(values);
|
const photo = new Photo(values);
|
||||||
const result = photo.hasLocation();
|
const result = photo.hasLocation();
|
||||||
assert.equal(result, false);
|
assert.equal(result, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get location", () => {
|
it("should get location", () => {
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocName: "Cape Point", LocCountry: "Africa"};
|
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocRegion: "Cape Point, South Africa", LocCountry: "South Africa"};
|
||||||
const photo = new Photo(values);
|
const photo = new Photo(values);
|
||||||
const result = photo.getLocation();
|
const result = photo.getLocation();
|
||||||
assert.equal(result, "Cape Point, Africa");
|
assert.equal(result, "Cape Point, South Africa");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get location", () => {
|
it("should get location", () => {
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocCountry: "Africa", LocCity: "Cape Town", LocCounty: "County", LocState: "State"};
|
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocRegion: "Cape Point, State, South Africa", LocCountry: "South Africa", LocCity: "Cape Town", LocCounty: "County", LocState: "State"};
|
||||||
const photo = new Photo(values);
|
const photo = new Photo(values);
|
||||||
const result = photo.getLocation();
|
const result = photo.getLocation();
|
||||||
assert.equal(result, "Cape Town, State, Africa");
|
assert.equal(result, "Cape Point, State, South Africa");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get location", () => {
|
it("should get location", () => {
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocType: "viewpoint", LocName: "Cape Point", LocCountry: "Africa", LocCity: "Cape Town", LocCounty: "County", LocState: "State"};
|
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocType: "viewpoint", LocTitle: "Cape Point", LocCountry: "Africa", LocCity: "Cape Town", LocCounty: "County", LocState: "State"};
|
||||||
const photo = new Photo(values);
|
const photo = new Photo(values);
|
||||||
const result = photo.getLocation();
|
const result = photo.getLocation();
|
||||||
assert.equal(result, "Unknown");
|
assert.equal(result, "Unknown");
|
||||||
|
@ -162,27 +162,6 @@ describe("model/photo", () => {
|
||||||
assert.equal(result, "Africa");
|
assert.equal(result, "Africa");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should get full location", () => {
|
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 55, LocName: "Cape Point", LocCountry: "Africa", LocCity: "Cape Town", LocCounty: "County", LocState: "State", LocPostcode: 12345};
|
|
||||||
const photo = new Photo(values);
|
|
||||||
const result = photo.getFullLocation();
|
|
||||||
assert.equal(result, "Cape Point, Cape Town, 12345, County, State, Africa");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should get full location", () => {
|
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", CountryName: "Africa"};
|
|
||||||
const photo = new Photo(values);
|
|
||||||
const result = photo.getFullLocation();
|
|
||||||
assert.equal(result, "Africa");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should get full location", () => {
|
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocCity: "Cape Town"};
|
|
||||||
const photo = new Photo(values);
|
|
||||||
const result = photo.getFullLocation();
|
|
||||||
assert.equal(result, "Unknown");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should get camera", () => {
|
it("should get camera", () => {
|
||||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", CameraModel: "EOSD10", CameraMake: "Canon"};
|
const values = {ID: 5, PhotoTitle: "Crazy Cat", CameraModel: "EOSD10", CameraMake: "Canon"};
|
||||||
const photo = new Photo(values);
|
const photo = new Photo(values);
|
||||||
|
|
|
@ -572,27 +572,22 @@ func (c *Config) ClientConfig() ClientConfig {
|
||||||
var cameras []*entity.Camera
|
var cameras []*entity.Camera
|
||||||
var albums []*entity.Album
|
var albums []*entity.Album
|
||||||
|
|
||||||
type country struct {
|
|
||||||
LocCountry string
|
|
||||||
LocCountryCode string
|
|
||||||
}
|
|
||||||
|
|
||||||
var position struct {
|
var position struct {
|
||||||
PhotoUUID string `json:"uuid"`
|
PhotoUUID string `json:"uuid"`
|
||||||
|
LocationID string `json:"olc"`
|
||||||
PhotoLat float64 `json:"lat"`
|
PhotoLat float64 `json:"lat"`
|
||||||
PhotoLong float64 `json:"long"`
|
PhotoLng float64 `json:"lng"`
|
||||||
TakenAt time.Time `json:"utc"`
|
TakenAt time.Time `json:"utc"`
|
||||||
TakenAtLocal time.Time `json:"time"`
|
TakenAtLocal time.Time `json:"time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Table("photos").
|
db.Table("photos").
|
||||||
Select("photo_uuid, photo_lat, photo_long, taken_at, taken_at_local").
|
Select("photo_uuid, location_id, photo_lat, photo_lng, taken_at, taken_at_local").
|
||||||
Where("deleted_at IS NULL AND photo_lat != 0 AND photo_long != 0").
|
Where("deleted_at IS NULL AND photo_lat != 0 AND photo_lng != 0").
|
||||||
Order("taken_at DESC").
|
Order("taken_at DESC").
|
||||||
Limit(1).Offset(0).
|
Limit(1).Offset(0).
|
||||||
Take(&position)
|
Take(&position)
|
||||||
|
|
||||||
var countries []country
|
|
||||||
var count = struct {
|
var count = struct {
|
||||||
Photos uint `json:"photos"`
|
Photos uint `json:"photos"`
|
||||||
Favorites uint `json:"favorites"`
|
Favorites uint `json:"favorites"`
|
||||||
|
@ -622,8 +617,15 @@ func (c *Config) ClientConfig() ClientConfig {
|
||||||
Select("COUNT(*) AS countries").
|
Select("COUNT(*) AS countries").
|
||||||
Take(&count)
|
Take(&count)
|
||||||
|
|
||||||
db.Model(&entity.Location{}).
|
type country struct {
|
||||||
Select("DISTINCT loc_country_code, loc_country").
|
ID string `json:"code"`
|
||||||
|
CountryName string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var countries []country
|
||||||
|
|
||||||
|
db.Model(&entity.Country{}).
|
||||||
|
Select("DISTINCT id, country_name").
|
||||||
Scan(&countries)
|
Scan(&countries)
|
||||||
|
|
||||||
db.Where("deleted_at IS NULL").
|
db.Where("deleted_at IS NULL").
|
||||||
|
|
|
@ -23,7 +23,7 @@ type Album struct {
|
||||||
AlbumFavorite bool
|
AlbumFavorite bool
|
||||||
AlbumPublic bool
|
AlbumPublic bool
|
||||||
AlbumLat float64
|
AlbumLat float64
|
||||||
AlbumLong float64
|
AlbumLng float64
|
||||||
AlbumRadius float64
|
AlbumRadius float64
|
||||||
AlbumOrder string `gorm:"type:varchar(16);"`
|
AlbumOrder string `gorm:"type:varchar(16);"`
|
||||||
AlbumTemplate string `gorm:"type:varchar(128);"`
|
AlbumTemplate string `gorm:"type:varchar(128);"`
|
||||||
|
|
|
@ -18,7 +18,7 @@ type Event struct {
|
||||||
EventBegin time.Time `gorm:"type:datetime;"`
|
EventBegin time.Time `gorm:"type:datetime;"`
|
||||||
EventEnd time.Time `gorm:"type:datetime;"`
|
EventEnd time.Time `gorm:"type:datetime;"`
|
||||||
EventLat float64
|
EventLat float64
|
||||||
EventLong float64
|
EventLng float64
|
||||||
EventDist float64
|
EventDist float64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
|
|
|
@ -1,71 +1,68 @@
|
||||||
package entity
|
package entity
|
||||||
|
|
||||||
var locTypeLabels = map[string]string{
|
import (
|
||||||
"bay": "bay",
|
"strings"
|
||||||
"art": "art exhibition",
|
"time"
|
||||||
"fire station": "fire station",
|
|
||||||
"hairdresser": "hairdresser",
|
olc "github.com/google/open-location-code/go"
|
||||||
"cape": "cape",
|
"github.com/jinzhu/gorm"
|
||||||
"coastline": "coastline",
|
"github.com/photoprism/photoprism/internal/maps"
|
||||||
"cliff": "cliff",
|
"github.com/photoprism/photoprism/internal/util"
|
||||||
"wetland": "wetland",
|
)
|
||||||
"nature reserve": "nature reserve",
|
|
||||||
"beach": "beach",
|
|
||||||
"cafe": "cafe",
|
|
||||||
"internet cafe": "cafe",
|
|
||||||
"ice cream": "ice cream parlor",
|
|
||||||
"bistro": "restaurant",
|
|
||||||
"restaurant": "restaurant",
|
|
||||||
"ship": "ship",
|
|
||||||
"wholesale": "shop",
|
|
||||||
"food": "shop",
|
|
||||||
"supermarket": "supermarket",
|
|
||||||
"florist": "florist",
|
|
||||||
"pharmacy": "pharmacy",
|
|
||||||
"seafood": "seafood",
|
|
||||||
"clothes": "clothing store",
|
|
||||||
"residential": "residential area",
|
|
||||||
"museum": "museum",
|
|
||||||
"castle": "castle",
|
|
||||||
"terminal": "airport",
|
|
||||||
"ferry terminal": "harbor",
|
|
||||||
"bridge": "bridge",
|
|
||||||
"university": "university",
|
|
||||||
"mall": "mall",
|
|
||||||
"marina": "marina",
|
|
||||||
"garden": "garden",
|
|
||||||
"pedestrian": "shopping area",
|
|
||||||
"bunker": "bunker",
|
|
||||||
"viewpoint": "viewpoint",
|
|
||||||
"train station": "train station",
|
|
||||||
"farm": "farm",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Photo location
|
// Photo location
|
||||||
type Location struct {
|
type Location struct {
|
||||||
Model
|
maps.Location
|
||||||
LocDisplayName string
|
|
||||||
LocLat float64
|
|
||||||
LocLong float64
|
|
||||||
LocCategory string
|
|
||||||
LocType string
|
|
||||||
LocName string
|
|
||||||
LocHouseNr string
|
|
||||||
LocStreet string
|
|
||||||
LocSuburb string
|
|
||||||
LocCity string
|
|
||||||
LocPostcode string
|
|
||||||
LocCounty string
|
|
||||||
LocState string
|
|
||||||
LocCountry string
|
|
||||||
LocCountryCode string
|
|
||||||
LocDescription string `gorm:"type:text;"`
|
LocDescription string `gorm:"type:text;"`
|
||||||
LocNotes string `gorm:"type:text;"`
|
LocNotes string `gorm:"type:text;"`
|
||||||
LocPhoto *Photo
|
|
||||||
LocPhotoID uint
|
|
||||||
LocFavorite bool
|
LocFavorite bool
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocation(lat, lng float64) *Location {
|
||||||
|
result := &Location{}
|
||||||
|
|
||||||
|
result.ID = olc.Encode(lat, lng, 11)
|
||||||
|
result.LocLat = lat
|
||||||
|
result.LocLng = lng
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Location) Label() string {
|
func (m *Location) Label() string {
|
||||||
return locTypeLabels[m.LocType]
|
return m.LocLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Location) Find(db *gorm.DB) error {
|
||||||
|
if err := db.First(m, "id = ?", m.ID).Error; err == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.Query(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Create(m).Error; err != nil {
|
||||||
|
log.Errorf("location: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Location) Keywords() []string {
|
||||||
|
result := []string{
|
||||||
|
strings.ToLower(m.LocCity),
|
||||||
|
strings.ToLower(m.LocSuburb),
|
||||||
|
strings.ToLower(m.LocState),
|
||||||
|
strings.ToLower(m.CountryName()),
|
||||||
|
strings.ToLower(m.LocLabel),
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, util.Keywords(m.LocTitle)...)
|
||||||
|
result = append(result, util.Keywords(m.LocDescription)...)
|
||||||
|
result = append(result, util.Keywords(m.LocNotes)...)
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLocation_Label(t *testing.T) {
|
func TestLocation_Label(t *testing.T) {
|
||||||
location := &Location{LocCategory: "restaurant", LocType: "bistro"}
|
l := NewLocation(1,1)
|
||||||
result := location.Label()
|
l.LocLabel = "restaurant"
|
||||||
|
result := l.Label()
|
||||||
|
|
||||||
assert.Equal(t, "restaurant", result)
|
assert.Equal(t, "restaurant", result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ type Photo struct {
|
||||||
PhotoNSFW bool `json:"PhotoNSFW"`
|
PhotoNSFW bool `json:"PhotoNSFW"`
|
||||||
PhotoStory bool `json:"PhotoStory"`
|
PhotoStory bool `json:"PhotoStory"`
|
||||||
PhotoLat float64 `gorm:"index;"`
|
PhotoLat float64 `gorm:"index;"`
|
||||||
PhotoLong float64 `gorm:"index;"`
|
PhotoLng float64 `gorm:"index;"`
|
||||||
PhotoAltitude int
|
PhotoAltitude int
|
||||||
PhotoFocalLength int
|
PhotoFocalLength int
|
||||||
PhotoIso int
|
PhotoIso int
|
||||||
|
@ -40,7 +40,7 @@ type Photo struct {
|
||||||
CountryID string `gorm:"index;"`
|
CountryID string `gorm:"index;"`
|
||||||
CountryChanged bool
|
CountryChanged bool
|
||||||
Location *Location
|
Location *Location
|
||||||
LocationID uint
|
LocationID string
|
||||||
LocationChanged bool
|
LocationChanged bool
|
||||||
LocationEstimated bool
|
LocationEstimated bool
|
||||||
TakenAt time.Time `gorm:"type:datetime;index;"`
|
TakenAt time.Time `gorm:"type:datetime;index;"`
|
||||||
|
|
|
@ -17,24 +17,24 @@ import (
|
||||||
type PhotoSearch struct {
|
type PhotoSearch struct {
|
||||||
Query string `form:"q"`
|
Query string `form:"q"`
|
||||||
|
|
||||||
Title string `form:"title"`
|
Title string `form:"title"`
|
||||||
Description string `form:"description"`
|
Description string `form:"description"`
|
||||||
Notes string `form:"notes"`
|
Notes string `form:"notes"`
|
||||||
Artist string `form:"artist"`
|
Artist string `form:"artist"`
|
||||||
Hash string `form:"hash"`
|
Hash string `form:"hash"`
|
||||||
Duplicate bool `form:"duplicate"`
|
Duplicate bool `form:"duplicate"`
|
||||||
Lat float64 `form:"lat"`
|
Lat float64 `form:"lat"`
|
||||||
Long float64 `form:"long"`
|
Lng float64 `form:"lng"`
|
||||||
Dist uint `form:"dist"`
|
Dist uint `form:"dist"`
|
||||||
Fmin float64 `form:"fmin"`
|
Fmin float64 `form:"fmin"`
|
||||||
Fmax float64 `form:"fmax"`
|
Fmax float64 `form:"fmax"`
|
||||||
Chroma uint `form:"chroma"`
|
Chroma uint `form:"chroma"`
|
||||||
Mono bool `form:"mono"`
|
Mono bool `form:"mono"`
|
||||||
Portrait bool `form:"portrait"`
|
Portrait bool `form:"portrait"`
|
||||||
Location bool `form:"location"`
|
Location bool `form:"location"`
|
||||||
Album string `form:"album"`
|
Album string `form:"album"`
|
||||||
Label string `form:"label"`
|
Label string `form:"label"`
|
||||||
Country string `form:"country"`
|
Country string `form:"country"`
|
||||||
Color string `form:"color"`
|
Color string `form:"color"`
|
||||||
Camera int `form:"camera"`
|
Camera int `form:"camera"`
|
||||||
Before time.Time `form:"before" time_format:"2006-01-02"`
|
Before time.Time `form:"before" time_format:"2006-01-02"`
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestParseQueryString(t *testing.T) {
|
||||||
assert.Equal(t, 33.45343166666667, form.Lat)
|
assert.Equal(t, 33.45343166666667, form.Lat)
|
||||||
})
|
})
|
||||||
t.Run("valid query 2", func(t *testing.T) {
|
t.Run("valid query 2", func(t *testing.T) {
|
||||||
form := &PhotoSearch{Query: "chroma:600 description:\"test\" after:2018-01-15 duplicate:false favorites:true long:33.45343166666667"}
|
form := &PhotoSearch{Query: "chroma:600 description:\"test\" after:2018-01-15 duplicate:false favorites:true lng:33.45343166666667"}
|
||||||
|
|
||||||
err := form.ParseQueryString()
|
err := form.ParseQueryString()
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ func TestParseQueryString(t *testing.T) {
|
||||||
assert.Equal(t, "test", form.Description)
|
assert.Equal(t, "test", form.Description)
|
||||||
assert.Equal(t, time.Date(2018, 01, 15, 0, 0, 0, 0, time.UTC), form.After)
|
assert.Equal(t, time.Date(2018, 01, 15, 0, 0, 0, 0, time.UTC), form.After)
|
||||||
assert.Equal(t, false, form.Duplicate)
|
assert.Equal(t, false, form.Duplicate)
|
||||||
assert.Equal(t, 33.45343166666667, form.Long)
|
assert.Equal(t, 33.45343166666667, form.Lng)
|
||||||
})
|
})
|
||||||
t.Run("valid query with umlauts", func(t *testing.T) {
|
t.Run("valid query with umlauts", func(t *testing.T) {
|
||||||
form := &PhotoSearch{Query: "description:\"tübingen\""}
|
form := &PhotoSearch{Query: "description:\"tübingen\""}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
package maps
|
package maps
|
||||||
|
|
||||||
var Countries = map[string]string{
|
var CountryNames = map[string]string{
|
||||||
"af": "Afghanistan",
|
"af": "Afghanistan",
|
||||||
"ax": "Åland Islands",
|
"ax": "Åland Islands",
|
||||||
"al": "Albania",
|
"al": "Albania",
|
||||||
|
|
|
@ -53,7 +53,7 @@ func main() {
|
||||||
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
|
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
|
||||||
package maps
|
package maps
|
||||||
|
|
||||||
var Countries = map[string]string{
|
var CountryNames = map[string]string{
|
||||||
{{- range .Countries }}
|
{{- range .Countries }}
|
||||||
{{ printf "%q" .Code }}: {{ printf "%q" .Name }},
|
{{ printf "%q" .Code }}: {{ printf "%q" .Name }},
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
@ -8,21 +8,19 @@ import (
|
||||||
"github.com/photoprism/photoprism/internal/maps/osm"
|
"github.com/photoprism/photoprism/internal/maps/osm"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SourceOSM = "osm"
|
|
||||||
|
|
||||||
// Photo location
|
// Photo location
|
||||||
type Location struct {
|
type Location struct {
|
||||||
ID string `gorm:"primary_key"`
|
ID string `gorm:"primary_key"`
|
||||||
LocLat float64
|
LocLat float64
|
||||||
LocLng float64
|
LocLng float64
|
||||||
LocTitle string
|
LocTitle string
|
||||||
LocCity string
|
LocRegion string
|
||||||
LocSuburb string
|
LocCity string
|
||||||
LocState string
|
LocSuburb string
|
||||||
LocCountry string
|
LocState string
|
||||||
LocRegion string
|
LocCountryCode string
|
||||||
LocLabel string
|
LocLabel string
|
||||||
LocSource string
|
LocSource string
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocationSource interface {
|
type LocationSource interface {
|
||||||
|
@ -32,13 +30,25 @@ type LocationSource interface {
|
||||||
City() string
|
City() string
|
||||||
Suburb() string
|
Suburb() string
|
||||||
State() string
|
State() string
|
||||||
Country() string
|
CountryCode() string
|
||||||
Label() string
|
Label() string
|
||||||
Source() string
|
Source() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Location) Query(lat, lng float64) error {
|
func NewLocation (lat, lng float64) *Location {
|
||||||
o, err := osm.FindLocation(lat, lng)
|
id := olc.Encode(lat, lng, 11)
|
||||||
|
|
||||||
|
result := &Location{
|
||||||
|
ID: id,
|
||||||
|
LocLat: lat,
|
||||||
|
LocLng: lng,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Location) Query() error {
|
||||||
|
o, err := osm.FindLocation(l.LocLat, l.LocLng)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -49,22 +59,24 @@ func (l *Location) Query(lat, lng float64) error {
|
||||||
|
|
||||||
func (l *Location) Assign(s LocationSource) error {
|
func (l *Location) Assign(s LocationSource) error {
|
||||||
l.LocSource = s.Source()
|
l.LocSource = s.Source()
|
||||||
l.LocLat = s.Latitude()
|
|
||||||
l.LocLng = s.Longitude()
|
if l.LocLat == 0 { l.LocLat = s.Latitude() }
|
||||||
|
if l.LocLng == 0 { l.LocLng = s.Longitude() }
|
||||||
|
|
||||||
if l.Unknown() {
|
if l.Unknown() {
|
||||||
l.LocLabel = "unknown"
|
l.LocLabel = "unknown"
|
||||||
return errors.New("maps: unknown location")
|
return errors.New("maps: unknown location")
|
||||||
}
|
}
|
||||||
|
|
||||||
l.ID = olc.Encode(l.LocLat, l.LocLng, 11)
|
if l.ID == "" { l.ID = olc.Encode(l.LocLat, l.LocLng, 11) }
|
||||||
|
|
||||||
l.LocTitle = s.Title()
|
l.LocTitle = s.Title()
|
||||||
l.LocCity = s.City()
|
l.LocCity = s.City()
|
||||||
l.LocSuburb = s.Suburb()
|
l.LocSuburb = s.Suburb()
|
||||||
l.LocState = s.State()
|
l.LocState = s.State()
|
||||||
l.LocCountry = s.Country()
|
l.LocCountryCode = s.CountryCode()
|
||||||
l.LocRegion = l.region()
|
|
||||||
l.LocLabel = s.Label()
|
l.LocLabel = s.Label()
|
||||||
|
l.LocRegion = l.region()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -82,8 +94,9 @@ func (l *Location) region() string {
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
var countryName = Countries[l.LocCountry]
|
var countryName = l.CountryName()
|
||||||
var loc []string
|
var loc []string
|
||||||
|
|
||||||
shortCountry := len([]rune(countryName)) <= 20
|
shortCountry := len([]rune(countryName)) <= 20
|
||||||
shortCity := len([]rune(l.LocCity)) <= 20
|
shortCity := len([]rune(l.LocCity)) <= 20
|
||||||
|
|
||||||
|
@ -101,3 +114,47 @@ func (l *Location) region() string {
|
||||||
|
|
||||||
return strings.Join(loc[:], ", ")
|
return strings.Join(loc[:], ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l Location) Latitude() float64 {
|
||||||
|
return l.LocLat
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) Longitude() float64 {
|
||||||
|
return l.LocLng
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) Title() string {
|
||||||
|
return l.LocTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) City() string {
|
||||||
|
return l.LocCity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) Suburb() string {
|
||||||
|
return l.LocSuburb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) State() string {
|
||||||
|
return l.LocState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) Label() string {
|
||||||
|
return l.LocLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) Source() string {
|
||||||
|
return l.LocSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) Region() string {
|
||||||
|
return l.LocRegion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) CountryCode() string {
|
||||||
|
return l.LocCountryCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Location) CountryName() string {
|
||||||
|
return CountryNames[l.LocCountryCode]
|
||||||
|
}
|
||||||
|
|
|
@ -12,9 +12,9 @@ func TestLocation_Query(t *testing.T) {
|
||||||
lat := 52.5208
|
lat := 52.5208
|
||||||
lng := 13.40953
|
lng := 13.40953
|
||||||
|
|
||||||
var l Location
|
l := NewLocation(lat, lng)
|
||||||
|
|
||||||
if err := l.Query(lat, lng); err != nil {
|
if err := l.Query(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ func TestLocation_Query(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocation_OpenStreetMap(t *testing.T) {
|
func TestLocation_Assign(t *testing.T) {
|
||||||
t.Run("BerlinFernsehturm", func(t *testing.T) {
|
t.Run("BerlinFernsehturm", func(t *testing.T) {
|
||||||
lat := 52.5208
|
lat := 52.5208
|
||||||
lng := 13.40953
|
lng := 13.40953
|
||||||
|
@ -223,7 +223,7 @@ func TestLocation_OpenStreetMap(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, "4GWF24FX+F5H", l.ID)
|
assert.Equal(t, "4GWF24FX+F5H", l.ID)
|
||||||
assert.Equal(t, "Route R411", l.LocTitle)
|
assert.Equal(t, "R411", l.LocTitle)
|
||||||
assert.Equal(t, "Eastern Cape, South Africa", l.LocRegion)
|
assert.Equal(t, "Eastern Cape, South Africa", l.LocRegion)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,45 +3,161 @@ package osm
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
var locationLabels = map[string]string{
|
var locationLabels = map[string]string{
|
||||||
"bay": "bay",
|
"aeroway=*": "airport",
|
||||||
"art": "art exhibition",
|
"natural=bay": "bay",
|
||||||
"fire station": "fire station",
|
"natural=peninsula": "peninsula",
|
||||||
"hairdresser": "hairdresser",
|
"natural=cape": "cape",
|
||||||
"cape": "cape",
|
"natural=wood": "forest",
|
||||||
"coastline": "coastline",
|
"natural=grassland": "grassland",
|
||||||
"cliff": "cliff",
|
"*=beach": "beach",
|
||||||
"wetland": "wetland",
|
"*=dune": "dune",
|
||||||
"nature reserve": "nature reserve",
|
"*=water": "water",
|
||||||
"natural=beach": "beach",
|
"*=wetland": "wetland",
|
||||||
"amenity=cafe": "cafe",
|
"*=glacier": "glacier",
|
||||||
"amenity=internet_cafe": "cafe",
|
"*=strait": "seashore",
|
||||||
"ice cream": "ice cream parlor",
|
"*=coastline": "seashore",
|
||||||
"bistro": "restaurant",
|
"*=reef": "reef",
|
||||||
"restaurant": "restaurant",
|
"*=geyser": "geyser",
|
||||||
"ship": "ship",
|
"natural=peak": "mountain",
|
||||||
"wholesale": "shop",
|
"natural=hill": "hill",
|
||||||
"food": "shop",
|
"natural=volcano": "volcano",
|
||||||
"supermarket": "supermarket",
|
"natural=valley": "valley",
|
||||||
"florist": "florist",
|
"natural=ridge": "mountain",
|
||||||
"pharmacy": "pharmacy",
|
"natural=cliff": "cliff",
|
||||||
"seafood": "seafood",
|
"natural=saddle": "mountain",
|
||||||
"clothes": "clothing store",
|
"natural=isthmus": "seashore",
|
||||||
"residential": "residential area",
|
"natural=sinkhole": "sinkhole",
|
||||||
"museum": "museum",
|
"natural=*": "nature",
|
||||||
"castle": "castle",
|
"place=sea": "ocean",
|
||||||
"aeroway=*": "airport",
|
"*=ocean": "ocean",
|
||||||
"ferry terminal": "harbor",
|
"*=gallery": "gallery",
|
||||||
"bridge": "bridge",
|
"*=museum": "museum",
|
||||||
"university": "university",
|
"*=alpine_hut": "alpine hut",
|
||||||
"mall": "mall",
|
"*=aquarium": "aquarium",
|
||||||
"marina": "marina",
|
"*=artwork": "exhibition",
|
||||||
"garden": "garden",
|
"*=camp_pitch": "camping",
|
||||||
"pedestrian": "shopping area",
|
"*=camp_site": "camping",
|
||||||
"bunker": "bunker",
|
"*=caravan_site": "camping",
|
||||||
"viewpoint": "viewpoint",
|
"*=hotel": "hotel",
|
||||||
"train station": "train station",
|
"*=hostel": "hotel",
|
||||||
"farm": "farm",
|
"*=motel": "hotel",
|
||||||
"highway=secondary": "highway",
|
"tourism=information": "visitor center",
|
||||||
|
"*=picnic_site": "hiking",
|
||||||
|
"*=theme_park": "theme park",
|
||||||
|
"*=viewpoint": "viewpoint",
|
||||||
|
"*=wilderness_hut": "hiking",
|
||||||
|
"*=zoo": "zoo",
|
||||||
|
"shop=*": "shop",
|
||||||
|
"shop=butcher": "butcher",
|
||||||
|
"shop=department_store": "department store",
|
||||||
|
"*=supermarket": "supermarket",
|
||||||
|
"*=mall": "mall",
|
||||||
|
"*=boutique": "boutique",
|
||||||
|
"*=fashion": "boutique",
|
||||||
|
"*=fashion_accessories": "boutique",
|
||||||
|
"*=clothes": "boutique",
|
||||||
|
"*=fabric": "boutique",
|
||||||
|
"shop=leather": "boutique",
|
||||||
|
"shop=baby_goods": "boutique",
|
||||||
|
"shop=bag": "boutique",
|
||||||
|
"shop=books": "bookstore",
|
||||||
|
"shop=cannabis": "headshop",
|
||||||
|
"*=fire_station": "fire station",
|
||||||
|
"amenity=bar": "bar",
|
||||||
|
"amenity=biergarten": "biergarten",
|
||||||
|
"amenity=cafe": "cafe",
|
||||||
|
"amenity=internet_cafe": "cafe",
|
||||||
|
"amenity=ice_cream": "ice cream parlor",
|
||||||
|
"amenity=bistro": "restaurant",
|
||||||
|
"amenity=restaurant": "restaurant",
|
||||||
|
"amenity=fast_food": "restaurant",
|
||||||
|
"amenity=food_court": "restaurant",
|
||||||
|
"amenity=pub": "pub",
|
||||||
|
"amenity=college": "university",
|
||||||
|
"amenity=university": "university",
|
||||||
|
"amenity=kindergarten": "kindergarten",
|
||||||
|
"amenity=language_school": "school",
|
||||||
|
"amenity=driving_school": "school",
|
||||||
|
"amenity=music_school": "school",
|
||||||
|
"amenity=school": "school",
|
||||||
|
"amenity=car_rental": "car",
|
||||||
|
"amenity=ferry_terminal": "harbor",
|
||||||
|
"amenity=parking": "parking",
|
||||||
|
"amenity=parking_entrance": "parking",
|
||||||
|
"amenity=parking_space": "parking",
|
||||||
|
"*=bank": "bank",
|
||||||
|
"*=clinic": "hospital",
|
||||||
|
"*=hospital": "hospital",
|
||||||
|
"*=pharmacy": "pharmacy",
|
||||||
|
"*=arts_centre": "exhibition",
|
||||||
|
"*=casino": "casino",
|
||||||
|
"*=cinema": "cinema",
|
||||||
|
"*=gambling": "casino",
|
||||||
|
"*=planetarium": "planetarium",
|
||||||
|
"*=nightclub": "nightclub",
|
||||||
|
"*=theatre": "theatre",
|
||||||
|
"*=embassy": "embassy",
|
||||||
|
"*=grave_yard": "cemetery",
|
||||||
|
"*=cemetery": "cemetery",
|
||||||
|
"*=marketplace": "marketplace",
|
||||||
|
"*=monastery": "monastery",
|
||||||
|
"*=police": "police",
|
||||||
|
"*=prison": "prison",
|
||||||
|
"*=public_bath": "swimming",
|
||||||
|
"*=shelter": "shelter",
|
||||||
|
"*=aircraft": "aircraft",
|
||||||
|
"*=castle": "castle",
|
||||||
|
"*=castle_wall": "castle",
|
||||||
|
"*=church": "church",
|
||||||
|
"*=farm": "farm",
|
||||||
|
"*=memorial": "memorial",
|
||||||
|
"*=ship": "ship",
|
||||||
|
"*=tank": "tank",
|
||||||
|
"*=tower": "tower",
|
||||||
|
"*=wreck": "ship",
|
||||||
|
"*=houseboat": "ship",
|
||||||
|
"*=office": "office",
|
||||||
|
"*=warehouse": "warehouse",
|
||||||
|
"*=cathedral": "cathedral",
|
||||||
|
"*=chapel": "chapel",
|
||||||
|
"*=mosque": "mosque",
|
||||||
|
"*=shrine": "shrine",
|
||||||
|
"*=synagogue": "synagogue",
|
||||||
|
"*=temple": "temple",
|
||||||
|
"*=train_station": "train station",
|
||||||
|
"*=cowshed": "farm",
|
||||||
|
"*=greenhouse": "greenhouse",
|
||||||
|
"*=stable": "farm",
|
||||||
|
"*=farm_auxiliary": "farm",
|
||||||
|
"*=barn": "farm",
|
||||||
|
"*=sty": "farm",
|
||||||
|
"*=stadium": "stadium",
|
||||||
|
"*=hangar": "hangar",
|
||||||
|
"*=parking": "parking",
|
||||||
|
"*=water_tower": "tower",
|
||||||
|
"*=transformer_tower": "tower",
|
||||||
|
"*=bunker": "bunker",
|
||||||
|
"*=bridge": "bridge",
|
||||||
|
"*=garden": "botanical garden",
|
||||||
|
"*=adult_gaming_centre": "casino",
|
||||||
|
"*=amusement_arcade": "casino",
|
||||||
|
"*=beach_resort": "beach",
|
||||||
|
"*=dog_park": "dog park",
|
||||||
|
"*=escape_game": "escape game",
|
||||||
|
"*=firepit": "camping",
|
||||||
|
"*=golf_course": "golf",
|
||||||
|
"*=miniature_golf": "golf",
|
||||||
|
"*=hackerspace": "hackerspace",
|
||||||
|
"*=marina": "marina",
|
||||||
|
"*=nature_reserve": "nature reserve",
|
||||||
|
"*=park": "park",
|
||||||
|
"*=picnic_table": "outdoor",
|
||||||
|
"*=pitch": "sports",
|
||||||
|
"*=sports_centre": "sports",
|
||||||
|
"*=swimming_area": "swimming",
|
||||||
|
"*=swimming_pool": "swimming",
|
||||||
|
"*=water_park": "water park",
|
||||||
|
"*=wildlife_hide": "wildlife",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Location) Label() (result string) {
|
func (o Location) Label() (result string) {
|
||||||
|
|
|
@ -92,7 +92,7 @@ func (o Location) Suburb() (result string) {
|
||||||
return strings.TrimSpace(result)
|
return strings.TrimSpace(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Location) Country() (result string) {
|
func (o Location) CountryCode() (result string) {
|
||||||
result = o.Address.CountryCode
|
result = o.Address.CountryCode
|
||||||
|
|
||||||
return strings.ToLower(strings.TrimSpace(result))
|
return strings.ToLower(strings.TrimSpace(result))
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
var labelTitles = map[string]string{
|
var labelTitles = map[string]string{
|
||||||
"airport": "Airport",
|
"airport": "Airport",
|
||||||
"highway": "Route %name%",
|
"visitor center": "Visitor Center",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Location) Title() (result string) {
|
func (o Location) Title() (result string) {
|
||||||
|
|
|
@ -21,22 +21,22 @@ type Exif struct {
|
||||||
Artist string
|
Artist string
|
||||||
CameraMake string
|
CameraMake string
|
||||||
CameraModel string
|
CameraModel string
|
||||||
Description string
|
Description string
|
||||||
LensMake string
|
LensMake string
|
||||||
LensModel string
|
LensModel string
|
||||||
Flash bool
|
Flash bool
|
||||||
FocalLength int
|
FocalLength int
|
||||||
Exposure string
|
Exposure string
|
||||||
Aperture float64
|
Aperture float64
|
||||||
FNumber float64
|
FNumber float64
|
||||||
Iso int
|
Iso int
|
||||||
Lat float64
|
Lat float64
|
||||||
Long float64
|
Lng float64
|
||||||
Altitude int
|
Altitude int
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
Orientation int
|
Orientation int
|
||||||
All map[string]string
|
All map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
var im *exif.IfdMapping
|
var im *exif.IfdMapping
|
||||||
|
@ -227,15 +227,15 @@ func (m *MediaFile) Exif() (result *Exif, err error) {
|
||||||
if ifd, err := index.RootIfd.ChildWithIfdPath(exif.IfdPathStandardGps); err == nil {
|
if ifd, err := index.RootIfd.ChildWithIfdPath(exif.IfdPathStandardGps); err == nil {
|
||||||
if gi, err := ifd.GpsInfo(); err == nil {
|
if gi, err := ifd.GpsInfo(); err == nil {
|
||||||
m.exifData.Lat = gi.Latitude.Decimal()
|
m.exifData.Lat = gi.Latitude.Decimal()
|
||||||
m.exifData.Long = gi.Longitude.Decimal()
|
m.exifData.Lng = gi.Longitude.Decimal()
|
||||||
m.exifData.Altitude = gi.Altitude
|
m.exifData.Altitude = gi.Altitude
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.exifData.Lat != 0 && m.exifData.Long != 0 {
|
if m.exifData.Lat != 0 && m.exifData.Lng != 0 {
|
||||||
zones, err := tz.GetZone(tz.Point{
|
zones, err := tz.GetZone(tz.Point{
|
||||||
Lat: m.exifData.Lat,
|
Lat: m.exifData.Lat,
|
||||||
Lon: m.exifData.Long,
|
Lon: m.exifData.Lng,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -38,7 +38,7 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
|
||||||
assert.Equal(t, 10.0, info.FNumber)
|
assert.Equal(t, 10.0, info.FNumber)
|
||||||
assert.Equal(t, 200, info.Iso)
|
assert.Equal(t, 200, info.Iso)
|
||||||
assert.Equal(t, -33.45347, info.Lat)
|
assert.Equal(t, -33.45347, info.Lat)
|
||||||
assert.Equal(t, 25.764645, info.Long)
|
assert.Equal(t, 25.764645, info.Lng)
|
||||||
assert.Equal(t, 190, info.Altitude)
|
assert.Equal(t, 190, info.Altitude)
|
||||||
assert.Equal(t, 1365, info.Width)
|
assert.Equal(t, 1365, info.Width)
|
||||||
assert.Equal(t, 0, info.Height)
|
assert.Equal(t, 0, info.Height)
|
||||||
|
@ -112,7 +112,7 @@ func TestMediaFile_Exif_DNG(t *testing.T) {
|
||||||
assert.Equal(t, 4.971, info.Aperture)
|
assert.Equal(t, 4.971, info.Aperture)
|
||||||
assert.Equal(t, 1000, info.Iso)
|
assert.Equal(t, 1000, info.Iso)
|
||||||
assert.Equal(t, 0.0, info.Lat)
|
assert.Equal(t, 0.0, info.Lat)
|
||||||
assert.Equal(t, 0.0, info.Long)
|
assert.Equal(t, 0.0, info.Lng)
|
||||||
assert.Equal(t, 0, info.Altitude)
|
assert.Equal(t, 0, info.Altitude)
|
||||||
assert.Equal(t, 171, info.Width)
|
assert.Equal(t, 171, info.Width)
|
||||||
assert.Equal(t, 0, info.Height)
|
assert.Equal(t, 0, info.Height)
|
||||||
|
@ -164,7 +164,7 @@ func TestMediaFile_Exif_HEIF(t *testing.T) {
|
||||||
assert.Equal(t, 1.696, jpegInfo.Aperture)
|
assert.Equal(t, 1.696, jpegInfo.Aperture)
|
||||||
assert.Equal(t, 20, jpegInfo.Iso)
|
assert.Equal(t, 20, jpegInfo.Iso)
|
||||||
assert.Equal(t, 34.79745, jpegInfo.Lat)
|
assert.Equal(t, 34.79745, jpegInfo.Lat)
|
||||||
assert.Equal(t, 134.76463333333334, jpegInfo.Long)
|
assert.Equal(t, 134.76463333333334, jpegInfo.Lng)
|
||||||
assert.Equal(t, 0, jpegInfo.Altitude)
|
assert.Equal(t, 0, jpegInfo.Altitude)
|
||||||
assert.Equal(t, 0, jpegInfo.Width)
|
assert.Equal(t, 0, jpegInfo.Width)
|
||||||
assert.Equal(t, 0, jpegInfo.Height)
|
assert.Equal(t, 0, jpegInfo.Height)
|
||||||
|
|
|
@ -56,7 +56,7 @@ func (i *Indexer) indexMediaFile(m *MediaFile, o IndexerOptions) IndexResult {
|
||||||
|
|
||||||
if photoQuery.Error != nil && m.HasTimeAndPlace() {
|
if photoQuery.Error != nil && m.HasTimeAndPlace() {
|
||||||
exifData, _ = m.Exif()
|
exifData, _ = m.Exif()
|
||||||
photoQuery = i.db.Unscoped().First(&photo, "photo_lat = ? AND photo_long = ? AND taken_at = ?", exifData.Lat, exifData.Long, exifData.TakenAt)
|
photoQuery = i.db.Unscoped().First(&photo, "photo_lat = ? AND photo_lng = ? AND taken_at = ?", exifData.Lat, exifData.Lng, exifData.TakenAt)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
photoQuery = i.db.Unscoped().First(&photo, "id = ?", file.PhotoID)
|
photoQuery = i.db.Unscoped().First(&photo, "id = ?", file.PhotoID)
|
||||||
|
@ -97,7 +97,7 @@ func (i *Indexer) indexMediaFile(m *MediaFile, o IndexerOptions) IndexResult {
|
||||||
// Read UpdateExif data
|
// Read UpdateExif data
|
||||||
if exifData, err := m.Exif(); err == nil {
|
if exifData, err := m.Exif(); err == nil {
|
||||||
photo.PhotoLat = exifData.Lat
|
photo.PhotoLat = exifData.Lat
|
||||||
photo.PhotoLong = exifData.Long
|
photo.PhotoLng = exifData.Lng
|
||||||
photo.TakenAt = exifData.TakenAt
|
photo.TakenAt = exifData.TakenAt
|
||||||
photo.TakenAtLocal = exifData.TakenAtLocal
|
photo.TakenAtLocal = exifData.TakenAtLocal
|
||||||
photo.TimeZone = exifData.TimeZone
|
photo.TimeZone = exifData.TimeZone
|
||||||
|
@ -128,7 +128,7 @@ func (i *Indexer) indexMediaFile(m *MediaFile, o IndexerOptions) IndexResult {
|
||||||
labels = append(labels, locLabels...)
|
labels = append(labels, locLabels...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if photo.PhotoTitle == "" || (fileChanged || o.UpdateTitle) && photo.PhotoTitleChanged == false && photo.LocationID == 0 {
|
if photo.PhotoTitle == "" || (fileChanged || o.UpdateTitle) && photo.PhotoTitleChanged == false && photo.LocationID == "" {
|
||||||
if len(labels) > 0 && labels[0].Priority >= -1 && labels[0].Uncertainty <= 85 && labels[0].Name != "" {
|
if len(labels) > 0 && labels[0].Priority >= -1 && labels[0].Uncertainty <= 85 && labels[0].Name != "" {
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", util.Title(labels[0].Name), m.DateCreated().Format("2006"))
|
photo.PhotoTitle = fmt.Sprintf("%s / %s", util.Title(labels[0].Name), m.DateCreated().Format("2006"))
|
||||||
} else if !photo.TakenAtLocal.IsZero() {
|
} else if !photo.TakenAtLocal.IsZero() {
|
||||||
|
@ -160,7 +160,7 @@ func (i *Indexer) indexMediaFile(m *MediaFile, o IndexerOptions) IndexResult {
|
||||||
|
|
||||||
if photoExists {
|
if photoExists {
|
||||||
// Estimate location
|
// Estimate location
|
||||||
if o.UpdateLocation && photo.LocationID == 0 {
|
if o.UpdateLocation && photo.LocationID == "" {
|
||||||
i.estimateLocation(&photo)
|
i.estimateLocation(&photo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,12 +368,18 @@ func (i *Indexer) indexLocation(mediaFile *MediaFile, photo *entity.Photo, label
|
||||||
var keywords []string
|
var keywords []string
|
||||||
|
|
||||||
if location, err := mediaFile.Location(); err == nil {
|
if location, err := mediaFile.Location(); err == nil {
|
||||||
i.db.FirstOrCreate(location, "id = ?", location.ID)
|
err := location.Find(i.db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return keywords, labels
|
||||||
|
}
|
||||||
|
|
||||||
photo.Location = location
|
photo.Location = location
|
||||||
photo.LocationID = location.ID
|
photo.LocationID = location.ID
|
||||||
photo.LocationEstimated = false
|
photo.LocationEstimated = false
|
||||||
|
|
||||||
photo.Country = entity.NewCountry(location.LocCountryCode, location.LocCountry).FirstOrCreate(i.db)
|
photo.Country = entity.NewCountry(location.CountryCode(), location.CountryName()).FirstOrCreate(i.db)
|
||||||
|
|
||||||
if photo.Country.New {
|
if photo.Country.New {
|
||||||
event.Publish("count.countries", event.Data{
|
event.Publish("count.countries", event.Data{
|
||||||
|
@ -384,7 +390,7 @@ func (i *Indexer) indexLocation(mediaFile *MediaFile, photo *entity.Photo, label
|
||||||
countryName := photo.Country.CountryName
|
countryName := photo.Country.CountryName
|
||||||
locLabel := location.Label()
|
locLabel := location.Label()
|
||||||
|
|
||||||
keywords = append(keywords, util.Keywords(location.LocDisplayName)...)
|
keywords = append(keywords, location.Keywords()...)
|
||||||
|
|
||||||
// Append label from OpenStreetMap
|
// Append label from OpenStreetMap
|
||||||
if locLabel != "" {
|
if locLabel != "" {
|
||||||
|
@ -393,33 +399,27 @@ func (i *Indexer) indexLocation(mediaFile *MediaFile, photo *entity.Photo, label
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileChanged || o.UpdateTitle) && photo.PhotoTitleChanged == false {
|
if (fileChanged || o.UpdateTitle) && photo.PhotoTitleChanged == false {
|
||||||
if title := labels.Title(location.LocName); title != "" { // TODO: User defined title format
|
if title := labels.Title(location.Title()); title != "" { // TODO: User defined title format
|
||||||
log.Infof("index: using label \"%s\" to create photo title", title)
|
log.Infof("index: using label \"%s\" to create photo title", title)
|
||||||
if location.LocCity == "" || len(location.LocCity) > 16 || strings.Contains(title, location.LocCity) {
|
if location.LocCity == "" || len(location.LocCity) > 16 || strings.Contains(title, location.LocCity) {
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", util.Title(title), countryName, photo.TakenAt.Format("2006"))
|
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", util.Title(title), countryName, photo.TakenAt.Format("2006"))
|
||||||
} else {
|
} else {
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", util.Title(title), location.LocCity, photo.TakenAt.Format("2006"))
|
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", util.Title(title), location.LocCity, photo.TakenAt.Format("2006"))
|
||||||
}
|
}
|
||||||
} else if location.LocName != "" && location.LocCity != "" {
|
} else if location.Title() != "" && location.City() != "" {
|
||||||
if len(location.LocName) > 45 {
|
if len(location.Title()) > 45 {
|
||||||
photo.PhotoTitle = util.Title(location.LocName)
|
photo.PhotoTitle = util.Title(location.Title())
|
||||||
} else if len(location.LocName) > 20 || len(location.LocCity) > 16 || strings.Contains(location.LocName, location.LocCity) {
|
} else if len(location.Title()) > 20 || len(location.City()) > 16 || strings.Contains(location.Title(), location.City()) {
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", util.Title(location.LocName), photo.TakenAt.Format("2006"))
|
photo.PhotoTitle = fmt.Sprintf("%s / %s", location.Title(), photo.TakenAt.Format("2006"))
|
||||||
} else {
|
} else {
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", util.Title(location.LocName), location.LocCity, photo.TakenAt.Format("2006"))
|
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", location.Title(), location.LocCity, photo.TakenAt.Format("2006"))
|
||||||
}
|
}
|
||||||
} else if location.LocCity != "" && countryName != "" {
|
} else if location.City() != "" && countryName != "" {
|
||||||
if len(location.LocCity) > 20 {
|
if len(location.City()) > 20 {
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", location.LocCity, photo.TakenAt.Format("2006"))
|
photo.PhotoTitle = fmt.Sprintf("%s / %s", location.LocCity, photo.TakenAt.Format("2006"))
|
||||||
} else {
|
} else {
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", location.LocCity, countryName, photo.TakenAt.Format("2006"))
|
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", location.LocCity, countryName, photo.TakenAt.Format("2006"))
|
||||||
}
|
}
|
||||||
} else if location.LocCounty != "" && countryName != "" {
|
|
||||||
if len(location.LocCounty) > 20 {
|
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", location.LocCounty, photo.TakenAt.Format("2006"))
|
|
||||||
} else {
|
|
||||||
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", location.LocCounty, countryName, photo.TakenAt.Format("2006"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if photo.PhotoTitle == "" {
|
if photo.PhotoTitle == "" {
|
||||||
|
|
26
internal/photoprism/location.go
Normal file
26
internal/photoprism/location.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package photoprism
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/photoprism/photoprism/internal/entity"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *MediaFile) Location() (*entity.Location, error) {
|
||||||
|
if m.location != nil {
|
||||||
|
return m.location, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := m.Exif()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Lat == 0 && data.Lng == 0 {
|
||||||
|
return nil, errors.New("mediafile: no latitude and longitude in image metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.location = entity.NewLocation(data.Lat, data.Lng)
|
||||||
|
|
||||||
|
return m.location, nil
|
||||||
|
}
|
88
internal/photoprism/location_test.go
Normal file
88
internal/photoprism/location_test.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package photoprism
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/internal/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMediaFile_Location(t *testing.T) {
|
||||||
|
t.Run("/iphone_7.heic", func(t *testing.T) {
|
||||||
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.heic")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
location, err := mediaFile.Location()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = location.Find(conf.Db()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Himeji", location.City())
|
||||||
|
assert.Equal(t, "Kinki Region", location.State())
|
||||||
|
assert.Equal(t, "Japan", location.CountryName())
|
||||||
|
assert.Equal(t, "", location.Label())
|
||||||
|
assert.Equal(t, 34.79745, location.Latitude())
|
||||||
|
assert.Equal(t, "8Q6PQQW7+XVJ", location.ID)
|
||||||
|
location2, err := mediaFile.Location()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = location.Find(conf.Db()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Himeji", location2.LocCity)
|
||||||
|
assert.Equal(t, "Kinki Region", location2.LocState)
|
||||||
|
})
|
||||||
|
t.Run("/cat_brown.jpg", func(t *testing.T) {
|
||||||
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/cat_brown.jpg")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
location, err := mediaFile.Location()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = location.Find(conf.Db()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Tübingen", location.City())
|
||||||
|
assert.Equal(t, "de", location.CountryCode())
|
||||||
|
assert.Equal(t, "Germany", location.CountryName())
|
||||||
|
assert.Equal(t, 48.53870833333333, location.Latitude())
|
||||||
|
assert.Equal(t, "8FWFG2Q6+FVM", location.ID)
|
||||||
|
})
|
||||||
|
t.Run("/dog_orange.jpg", func(t *testing.T) {
|
||||||
|
conf := config.TestConfig()
|
||||||
|
|
||||||
|
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/dog_orange.jpg")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := mediaFile.Location(); err == nil {
|
||||||
|
t.Fatal("mediaFile.Location() should return error")
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, "mediafile: no latitude and longitude in image metadata", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -85,7 +85,7 @@ func (m *MediaFile) HasTimeAndPlace() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
result := !exifData.TakenAt.IsZero() && exifData.Lat != 0 && exifData.Long != 0
|
result := !exifData.TakenAt.IsZero() && exifData.Lat != 0 && exifData.Lng != 0
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
package photoprism
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/entity"
|
|
||||||
"github.com/photoprism/photoprism/internal/util"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type openstreetmapAddress struct {
|
|
||||||
HouseNumber string `json:"house_number"`
|
|
||||||
Road string `json:"road"`
|
|
||||||
Suburb string `json:"suburb"`
|
|
||||||
Town string `json:"town"`
|
|
||||||
City string `json:"city"`
|
|
||||||
Postcode string `json:"postcode"`
|
|
||||||
County string `json:"county"`
|
|
||||||
State string `json:"state"`
|
|
||||||
Country string `json:"country"`
|
|
||||||
CountryCode string `json:"country_code"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type openstreetmapLocation struct {
|
|
||||||
PlaceID uint `json:"place_id"`
|
|
||||||
Lat string `json:"lat"`
|
|
||||||
Lon string `json:"lon"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Category string `json:"category"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
DisplayName string `json:"display_name"`
|
|
||||||
Address *openstreetmapAddress `json:"address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Location See https://wiki.openstreetmap.org/wiki/Nominatim#Reverse_Geocoding
|
|
||||||
func (m *MediaFile) Location() (*entity.Location, error) {
|
|
||||||
if m.location != nil {
|
|
||||||
return m.location, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
location := &entity.Location{}
|
|
||||||
|
|
||||||
openstreetmapLocation := &openstreetmapLocation{
|
|
||||||
Address: &openstreetmapAddress{},
|
|
||||||
}
|
|
||||||
|
|
||||||
if exifData, err := m.Exif(); err == nil {
|
|
||||||
if exifData.Lat == 0 && exifData.Long == 0 {
|
|
||||||
return nil, errors.New("no latitude and longitude in image metadata")
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf(
|
|
||||||
"https://nominatim.openstreetmap.org/reverse?lat=%f&lon=%f&format=jsonv2&accept-language=en&zoom=18",
|
|
||||||
exifData.Lat,
|
|
||||||
exifData.Long)
|
|
||||||
|
|
||||||
if res, err := http.Get(url); err == nil {
|
|
||||||
err = json.NewDecoder(res.Body).Decode(openstreetmapLocation)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if id := openstreetmapLocation.PlaceID; id > 0 {
|
|
||||||
location.ID = id
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("query returned no result")
|
|
||||||
}
|
|
||||||
|
|
||||||
if openstreetmapLocation.Address.City != "" {
|
|
||||||
location.LocCity = openstreetmapLocation.Address.City
|
|
||||||
} else {
|
|
||||||
location.LocCity = openstreetmapLocation.Address.Town
|
|
||||||
}
|
|
||||||
|
|
||||||
if lat, err := strconv.ParseFloat(openstreetmapLocation.Lat, 64); err == nil {
|
|
||||||
location.LocLat = lat
|
|
||||||
}
|
|
||||||
|
|
||||||
if lon, err := strconv.ParseFloat(openstreetmapLocation.Lon, 64); err == nil {
|
|
||||||
location.LocLong = lon
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(openstreetmapLocation.Name) > 1 {
|
|
||||||
s := openstreetmapLocation.Name
|
|
||||||
s = strings.Replace(s, " - ", " / ", -1)
|
|
||||||
s = strings.Replace(s, ", ", " / ", -1)
|
|
||||||
location.LocName = util.Title(strings.TrimSpace(strings.Replace(s, "_", " ", -1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(openstreetmapLocation.Address.County) > 1 {
|
|
||||||
s := openstreetmapLocation.Address.County
|
|
||||||
s = strings.Replace(s, " - ", " / ", -1)
|
|
||||||
s = strings.Replace(s, ", ", " / ", -1)
|
|
||||||
location.LocCounty = util.Title(strings.TrimSpace(strings.Replace(s, "_", " ", -1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
location.LocHouseNr = strings.TrimSpace(openstreetmapLocation.Address.HouseNumber)
|
|
||||||
location.LocStreet = strings.TrimSpace(openstreetmapLocation.Address.Road)
|
|
||||||
location.LocSuburb = strings.TrimSpace(openstreetmapLocation.Address.Suburb)
|
|
||||||
location.LocPostcode = strings.TrimSpace(openstreetmapLocation.Address.Postcode)
|
|
||||||
location.LocState = strings.TrimSpace(openstreetmapLocation.Address.State)
|
|
||||||
location.LocCountry = strings.TrimSpace(openstreetmapLocation.Address.Country)
|
|
||||||
location.LocCountryCode = strings.TrimSpace(openstreetmapLocation.Address.CountryCode)
|
|
||||||
location.LocDisplayName = strings.TrimSpace(openstreetmapLocation.DisplayName)
|
|
||||||
|
|
||||||
locationCategory := strings.TrimSpace(strings.Replace(openstreetmapLocation.Category, "_", " ", -1))
|
|
||||||
location.LocCategory = locationCategory
|
|
||||||
|
|
||||||
locationType := strings.TrimSpace(strings.Replace(openstreetmapLocation.Type, "_", " ", -1))
|
|
||||||
location.LocType = locationType
|
|
||||||
|
|
||||||
m.location = location
|
|
||||||
|
|
||||||
return m.location, nil
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
package photoprism
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/config"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMediaFile_Location(t *testing.T) {
|
|
||||||
t.Run("/iphone_7.heic", func(t *testing.T) {
|
|
||||||
conf := config.TestConfig()
|
|
||||||
|
|
||||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/iphone_7.heic")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
location, err := mediaFile.Location()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "Himeji", location.LocCity)
|
|
||||||
assert.Equal(t, "Kinki Region", location.LocState)
|
|
||||||
assert.Equal(t, "Japan", location.LocCountry)
|
|
||||||
assert.Equal(t, "highway", location.LocCategory)
|
|
||||||
assert.Equal(t, 34.7974872, location.LocLat)
|
|
||||||
location2, err := mediaFile.Location()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "Himeji", location2.LocCity)
|
|
||||||
assert.Equal(t, "Kinki Region", location2.LocState)
|
|
||||||
})
|
|
||||||
t.Run("/cat_brown.jpg", func(t *testing.T) {
|
|
||||||
conf := config.TestConfig()
|
|
||||||
|
|
||||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/cat_brown.jpg")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
location, err := mediaFile.Location()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, "Geißwiesenstraße", location.LocStreet)
|
|
||||||
assert.Equal(t, "14", location.LocHouseNr)
|
|
||||||
assert.Equal(t, "72070", location.LocPostcode)
|
|
||||||
assert.Equal(t, "Tübingen", location.LocCity)
|
|
||||||
assert.Equal(t, "Landkreis Tübingen", location.LocCounty)
|
|
||||||
assert.Equal(t, "Germany", location.LocCountry)
|
|
||||||
assert.Equal(t, "building", location.LocCategory)
|
|
||||||
assert.Equal(t, 48.53870475, location.LocLat)
|
|
||||||
})
|
|
||||||
t.Run("/dog_orange.jpg", func(t *testing.T) {
|
|
||||||
conf := config.TestConfig()
|
|
||||||
|
|
||||||
mediaFile, err := NewMediaFile(conf.ExamplesPath() + "/dog_orange.jpg")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
location, err := mediaFile.Location()
|
|
||||||
assert.Nil(t, location)
|
|
||||||
assert.Equal(t, "no latitude and longitude in image metadata", err.Error())
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -14,12 +14,12 @@ func (m *MediaFile) TimeZone() (string, error) {
|
||||||
return "UTC", errors.New("no image metadata")
|
return "UTC", errors.New("no image metadata")
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta.Lat == 0 && meta.Long == 0 {
|
if meta.Lat == 0 && meta.Lng == 0 {
|
||||||
return "UTC", errors.New("no latitude and longitude in image metadata")
|
return "UTC", errors.New("no latitude and longitude in image metadata")
|
||||||
}
|
}
|
||||||
|
|
||||||
zones, err := tz.GetZone(tz.Point{
|
zones, err := tz.GetZone(tz.Point{
|
||||||
Lon: meta.Long, Lat: meta.Lat,
|
Lon: meta.Lng, Lat: meta.Lat,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -35,7 +35,7 @@ type PhotoResult struct {
|
||||||
PhotoSensitive bool
|
PhotoSensitive bool
|
||||||
PhotoStory bool
|
PhotoStory bool
|
||||||
PhotoLat float64
|
PhotoLat float64
|
||||||
PhotoLong float64
|
PhotoLng float64
|
||||||
PhotoAltitude int
|
PhotoAltitude int
|
||||||
PhotoFocalLength int
|
PhotoFocalLength int
|
||||||
PhotoIso int
|
PhotoIso int
|
||||||
|
@ -57,17 +57,15 @@ type PhotoResult struct {
|
||||||
CountryName string
|
CountryName string
|
||||||
|
|
||||||
// Location
|
// Location
|
||||||
LocationID uint
|
LocationID string
|
||||||
LocDisplayName string
|
LocTitle string
|
||||||
LocName string
|
|
||||||
LocCity string
|
LocCity string
|
||||||
LocPostcode string
|
LocSuburb string
|
||||||
LocCounty string
|
|
||||||
LocState string
|
LocState string
|
||||||
LocCountry string
|
|
||||||
LocCountryCode string
|
LocCountryCode string
|
||||||
LocCategory string
|
LocRegion string
|
||||||
LocType string
|
LocLabel string
|
||||||
|
LocSource string
|
||||||
LocationChanged bool
|
LocationChanged bool
|
||||||
LocationEstimated bool
|
LocationEstimated bool
|
||||||
|
|
||||||
|
@ -123,8 +121,8 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
||||||
cameras.camera_make, cameras.camera_model,
|
cameras.camera_make, cameras.camera_model,
|
||||||
lenses.lens_make, lenses.lens_model,
|
lenses.lens_make, lenses.lens_model,
|
||||||
countries.country_name,
|
countries.country_name,
|
||||||
locations.loc_display_name, locations.loc_name, locations.loc_city, locations.loc_postcode, locations.loc_county,
|
locations.loc_title, locations.loc_city, locations.loc_suburb, locations.loc_state, locations.loc_country_code,
|
||||||
locations.loc_state, locations.loc_country, locations.loc_country_code, locations.loc_category, locations.loc_type`).
|
locations.loc_region, locations.loc_label, locations.loc_source`).
|
||||||
Joins("JOIN files ON files.photo_id = photos.id AND files.file_primary AND files.deleted_at IS NULL").
|
Joins("JOIN files ON files.photo_id = photos.id AND files.file_primary AND files.deleted_at IS NULL").
|
||||||
Joins("JOIN cameras ON cameras.id = photos.camera_id").
|
Joins("JOIN cameras ON cameras.id = photos.camera_id").
|
||||||
Joins("JOIN lenses ON lenses.id = photos.lens_id").
|
Joins("JOIN lenses ON lenses.id = photos.lens_id").
|
||||||
|
@ -278,10 +276,10 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
||||||
q = q.Where("photos.photo_lat BETWEEN ? AND ?", latMin, latMax)
|
q = q.Where("photos.photo_lat BETWEEN ? AND ?", latMin, latMax)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Long > 0 {
|
if f.Lng > 0 {
|
||||||
longMin := f.Long - SearchRadius*float64(f.Dist)
|
lngMin := f.Lng - SearchRadius*float64(f.Dist)
|
||||||
longMax := f.Long + SearchRadius*float64(f.Dist)
|
lngMax := f.Lng + SearchRadius*float64(f.Dist)
|
||||||
q = q.Where("photos.photo_long BETWEEN ? AND ?", longMin, longMax)
|
q = q.Where("photos.photo_lng BETWEEN ? AND ?", lngMin, lngMax)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !f.Before.IsZero() {
|
if !f.Before.IsZero() {
|
||||||
|
|
|
@ -270,9 +270,9 @@ func TestSearch_Photos_Query(t *testing.T) {
|
||||||
|
|
||||||
t.Logf("results: %+v", photos)
|
t.Logf("results: %+v", photos)
|
||||||
})
|
})
|
||||||
t.Run("form.Lat and form.Long and Order:imported", func(t *testing.T) {
|
t.Run("form.Lat and form.Lng and Order:imported", func(t *testing.T) {
|
||||||
var f form.PhotoSearch
|
var f form.PhotoSearch
|
||||||
f.Query = "Lat:33.45343166666667 Long:25.764711666666667 Dist:2000 Order:imported"
|
f.Query = "Lat:33.45343166666667 Lng:25.764711666666667 Dist:2000 Order:imported"
|
||||||
f.Count = 3
|
f.Count = 3
|
||||||
f.Offset = 0
|
f.Offset = 0
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue