Add more place types, refactor tag usage
This commit is contained in:
@@ -2,7 +2,7 @@ import Component from '@glimmer/component';
|
|||||||
import { fn } from '@ember/helper';
|
import { fn } from '@ember/helper';
|
||||||
import { on } from '@ember/modifier';
|
import { on } from '@ember/modifier';
|
||||||
import { humanizeOsmTag } from '../utils/format-text';
|
import { humanizeOsmTag } from '../utils/format-text';
|
||||||
import { getLocalizedName } from '../utils/osm';
|
import { getLocalizedName, getPlaceType } from '../utils/osm';
|
||||||
import Icon from '../components/icon';
|
import Icon from '../components/icon';
|
||||||
import PlaceEditForm from './place-edit-form';
|
import PlaceEditForm from './place-edit-form';
|
||||||
|
|
||||||
@@ -47,39 +47,50 @@ export default class PlaceDetails extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get type() {
|
get type() {
|
||||||
const rawType =
|
return getPlaceType(this.tags);
|
||||||
this.tags.amenity ||
|
|
||||||
this.tags.shop ||
|
|
||||||
this.tags.tourism ||
|
|
||||||
this.tags.leisure ||
|
|
||||||
this.tags.historic ||
|
|
||||||
'Point of Interest';
|
|
||||||
|
|
||||||
return humanizeOsmTag(rawType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get address() {
|
get address() {
|
||||||
const t = this.tags;
|
const t = this.tags;
|
||||||
const parts = [];
|
const parts = [];
|
||||||
|
|
||||||
|
// Helper to get value from multiple keys
|
||||||
|
const get = (...keys) => {
|
||||||
|
for (const k of keys) {
|
||||||
|
if (t[k]) return t[k];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
// Street + Number
|
// Street + Number
|
||||||
if (t['addr:street']) {
|
let street = get('addr:street', 'street');
|
||||||
let street = t['addr:street'];
|
const number = get('addr:housenumber', 'housenumber');
|
||||||
if (t['addr:housenumber']) {
|
|
||||||
street += ` ${t['addr:housenumber']}`;
|
if (street) {
|
||||||
|
if (number) {
|
||||||
|
street = `${street} ${number}`;
|
||||||
}
|
}
|
||||||
parts.push(street);
|
parts.push(street);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Postcode + City
|
// Postcode + City
|
||||||
if (t['addr:city']) {
|
let city = get('addr:city', 'city');
|
||||||
let city = t['addr:city'];
|
const postcode = get('addr:postcode', 'postcode');
|
||||||
if (t['addr:postcode']) {
|
|
||||||
city = `${t['addr:postcode']} ${city}`;
|
if (city) {
|
||||||
|
if (postcode) {
|
||||||
|
city = `${postcode} ${city}`;
|
||||||
}
|
}
|
||||||
parts.push(city);
|
parts.push(city);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// State + Country (if not already covered)
|
||||||
|
const state = get('addr:state', 'state');
|
||||||
|
const country = get('addr:country', 'country');
|
||||||
|
|
||||||
|
if (state && state !== city) parts.push(state);
|
||||||
|
if (country) parts.push(country);
|
||||||
|
|
||||||
if (parts.length === 0) return null;
|
if (parts.length === 0) return null;
|
||||||
return parts.join(', ');
|
return parts.join(', ');
|
||||||
}
|
}
|
||||||
@@ -141,6 +152,16 @@ export default class PlaceDetails extends Component {
|
|||||||
return `https://www.google.com/maps/search/?api=1&query=${this.name}&query=${this.place.lat},${this.place.lon}`;
|
return `https://www.google.com/maps/search/?api=1&query=${this.name}&query=${this.place.lat},${this.place.lon}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showDescription() {
|
||||||
|
// If it's a Photon result, the description IS the address.
|
||||||
|
// Since we are showing the address in the meta section (bottom),
|
||||||
|
// we should hide the description to avoid duplication.
|
||||||
|
if (this.place.source === 'photon') return false;
|
||||||
|
|
||||||
|
// Otherwise (e.g. saved place with custom description), show it.
|
||||||
|
return !!this.place.description;
|
||||||
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="place-details">
|
<div class="place-details">
|
||||||
{{#if this.isEditing}}
|
{{#if this.isEditing}}
|
||||||
@@ -154,7 +175,7 @@ export default class PlaceDetails extends Component {
|
|||||||
<p class="place-type">
|
<p class="place-type">
|
||||||
{{this.type}}
|
{{this.type}}
|
||||||
</p>
|
</p>
|
||||||
{{#if this.place.description}}
|
{{#if this.showDescription}}
|
||||||
<p class="place-description">
|
<p class="place-description">
|
||||||
{{this.place.description}}
|
{{this.place.description}}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import { action } from '@ember/object';
|
|||||||
import { on } from '@ember/modifier';
|
import { on } from '@ember/modifier';
|
||||||
import { fn } from '@ember/helper';
|
import { fn } from '@ember/helper';
|
||||||
import or from 'ember-truth-helpers/helpers/or';
|
import or from 'ember-truth-helpers/helpers/or';
|
||||||
|
import eq from 'ember-truth-helpers/helpers/eq';
|
||||||
import PlaceDetails from './place-details';
|
import PlaceDetails from './place-details';
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
import humanizeOsmTag from '../helpers/humanize-osm-tag';
|
import humanizeOsmTag from '../helpers/humanize-osm-tag';
|
||||||
import { getLocalizedName } from '../utils/osm';
|
import { getLocalizedName, getPlaceType } from '../utils/osm';
|
||||||
|
|
||||||
export default class PlacesSidebar extends Component {
|
export default class PlacesSidebar extends Component {
|
||||||
@service storage;
|
@service storage;
|
||||||
@@ -186,16 +187,17 @@ export default class PlacesSidebar extends Component {
|
|||||||
place.osmTags.name:en
|
place.osmTags.name:en
|
||||||
"Unnamed Place"
|
"Unnamed Place"
|
||||||
}}</div>
|
}}</div>
|
||||||
<div class="place-type">{{humanizeOsmTag
|
<div class="place-type">
|
||||||
(or
|
{{#if (eq place.source "osm")}}
|
||||||
place.osmTags.amenity
|
{{humanizeOsmTag place.type}}
|
||||||
place.osmTags.shop
|
{{else}}
|
||||||
place.osmTags.tourism
|
{{#if place.osmTags}}
|
||||||
place.osmTags.leisure
|
{{humanizeOsmTag (getPlaceType place.osmTags)}}
|
||||||
place.osmTags.historic
|
{{else if place.description}}
|
||||||
"Point of Interest"
|
{{place.description}}
|
||||||
)
|
{{/if}}
|
||||||
}}</div>
|
{{/if}}
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { on } from '@ember/modifier';
|
|||||||
import { fn } from '@ember/helper';
|
import { fn } from '@ember/helper';
|
||||||
import { task, timeout } from 'ember-concurrency';
|
import { task, timeout } from 'ember-concurrency';
|
||||||
import Icon from '#components/icon';
|
import Icon from '#components/icon';
|
||||||
|
import humanizeOsmTag from '../helpers/humanize-osm-tag';
|
||||||
|
import eq from 'ember-truth-helpers/helpers/eq';
|
||||||
|
|
||||||
export default class SearchBoxComponent extends Component {
|
export default class SearchBoxComponent extends Component {
|
||||||
@service photon;
|
@service photon;
|
||||||
@@ -178,8 +180,12 @@ export default class SearchBoxComponent extends Component {
|
|||||||
</div>
|
</div>
|
||||||
<div class="result-info">
|
<div class="result-info">
|
||||||
<span class="result-title">{{result.title}}</span>
|
<span class="result-title">{{result.title}}</span>
|
||||||
{{#if result.description}}
|
{{#if (eq result.source "osm")}}
|
||||||
<span class="result-desc">{{result.description}}</span>
|
<span class="result-desc">{{humanizeOsmTag result.type}}</span>
|
||||||
|
{{else}}
|
||||||
|
{{#if result.description}}
|
||||||
|
<span class="result-desc">{{result.description}}</span>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Service, { service } from '@ember/service';
|
import Service, { service } from '@ember/service';
|
||||||
import { getLocalizedName } from '../utils/osm';
|
import { getLocalizedName, getPlaceType } from '../utils/osm';
|
||||||
|
|
||||||
export default class OsmService extends Service {
|
export default class OsmService extends Service {
|
||||||
@service settings;
|
@service settings;
|
||||||
@@ -61,15 +61,20 @@ out center;
|
|||||||
}
|
}
|
||||||
|
|
||||||
normalizePoi(poi) {
|
normalizePoi(poi) {
|
||||||
|
const tags = poi.tags || {};
|
||||||
|
const type = getPlaceType(tags) || 'Point of Interest';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: getLocalizedName(poi.tags),
|
title: getLocalizedName(tags),
|
||||||
lat: poi.lat || poi.center?.lat,
|
lat: poi.lat || poi.center?.lat,
|
||||||
lon: poi.lon || poi.center?.lon,
|
lon: poi.lon || poi.center?.lon,
|
||||||
url: poi.tags?.website,
|
url: tags.website,
|
||||||
osmId: String(poi.id),
|
osmId: String(poi.id),
|
||||||
osmType: poi.type,
|
osmType: poi.type,
|
||||||
osmTags: poi.tags || {},
|
osmTags: tags,
|
||||||
description: poi.tags?.description,
|
description: tags.description,
|
||||||
|
source: 'osm',
|
||||||
|
type: type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,15 +229,20 @@ out center;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tags = mainElement.tags || {};
|
||||||
|
const type = getPlaceType(tags) || 'Point of Interest';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: getLocalizedName(mainElement.tags),
|
title: getLocalizedName(tags),
|
||||||
lat,
|
lat,
|
||||||
lon,
|
lon,
|
||||||
url: mainElement.tags?.website,
|
url: tags.website,
|
||||||
osmId: String(mainElement.id),
|
osmId: String(mainElement.id),
|
||||||
osmType: mainElement.type,
|
osmType: mainElement.type,
|
||||||
osmTags: mainElement.tags || {},
|
osmTags: tags,
|
||||||
description: mainElement.tags?.description,
|
description: tags.description,
|
||||||
|
source: 'osm',
|
||||||
|
type: type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
|
import { getPlaceType } from '../utils/osm';
|
||||||
|
import { humanizeOsmTag } from '../utils/format-text';
|
||||||
|
|
||||||
export default class PhotonService extends Service {
|
export default class PhotonService extends Service {
|
||||||
baseUrl = 'https://photon.komoot.io/api/';
|
baseUrl = 'https://photon.komoot.io/api/';
|
||||||
@@ -67,15 +69,24 @@ export default class PhotonService extends Service {
|
|||||||
R: 'relation',
|
R: 'relation',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const osmTags = { ...props };
|
||||||
|
// Photon often returns osm_key and osm_value for the main tag
|
||||||
|
if (props.osm_key && props.osm_value) {
|
||||||
|
osmTags[props.osm_key] = props.osm_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = getPlaceType(osmTags) || humanizeOsmTag(props.osm_value);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
lat,
|
lat,
|
||||||
lon,
|
lon,
|
||||||
osmId: props.osm_id,
|
osmId: props.osm_id,
|
||||||
osmType: osmTypeMap[props.osm_type] || props.osm_type, // 'node', 'way', 'relation'
|
osmType: osmTypeMap[props.osm_type] || props.osm_type, // 'node', 'way', 'relation'
|
||||||
osmTags: props, // Keep all properties as tags for now
|
osmTags,
|
||||||
description: props.name ? description : addressParts.slice(1).join(', '),
|
description: props.name ? description : addressParts.slice(1).join(', '),
|
||||||
source: 'photon',
|
source: 'photon',
|
||||||
|
type: type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { humanizeOsmTag } from './format-text';
|
||||||
|
|
||||||
export function getLocalizedName(tags, defaultName = 'Untitled Place') {
|
export function getLocalizedName(tags, defaultName = 'Untitled Place') {
|
||||||
if (!tags) return defaultName;
|
if (!tags) return defaultName;
|
||||||
|
|
||||||
@@ -30,3 +32,22 @@ export function getLocalizedName(tags, defaultName = 'Untitled Place') {
|
|||||||
// 5. Final fallback
|
// 5. Final fallback
|
||||||
return defaultName;
|
return defaultName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPlaceType(tags) {
|
||||||
|
if (!tags) return null;
|
||||||
|
|
||||||
|
const rawType =
|
||||||
|
tags.amenity ||
|
||||||
|
tags.shop ||
|
||||||
|
tags.tourism ||
|
||||||
|
tags.leisure ||
|
||||||
|
tags.office ||
|
||||||
|
tags.craft ||
|
||||||
|
tags.historic ||
|
||||||
|
tags.place ||
|
||||||
|
tags.building ||
|
||||||
|
tags.landuse ||
|
||||||
|
tags.natural;
|
||||||
|
|
||||||
|
return humanizeOsmTag(rawType);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user