Skip to content

Commit e1e1bb6

Browse files
authored
Merge pull request #62 from mapbox/zmiao-duplicate-feature-keys
Loose failure check for cases when duplicate keys are mapping to different feature tags
2 parents bc2f7f4 + d9fbe9d commit e1e1bb6

3 files changed

Lines changed: 63 additions & 13 deletions

File tree

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)