File indexing completed on 2025-07-06 04:18:04

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "penwidthutil_p.h"
0008 #include "logging.h"
0009 #include "style/mapcssdeclaration_p.h"
0010 
0011 #include <cmath>
0012 
0013 using namespace KOSMIndoorMap;
0014 
0015 struct {
0016     const char* unit;
0017     double factor;
0018 } static constexpr const unit_conversion_table[] = {
0019     { "cm", 0.01 },
0020     { "ft", 0.3048 },
0021 };
0022 
0023 double PenWidthUtil::penWidth(OSM::Element e, const MapCSSDeclaration *decl, Unit &unit)
0024 {
0025     // literal value, possibly with a unit
0026     if (decl->keyValue().isEmpty()) {
0027         if (decl->unit() != MapCSSDeclaration::NoUnit) {
0028             unit = decl->unit() == MapCSSDeclaration::Meters ? Unit::Meter : Unit::Pixel;
0029         }
0030         return decl->doubleValue();
0031     }
0032 
0033     // referenced value from a tag value
0034     // see https://wiki.openstreetmap.org/wiki/Map_Features/Units
0035     double unitConversionFactor = 1.0;
0036     double num = NAN;
0037 
0038     const auto value = e.tagValue(decl->keyValue().constData()).constData();
0039     const auto valueLen = std::strlen(value);
0040     const auto valueEnd = value + valueLen;
0041     const char* it = value;
0042 
0043     while (it < valueEnd) {
0044         char* numEnd;
0045         num = std::isnan(num) ? std::strtod(it, &numEnd) : std::max(num, std::strtod(it, &numEnd));
0046         if (it == numEnd) {
0047             break;
0048         }
0049         it = numEnd;
0050 
0051         while (it != valueEnd && std::isspace(static_cast<unsigned char>(*it))) {
0052             ++it;
0053         }
0054 
0055         // there is an explicit unit suffix;
0056         for (const auto &unit : unit_conversion_table) {
0057             const auto unitLen = std::strlen(unit.unit);
0058             if (std::strncmp(it, unit.unit, unitLen) == 0) {
0059                 unitConversionFactor = unit.factor;
0060                 it += unitLen;
0061                 break;
0062             }
0063         }
0064 
0065         if (it != valueEnd && (*it) == ';') {
0066             ++it;
0067             continue;
0068         }
0069         break;
0070     }
0071 
0072     if (std::isnan(num)) {
0073         qCDebug(Log) << "Failed to parse width from tag value:" << value << decl->keyValue() << e.url();
0074         return 0.0;
0075     }
0076 
0077     // no explicit unit, use default unit for this tag
0078     if (it == value + valueLen) {
0079         if (std::strcmp(decl->keyValue().constData(), "gauge") == 0) {
0080             unitConversionFactor = 0.001;
0081         }
0082     }
0083 
0084     unit = Unit::Meter;
0085     return num * unitConversionFactor;
0086 }