7#include "osmelementinformationmodel.h"
8#include "osmelementinformationmodel_data.cpp"
10#include "localization.h"
11#include "osmaddress.h"
13#include <wikidata/wikidataquery.h>
15#include <KLocalizedString>
24[[nodiscard]]
static QString formatDistance(
int meter)
27 return i18n(
"%1m", meter);
30 return i18n(
"%1km", ((
int)meter/100)/10.0);
32 return i18n(
"%1km", (
int)qRound(meter/1000.0));
35bool OSMElementInformationModel::Info::operator<(OSMElementInformationModel::Info other)
const
37 if (category == other.category) {
38 return key < other.key;
40 return category < other.category;
43bool OSMElementInformationModel::Info::operator==(OSMElementInformationModel::Info other)
const
45 return category == other.category && key == other.key;
49OSMElementInformationModel::OSMElementInformationModel(
QObject *
parent)
51 , m_langs(
OSM::Languages::fromQLocale(
QLocale()))
53 m_wikidataMgr.setUserAgentEmailAddress(u
"kde-pim@kde.org"_s);
56OSMElementInformationModel::~OSMElementInformationModel() =
default;
58OSMElement OSMElementInformationModel::element()
const
63void OSMElementInformationModel::setElement(
const OSMElement &element)
65 if (m_element == element.element()) {
70 m_element = element.element();
72 if (m_element.type() != OSM::Type::Null) {
79void OSMElementInformationModel::clear()
81 if (m_element.type() == OSM::Type::Null) {
91QString OSMElementInformationModel::name()
const
93 return valueForKey(Info{m_nameKey, Header}).
toString();
96QString OSMElementInformationModel::category()
const
98 return valueForKey(Info{m_categoryKey, Header}).
toString();
101int OSMElementInformationModel::rowCount(
const QModelIndex &parent)
const
103 if (
parent.isValid() || m_element.type() == OSM::Type::Null) {
106 return (
int)m_infos.size();
115 const auto info = m_infos[
index.
row()];
123 case OperatorWikipedia:
127 return PostalAddress;
129 return OpeningHoursType;
141 if (info.key == DebugKey) {
144 return keyName(info.key);
147 case DebugKey:
return debugTagValue(
index.
row());
148 case Wikipedia:
return i18n(
"Wikipedia");
149 default:
return valueForKey(info);
152 if (info.key == DebugKey) {
155 return urlify(valueForKey(info), info.key);
157 return info.category;
158 case CategoryLabelRole:
159 return categoryLabel(info.category);
168 r.insert(KeyRole,
"key");
169 r.insert(KeyLabelRole,
"keyLabel");
170 r.insert(ValueRole,
"value");
171 r.insert(ValueUrlRole,
"url");
172 r.insert(CategoryRole,
"category");
173 r.insert(CategoryLabelRole,
"categoryLabel");
174 r.insert(TypeRole,
"type");
178#define M(name, key, category) { name, OSMElementInformationModel::key, OSMElementInformationModel::category }
179struct KeyCategoryMapEntry {
181 OSMElementInformationModel::Key m_key;
182 OSMElementInformationModel::KeyCategory m_category;
184 [[nodiscard]]
constexpr inline OSMElementInformationModel::Key key()
const {
return m_key; }
185 [[nodiscard]]
constexpr inline OSMElementInformationModel::KeyCategory
category()
const {
return m_category; }
188static constexpr const KeyCategoryMapEntry simple_key_map[] = {
189 M(
"addr:city", Address, Contact),
190 M(
"addr:street", Address, Contact),
191 M(
"amenity", Category, Header),
192 M(
"bicycle_parking", BicycleParking, Parking),
193 M(
"brand", Name, Header),
194 M(
"brand:wikidata", Image, Main),
195 M(
"brand:wikipedia", Wikipedia, UnresolvedCategory),
196 M(
"building", Category, Header),
197 M(
"bus_lines", Routes, Main),
198 M(
"bus_routes", Routes, Main),
199 M(
"buses", Routes, Main),
200 M(
"capacity", Capacity, UnresolvedCategory),
201 M(
"capacity:charging", CapacityCharing, Parking),
202 M(
"capacity:disabled", CapacityDisabled, Parking),
203 M(
"capacity:parent", CapacityParent, Parking),
204 M(
"capacity:women", CapacityWomen, Parking),
205 M(
"centralkey", CentralKey, Accessibility),
206 M(
"changing_table", DiaperChangingTable, UnresolvedCategory),
207 M(
"charge", Fee, UnresolvedCategory),
208 M(
"contact:city", Address, Contact),
209 M(
"contact:email", Email, Contact),
210 M(
"contact:phone", Phone, Contact),
211 M(
"contact:street", Address, Contact),
212 M(
"contact:website", Website, Contact),
213 M(
"cuisine", Cuisine, Main),
214 M(
"description", Description, Main),
215 M(
"diaper", DiaperChangingTable, UnresolvedCategory),
216 M(
"diplomatic", Category, Header),
217 M(
"email", Email, Contact),
218 M(
"fee", Fee, UnresolvedCategory),
219 M(
"genus", Name, Header),
220 M(
"genus:wikidata", Image, Main),
221 M(
"historic", Category, Header),
222 M(
"image", Image, Main),
223 M(
"int_name", Name, Header),
224 M(
"leisure", Category, Header),
225 M(
"maxstay", MaxStay, Parking),
226 M(
"memorial:text", Description, Main),
227 M(
"mx:realtime_available", AvailableVehicles, Main),
228 M(
"mx:remaining_range", RemainingRange, Main),
229 M(
"mx:vehicle", Category, Header),
230 M(
"network", Network, Operator),
231 M(
"network:wikidata", Image, Main),
232 M(
"network:wikipedia", OperatorWikipedia, Operator),
233 M(
"office", Category, Header),
234 M(
"old_name", OldName, UnresolvedCategory),
235 M(
"opening_hours", OpeningHours, OpeningHoursCategory),
236 M(
"operator", OperatorName, Operator),
237 M(
"operator:email", Email, Contact),
238 M(
"operator:phone", Phone, Contact),
239 M(
"operator:website", Website, Contact),
240 M(
"operator:wikidata", Image, Main),
241 M(
"operator:wikipedia", OperatorWikipedia, Operator),
242 M(
"parking:fee", Fee, Parking),
243 M(
"payment:cash", PaymentCash, Payment),
244 M(
"payment:coins", PaymentCash, Payment),
245 M(
"payment:notes", PaymentCash, Payment),
246 M(
"phone", Phone, Contact),
247 M(
"room", Category, Header),
248 M(
"route_ref", Routes, Main),
249 M(
"shop", Category, Header),
250 M(
"species:wikidata", Image, Main),
251 M(
"tactile_writing", TactileWriting, Accessibility),
252 M(
"takeaway", Takeaway, Main),
253 M(
"toilets:fee", Fee, Toilets),
254 M(
"toilets:wheelchair", Wheelchair, Toilets),
255 M(
"tourism", Category, Header),
256 M(
"url", Website, Contact),
257 M(
"vending", VendingMachineOffer, Header),
258 M(
"website", Website, Contact),
259 M(
"wheelchair", Wheelchair, Accessibility),
260 M(
"wheelchair:lift", WheelchairLift, Accessibility),
261 M(
"wikidata", Image, Main),
262 M(
"wikimedia_commons", Image, Main),
264static_assert(isSortedLookupTable(simple_key_map),
"key map is not sorted!");
266static constexpr const KeyCategoryMapEntry localized_key_map[] = {
267 M(
"name", Name, Header),
268 M(
"loc_name", Name, Header),
269 M(
"species", Name, Header),
270 M(
"species:wikipedia", Wikipedia, UnresolvedCategory),
271 M(
"speech_output", SpeechOutput, Accessibility),
272 M(
"wikipedia", Wikipedia, UnresolvedCategory),
276template <
typename KeyMapEntry, std::
size_t N>
277void OSMElementInformationModel::addEntryForKey(
const char *keyName,
const KeyMapEntry(&map)[N])
279 const auto it = std::lower_bound(std::begin(map), std::end(map), keyName, [](
const auto &lhs,
auto rhs) {
280 return std::strcmp(lhs.keyName, rhs) < 0;
282 if (it != std::end(map) && std::strcmp((*it).keyName, keyName) == 0) {
283 m_infos.push_back(Info{(*it).key(), (*it).category()});
287template <
typename KeyMapEntry, std::
size_t N>
288void OSMElementInformationModel::addEntryForLocalizedKey(
const char *keyName,
const KeyMapEntry(&map)[N])
290 for (
const auto &entry : map) {
291 const auto mapKeyLen = std::strlen(entry.keyName);
292 if (std::strncmp(keyName, entry.keyName, mapKeyLen) != 0) {
295 const auto keyNameLen = std::strlen(keyName);
296 if (keyNameLen == mapKeyLen || (keyNameLen == mapKeyLen + 3 && keyName[mapKeyLen] ==
':')) {
297 m_infos.push_back(Info{entry.key(), entry.category()});
303void OSMElementInformationModel::reload()
306 m_categoryKey = NoKey;
308 const bool isRoom = m_element.tagValue(
"indoor") ==
"room";
309 for (
auto it = m_element.tagsBegin(); it != m_element.tagsEnd(); ++it) {
310 addEntryForLocalizedKey((*it).key.name(), localized_key_map);
311 addEntryForKey((*it).key.name(), simple_key_map);
312 addEntryForKey((*it).key.name(), payment_generic_type_map);
313 addEntryForKey((*it).key.name(), payment_type_map);
314 addEntryForKey((*it).key.name(), diet_type_map);
315 addEntryForKey((*it).key.name(), socket_type_map);
316 addEntryForKey((*it).key.name(), authentication_type_map);
317 addEntryForLocalizedKey((*it).key.name(), tactile_writing_map);
319 if (isRoom && std::strcmp((*it).key.name(),
"ref") == 0) {
320 m_infos.push_back(Info{Name, Header});
324 m_infos.emplace_back(Gender, UnresolvedCategory);
327 std::sort(m_infos.begin(), m_infos.end());
328 m_infos.erase(std::unique(m_infos.begin(), m_infos.end()), m_infos.end());
329 resolveOnlineContent();
334 for (
auto cat : {Parking, Toilets}) {
335 if (promoteMainCategory(cat)) {
341 for (
auto &info : m_infos) {
342 if (info.category == UnresolvedCategory) {
343 info.category = Main;
346 std::sort(m_infos.begin(), m_infos.end());
347 m_infos.erase(std::unique(m_infos.begin(), m_infos.end()), m_infos.end());
350 m_infos.push_back(Info{ DebugLink, DebugCategory });
351 const auto count = std::distance(m_element.tagsBegin(), m_element.tagsEnd());
352 std::fill_n(std::back_inserter(m_infos), count, Info{ DebugKey, DebugCategory });
356void OSMElementInformationModel::resolveOnlineContent()
358 if (!m_allowOnlineContent) {
359 m_infos.erase(std::remove_if(m_infos.begin(), m_infos.end(), [](
const auto &info) {
360 return info.key == Image || info.key == Logo;
365 const auto commons = m_element.tagValue(
"wikimedia_commons");
366 const auto hasValidCommons = commons.
startsWith(
"File:");
367 const auto image = m_element.tagValue(
"image");
368 const auto hasValidImage = image.
contains(
"://commons.wikimedia.org/");
369 const auto wdId = m_element.tagValue(
"wikidata",
"species:wikidata",
"genus:wikidata",
"subject:wikidata",
"operator:wikidata",
"network:wikidata",
"brand:wikidata");
372 if (!hasValidCommons && !hasValidImage && !wdId.isEmpty()) {
375 connect(query, &Wikidata::EntitiesQuery::finished,
this, [query,
this]() {
376 query->deleteLater();
377 auto res =
query->takeResult();
378 for (
const auto &item : res) {
379 std::vector<Wikidata::P> props({Wikidata::P::image, Wikidata::P::imageOfInterior, Wikidata::P::aerialView, Wikidata::P::view, Wikidata::P::modelImage});
382 props.insert(props.begin(), Wikidata::P::nighttimeView);
384 props.emplace_back(Wikidata::P::nighttimeView);
388 props.insert(props.begin(), Wikidata::P::winterView);
390 props.emplace_back(Wikidata::P::nighttimeView);
394 if (item.id() ==
Wikidata::Q(m_element.tagValue(
"operator:wikidata",
"network:wikidata",
"brand:wikidata"))) {
395 props = {Wikidata::P::logoImage};
398 props.emplace_back(Wikidata::P::logoImage);
401 for (
const auto p : props) {
402 const auto img = item.value<
QString>(p);
406 m_wikidataImageMap.
insert(item.id(), img);
407 const auto it = std::find_if(m_infos.begin(), m_infos.end(), [](
const auto &info) { return info.key == Image; });
411 const auto idx =
index((
int)std::distance(m_infos.begin(), it), 0);
420 if (!hasValidCommons && !hasValidImage && wdId.isEmpty()) {
421 m_infos.erase(std::remove_if(m_infos.begin(), m_infos.end(), [](
const auto &info) { return info.key == Image; }), m_infos.end());
425void OSMElementInformationModel::resolveCategories()
427 if (m_infos.empty() || m_infos[0].category != UnresolvedCategory) {
430 for (
auto &info : m_infos) {
431 if (info.category != UnresolvedCategory) {
436 if (m_element.tagValue(
"parking:fee").
isEmpty() && (!m_element.tagValue(
"parking").
isEmpty()
437 || m_element.tagValue(
"amenity") ==
"parking" || m_element.tagValue(
"amenity") ==
"bicycle_parking"))
439 info.category = Parking;
440 }
else if (m_element.tagValue(
"toilets:fee").
isEmpty() && (m_element.tagValue(
"toilets") ==
"yes" || m_element.tagValue(
"amenity") ==
"toilets")) {
441 info.category = Toilets;
443 info.category = Main;
447 if (m_element.tagValue(
"amenity").
endsWith(
"rental")) {
448 info.category = Main;
450 info.category = Parking;
456 const auto amenity = m_element.tagValue(
"amenity");
457 if ((amenity !=
"parking" && amenity !=
"toilets")
458 || !m_element.tagValue(
"office").
isEmpty()
459 || (!m_element.tagValue(
"room").
isEmpty() && m_element.tagValue(
"room") !=
"toilets")
460 || !m_element.tagValue(
"shop").
isEmpty()
461 || !m_element.tagValue(
"tourism").
isEmpty()) {
462 info.category = Main;
468 std::sort(m_infos.begin(), m_infos.end());
471void OSMElementInformationModel::resolveHeaders()
474 for (
auto key : { Name, VendingMachineOffer, Network, OperatorName, Category }) {
475 if (m_nameKey != NoKey) {
479 const auto it = std::find_if(m_infos.begin(), m_infos.end(), [key](Info info) {
480 return info.key == key;
482 if (it == m_infos.end()) {
486 m_nameKey = (*it).key;
492 for (
auto key : { VendingMachineOffer, Category }) {
493 const auto it = std::find_if(m_infos.begin(), m_infos.end(), [key](Info info) {
494 return info.key == key;
496 if (it == m_infos.end()) {
500 if (m_categoryKey == NoKey && m_nameKey != key) {
501 m_categoryKey = (*it).key;
508bool OSMElementInformationModel::promoteMainCategory(OSMElementInformationModel::KeyCategory cat)
510 const auto hasMain = std::any_of(m_infos.begin(), m_infos.end(), [](
const auto &info) {
511 return info.category == Main;
518 bool didPromote =
false;
519 for (
auto &info : m_infos) {
520 if (info.category == cat) {
521 info.category = (info.key == Wheelchair ? Accessibility : Main);
527 std::sort(m_infos.begin(), m_infos.end());
532QString OSMElementInformationModel::categoryLabel(OSMElementInformationModel::KeyCategory cat)
const
535 case UnresolvedCategory:
537 case Main:
return {};
538 case OpeningHoursCategory:
return i18n(
"Opening Hours");
539 case Contact:
return i18n(
"Contact");
540 case Payment:
return i18n(
"Payment");
541 case Toilets:
return i18n(
"Toilets");
542 case Accessibility:
return i18n(
"Accessibility");
543 case Parking:
return i18n(
"Parking");
544 case Operator:
return i18n(
"Operator");
545 case DebugCategory:
return QStringLiteral(
"Debug");
550QString OSMElementInformationModel::debugTagKey(
int row)
const
552 const auto tagCount = std::distance(m_element.tagsBegin(), m_element.tagsEnd());
553 const auto tagIdx = row - (rowCount() - tagCount);
557QString OSMElementInformationModel::debugTagValue(
int row)
const
559 const auto tagCount = std::distance(m_element.tagsBegin(), m_element.tagsEnd());
560 const auto tagIdx = row - (rowCount() - tagCount);
564QUrl OSMElementInformationModel::debugTagUrl(
int row)
const
566 const auto tagCount = std::distance(m_element.tagsBegin(), m_element.tagsEnd());
567 const auto tagIdx = row - (rowCount() - tagCount);
568 const auto key =
QByteArrayView((*(m_element.tagsBegin() + tagIdx)).key.name());
569 const auto value = (*(m_element.tagsBegin() + tagIdx)).value;
570 if (key.endsWith(
":wikipedia") || key ==
"wikipedia") {
571 return wikipediaUrl(value);
573 if (key.endsWith(
":wikidata") || key ==
"wikidata") {
576 if (value.startsWith(
"http"_L1)) {
582QString OSMElementInformationModel::keyName(OSMElementInformationModel::Key key)
const
588 case VendingMachineOffer:
592 case OldName:
return i18n(
"Formerly");
593 case Description:
return i18n(
"Description");
594 case Routes:
return i18n(
"Routes");
595 case Cuisine:
return i18n(
"Cuisine");
596 case Diet:
return i18n(
"Diet");
597 case Takeaway:
return i18n(
"Takeaway");
598 case Socket:
return i18nc(
"electrical power socket",
"Socket");
599 case OpeningHours:
return {};
600 case AvailableVehicles:
return i18n(
"Available vehicles");
601 case Fee:
return i18n(
"Fee");
602 case Authentication:
return i18n(
"Authentication");
603 case BicycleParking:
return i18n(
"Bicycle parking");
604 case Capacity:
return i18n(
"Capacity");
605 case CapacityDisabled:
return i18n(
"Disabled parking spaces");
606 case CapacityWomen:
return i18n(
"Women parking spaces");
607 case CapacityParent:
return i18n(
"Parent parking spaces");
608 case CapacityCharing:
return i18n(
"Parking spaces for charging");
609 case MaxStay:
return i18n(
"Maximum stay");
610 case DiaperChangingTable:
return i18n(
"Diaper changing table");
611 case Gender:
return i18n(
"Gender");
612 case Wikipedia:
return {};
613 case Address:
return i18n(
"Address");
614 case Phone:
return i18n(
"Phone");
615 case Email:
return i18n(
"Email");
616 case Website:
return i18n(
"Website");
617 case PaymentCash:
return i18n(
"Cash");
618 case PaymentDigital:
return i18n(
"Digital");
619 case PaymentDebitCard:
return i18n(
"Debit cards");
620 case PaymentCreditCard:
return i18n(
"Credit cards");
621 case PaymentStoredValueCard:
return i18n(
"Stored value cards");
622 case Wheelchair:
return i18n(
"Wheelchair access");
623 case WheelchairLift:
return i18n(
"Wheelchair lift");
624 case CentralKey:
return i18n(
"Central key");
625 case SpeechOutput:
return i18n(
"Speech output");
626 case TactileWriting:
return i18n(
"Tactile writing");
627 case OperatorName:
return {};
628 case Network:
return i18nc(
"transport network",
"Network");
629 case OperatorWikipedia:
return {};
630 case RemainingRange:
return i18nc(
"remaining travel range of a battery powered vehicle",
"Remaining range");
631 case DebugLink:
return QStringLiteral(
"OSM");
632 case DebugKey:
return {};
643 for (
const auto &s : split) {
660}
static constexpr const script_map[] = {
667 return std::find_if(std::begin(script_map), std::end(script_map), [ls, cs](
const auto &m) {
return m.localeScript == ls && m.charScript == cs; }) != std::end(script_map);
670[[nodiscard]]
static QUrl wikimediaCommondRedirect(
const QString &fileName)
678 redirectUrl.
setHost(u
"commons.wikimedia.org"_s);
679 redirectUrl.
setPath(u
"/wiki/Special:Redirect/file"_s);
681 query.addQueryItem(u
"wptype"_s, u
"file"_s);
682 query.addQueryItem(u
"wpvalue"_s, fileName);
683 query.addQueryItem(u
"width"_s, u
"512"_s);
688QVariant OSMElementInformationModel::valueForKey(Info info)
const
691 case NoKey:
return {};
693 const auto n =
QString::fromUtf8(m_element.tagValue(m_langs,
"name",
"loc_name",
"int_name",
"brand",
"ref",
"species",
"genus"));
694 const auto script = scriptForString(n);
696 const auto transliterated =
QString::fromUtf8(m_element.tagValue(m_langs,
"int_name"));
697 if (transliterated.isEmpty() || transliterated == n) {
700 return i18nc(
"local name (transliterated name)",
"%1 (%2)", n, transliterated);
707 appendNonEmpty(m_element.tagValue(
"amenity"), l);
708 appendNonEmpty(m_element.tagValue(
"shop"), l);
709 appendNonEmpty(m_element.tagValue(
"tourism"), l);
711 const auto diplomatic = m_element.tagValue(
"diplomatic");
712 appendNonEmpty(diplomatic, l);
713 if (diplomatic.isEmpty()) {
714 appendNonEmpty(m_element.tagValue(
"office"), l);
716 appendNonEmpty(m_element.tagValue(
"leisure"), l);
717 appendNonEmpty(m_element.tagValue(
"historic"), l);
718 appendNonEmpty(m_element.tagValue(
"mx:vehicle"), l);
720 appendNonEmpty(m_element.tagValue(
"room"), l);
728 for (
auto it = l.
begin(); it != l.
end();++it) {
729 if ((*it).isEmpty() || (*it) ==
"yes" || (*it) ==
"no" || (*it) ==
"building") {
736 appendNonEmpty(m_element.tagValue(
"building"), l);
737 for (
const auto &key : l) {
752 const auto commons = m_element.tagValue(
"wikimedia_commons");
753 if (commons.startsWith(
"File:")) {
757 if (url.host() ==
"commons.wikimedia.org"_L1) {
758 return wikimediaCommondRedirect(url.fileName());
760 const auto wdId = m_element.tagValue(
"wikidata",
"species:wikidata",
"genus:wikidata",
"subject:wikidata",
"operator:wikidata",
"network:wikidata",
"brand:wikidata");
761 return wikimediaCommondRedirect(m_wikidataImageMap.
value(
Wikidata::Q{wdId}));
769 return m_element.tagValue(m_langs,
"description",
"memorial:text");
779 case VendingMachineOffer:
784 for (
const auto &d : diet_type_map) {
785 const auto v = m_element.tagValue(d.keyName);
786 const auto label = d.label.
toString();
789 }
else if (v ==
"only") {
791 }
else if (v ==
"no") {
797 case Takeaway:
return translatedBoolValue(m_element.tagValue(
"takeaway"));
801 for (
const auto &socket : socket_type_map) {
802 const auto value = m_element.tagValue(socket.keyName);
803 if (value.isEmpty() || value ==
"no") {
807 auto s = socket.label.toString();
810 if (value !=
"yes") {
814 const auto current = m_element.tagValue(
QByteArray(socket.keyName +
QByteArray(
":current")).constData());
815 if (!current.isEmpty()) {
816 if (std::all_of(current.begin(), current.end(), [](
unsigned char c) { return std::isdigit(c); })) {
822 const auto output = m_element.tagValue(
QByteArray(socket.keyName +
QByteArray(
":output")).constData());
823 if (!output.isEmpty()) {
824 if (std::all_of(output.begin(), output.end(), [](
unsigned char c) { return std::isdigit(c); })) {
831 if (!details.
empty()) {
838 case OpeningHours:
return QString::fromUtf8(m_element.tagValue(
"opening_hours"));
839 case AvailableVehicles:
841 const auto total = m_element.tagValue(
"mx:realtime_available").
toInt();
843 for (
const auto &v : available_vehicles_map) {
844 const auto b = m_element.tagValue(v.keyName);
848 types.
push_back(v.label.subs(b.toInt()).toString());
853 }
else if (types.
size() == 1) {
856 return i18n(
"%1 (%2)", total,
QLocale().createSeparatedList(types));
862 switch (info.category) {
863 case Parking: fee = m_element.tagValue(
"parking:fee",
"fee");
break;
864 case Toilets: fee = m_element.tagValue(
"toilets:fee",
"fee");
break;
865 default: fee = m_element.tagValue(
"fee");
872 if (!charge.isEmpty()) {
880 for (
const auto &auth : authentication_type_map) {
881 const auto v = m_element.tagValue(auth.keyName);
882 if (v.isEmpty() || v ==
"no") {
889 case BicycleParking:
return translateValues(m_element.tagValue(
"bicycle_parking"), bicycle_parking_map);
891 case CapacityDisabled:
return capacitryValue(
"capacity:disabled");
892 case CapacityWomen:
return capacitryValue(
"capacity:women");
893 case CapacityParent:
return capacitryValue(
"capacity:parent");
894 case CapacityCharing:
return capacitryValue(
"capacity:charging");
896 case DiaperChangingTable:
898 return translatedBoolValue(m_element.tagValue(
"changing_table",
"diaper"));
901 case Wikipedia:
return wikipediaUrl(m_element.tagValue(m_langs,
"wikipedia",
"brand:wikipedia",
"species:wikipedia"));
903 case Phone:
return QString::fromUtf8(m_element.tagValue(
"contact:phone",
"phone",
"telephone",
"operator:phone"));
904 case Email:
return QString::fromUtf8(m_element.tagValue(
"contact:email",
"email",
"operator:email"));
905 case Website:
return QString::fromUtf8(m_element.tagValue(
"website",
"contact:website",
"url",
"operator:website"));
909 const auto coins = m_element.tagValue(
"payment:coins");
910 const auto notes = m_element.tagValue(
"payment:notes");
911 if (coins.isEmpty() && notes.isEmpty()) {
912 return translatedBoolValue(m_element.tagValue(
"payment:cash"));
914 if (!coins.isEmpty() && !notes.isEmpty() && coins !=
"no" && notes !=
"no") {
917 if (!coins.isEmpty() && coins !=
"no" && (notes ==
"no" || notes.isEmpty())) {
918 return i18nc(
"payment option",
"coins only");
920 if (!notes.isEmpty() && notes !=
"no" && (coins ==
"no" || coins.isEmpty())) {
921 return i18nc(
"payment option",
"notes only");
926 case PaymentDebitCard:
927 case PaymentCreditCard:
928 case PaymentStoredValueCard:
929 return paymentMethodValue(info.key);
933 if (info.category == Toilets) {
934 wheelchair = m_element.tagValue(
"toilets:wheelchair",
"wheelchair");
936 wheelchair = m_element.tagValue(
"wheelchair");
938 const auto a = translateValue(wheelchair.
constData(), wheelchair_map);
939 const auto d =
QString::fromUtf8(m_element.tagValue(m_langs,
"wheelchair:description"));
946 return translatedBoolValue(m_element.tagValue(
"wheelchair:lift"));
952 return translatedBoolValue(m_element.tagValue(m_langs,
"speech_output"));
957 bool explicitNo =
false;
958 for (
const auto &writing : tactile_writing_map) {
959 const auto v = m_element.tagValue(m_langs, writing.keyName);
972 const auto v = m_element.tagValue(m_langs,
"tactile_writing");
973 if (explicitNo && v.isEmpty()) {
976 return translatedBoolValue(v);
980 case OperatorWikipedia:
return wikipediaUrl(m_element.tagValue(m_langs,
"operator:wikipedia",
"network:wikipedia"));
983 const auto range = m_element.tagValue(
"mx:remaining_range").
toInt();
984 return formatDistance(range);
986 case DebugLink:
return m_element.url();
987 case DebugKey:
return {};
992QVariant OSMElementInformationModel::urlify(
const QVariant& v, OSMElementInformationModel::Key key)
const
1003 if (
const auto commons = m_element.tagValue(
"wikimedia_commons"); commons.
startsWith(
"File:")) {
1007 return wikimediaCommondRedirect(url.fileName());
1009 if (
const auto wdId = m_element.tagValue(
"wikidata",
"species:wikidata",
"genus:wikidata",
"subject:wikidata",
"operator:wikidata",
"network:wikidata",
"brand:wikidata"); !wdId.
isEmpty()) {
1041QString OSMElementInformationModel::paymentMethodList(OSMElementInformationModel::Key key)
const
1044 for (
const auto &payment : payment_type_map) {
1045 if (payment.key() != key) {
1048 if (m_element.tagValue(payment.keyName) ==
"yes") {
1056QString OSMElementInformationModel::paymentMethodValue(OSMElementInformationModel::Key key)
const
1058 const auto s = paymentMethodList(key);
1063 for (
const auto &payment : payment_generic_type_map) {
1064 if (payment.key() != key) {
1067 const auto s = m_element.tagValue(payment.keyName);
1075QUrl OSMElementInformationModel::wikipediaUrl(
const QByteArray &wp)
const
1094QString OSMElementInformationModel::capacitryValue(
const char *prop)
const
1096 const auto v = m_element.tagValue(prop);
1097 return translatedBoolValue(v);
1100QString OSMElementInformationModel::translatedBoolValue(
const QByteArray &value)
const
1102 if (value ==
"yes") {
1105 if (value ==
"no") {
1111#include "moc_osmelementinformationmodel.cpp"
Postal address from OSM data.
QML wrapper around an OSM element.
Wikidata multi-entity retrieval query.
Wikidata item identifier.
void execute(Query *query)
Execute query.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
std::optional< QSqlQuery > query(const QString &queryStatement)
char * toString(const EngineQuery &query)
QString cuisineTypes(const QByteArray &value, Localization::TranslationOption opt=Localization::ReturnUnknownKey)
Translated values of the cuisine tag (does list splitting).
QString genderSegregation(OSM::Element element)
Translated gender segregation information e.g.
bool hasGenderSegregrationKey(OSM::Element element)
Checks whether element contains a known key for gender segregation information.
QString amenityTypes(const QByteArray &value, Localization::TranslationOption opt=Localization::ReturnUnknownKey)
Translated list of amenity tag values (including list splitting).
QString amenityType(const char *value, Localization::TranslationOption opt=Localization::ReturnUnknownKey)
Translated name for an amenity tag value (after list splitting).
OSM-based multi-floor indoor maps for buildings.
Category category(StandardShortcut id)
Low-level types and functions to work with raw OSM data as efficiently as possible.
QByteArray tagValue(const Elem &elem, TagKey key)
Returns the tag value for key of elem.
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual QHash< int, QByteArray > roleNames() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
const char * constData() const const
bool contains(QByteArrayView bv) const const
bool endsWith(QByteArrayView bv) const const
bool isEmpty() const const
QList< QByteArray > split(char sep) const const
bool startsWith(QByteArrayView bv) const const
int toInt(bool *ok, int base) const const
iterator insert(const Key &key, const T &value)
T value(const Key &key) const const
const_reference at(qsizetype i) const const
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
void push_back(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
QString createSeparatedList(const QStringList &list) const const
QString toString(QDate date, FormatType format) const const
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
const_iterator begin() const const
const_iterator end() const const
QTextStream & left(QTextStream &stream)
QString host(ComponentFormattingOptions options) const const
bool isValid() const const
void setHost(const QString &host, ParsingMode mode)
void setPath(const QString &path, ParsingMode mode)
void setQuery(const QString &query, ParsingMode mode)
void setScheme(const QString &scheme)
QVariant fromValue(T &&value)
QString toString() const const
int userType() const const