Skip to content

Commit 9b0e0de

Browse files
committed
Loose failure check for cases when duplicate keys are mapping to different feature tags
Address review findings Add new test rename argument make the argument name more generic Update test Update unit test update test to include unique key and value Update mvt file Update comment and refine the code
1 parent a972c9f commit 9b0e0de

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

include/mapbox/vector_tile.hpp

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,23 @@ class feature {
4040
feature(protozero::data_view const&, layer const&);
4141

4242
GeomType getType() const { return type; }
43-
mapbox::feature::value getValue(std::string const&) const;
43+
/**
44+
* Retrieve the value associated with a given key from the feature.
45+
*
46+
* @param key The key used to look up the corresponding value.
47+
* @param warning A pointer to a string that may be used to record any warnings that
48+
* occur during the lookup process.
49+
* The caller is responsible for managing the memory of this string.
50+
* @return The value associated with the specified key, or a null value if the key is not found.
51+
*
52+
* Note: If the lookup process encounters a duplicate key in the feature, the function will
53+
* return the value in the `values` set to which the associated tag index points to, and
54+
* will append a message to the `warning` string (if provided) to alert the caller to the
55+
* presence of the duplicate key.
56+
* The caller should ensure that the `warning` string is properly initialized
57+
* and cleaned up after use.
58+
*/
59+
mapbox::feature::value getValue(std::string const&, std::string* warning = nullptr) const;
4460
properties_type getProperties() const;
4561
mapbox::feature::identifier const& getID() const;
4662
std::uint32_t getExtent() const;
@@ -72,7 +88,7 @@ class layer {
7288
std::string name;
7389
std::uint32_t version;
7490
std::uint32_t extent;
75-
std::map<std::string, std::uint32_t> keysMap;
91+
std::multimap<std::string, std::uint32_t> keysMap;
7692
std::vector<std::reference_wrapper<const std::string>> keys;
7793
std::vector<protozero::data_view> values;
7894
std::vector<protozero::data_view> features;
@@ -153,23 +169,19 @@ inline feature::feature(protozero::data_view const& feature_view, layer const& l
153169
}
154170
}
155171

156-
inline mapbox::feature::value feature::getValue(const std::string& key) const {
157-
auto keyIter = layer_.keysMap.find(key);
158-
if (keyIter == layer_.keysMap.end()) {
172+
inline mapbox::feature::value feature::getValue(const std::string& key, std::string* warning ) const {
173+
const auto key_range = layer_.keysMap.equal_range(key);
174+
const auto key_count = std::distance(key_range.first, key_range.second) ;
175+
if (key_count < 1) {
159176
return mapbox::feature::null_value;
160177
}
161178

162179
const auto values_count = layer_.values.size();
163-
const auto keymap_count = layer_.keysMap.size();
164180
auto start_itr = tags_iter.begin();
165181
const auto end_itr = tags_iter.end();
166182
while (start_itr != end_itr) {
167183
std::uint32_t tag_key = static_cast<std::uint32_t>(*start_itr++);
168184

169-
if (keymap_count <= tag_key) {
170-
throw std::runtime_error("feature referenced out of range key");
171-
}
172-
173185
if (start_itr == end_itr) {
174186
throw std::runtime_error("uneven number of feature tag ids");
175187
}
@@ -179,7 +191,19 @@ inline mapbox::feature::value feature::getValue(const std::string& key) const {
179191
throw std::runtime_error("feature referenced out of range value");
180192
}
181193

182-
if (tag_key == keyIter->second) {
194+
bool key_found = false;
195+
for (auto i = key_range.first; i != key_range.second; ++i) {
196+
if (i->second == tag_key) {
197+
key_found = true;
198+
break;
199+
}
200+
}
201+
202+
if (key_found) {
203+
// Continue process with case when same keys having multiple tag ids.
204+
if (key_count > 1 && warning) {
205+
*warning = std::string("duplicate keys with different tag ids are found");
206+
}
183207
return parseValue(layer_.values[tag_val]);
184208
}
185209
}
@@ -403,8 +427,8 @@ inline layer::layer(protozero::data_view const& layer_view) :
403427
{
404428
// We want to keep the keys in the order of the vector tile
405429
// https://github.com/mapbox/mapbox-gl-native/pull/5183
406-
auto iter = keysMap.emplace(layer_pbf.get_string(), keysMap.size());
407-
keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first));
430+
auto iter = keysMap.emplace(layer_pbf.get_string(), keys.size());
431+
keys.emplace_back(std::reference_wrapper<const std::string>(iter->first));
408432
}
409433
break;
410434
case LayerType::VALUES:

test/duplicate-keys-values.mvt

100 Bytes
Binary file not shown.

test/unit/vector_tile.test.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,29 @@ TEST_CASE( "Prevent underflow in case of LineTo with 0 command count" ) {
128128
REQUIRE(!geom.empty());
129129
}
130130
}
131+
132+
TEST_CASE( "Allow multiple keys mapping to different tag ids" ) {
133+
// duplicate key 'hello' and duplicate value 'world'
134+
std::string buffer = open_tile("test/duplicate-keys-values.mvt");
135+
mapbox::vector_tile::buffer tile(buffer);
136+
auto const layer_names = tile.layerNames();
137+
REQUIRE(layer_names.size() == 1);
138+
REQUIRE(layer_names[0] == "duplicates");
139+
auto const layer = tile.getLayer("duplicates");
140+
REQUIRE(layer.featureCount() == 1);
141+
auto const feature = mapbox::vector_tile::feature(layer.getFeature(0), layer);
142+
REQUIRE(feature.getType() == mapbox::vector_tile::GeomType::POLYGON);
143+
144+
std::string error;
145+
auto const val = feature.getValue("hello", &error);
146+
REQUIRE(!error.empty());
147+
REQUIRE(error == "duplicate keys with different tag ids are found");
148+
REQUIRE(val.is<std::string>());
149+
REQUIRE(val.get<std::string>() == "world");
150+
error.clear();
151+
REQUIRE(error.empty());
152+
auto const val1 = feature.getValue("unique", &error);
153+
REQUIRE(error.empty());
154+
REQUIRE(val1.is<std::string>());
155+
REQUIRE(val1.get<std::string>() == "single_value");
156+
}

0 commit comments

Comments
 (0)