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:
|
||||
label: frog
|
||||
threshold: 0.3
|
||||
categories:
|
||||
- animal
|
||||
|
||||
|
@ -594,21 +595,18 @@ black grouse:
|
|||
categories:
|
||||
- animal
|
||||
- bird
|
||||
- chicken
|
||||
|
||||
ptarmigan:
|
||||
threshold: 0.3
|
||||
categories:
|
||||
- animal
|
||||
- bird
|
||||
- chicken
|
||||
|
||||
ruffed grouse:
|
||||
threshold: 0.3
|
||||
categories:
|
||||
- animal
|
||||
- bird
|
||||
- chicken
|
||||
|
||||
prairie chicken:
|
||||
threshold: 0.3
|
||||
|
@ -1318,7 +1316,7 @@ hyena:
|
|||
|
||||
fox:
|
||||
label: fox
|
||||
threshold: 0.3
|
||||
threshold: 0.5
|
||||
categories:
|
||||
- animal
|
||||
|
||||
|
@ -1414,7 +1412,7 @@ ant:
|
|||
|
||||
walking stick:
|
||||
threshold: 0.3
|
||||
priority: -1
|
||||
priority: -2
|
||||
|
||||
cockroach:
|
||||
threshold: 0.3
|
||||
|
@ -1858,7 +1856,7 @@ airship:
|
|||
ambulance:
|
||||
label: van
|
||||
categories:
|
||||
- street
|
||||
- road
|
||||
- car
|
||||
- vehicle
|
||||
|
||||
|
@ -2008,7 +2006,7 @@ cab:
|
|||
threshold: 0.6
|
||||
categories:
|
||||
- car
|
||||
- street
|
||||
- road
|
||||
|
||||
cannon:
|
||||
categories:
|
||||
|
@ -2285,7 +2283,7 @@ freight car:
|
|||
catgories:
|
||||
- train
|
||||
- vehicle
|
||||
- street
|
||||
- road
|
||||
|
||||
french horn:
|
||||
see: instrument
|
||||
|
@ -2363,6 +2361,11 @@ hatchet:
|
|||
|
||||
home theater:
|
||||
label: screen
|
||||
threshold: 0.3
|
||||
|
||||
television:
|
||||
label: screen
|
||||
threshold: 0.3
|
||||
|
||||
honeycomb:
|
||||
priority: -1
|
||||
|
@ -2481,12 +2484,12 @@ minibus:
|
|||
categories:
|
||||
- car
|
||||
- van
|
||||
- street
|
||||
- road
|
||||
|
||||
minivan:
|
||||
label: van
|
||||
categories:
|
||||
- street
|
||||
- road
|
||||
- car
|
||||
- bus
|
||||
|
||||
|
@ -2538,7 +2541,7 @@ mountain tent:
|
|||
moving van:
|
||||
label: van
|
||||
categories:
|
||||
- street
|
||||
- road
|
||||
- car
|
||||
- bus
|
||||
|
||||
|
@ -2583,13 +2586,16 @@ paddlewheel:
|
|||
- sea
|
||||
|
||||
padlock:
|
||||
label: tool
|
||||
threshold: 0.5
|
||||
priority: -1
|
||||
|
||||
paintbrush:
|
||||
threshold: 0.3
|
||||
categories:
|
||||
- drawing
|
||||
- painting
|
||||
- art
|
||||
- exhibition
|
||||
|
||||
palace:
|
||||
label: historic architecture
|
||||
|
@ -2613,7 +2619,7 @@ parachute:
|
|||
car:
|
||||
label: car
|
||||
categories:
|
||||
- street
|
||||
- road
|
||||
- vehicle
|
||||
|
||||
racer:
|
||||
|
@ -2621,12 +2627,12 @@ racer:
|
|||
threshold: 0.2
|
||||
categories:
|
||||
- car
|
||||
- street
|
||||
- road
|
||||
|
||||
passenger car:
|
||||
see: car
|
||||
categories:
|
||||
- street
|
||||
- road
|
||||
- vehicle
|
||||
|
||||
beach wagon:
|
||||
|
@ -2691,7 +2697,7 @@ polaroid camera:
|
|||
police van:
|
||||
label: van
|
||||
categories:
|
||||
- street
|
||||
- road
|
||||
- car
|
||||
- bus
|
||||
|
||||
|
@ -2740,7 +2746,7 @@ recreational vehicle:
|
|||
- bus
|
||||
- van
|
||||
- vehicle
|
||||
- street
|
||||
- road
|
||||
|
||||
reflex camera:
|
||||
categories:
|
||||
|
@ -2802,7 +2808,7 @@ school bus:
|
|||
categories:
|
||||
- bus
|
||||
- van
|
||||
- street
|
||||
- road
|
||||
|
||||
schooner:
|
||||
see: ship
|
||||
|
@ -2892,7 +2898,7 @@ sports car:
|
|||
categories:
|
||||
- car
|
||||
- vehicle
|
||||
- street
|
||||
- road
|
||||
|
||||
spotlight:
|
||||
categories:
|
||||
|
@ -2921,7 +2927,7 @@ streetcar:
|
|||
categories:
|
||||
- train
|
||||
- vehicle
|
||||
- street
|
||||
- road
|
||||
|
||||
studio couch:
|
||||
label: couch
|
||||
|
@ -2998,7 +3004,7 @@ trolleybus:
|
|||
label: bus
|
||||
categories:
|
||||
- van
|
||||
- street
|
||||
- road
|
||||
|
||||
trombone:
|
||||
see: instrument
|
||||
|
@ -3298,6 +3304,7 @@ eggnog:
|
|||
- alcohol
|
||||
|
||||
alp:
|
||||
label: alpine hut
|
||||
categories:
|
||||
- landscape
|
||||
- mountains
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
:label="labels.country"
|
||||
flat solo hide-details
|
||||
color="secondary-dark"
|
||||
item-value="LocCountryCode"
|
||||
item-text="LocCountry"
|
||||
item-value="code"
|
||||
item-text="name"
|
||||
v-model="filter.country"
|
||||
:items="options.countries">
|
||||
</v-select>
|
||||
|
@ -106,8 +106,8 @@
|
|||
data() {
|
||||
const cameras = [{ID: 0, CameraModel: this.$gettext('All Cameras')}].concat(this.$config.getValue('cameras'));
|
||||
const countries = [{
|
||||
LocCountryCode: '',
|
||||
LocCountry: this.$gettext('All Countries')
|
||||
code: '',
|
||||
name: this.$gettext('All Countries')
|
||||
}].concat(this.$config.getValue('countries'));
|
||||
|
||||
return {
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
{{ photo.getCamera() }}
|
||||
<br/>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</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 @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><v-btn icon small flat :ripple="false"
|
||||
class="p-photo-like"
|
||||
|
@ -49,7 +49,7 @@
|
|||
{text: '', value: '', align: 'center', sortable: false, class: 'p-col-select'},
|
||||
{text: this.$gettext('Title'), value: 'PhotoTitle'},
|
||||
{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('Favorite'), value: 'PhotoFavorite', align: 'left'},
|
||||
],
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
:label="labels.country"
|
||||
flat solo hide-details
|
||||
color="secondary-dark"
|
||||
item-value="LocCountryCode"
|
||||
item-text="LocCountry"
|
||||
item-value="code"
|
||||
item-text="name"
|
||||
v-model="filter.country"
|
||||
:items="options.countries">
|
||||
</v-select>
|
||||
|
@ -156,8 +156,8 @@
|
|||
data() {
|
||||
const cameras = [{ID: 0, CameraModel: this.$gettext('All Cameras')}].concat(this.$config.getValue('cameras'));
|
||||
const countries = [{
|
||||
LocCountryCode: '',
|
||||
LocCountry: this.$gettext('All Countries')
|
||||
code: '',
|
||||
name: this.$gettext('All Countries')
|
||||
}].concat(this.$config.getValue('countries'));
|
||||
|
||||
return {
|
||||
|
|
|
@ -33,7 +33,7 @@ class Photo extends Abstract {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -101,71 +101,17 @@ class Photo extends Abstract {
|
|||
}
|
||||
|
||||
hasLocation() {
|
||||
return this.PhotoLat !== 0 || this.PhotoLong !== 0;
|
||||
return this.PhotoLat !== 0 || this.PhotoLng !== 0;
|
||||
}
|
||||
|
||||
getLocation() {
|
||||
const location = [];
|
||||
|
||||
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);
|
||||
}
|
||||
if (this.LocRegion) {
|
||||
return this.LocRegion
|
||||
} else if (this.CountryName) {
|
||||
location.push(this.CountryName);
|
||||
} else {
|
||||
location.push("Unknown");
|
||||
return this.CountryName;
|
||||
}
|
||||
|
||||
return location.join(", ");
|
||||
}
|
||||
|
||||
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(", ");
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
getCamera() {
|
||||
|
|
|
@ -115,14 +115,10 @@
|
|||
openLocation(index) {
|
||||
const photo = this.results[index];
|
||||
|
||||
if (photo.PhotoLat && photo.PhotoLong) {
|
||||
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), long: String(photo.PhotoLong)}});
|
||||
} else if (photo.LocName) {
|
||||
this.$router.push({name: "places", query: {q: photo.LocName}});
|
||||
if (photo.PhotoLat && photo.PhotoLng) {
|
||||
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), lng: String(photo.PhotoLng)}});
|
||||
} else if (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 {
|
||||
this.$router.push({name: "places", query: {q: photo.CountryName}});
|
||||
}
|
||||
|
|
|
@ -105,14 +105,10 @@
|
|||
openLocation(index) {
|
||||
const photo = this.results[index];
|
||||
|
||||
if (photo.PhotoLat && photo.PhotoLong) {
|
||||
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), long: String(photo.PhotoLong)}});
|
||||
} else if (photo.LocName) {
|
||||
this.$router.push({name: "places", query: {q: photo.LocName}});
|
||||
if (photo.PhotoLat && photo.PhotoLng) {
|
||||
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), lng: String(photo.PhotoLng)}});
|
||||
} else if (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 {
|
||||
this.$router.push({name: "places", query: {q: photo.CountryName}});
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
loading: false,
|
||||
zoom: zoom,
|
||||
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',
|
||||
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||
options: {
|
||||
|
@ -65,7 +65,7 @@
|
|||
query: {
|
||||
q: q,
|
||||
lat: pos.lat,
|
||||
long: pos.long,
|
||||
lng: pos.lng,
|
||||
dist: dist.toString(),
|
||||
zoom: zoom.toString(),
|
||||
},
|
||||
|
@ -75,8 +75,8 @@
|
|||
bounds: null,
|
||||
minLat: null,
|
||||
maxLat: null,
|
||||
minLong: null,
|
||||
maxLong: null,
|
||||
minLng: null,
|
||||
maxLng: null,
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
},
|
||||
|
@ -128,12 +128,12 @@
|
|||
},
|
||||
onCenter(pos) {
|
||||
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;
|
||||
|
||||
this.query.lat = pos.lat.toString();
|
||||
this.query.long = pos.lng.toString();
|
||||
this.query.lng = pos.lng.toString();
|
||||
|
||||
this.search();
|
||||
},
|
||||
|
@ -143,21 +143,21 @@
|
|||
|
||||
let result = {
|
||||
lat: pos.lat.toString(),
|
||||
long: pos.long.toString(),
|
||||
lng: pos.lng.toString(),
|
||||
};
|
||||
|
||||
const queryLat = query['lat'];
|
||||
const queryLong = query['long'];
|
||||
const queryLng = query['lng'];
|
||||
|
||||
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.long = queryLong;
|
||||
} else if (storedLat && storedLong) {
|
||||
result.lng = queryLng;
|
||||
} else if (storedLat && storedLng) {
|
||||
result.lat = storedLat;
|
||||
result.long = storedLong;
|
||||
result.lng = storedLng;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -183,23 +183,23 @@
|
|||
},
|
||||
formChange() {
|
||||
this.query.lat = "";
|
||||
this.query.long = "";
|
||||
this.query.lng = "";
|
||||
this.search();
|
||||
},
|
||||
clearQuery() {
|
||||
this.position = null;
|
||||
this.query.q = "";
|
||||
this.query.lat = "";
|
||||
this.query.long = "";
|
||||
this.query.lng = "";
|
||||
this.search();
|
||||
},
|
||||
resetBoundingBox() {
|
||||
this.minLat = null;
|
||||
this.maxLat = null;
|
||||
this.minLong = null;
|
||||
this.maxLong = null;
|
||||
this.minLng = null;
|
||||
this.maxLng = null;
|
||||
},
|
||||
fitBoundingBox(lat, long) {
|
||||
fitBoundingBox(lat, lng) {
|
||||
if (this.maxLat === null || lat > this.maxLat) {
|
||||
this.maxLat = lat;
|
||||
}
|
||||
|
@ -208,12 +208,12 @@
|
|||
this.minLat = lat;
|
||||
}
|
||||
|
||||
if (this.maxLong === null || long > this.maxLong) {
|
||||
this.maxLong = long;
|
||||
if (this.maxLng === null || lng > this.maxLng) {
|
||||
this.maxLng = lng;
|
||||
}
|
||||
|
||||
if (this.minLong === null || long < this.minLong) {
|
||||
this.minLong = long;
|
||||
if (this.minLng === null || lng < this.minLng) {
|
||||
this.minLng = lng;
|
||||
}
|
||||
},
|
||||
updateMap(results) {
|
||||
|
@ -239,7 +239,7 @@
|
|||
iconSize: [50, 50],
|
||||
className: 'leaflet-marker-photo',
|
||||
}),
|
||||
location: L.latLng(result.PhotoLat, result.PhotoLong),
|
||||
location: L.latLng(result.PhotoLat, result.PhotoLng),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -257,9 +257,9 @@
|
|||
updateQuery() {
|
||||
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("long", this.query.long.toString());
|
||||
window.localStorage.setItem("lng", this.query.lng.toString());
|
||||
} else {
|
||||
this.position = null;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ describe("model/photo", () => {
|
|||
});
|
||||
|
||||
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 result = photo.getGoogleMapsLink();
|
||||
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", () => {
|
||||
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 result = photo.hasLocation();
|
||||
assert.equal(result, true);
|
||||
});
|
||||
|
||||
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 result = photo.hasLocation();
|
||||
assert.equal(result, false);
|
||||
});
|
||||
|
||||
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 result = photo.getLocation();
|
||||
assert.equal(result, "Cape Point, Africa");
|
||||
assert.equal(result, "Cape Point, South Africa");
|
||||
});
|
||||
|
||||
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 result = photo.getLocation();
|
||||
assert.equal(result, "Cape Town, State, Africa");
|
||||
assert.equal(result, "Cape Point, State, South Africa");
|
||||
});
|
||||
|
||||
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 result = photo.getLocation();
|
||||
assert.equal(result, "Unknown");
|
||||
|
@ -162,27 +162,6 @@ describe("model/photo", () => {
|
|||
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", () => {
|
||||
const values = {ID: 5, PhotoTitle: "Crazy Cat", CameraModel: "EOSD10", CameraMake: "Canon"};
|
||||
const photo = new Photo(values);
|
||||
|
|
|
@ -572,27 +572,22 @@ func (c *Config) ClientConfig() ClientConfig {
|
|||
var cameras []*entity.Camera
|
||||
var albums []*entity.Album
|
||||
|
||||
type country struct {
|
||||
LocCountry string
|
||||
LocCountryCode string
|
||||
}
|
||||
|
||||
var position struct {
|
||||
PhotoUUID string `json:"uuid"`
|
||||
LocationID string `json:"olc"`
|
||||
PhotoLat float64 `json:"lat"`
|
||||
PhotoLong float64 `json:"long"`
|
||||
PhotoLng float64 `json:"lng"`
|
||||
TakenAt time.Time `json:"utc"`
|
||||
TakenAtLocal time.Time `json:"time"`
|
||||
}
|
||||
|
||||
db.Table("photos").
|
||||
Select("photo_uuid, photo_lat, photo_long, taken_at, taken_at_local").
|
||||
Where("deleted_at IS NULL AND photo_lat != 0 AND photo_long != 0").
|
||||
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_lng != 0").
|
||||
Order("taken_at DESC").
|
||||
Limit(1).Offset(0).
|
||||
Take(&position)
|
||||
|
||||
var countries []country
|
||||
var count = struct {
|
||||
Photos uint `json:"photos"`
|
||||
Favorites uint `json:"favorites"`
|
||||
|
@ -622,8 +617,15 @@ func (c *Config) ClientConfig() ClientConfig {
|
|||
Select("COUNT(*) AS countries").
|
||||
Take(&count)
|
||||
|
||||
db.Model(&entity.Location{}).
|
||||
Select("DISTINCT loc_country_code, loc_country").
|
||||
type country struct {
|
||||
ID string `json:"code"`
|
||||
CountryName string `json:"name"`
|
||||
}
|
||||
|
||||
var countries []country
|
||||
|
||||
db.Model(&entity.Country{}).
|
||||
Select("DISTINCT id, country_name").
|
||||
Scan(&countries)
|
||||
|
||||
db.Where("deleted_at IS NULL").
|
||||
|
|
|
@ -23,7 +23,7 @@ type Album struct {
|
|||
AlbumFavorite bool
|
||||
AlbumPublic bool
|
||||
AlbumLat float64
|
||||
AlbumLong float64
|
||||
AlbumLng float64
|
||||
AlbumRadius float64
|
||||
AlbumOrder string `gorm:"type:varchar(16);"`
|
||||
AlbumTemplate string `gorm:"type:varchar(128);"`
|
||||
|
|
|
@ -18,7 +18,7 @@ type Event struct {
|
|||
EventBegin time.Time `gorm:"type:datetime;"`
|
||||
EventEnd time.Time `gorm:"type:datetime;"`
|
||||
EventLat float64
|
||||
EventLong float64
|
||||
EventLng float64
|
||||
EventDist float64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
|
|
|
@ -1,71 +1,68 @@
|
|||
package entity
|
||||
|
||||
var locTypeLabels = map[string]string{
|
||||
"bay": "bay",
|
||||
"art": "art exhibition",
|
||||
"fire station": "fire station",
|
||||
"hairdresser": "hairdresser",
|
||||
"cape": "cape",
|
||||
"coastline": "coastline",
|
||||
"cliff": "cliff",
|
||||
"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",
|
||||
}
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
olc "github.com/google/open-location-code/go"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/photoprism/photoprism/internal/maps"
|
||||
"github.com/photoprism/photoprism/internal/util"
|
||||
)
|
||||
|
||||
// Photo location
|
||||
type Location struct {
|
||||
Model
|
||||
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
|
||||
maps.Location
|
||||
LocDescription string `gorm:"type:text;"`
|
||||
LocNotes string `gorm:"type:text;"`
|
||||
LocPhoto *Photo
|
||||
LocPhotoID uint
|
||||
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 {
|
||||
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) {
|
||||
location := &Location{LocCategory: "restaurant", LocType: "bistro"}
|
||||
result := location.Label()
|
||||
l := NewLocation(1,1)
|
||||
l.LocLabel = "restaurant"
|
||||
result := l.Label()
|
||||
|
||||
assert.Equal(t, "restaurant", result)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ type Photo struct {
|
|||
PhotoNSFW bool `json:"PhotoNSFW"`
|
||||
PhotoStory bool `json:"PhotoStory"`
|
||||
PhotoLat float64 `gorm:"index;"`
|
||||
PhotoLong float64 `gorm:"index;"`
|
||||
PhotoLng float64 `gorm:"index;"`
|
||||
PhotoAltitude int
|
||||
PhotoFocalLength int
|
||||
PhotoIso int
|
||||
|
@ -40,7 +40,7 @@ type Photo struct {
|
|||
CountryID string `gorm:"index;"`
|
||||
CountryChanged bool
|
||||
Location *Location
|
||||
LocationID uint
|
||||
LocationID string
|
||||
LocationChanged bool
|
||||
LocationEstimated bool
|
||||
TakenAt time.Time `gorm:"type:datetime;index;"`
|
||||
|
|
|
@ -17,24 +17,24 @@ import (
|
|||
type PhotoSearch struct {
|
||||
Query string `form:"q"`
|
||||
|
||||
Title string `form:"title"`
|
||||
Description string `form:"description"`
|
||||
Notes string `form:"notes"`
|
||||
Artist string `form:"artist"`
|
||||
Hash string `form:"hash"`
|
||||
Duplicate bool `form:"duplicate"`
|
||||
Lat float64 `form:"lat"`
|
||||
Long float64 `form:"long"`
|
||||
Dist uint `form:"dist"`
|
||||
Fmin float64 `form:"fmin"`
|
||||
Fmax float64 `form:"fmax"`
|
||||
Chroma uint `form:"chroma"`
|
||||
Mono bool `form:"mono"`
|
||||
Portrait bool `form:"portrait"`
|
||||
Location bool `form:"location"`
|
||||
Album string `form:"album"`
|
||||
Label string `form:"label"`
|
||||
Country string `form:"country"`
|
||||
Title string `form:"title"`
|
||||
Description string `form:"description"`
|
||||
Notes string `form:"notes"`
|
||||
Artist string `form:"artist"`
|
||||
Hash string `form:"hash"`
|
||||
Duplicate bool `form:"duplicate"`
|
||||
Lat float64 `form:"lat"`
|
||||
Lng float64 `form:"lng"`
|
||||
Dist uint `form:"dist"`
|
||||
Fmin float64 `form:"fmin"`
|
||||
Fmax float64 `form:"fmax"`
|
||||
Chroma uint `form:"chroma"`
|
||||
Mono bool `form:"mono"`
|
||||
Portrait bool `form:"portrait"`
|
||||
Location bool `form:"location"`
|
||||
Album string `form:"album"`
|
||||
Label string `form:"label"`
|
||||
Country string `form:"country"`
|
||||
Color string `form:"color"`
|
||||
Camera int `form:"camera"`
|
||||
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)
|
||||
})
|
||||
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()
|
||||
|
||||
|
@ -45,7 +45,7 @@ func TestParseQueryString(t *testing.T) {
|
|||
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, 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) {
|
||||
form := &PhotoSearch{Query: "description:\"tübingen\""}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package maps
|
||||
|
||||
var Countries = map[string]string{
|
||||
var CountryNames = map[string]string{
|
||||
"af": "Afghanistan",
|
||||
"ax": "Åland Islands",
|
||||
"al": "Albania",
|
||||
|
|
|
@ -53,7 +53,7 @@ func main() {
|
|||
var packageTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
|
||||
package maps
|
||||
|
||||
var Countries = map[string]string{
|
||||
var CountryNames = map[string]string{
|
||||
{{- range .Countries }}
|
||||
{{ printf "%q" .Code }}: {{ printf "%q" .Name }},
|
||||
{{- end }}
|
||||
|
|
|
@ -8,21 +8,19 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/maps/osm"
|
||||
)
|
||||
|
||||
const SourceOSM = "osm"
|
||||
|
||||
// Photo location
|
||||
type Location struct {
|
||||
ID string `gorm:"primary_key"`
|
||||
LocLat float64
|
||||
LocLng float64
|
||||
LocTitle string
|
||||
LocCity string
|
||||
LocSuburb string
|
||||
LocState string
|
||||
LocCountry string
|
||||
LocRegion string
|
||||
LocLabel string
|
||||
LocSource string
|
||||
ID string `gorm:"primary_key"`
|
||||
LocLat float64
|
||||
LocLng float64
|
||||
LocTitle string
|
||||
LocRegion string
|
||||
LocCity string
|
||||
LocSuburb string
|
||||
LocState string
|
||||
LocCountryCode string
|
||||
LocLabel string
|
||||
LocSource string
|
||||
}
|
||||
|
||||
type LocationSource interface {
|
||||
|
@ -32,13 +30,25 @@ type LocationSource interface {
|
|||
City() string
|
||||
Suburb() string
|
||||
State() string
|
||||
Country() string
|
||||
CountryCode() string
|
||||
Label() string
|
||||
Source() string
|
||||
}
|
||||
|
||||
func (l *Location) Query(lat, lng float64) error {
|
||||
o, err := osm.FindLocation(lat, lng)
|
||||
func NewLocation (lat, lng float64) *Location {
|
||||
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 {
|
||||
return err
|
||||
|
@ -49,22 +59,24 @@ func (l *Location) Query(lat, lng float64) error {
|
|||
|
||||
func (l *Location) Assign(s LocationSource) error {
|
||||
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() {
|
||||
l.LocLabel = "unknown"
|
||||
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.LocCity = s.City()
|
||||
l.LocSuburb = s.Suburb()
|
||||
l.LocState = s.State()
|
||||
l.LocCountry = s.Country()
|
||||
l.LocRegion = l.region()
|
||||
l.LocCountryCode = s.CountryCode()
|
||||
l.LocLabel = s.Label()
|
||||
l.LocRegion = l.region()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -82,8 +94,9 @@ func (l *Location) region() string {
|
|||
return "Unknown"
|
||||
}
|
||||
|
||||
var countryName = Countries[l.LocCountry]
|
||||
var countryName = l.CountryName()
|
||||
var loc []string
|
||||
|
||||
shortCountry := len([]rune(countryName)) <= 20
|
||||
shortCity := len([]rune(l.LocCity)) <= 20
|
||||
|
||||
|
@ -101,3 +114,47 @@ func (l *Location) region() string {
|
|||
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
lat := 52.5208
|
||||
lng := 13.40953
|
||||
|
@ -223,7 +223,7 @@ func TestLocation_OpenStreetMap(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,45 +3,161 @@ package osm
|
|||
import "fmt"
|
||||
|
||||
var locationLabels = map[string]string{
|
||||
"bay": "bay",
|
||||
"art": "art exhibition",
|
||||
"fire station": "fire station",
|
||||
"hairdresser": "hairdresser",
|
||||
"cape": "cape",
|
||||
"coastline": "coastline",
|
||||
"cliff": "cliff",
|
||||
"wetland": "wetland",
|
||||
"nature reserve": "nature reserve",
|
||||
"natural=beach": "beach",
|
||||
"amenity=cafe": "cafe",
|
||||
"amenity=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",
|
||||
"aeroway=*": "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",
|
||||
"highway=secondary": "highway",
|
||||
"aeroway=*": "airport",
|
||||
"natural=bay": "bay",
|
||||
"natural=peninsula": "peninsula",
|
||||
"natural=cape": "cape",
|
||||
"natural=wood": "forest",
|
||||
"natural=grassland": "grassland",
|
||||
"*=beach": "beach",
|
||||
"*=dune": "dune",
|
||||
"*=water": "water",
|
||||
"*=wetland": "wetland",
|
||||
"*=glacier": "glacier",
|
||||
"*=strait": "seashore",
|
||||
"*=coastline": "seashore",
|
||||
"*=reef": "reef",
|
||||
"*=geyser": "geyser",
|
||||
"natural=peak": "mountain",
|
||||
"natural=hill": "hill",
|
||||
"natural=volcano": "volcano",
|
||||
"natural=valley": "valley",
|
||||
"natural=ridge": "mountain",
|
||||
"natural=cliff": "cliff",
|
||||
"natural=saddle": "mountain",
|
||||
"natural=isthmus": "seashore",
|
||||
"natural=sinkhole": "sinkhole",
|
||||
"natural=*": "nature",
|
||||
"place=sea": "ocean",
|
||||
"*=ocean": "ocean",
|
||||
"*=gallery": "gallery",
|
||||
"*=museum": "museum",
|
||||
"*=alpine_hut": "alpine hut",
|
||||
"*=aquarium": "aquarium",
|
||||
"*=artwork": "exhibition",
|
||||
"*=camp_pitch": "camping",
|
||||
"*=camp_site": "camping",
|
||||
"*=caravan_site": "camping",
|
||||
"*=hotel": "hotel",
|
||||
"*=hostel": "hotel",
|
||||
"*=motel": "hotel",
|
||||
"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) {
|
||||
|
|
|
@ -92,7 +92,7 @@ func (o Location) Suburb() (result string) {
|
|||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
func (o Location) Country() (result string) {
|
||||
func (o Location) CountryCode() (result string) {
|
||||
result = o.Address.CountryCode
|
||||
|
||||
return strings.ToLower(strings.TrimSpace(result))
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
var labelTitles = map[string]string{
|
||||
"airport": "Airport",
|
||||
"highway": "Route %name%",
|
||||
"visitor center": "Visitor Center",
|
||||
}
|
||||
|
||||
func (o Location) Title() (result string) {
|
||||
|
|
|
@ -21,22 +21,22 @@ type Exif struct {
|
|||
Artist string
|
||||
CameraMake string
|
||||
CameraModel string
|
||||
Description string
|
||||
LensMake string
|
||||
LensModel string
|
||||
Flash bool
|
||||
FocalLength int
|
||||
Exposure string
|
||||
Aperture float64
|
||||
FNumber float64
|
||||
Iso int
|
||||
Lat float64
|
||||
Long float64
|
||||
Altitude int
|
||||
Width int
|
||||
Height int
|
||||
Orientation int
|
||||
All map[string]string
|
||||
Description string
|
||||
LensMake string
|
||||
LensModel string
|
||||
Flash bool
|
||||
FocalLength int
|
||||
Exposure string
|
||||
Aperture float64
|
||||
FNumber float64
|
||||
Iso int
|
||||
Lat float64
|
||||
Lng float64
|
||||
Altitude int
|
||||
Width int
|
||||
Height int
|
||||
Orientation int
|
||||
All map[string]string
|
||||
}
|
||||
|
||||
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 gi, err := ifd.GpsInfo(); err == nil {
|
||||
m.exifData.Lat = gi.Latitude.Decimal()
|
||||
m.exifData.Long = gi.Longitude.Decimal()
|
||||
m.exifData.Lng = gi.Longitude.Decimal()
|
||||
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{
|
||||
Lat: m.exifData.Lat,
|
||||
Lon: m.exifData.Long,
|
||||
Lon: m.exifData.Lng,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestMediaFile_Exif_JPEG(t *testing.T) {
|
|||
assert.Equal(t, 10.0, info.FNumber)
|
||||
assert.Equal(t, 200, info.Iso)
|
||||
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, 1365, info.Width)
|
||||
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, 1000, info.Iso)
|
||||
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, 171, info.Width)
|
||||
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, 20, jpegInfo.Iso)
|
||||
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.Width)
|
||||
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() {
|
||||
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 {
|
||||
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
|
||||
if exifData, err := m.Exif(); err == nil {
|
||||
photo.PhotoLat = exifData.Lat
|
||||
photo.PhotoLong = exifData.Long
|
||||
photo.PhotoLng = exifData.Lng
|
||||
photo.TakenAt = exifData.TakenAt
|
||||
photo.TakenAtLocal = exifData.TakenAtLocal
|
||||
photo.TimeZone = exifData.TimeZone
|
||||
|
@ -128,7 +128,7 @@ func (i *Indexer) indexMediaFile(m *MediaFile, o IndexerOptions) IndexResult {
|
|||
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 != "" {
|
||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", util.Title(labels[0].Name), m.DateCreated().Format("2006"))
|
||||
} else if !photo.TakenAtLocal.IsZero() {
|
||||
|
@ -160,7 +160,7 @@ func (i *Indexer) indexMediaFile(m *MediaFile, o IndexerOptions) IndexResult {
|
|||
|
||||
if photoExists {
|
||||
// Estimate location
|
||||
if o.UpdateLocation && photo.LocationID == 0 {
|
||||
if o.UpdateLocation && photo.LocationID == "" {
|
||||
i.estimateLocation(&photo)
|
||||
}
|
||||
|
||||
|
@ -368,12 +368,18 @@ func (i *Indexer) indexLocation(mediaFile *MediaFile, photo *entity.Photo, label
|
|||
var keywords []string
|
||||
|
||||
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.LocationID = location.ID
|
||||
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 {
|
||||
event.Publish("count.countries", event.Data{
|
||||
|
@ -384,7 +390,7 @@ func (i *Indexer) indexLocation(mediaFile *MediaFile, photo *entity.Photo, label
|
|||
countryName := photo.Country.CountryName
|
||||
locLabel := location.Label()
|
||||
|
||||
keywords = append(keywords, util.Keywords(location.LocDisplayName)...)
|
||||
keywords = append(keywords, location.Keywords()...)
|
||||
|
||||
// Append label from OpenStreetMap
|
||||
if locLabel != "" {
|
||||
|
@ -393,33 +399,27 @@ func (i *Indexer) indexLocation(mediaFile *MediaFile, photo *entity.Photo, label
|
|||
}
|
||||
|
||||
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)
|
||||
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"))
|
||||
} else {
|
||||
photo.PhotoTitle = fmt.Sprintf("%s / %s / %s", util.Title(title), location.LocCity, photo.TakenAt.Format("2006"))
|
||||
}
|
||||
} else if location.LocName != "" && location.LocCity != "" {
|
||||
if len(location.LocName) > 45 {
|
||||
photo.PhotoTitle = util.Title(location.LocName)
|
||||
} else if len(location.LocName) > 20 || len(location.LocCity) > 16 || strings.Contains(location.LocName, location.LocCity) {
|
||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", util.Title(location.LocName), photo.TakenAt.Format("2006"))
|
||||
} else if location.Title() != "" && location.City() != "" {
|
||||
if len(location.Title()) > 45 {
|
||||
photo.PhotoTitle = util.Title(location.Title())
|
||||
} else if len(location.Title()) > 20 || len(location.City()) > 16 || strings.Contains(location.Title(), location.City()) {
|
||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", location.Title(), photo.TakenAt.Format("2006"))
|
||||
} 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 != "" {
|
||||
if len(location.LocCity) > 20 {
|
||||
} else if location.City() != "" && countryName != "" {
|
||||
if len(location.City()) > 20 {
|
||||
photo.PhotoTitle = fmt.Sprintf("%s / %s", location.LocCity, photo.TakenAt.Format("2006"))
|
||||
} else {
|
||||
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 == "" {
|
||||
|
|
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
|
||||
}
|
||||
|
||||
result := !exifData.TakenAt.IsZero() && exifData.Lat != 0 && exifData.Long != 0
|
||||
result := !exifData.TakenAt.IsZero() && exifData.Lat != 0 && exifData.Lng != 0
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
zones, err := tz.GetZone(tz.Point{
|
||||
Lon: meta.Long, Lat: meta.Lat,
|
||||
Lon: meta.Lng, Lat: meta.Lat,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -35,7 +35,7 @@ type PhotoResult struct {
|
|||
PhotoSensitive bool
|
||||
PhotoStory bool
|
||||
PhotoLat float64
|
||||
PhotoLong float64
|
||||
PhotoLng float64
|
||||
PhotoAltitude int
|
||||
PhotoFocalLength int
|
||||
PhotoIso int
|
||||
|
@ -57,17 +57,15 @@ type PhotoResult struct {
|
|||
CountryName string
|
||||
|
||||
// Location
|
||||
LocationID uint
|
||||
LocDisplayName string
|
||||
LocName string
|
||||
LocationID string
|
||||
LocTitle string
|
||||
LocCity string
|
||||
LocPostcode string
|
||||
LocCounty string
|
||||
LocSuburb string
|
||||
LocState string
|
||||
LocCountry string
|
||||
LocCountryCode string
|
||||
LocCategory string
|
||||
LocType string
|
||||
LocRegion string
|
||||
LocLabel string
|
||||
LocSource string
|
||||
LocationChanged bool
|
||||
LocationEstimated bool
|
||||
|
||||
|
@ -123,8 +121,8 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
cameras.camera_make, cameras.camera_model,
|
||||
lenses.lens_make, lenses.lens_model,
|
||||
countries.country_name,
|
||||
locations.loc_display_name, locations.loc_name, locations.loc_city, locations.loc_postcode, locations.loc_county,
|
||||
locations.loc_state, locations.loc_country, locations.loc_country_code, locations.loc_category, locations.loc_type`).
|
||||
locations.loc_title, locations.loc_city, locations.loc_suburb, locations.loc_state, locations.loc_country_code,
|
||||
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 cameras ON cameras.id = photos.camera_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)
|
||||
}
|
||||
|
||||
if f.Long > 0 {
|
||||
longMin := f.Long - SearchRadius*float64(f.Dist)
|
||||
longMax := f.Long + SearchRadius*float64(f.Dist)
|
||||
q = q.Where("photos.photo_long BETWEEN ? AND ?", longMin, longMax)
|
||||
if f.Lng > 0 {
|
||||
lngMin := f.Lng - SearchRadius*float64(f.Dist)
|
||||
lngMax := f.Lng + SearchRadius*float64(f.Dist)
|
||||
q = q.Where("photos.photo_lng BETWEEN ? AND ?", lngMin, lngMax)
|
||||
}
|
||||
|
||||
if !f.Before.IsZero() {
|
||||
|
|
|
@ -270,9 +270,9 @@ func TestSearch_Photos_Query(t *testing.T) {
|
|||
|
||||
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
|
||||
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.Offset = 0
|
||||
|
||||
|
|
Loading…
Reference in a new issue