From 9555ca91d91ef6c0ee85dbfab829beab621595b2 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 17 Oct 2022 12:50:43 -0700 Subject: [PATCH 01/41] add note in contributing.md --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 062756ca..8bf37a4a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,8 @@ npm install -g documentation npm run docs ``` +* Note for MacOS: if you're having issue building on MacOS, try commenting out [`make_global_settings` in binding.gyp](https://github.com/mapbox/vtcomposite/blob/main/binding.gyp#L5-L9) (for more info see [this](https://github.com/mapbox/node-cpp-skel/pull/169#issuecomment-1068127191)). + # Benchmarks Benchmarks can be run with the bench/bench.js script to test vtcomposite against common real-world fixtures (provided by mvt-fixtures) and to test vtcomposite against its predecessor compositing library node-mapnik. When making changes in a pull request, please provide the benchmarks from the master branch and the HEAD of your current branch. You can control the `concurrency`, `iterations`, and `package` of the benchmarks with the following command: @@ -96,4 +98,4 @@ These commands are set from within [the Makefile](./Makefile). # Releasing -In short, you'll need to push a commit with the log line containing `[publish binary]` to build the binary, followed by an `npm publish`. See [node-cpp-skel](https://github.com/mapbox/node-cpp-skel/blob/d2848ed5bcc5a798ff39a2ac139b70844043ff11/docs/publishing-binaries.md) for all the details. \ No newline at end of file +In short, you'll need to push a commit with the log line containing `[publish binary]` to build the binary, followed by an `npm publish`. See [node-cpp-skel](https://github.com/mapbox/node-cpp-skel/blob/d2848ed5bcc5a798ff39a2ac139b70844043ff11/docs/publishing-binaries.md) for all the details. From 05dc619937b9a3fef6ee4c3d493dcb786a06778c Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 17 Oct 2022 12:55:53 -0700 Subject: [PATCH 02/41] [revert me] comment out make_global_settings --- CONTRIBUTING.md | 2 +- binding.gyp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8bf37a4a..e44656d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ npm install -g documentation npm run docs ``` -* Note for MacOS: if you're having issue building on MacOS, try commenting out [`make_global_settings` in binding.gyp](https://github.com/mapbox/vtcomposite/blob/main/binding.gyp#L5-L9) (for more info see [this](https://github.com/mapbox/node-cpp-skel/pull/169#issuecomment-1068127191)). +* Note for MacOS: if you're having problem building on MacOS, try commenting out [`make_global_settings` in binding.gyp](https://github.com/mapbox/vtcomposite/blob/main/binding.gyp#L5-L9) (for more info see [this](https://github.com/mapbox/node-cpp-skel/pull/169#issuecomment-1068127191)). # Benchmarks diff --git a/binding.gyp b/binding.gyp index d85699c2..c5ea5f42 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,11 +2,11 @@ { # https://github.com/springmeyer/gyp/blob/master/test/make_global_settings/wrapper/wrapper.gyp 'make_global_settings': [ - ['CXX', '<(module_root_dir)/mason_packages/.link/bin/clang++'], - ['CC', '<(module_root_dir)/mason_packages/.link/bin/clang'], - ['LINK', '<(module_root_dir)/mason_packages/.link/bin/clang++'], - ['AR', '<(module_root_dir)/mason_packages/.link/bin/llvm-ar'], - ['NM', '<(module_root_dir)/mason_packages/.link/bin/llvm-nm'] + # ['CXX', '<(module_root_dir)/mason_packages/.link/bin/clang++'], + # ['CC', '<(module_root_dir)/mason_packages/.link/bin/clang'], + # ['LINK', '<(module_root_dir)/mason_packages/.link/bin/clang++'], + # ['AR', '<(module_root_dir)/mason_packages/.link/bin/llvm-ar'], + # ['NM', '<(module_root_dir)/mason_packages/.link/bin/llvm-nm'] ], 'includes': [ 'common.gypi'], # brings in a default set of options that are inherited from gyp 'variables': { # custom variables we use specific to this file From c23a5f9a4073cadc660d93d271141432ed2cea94 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 17 Oct 2022 13:55:28 -0700 Subject: [PATCH 03/41] make LocalizeBatonType members consistent --- src/vtcomposite.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 56d32129..982f7310 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -95,21 +95,25 @@ struct BatonType struct LocalizeBatonType { LocalizeBatonType(Napi::Buffer const& buffer, - std::string language_, + std::vector languages_, std::string language_property_, std::string language_prefix_, std::vector worldviews_, std::string worldview_property_, + std::string worldview_prefix_, std::string class_property_, + std::string class_prefix_, bool compress_) : data{buffer.Data(), buffer.Length()}, buffer_ref{Napi::Persistent(buffer)}, - language{std::move(language_)}, + languages{std::move(languages_)}, language_property{std::move(language_property_)}, language_prefix{std::move(language_prefix_)}, worldviews{std::move(worldviews_)}, worldview_property{std::move(worldview_property_)}, + worldview_prefix{std::move(worldview_prefix_)}, class_property{std::move(class_property_)}, + class_prefix{std::move(class_prefix_)}, compress{compress_} { } @@ -134,12 +138,14 @@ struct LocalizeBatonType // members vtzero::data_view data; Napi::Reference> buffer_ref; - std::string language; + std::vector languages; std::string language_property; std::string language_prefix; std::vector worldviews; std::string worldview_property; + std::string worldview_prefix; std::string class_property; + std::string class_prefix; bool compress; }; From 2d789e30b39c72395101d421053c2cc81840a0a2 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 17 Oct 2022 14:35:19 -0700 Subject: [PATCH 04/41] validate new LocalizeBatonType members in localize function --- src/vtcomposite.cpp | 114 +++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 38 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 982f7310..224157c0 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -871,16 +871,22 @@ Napi::Value localize(Napi::CallbackInfo const& info) } Napi::Function callback = callback_val.As(); - // validate params object - Napi::Value params_val = info[0]; + // mandatory params Napi::Buffer buffer; - std::string language; + + // default param values + std::vector languages; // default is undefined std::string language_property = "name"; std::string language_prefix = "_mbx_"; - std::vector worldviews; - std::string worldview_property = "_mbx_worldview"; - std::string class_property = "_mbx_class"; + std::vector worldviews; // default is undefined + std::string worldview_property = "worldview"; + std::string worldview_prefix = "_mbx_"; + std::string class_property = "class"; + std::string class_prefix = "_mbx_"; bool compress = false; + + // validate params object + Napi::Value params_val = info[0]; if (!params_val.IsObject()) { Napi::Error::New(info.Env(), "first argument must be an object").ThrowAsJavaScriptException(); @@ -898,7 +904,6 @@ Napi::Value localize(Napi::CallbackInfo const& info) { return utils::CallbackError("params.buffer must be a Buffer", info); } - Napi::Object buffer_obj = buffer_val.As(); if (!buffer_obj.IsBuffer()) { @@ -906,20 +911,29 @@ Napi::Value localize(Napi::CallbackInfo const& info) } buffer = buffer_obj.As>(); - // params.language (optional) - if (params.Has(Napi::String::New(info.Env(), "language"))) + // params.languages (optional) + if (params.Has(Napi::String::New(info.Env(), "languages"))) { - Napi::Value language_val = params.Get(Napi::String::New(info.Env(), "language")); - if (!language_val.IsString() && !language_val.IsNull() && !language_val.IsUndefined()) + Napi::Value language_val = params.Get(Napi::String::New(info.Env(), "languages")); + if (!language_val.IsArray() && !language_val.IsNull() && !language_val.IsUndefined()) { - return utils::CallbackError("params.language must be null or a string", info); + return utils::CallbackError("params.languages must be an array or null", info); } - if (language_val.IsString()) - { - language = language_val.As(); - if (language.length() == 0) + + if (language_val.IsArray()) { + Napi::Array language_array = language_val.As(); + std::uint32_t num_languages = language_array.Length(); + languages.reserve(num_languages); + + for (std::uint32_t lg = 0; lg < num_languages; ++lg) { - return utils::CallbackError("params.language cannot be an empty string", info); + Napi::Value language_item_val = language_array.Get(lg); + if (!language_item_val.IsString()) + { + return utils::CallbackError("params.languages must be an array of strings", info); + } + std::string language_item = language_item_val.As(); + languages.push_back(language_item); } } } @@ -946,34 +960,34 @@ Napi::Value localize(Napi::CallbackInfo const& info) language_prefix = language_prefix_val.As(); } - // params.worldview + // params.worldviews (optional) if (params.Has(Napi::String::New(info.Env(), "worldviews"))) { Napi::Value worldview_val = params.Get(Napi::String::New(info.Env(), "worldviews")); - if (!worldview_val.IsArray()) + if (!worldview_val.IsArray() && !worldview_val.IsNull() && !worldview_val.IsUndefined()) { - return utils::CallbackError("params.worldview must be an array", info); + return utils::CallbackError("params.worldviews must be an array or null", info); } - Napi::Array worldview_array = worldview_val.As(); - std::uint32_t num_worldviews = worldview_array.Length(); - worldviews.reserve(num_worldviews); - for (std::uint32_t wv = 0; wv < num_worldviews; ++wv) - { - Napi::Value worldview_item_val = worldview_array.Get(wv); - if (!worldview_item_val.IsString()) - { - return utils::CallbackError("params.worldview must be an array of strings", info); - } - std::string worldview_item = worldview_item_val.As(); - if (worldview_item.length() != 2) + + if (worldview_val.IsArray()) { + Napi::Array worldview_array = worldview_val.As(); + std::uint32_t num_worldviews = worldview_array.Length(); + worldviews.reserve(num_worldviews); + + for (std::uint32_t wv = 0; wv < num_worldviews; ++wv) { - return utils::CallbackError("params.worldview items must be strings of 2 characters", info); + Napi::Value worldview_item_val = worldview_array.Get(wv); + if (!worldview_item_val.IsString()) + { + return utils::CallbackError("params.worldviews must be an array of strings", info); + } + std::string worldview_item = worldview_item_val.As(); + worldviews.push_back(worldview_item); } - worldviews.push_back(worldview_item); } } - // params.worldview_property + // params.worldview_property (optional) if (params.Has(Napi::String::New(info.Env(), "worldview_property"))) { Napi::Value worldview_property_val = params.Get(Napi::String::New(info.Env(), "worldview_property")); @@ -984,7 +998,18 @@ Napi::Value localize(Napi::CallbackInfo const& info) worldview_property = worldview_property_val.As(); } - // params.class_property + // params.worldview_prefix (optional) + if (params.Has(Napi::String::New(info.Env(), "worldview_prefix"))) + { + Napi::Value worldview_prefix_val = params.Get(Napi::String::New(info.Env(), "worldview_prefix")); + if (!worldview_prefix_val.IsString()) + { + return utils::CallbackError("params.worldview_prefix must be a string", info); + } + worldview_prefix = worldview_prefix_val.As(); + } + + // params.class_property (optional) if (params.Has(Napi::String::New(info.Env(), "class_property"))) { Napi::Value class_property_val = params.Get(Napi::String::New(info.Env(), "class_property")); @@ -995,7 +1020,18 @@ Napi::Value localize(Napi::CallbackInfo const& info) class_property = class_property_val.As(); } - // params.compress + // params.class_prefix (optional) + if (params.Has(Napi::String::New(info.Env(), "class_prefix"))) + { + Napi::Value class_prefix_val = params.Get(Napi::String::New(info.Env(), "class_prefix")); + if (!class_prefix_val.IsString()) + { + return utils::CallbackError("params.class_prefix must be a string", info); + } + class_prefix = class_prefix_val.As(); + } + + // params.compress (optional) if (params.Has(Napi::String::New(info.Env(), "compress"))) { Napi::Value comp_value = params.Get(Napi::String::New(info.Env(), "compress")); @@ -1008,12 +1044,14 @@ Napi::Value localize(Napi::CallbackInfo const& info) std::unique_ptr baton_data = std::make_unique( buffer, - language, + languages, language_property, language_prefix, worldviews, worldview_property, + worldview_prefix, class_property, + class_prefix, compress); auto* worker = new LocalizeWorker{std::move(baton_data), callback}; From 06725fabb1f6b269f92424fbe486106f646199a6 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 17 Oct 2022 14:52:44 -0700 Subject: [PATCH 05/41] add return_localized_tile to LocalizeBatonType --- src/vtcomposite.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 224157c0..cb821d21 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -146,6 +146,7 @@ struct LocalizeBatonType std::string worldview_prefix; std::string class_property; std::string class_prefix; + bool return_localized_tile = (languages.isArray() || worldviews.isArray()); bool compress; }; From 617d4d57a140dca550c8ae805ee0fbdf444a3ced Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 18 Oct 2022 22:22:04 -0700 Subject: [PATCH 06/41] retain only those features with compatible worldview and adjust their class --- src/vtcomposite.cpp | 160 ++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 86 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index cb821d21..0ef72ced 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -609,56 +609,19 @@ struct LocalizeWorker : Napi::AsyncWorker baton_data_{std::move(baton_data)}, output_buffer_{std::make_unique()} {} - // for a given feature, determine how many clones of the feature are - // required given a worldview. - // - If the feature has _mbx_worldview: all, return {"all"} - // - If there is no requested worldview "null", determine which legacy worldviews should be created - std::vector worldviews_for_feature(std::string const& pval) const - { - if (pval == "all") - { - return {"all"}; - } - - std::vector matching_worldviews; - // convert pval into vector of strings US,CN => {"US", "CN"} - std::vector worldview_values = utils::split(pval); - // worldview: XX - if (!baton_data_->worldviews.empty()) - { - utils::intersection(worldview_values, baton_data_->worldviews, matching_worldviews); - } - - return matching_worldviews; - } - - // create a feature from a list of properties - // optionally define a worldview property if provided - static void build_localized_feature( - vtzero::feature const& feature, - std::vector> const& properties, - std::string const& worldview, - vtzero::layer_builder& lbuilder) + // create a feature with new properties from a template feature + static void create_new_feature( + vtzero::feature const& template_feature, + std::vector> const& properties, + vtzero::layer_builder& lbuilder) { vtzero::geometry_feature_builder fbuilder{lbuilder}; - fbuilder.copy_id(feature); // todo deduplicate this (vector tile spec says SHOULD be unique) - fbuilder.set_geometry(feature.geometry()); - - if (!worldview.empty()) - { - fbuilder.add_property("worldview", worldview); - } - for (auto const& property : properties) - { - // no-op on any property called "worldview" if worldviews have been - // created dynamically from _mbx_wordlview - if (!worldview.empty() && property.first == "worldview") - { - continue; - } + fbuilder.copy_id(template_feature); // TODO: deduplicate this (vector tile spec says SHOULD be unique) + fbuilder.set_geometry(template_feature.geometry()); - // add property to feature - fbuilder.add_property(property.first, property.second); + // add property to feature + for (auto const& property : properties) { + fbuilder.add_property(property.first, property.second); } fbuilder.commit(); @@ -668,6 +631,19 @@ struct LocalizeWorker : Napi::AsyncWorker { try { + std::string incompatible_worldview_key; + std::string compatible_worldview_key; + std::string replacement_class_key; + if (baton_data_->return_localized_tile) { + incompatible_worldview_key = baton_data_->worldview_property; + compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; + replacement_class_key = baton_data_->class_prefix + baton_data_->class_property; + } else { + incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; + compatible_worldview_key = baton_data_->worldview_property; + replacement_class_key = baton_data_->class_property; + } + vtzero::tile_builder tbuilder; std::string language_key; std::string language_key_prefixed; @@ -699,33 +675,57 @@ struct LocalizeWorker : Napi::AsyncWorker vtzero::layer_builder lbuilder{tbuilder, layer.name(), layer.version(), layer.extent()}; while (auto feature = layer.next_feature()) { - std::vector worldviews_to_create; - bool has_worldview_key = false; + bool skip_feature = false; bool name_was_set = false; vtzero::property_value name_value; - vtzero::property_value class_value; - // accumulate final properties (except _mbx_worldview translation to worldview) here + + // collect final properties std::vector> properties; while (auto property = feature.next_property()) { + // if already know we'll be skipping this feature, don't need to comb through its properties + if (skip_feature) continue; + std::string property_key = property.key().to_string(); - if (!has_worldview_key && property_key == baton_data_->worldview_property) - { - has_worldview_key = true; - if (property.value().type() == vtzero::property_value_type::string_value) - { - worldviews_to_create = worldviews_for_feature(static_cast(property.value().string_value())); - } - continue; + // skip feature only if the value of incompatible worldview key is not 'all' + if (property_key == incompatible_worldview_key) { + if (property.value().type() == vtzero::property_value_type::string_value) { + if (property.value() == "all") { + // do nothing – keep this feature but don't need to preserve this property + continue; + } else { + skip_feature = true; + continue; + } + } else { + skip_feature = true; + continue; + } } - if (property_key == baton_data_->class_property) - { - if (property.value().type() == vtzero::property_value_type::string_value) - { - class_value = property.value(); + // keep feature and collect its compatible worldview value if it is in the selected worldview or 'all' worldview; skip otherwise + else if (property_key == compatible_worldview_key) { + if (property.value().type() == vtzero::property_value_type::string_value) { + if (property.value() == "all") { + properties.emplace_back(baton_data_->worldview_property, property.value()); + continue; + } else if (property.value().contains(baton_data_->worldviews[0])) { + properties.emplace_back(baton_data_->worldview_property, baton_data_->worldviews[0]); + continue; + } else { + skip_feature = true; + continue; + } + } else { + skip_feature = true; + continue; } + } + + // collect class value + else if (property_key == replacement_class_key) { + properties.emplace_back(baton_data_->class_property, property.value()); continue; } @@ -763,7 +763,11 @@ struct LocalizeWorker : Napi::AsyncWorker } properties.emplace_back(property_key, property.value()); - } + + } // end of properties loop + + // if skip feature, proceed to next feature + if (skip_feature) continue; if (name_value.valid()) { @@ -776,27 +780,11 @@ struct LocalizeWorker : Napi::AsyncWorker } } - if (has_worldview_key) - { - if (class_value.valid()) - { - properties.emplace_back("class", class_value); - } - for (auto const& wv : worldviews_to_create) - { - build_localized_feature(feature, properties, wv, lbuilder); - } - } - else - { - if (class_value.valid() && name_was_set) - { - properties.emplace_back("class", class_value); - } - build_localized_feature(feature, properties, "", lbuilder); - } - } - } + create_new_feature(feature, properties, lbuilder); + + } // end of features loop + } // end of layers loop + std::string& tile_buffer = *output_buffer_; if (baton_data_->compress) { From e61a6241530a7b36b5f90c388a1ab436ef01d586 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 18 Oct 2022 22:31:42 -0700 Subject: [PATCH 07/41] be more explicity with class --- src/vtcomposite.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 0ef72ced..c46fdf50 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -632,16 +632,19 @@ struct LocalizeWorker : Napi::AsyncWorker try { std::string incompatible_worldview_key; + std::string incompatible_class_key; std::string compatible_worldview_key; - std::string replacement_class_key; + std::string compatible_class_key; if (baton_data_->return_localized_tile) { incompatible_worldview_key = baton_data_->worldview_property; + incompatible_class_key = baton_data_->class_property; compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; - replacement_class_key = baton_data_->class_prefix + baton_data_->class_property; + compatible_class_key = baton_data_->class_prefix + baton_data_->class_property; } else { incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; + incompatible_class_key = baton_data_->class_prefix + baton_data_->class_property; compatible_worldview_key = baton_data_->worldview_property; - replacement_class_key = baton_data_->class_property; + compatible_class_key = baton_data_->class_property; } vtzero::tile_builder tbuilder; @@ -704,7 +707,9 @@ struct LocalizeWorker : Napi::AsyncWorker } } - // keep feature and collect its compatible worldview value if it is in the selected worldview or 'all' worldview; skip otherwise + // keep feature and retain its compatible worldview value + // if it is in the selected worldview or 'all' worldview; + // skip otherwise else if (property_key == compatible_worldview_key) { if (property.value().type() == vtzero::property_value_type::string_value) { if (property.value() == "all") { @@ -723,8 +728,14 @@ struct LocalizeWorker : Napi::AsyncWorker } } + // drop incompatible class property + else if (property_key == incompatible_class_key) { + // do nothing – keep this feature but don't need to retain this property + continue; + } + // collect class value - else if (property_key == replacement_class_key) { + else if (property_key == compatible_class_key) { properties.emplace_back(baton_data_->class_property, property.value()); continue; } From 2e89c7e7d7ad6191dbb5ec30ac94f6ef06bf6fd5 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 18 Oct 2022 22:34:22 -0700 Subject: [PATCH 08/41] easier to read --- src/vtcomposite.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index c46fdf50..93ad2482 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -632,18 +632,18 @@ struct LocalizeWorker : Napi::AsyncWorker try { std::string incompatible_worldview_key; - std::string incompatible_class_key; std::string compatible_worldview_key; + std::string incompatible_class_key; std::string compatible_class_key; if (baton_data_->return_localized_tile) { incompatible_worldview_key = baton_data_->worldview_property; - incompatible_class_key = baton_data_->class_property; compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; + incompatible_class_key = baton_data_->class_property; compatible_class_key = baton_data_->class_prefix + baton_data_->class_property; } else { incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; - incompatible_class_key = baton_data_->class_prefix + baton_data_->class_property; compatible_worldview_key = baton_data_->worldview_property; + incompatible_class_key = baton_data_->class_prefix + baton_data_->class_property; compatible_class_key = baton_data_->class_property; } From 61e591e306dc91d1ee8a5afe9f177ed87b5ddcce Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 18 Oct 2022 22:39:47 -0700 Subject: [PATCH 09/41] consistent bracket format --- src/vtcomposite.cpp | 50 +++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 93ad2482..c80f3f9a 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -620,7 +620,8 @@ struct LocalizeWorker : Napi::AsyncWorker fbuilder.set_geometry(template_feature.geometry()); // add property to feature - for (auto const& property : properties) { + for (auto const& property : properties) + { fbuilder.add_property(property.first, property.second); } @@ -635,7 +636,8 @@ struct LocalizeWorker : Napi::AsyncWorker std::string compatible_worldview_key; std::string incompatible_class_key; std::string compatible_class_key; - if (baton_data_->return_localized_tile) { + if (baton_data_->return_localized_tile) + { incompatible_worldview_key = baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; incompatible_class_key = baton_data_->class_property; @@ -692,16 +694,23 @@ struct LocalizeWorker : Napi::AsyncWorker std::string property_key = property.key().to_string(); // skip feature only if the value of incompatible worldview key is not 'all' - if (property_key == incompatible_worldview_key) { - if (property.value().type() == vtzero::property_value_type::string_value) { - if (property.value() == "all") { + if (property_key == incompatible_worldview_key) + { + if (property.value().type() == vtzero::property_value_type::string_value) + { + if (property.value() == "all") + { // do nothing – keep this feature but don't need to preserve this property continue; - } else { + } + else + { skip_feature = true; continue; } - } else { + } + else + { skip_feature = true; continue; } @@ -710,32 +719,43 @@ struct LocalizeWorker : Napi::AsyncWorker // keep feature and retain its compatible worldview value // if it is in the selected worldview or 'all' worldview; // skip otherwise - else if (property_key == compatible_worldview_key) { - if (property.value().type() == vtzero::property_value_type::string_value) { - if (property.value() == "all") { + else if (property_key == compatible_worldview_key) + { + if (property.value().type() == vtzero::property_value_type::string_value) + { + if (property.value() == "all") + { properties.emplace_back(baton_data_->worldview_property, property.value()); continue; - } else if (property.value().contains(baton_data_->worldviews[0])) { + } + else if (property.value().contains(baton_data_->worldviews[0])) + { properties.emplace_back(baton_data_->worldview_property, baton_data_->worldviews[0]); continue; - } else { + } + else + { skip_feature = true; continue; } - } else { + } + else + { skip_feature = true; continue; } } // drop incompatible class property - else if (property_key == incompatible_class_key) { + else if (property_key == incompatible_class_key) + { // do nothing – keep this feature but don't need to retain this property continue; } // collect class value - else if (property_key == compatible_class_key) { + else if (property_key == compatible_class_key) + { properties.emplace_back(baton_data_->class_property, property.value()); continue; } From c70ddb8464b2878e5028d7576aa72a85041bf863 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 18 Oct 2022 23:23:56 -0700 Subject: [PATCH 10/41] add string startswith helper function --- src/module_utils.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/module_utils.hpp b/src/module_utils.hpp index 658e3fb6..bb1f86e2 100644 --- a/src/module_utils.hpp +++ b/src/module_utils.hpp @@ -29,6 +29,12 @@ inline std::vector split(std::string const& input) return values; } +// checks if a string starts with a given substring +inline bool startswith(std::string const& s, std::string const& ss) { + return ss.length() <= s.length() + && std::equal(ss.begin(), ss.end(), s.begin()); +} + // finds the intersection of two vectors of strings // and assigns the intersection to a new vector passed by reference // results are returned in alphabetically ascending order @@ -44,4 +50,4 @@ void inline intersection( v2.begin(), v2.end(), std::back_inserter(result)); } -} // namespace utils \ No newline at end of file +} // namespace utils From 597d561984c25a5919e6e93e29d6ec60a3c6f51e Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 18 Oct 2022 23:56:14 -0700 Subject: [PATCH 11/41] not all layers have both {class_prefix}{class_property} --- src/vtcomposite.cpp | 64 +++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index c80f3f9a..f63462a8 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -634,19 +634,19 @@ struct LocalizeWorker : Napi::AsyncWorker { std::string incompatible_worldview_key; std::string compatible_worldview_key; - std::string incompatible_class_key; - std::string compatible_class_key; + std::vector class_key_precedence; if (baton_data_->return_localized_tile) { incompatible_worldview_key = baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; - incompatible_class_key = baton_data_->class_property; - compatible_class_key = baton_data_->class_prefix + baton_data_->class_property; + + class_key_precedence.push_back(baton_data_->class_prefix + baton_data->class_property) + class_key_precedence.push_back(baton_data->class_property); } else { incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; - incompatible_class_key = baton_data_->class_prefix + baton_data_->class_property; - compatible_class_key = baton_data_->class_property; + + class_key_precedence.push_back(baton_data->class_property); } vtzero::tile_builder tbuilder; @@ -680,12 +680,18 @@ struct LocalizeWorker : Napi::AsyncWorker vtzero::layer_builder lbuilder{tbuilder, layer.name(), layer.version(), layer.extent()}; while (auto feature = layer.next_feature()) { + // will be skipping features with incompatible worldview bool skip_feature = false; + + // will be searching for the class with lowest index in class_key_precedence + std::uint32_t class_key_idx = class_key_precedence.size(); + vtzero::property_value class_value; + bool name_was_set = false; vtzero::property_value name_value; // collect final properties - std::vector> properties; + std::vector> final_properties; while (auto property = feature.next_property()) { // if already know we'll be skipping this feature, don't need to comb through its properties @@ -725,12 +731,12 @@ struct LocalizeWorker : Napi::AsyncWorker { if (property.value() == "all") { - properties.emplace_back(baton_data_->worldview_property, property.value()); + final_properties.emplace_back(baton_data_->worldview_property, property.value()); continue; } else if (property.value().contains(baton_data_->worldviews[0])) { - properties.emplace_back(baton_data_->worldview_property, baton_data_->worldviews[0]); + final_properties.emplace_back(baton_data_->worldview_property, baton_data_->worldviews[0]); continue; } else @@ -746,17 +752,20 @@ struct LocalizeWorker : Napi::AsyncWorker } } - // drop incompatible class property - else if (property_key == incompatible_class_key) - { - // do nothing – keep this feature but don't need to retain this property - continue; - } - - // collect class value - else if (property_key == compatible_class_key) + else if + ( + utils::startswith(property_key, baton_data_->class_property) || + utils::startswith(property_key, baton_data_->class_prefix + baton_data_->class_property) + ) { - properties.emplace_back(baton_data_->class_property, property.value()); + // check if the property is of higher precedence that class key encountered so far + std::uint32_t idx = std::find(class_key_precedence.begin(), class_key_precedence.end(), property_key) + if (idx < class_key_idx) + { + class_key_idx = idx; + class_value = property.value(); + } + // wait till we are done looping through all properties to add class value to final_properties continue; } @@ -768,7 +777,7 @@ struct LocalizeWorker : Napi::AsyncWorker // if no language was specified, we want the name value to be constant if (baton_data_->language.empty()) { - properties.emplace_back(baton_data_->language_property, property.value()); + final_properties.emplace_back(baton_data_->language_property, property.value()); name_was_set = true; } continue; @@ -776,7 +785,7 @@ struct LocalizeWorker : Napi::AsyncWorker // set name to value from {prefix}{language_property}_{language}, if existing if (!baton_data_->language.empty() && !name_was_set && language_key_prefixed == property_key) { - properties.emplace_back(baton_data_->language_property, property.value()); + final_properties.emplace_back(baton_data_->language_property, property.value()); name_was_set = true; continue; } @@ -789,7 +798,7 @@ struct LocalizeWorker : Napi::AsyncWorker // and keep these legacy properties on the feature if (!baton_data_->language.empty() && !name_was_set && language_key == property_key) { - properties.emplace_back(baton_data_->language_property, property.value()); + final_properties.emplace_back(baton_data_->language_property, property.value()); name_was_set = true; } @@ -800,18 +809,23 @@ struct LocalizeWorker : Napi::AsyncWorker // if skip feature, proceed to next feature if (skip_feature) continue; + // use the class value of highest precedence + if (class_value.valid()) { + final_properties.emplace_back(baton_data->class_property, class_value); + } + if (name_value.valid()) { std::string preserved_key = baton_data_->language_property + "_local"; - properties.emplace_back(preserved_key, name_value); + final_properties.emplace_back(preserved_key, name_value); if (!name_was_set) { - properties.emplace_back(baton_data_->language_property, name_value); + final_properties.emplace_back(baton_data_->language_property, name_value); } } - create_new_feature(feature, properties, lbuilder); + create_new_feature(feature, final_properties, lbuilder); } // end of features loop } // end of layers loop From fe595bd58fd65da9942e5eab2d28c24781e57f05 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 19 Oct 2022 00:08:52 -0700 Subject: [PATCH 12/41] non-localized tile need to keep features in every worldview --- src/vtcomposite.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index f63462a8..da8f8852 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -632,17 +632,20 @@ struct LocalizeWorker : Napi::AsyncWorker { try { + bool keep_every_worldview; std::string incompatible_worldview_key; std::string compatible_worldview_key; std::vector class_key_precedence; if (baton_data_->return_localized_tile) { + keep_every_worldview = false; incompatible_worldview_key = baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; class_key_precedence.push_back(baton_data_->class_prefix + baton_data->class_property) class_key_precedence.push_back(baton_data->class_property); } else { + keep_every_worldview = true; incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; @@ -706,7 +709,7 @@ struct LocalizeWorker : Napi::AsyncWorker { if (property.value() == "all") { - // do nothing – keep this feature but don't need to preserve this property + // do nothing - keep this feature but don't need to preserve this property continue; } else @@ -723,26 +726,28 @@ struct LocalizeWorker : Napi::AsyncWorker } // keep feature and retain its compatible worldview value - // if it is in the selected worldview or 'all' worldview; - // skip otherwise else if (property_key == compatible_worldview_key) { if (property.value().type() == vtzero::property_value_type::string_value) { - if (property.value() == "all") + if (keep_every_worldview) { final_properties.emplace_back(baton_data_->worldview_property, property.value()); continue; } - else if (property.value().contains(baton_data_->worldviews[0])) - { - final_properties.emplace_back(baton_data_->worldview_property, baton_data_->worldviews[0]); - continue; - } else { - skip_feature = true; - continue; + // keep only the feature in selected worldview or in 'all' worldview + if (property.value() == "all" || property.value().contains(baton_data_->worldviews[0])) + { + final_properties.emplace_back(baton_data_->worldview_property, property.value()); + continue; + } + else + { + skip_feature = true; + continue; + } } } else From 8eb553ee8ef2a52fcb4e1151b1a4ba461fd25d15 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 19 Oct 2022 00:46:28 -0700 Subject: [PATCH 13/41] keep only relevant names --- src/vtcomposite.cpp | 99 ++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index da8f8852..c148dca4 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -636,6 +636,8 @@ struct LocalizeWorker : Napi::AsyncWorker std::string incompatible_worldview_key; std::string compatible_worldview_key; std::vector class_key_precedence; + bool keep_every_name; + std::vector name_key_precedence; if (baton_data_->return_localized_tile) { keep_every_worldview = false; @@ -644,12 +646,24 @@ struct LocalizeWorker : Napi::AsyncWorker class_key_precedence.push_back(baton_data_->class_prefix + baton_data->class_property) class_key_precedence.push_back(baton_data->class_property); + + keep_every_name = false; + for (auto const& lang : baton_data_->languages) + { + name_key_precedence.push_back(baton_data_->language_property + "_" + lang); + name_key_precedence.push_back(baton_data_->language_prefix + baton_data_->language_property + "_" + lang); + } + name_key_precedence.push_back(baton_data_->language) + } else { keep_every_worldview = true; incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; class_key_precedence.push_back(baton_data->class_property); + + keep_every_name = true; + name_key_precedence.push_back(baton_data_->language) } vtzero::tile_builder tbuilder; @@ -690,8 +704,9 @@ struct LocalizeWorker : Napi::AsyncWorker std::uint32_t class_key_idx = class_key_precedence.size(); vtzero::property_value class_value; - bool name_was_set = false; + std::uint32_t name_key_idx = name_key_precedence.size(); vtzero::property_value name_value; + vtzero::property_value original_name_value; // collect final properties std::vector> final_properties; @@ -774,40 +789,54 @@ struct LocalizeWorker : Napi::AsyncWorker continue; } - // preserve original params.language_property value - if (property_key == baton_data_->language_property) + else if + ( + utils::startswith(property_key, baton_data_->language_property) || + utils::startswith(property_key, baton_data_->language_prefix + baton_data_->language_property) + ) { - name_value = property.value(); + // check if the property is of higher precedence that class key encountered so far + std::uint32_t idx = std::find(name_key_precedence.begin(), name_key_precedence.end(), property_key) + if (idx < name_key_idx) + { + name_key_idx = idx; + name_value = property.value(); + } - // if no language was specified, we want the name value to be constant - if (baton_data_->language.empty()) + // preserve original name value and wait till finish looping through all properties to assign a value + if (property_key == baton_data_->language_property) { - final_properties.emplace_back(baton_data_->language_property, property.value()); - name_was_set = true; + original_name_value = property.value(); + continue; + } + else + { + if (keep_every_name) + { + if (utils::startswith(property_key, baton_data_->language_prefix)) + { + // drop properties that start with a prefix + continue; + } + else + { + final_properties.emplace(property_key, property.value()); + continue; + } + } + else + { + // wait till we are done looping through all properties to add class value to final_properties + continue; + } } - continue; - } - // set name to value from {prefix}{language_property}_{language}, if existing - if (!baton_data_->language.empty() && !name_was_set && language_key_prefixed == property_key) - { - final_properties.emplace_back(baton_data_->language_property, property.value()); - name_was_set = true; - continue; } - // remove any properties that starts with the given params.language_prefix value - if (property_key.find(baton_data_->language_prefix) == 0) // NOLINT(abseil-string-find-startswith) - { + + // all other properties + else { + properties.emplace_back(property_key, property.value()); continue; } - // set name to {language_property}_{language}, if existing - // and keep these legacy properties on the feature - if (!baton_data_->language.empty() && !name_was_set && language_key == property_key) - { - final_properties.emplace_back(baton_data_->language_property, property.value()); - name_was_set = true; - } - - properties.emplace_back(property_key, property.value()); } // end of properties loop @@ -819,15 +848,13 @@ struct LocalizeWorker : Napi::AsyncWorker final_properties.emplace_back(baton_data->class_property, class_value); } - if (name_value.valid()) - { - std::string preserved_key = baton_data_->language_property + "_local"; - final_properties.emplace_back(preserved_key, name_value); + // use the name value of highest precedence + if (name_value.valid()) { + final_properties.emplace_back(baton_data->language_property, name_value); + } - if (!name_was_set) - { - final_properties.emplace_back(baton_data_->language_property, name_value); - } + if (return_localized_tile) { + final_properties.emplace_back(baton_data_->language_property + "_local", original_name_value); } create_new_feature(feature, final_properties, lbuilder); From fe48ec0aad339c1fd3f29d7e50f21471994be539 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 19 Oct 2022 00:58:11 -0700 Subject: [PATCH 14/41] do not interchange name and language --- src/vtcomposite.cpp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index c148dca4..f3bc5ec4 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -636,8 +636,8 @@ struct LocalizeWorker : Napi::AsyncWorker std::string incompatible_worldview_key; std::string compatible_worldview_key; std::vector class_key_precedence; - bool keep_every_name; - std::vector name_key_precedence; + bool keep_every_language; + std::vector language_key_precedence; if (baton_data_->return_localized_tile) { keep_every_worldview = false; @@ -647,13 +647,13 @@ struct LocalizeWorker : Napi::AsyncWorker class_key_precedence.push_back(baton_data_->class_prefix + baton_data->class_property) class_key_precedence.push_back(baton_data->class_property); - keep_every_name = false; + keep_every_language = false; for (auto const& lang : baton_data_->languages) { - name_key_precedence.push_back(baton_data_->language_property + "_" + lang); - name_key_precedence.push_back(baton_data_->language_prefix + baton_data_->language_property + "_" + lang); + language_key_precedence.push_back(baton_data_->language_property + "_" + lang); + language_key_precedence.push_back(baton_data_->language_prefix + baton_data_->language_property + "_" + lang); } - name_key_precedence.push_back(baton_data_->language) + language_key_precedence.push_back(baton_data_->language) } else { keep_every_worldview = true; @@ -662,8 +662,8 @@ struct LocalizeWorker : Napi::AsyncWorker class_key_precedence.push_back(baton_data->class_property); - keep_every_name = true; - name_key_precedence.push_back(baton_data_->language) + keep_every_language = true; + language_key_precedence.push_back(baton_data_->language) } vtzero::tile_builder tbuilder; @@ -704,9 +704,9 @@ struct LocalizeWorker : Napi::AsyncWorker std::uint32_t class_key_idx = class_key_precedence.size(); vtzero::property_value class_value; - std::uint32_t name_key_idx = name_key_precedence.size(); - vtzero::property_value name_value; - vtzero::property_value original_name_value; + std::uint32_t language_key_idx = language_key_precedence.size(); + vtzero::property_value language_value; + vtzero::property_value original_language_value; // collect final properties std::vector> final_properties; @@ -796,22 +796,22 @@ struct LocalizeWorker : Napi::AsyncWorker ) { // check if the property is of higher precedence that class key encountered so far - std::uint32_t idx = std::find(name_key_precedence.begin(), name_key_precedence.end(), property_key) - if (idx < name_key_idx) + std::uint32_t idx = std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key) + if (idx < language_key_idx) { - name_key_idx = idx; - name_value = property.value(); + language_key_idx = idx; + language_value = property.value(); } - // preserve original name value and wait till finish looping through all properties to assign a value + // preserve original language value and wait till finish looping through all properties to assign a value if (property_key == baton_data_->language_property) { - original_name_value = property.value(); + original_language_value = property.value(); continue; } else { - if (keep_every_name) + if (keep_every_language) { if (utils::startswith(property_key, baton_data_->language_prefix)) { @@ -848,13 +848,13 @@ struct LocalizeWorker : Napi::AsyncWorker final_properties.emplace_back(baton_data->class_property, class_value); } - // use the name value of highest precedence - if (name_value.valid()) { - final_properties.emplace_back(baton_data->language_property, name_value); + // use the language value of highest precedence + if (language_value.valid()) { + final_properties.emplace_back(baton_data->language_property, language_value); } if (return_localized_tile) { - final_properties.emplace_back(baton_data_->language_property + "_local", original_name_value); + final_properties.emplace_back(baton_data_->language_property + "_local", original_language_value); } create_new_feature(feature, final_properties, lbuilder); From 0f282954fcc77ccb447a13a37ad01c84c8e63358 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 19 Oct 2022 01:33:49 -0700 Subject: [PATCH 15/41] fix typos and syntax errors --- src/vtcomposite.cpp | 51 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index f3bc5ec4..f39e22e4 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -103,6 +103,7 @@ struct LocalizeBatonType std::string worldview_prefix_, std::string class_property_, std::string class_prefix_, + bool return_localized_tile_, bool compress_) : data{buffer.Data(), buffer.Length()}, buffer_ref{Napi::Persistent(buffer)}, @@ -114,6 +115,7 @@ struct LocalizeBatonType worldview_prefix{std::move(worldview_prefix_)}, class_property{std::move(class_property_)}, class_prefix{std::move(class_prefix_)}, + return_localized_tile{std::move(return_localized_tile_)}, compress{compress_} { } @@ -146,7 +148,7 @@ struct LocalizeBatonType std::string worldview_prefix; std::string class_property; std::string class_prefix; - bool return_localized_tile = (languages.isArray() || worldviews.isArray()); + bool return_localized_tile; bool compress; }; @@ -644,8 +646,8 @@ struct LocalizeWorker : Napi::AsyncWorker incompatible_worldview_key = baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; - class_key_precedence.push_back(baton_data_->class_prefix + baton_data->class_property) - class_key_precedence.push_back(baton_data->class_property); + class_key_precedence.push_back(baton_data_->class_prefix + baton_data_->class_property); + class_key_precedence.push_back(baton_data_->class_property); keep_every_language = false; for (auto const& lang : baton_data_->languages) @@ -653,29 +655,20 @@ struct LocalizeWorker : Napi::AsyncWorker language_key_precedence.push_back(baton_data_->language_property + "_" + lang); language_key_precedence.push_back(baton_data_->language_prefix + baton_data_->language_property + "_" + lang); } - language_key_precedence.push_back(baton_data_->language) + language_key_precedence.push_back(baton_data_->language_property); } else { keep_every_worldview = true; incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; - class_key_precedence.push_back(baton_data->class_property); + class_key_precedence.push_back(baton_data_->class_property); keep_every_language = true; - language_key_precedence.push_back(baton_data_->language) + language_key_precedence.push_back(baton_data_->language_property); } vtzero::tile_builder tbuilder; - std::string language_key; - std::string language_key_prefixed; - if (!baton_data_->language.empty()) - { - // {language_property}_{language} -> retain - language_key = baton_data_->language_property + "_" + baton_data_->language; - // {language_prefix}{language_property}_{language} -> drop - language_key_prefixed = baton_data_->language_prefix + language_key; - } std::vector buffer_cache; vtzero::data_view tile_view{}; if (gzip::is_compressed(baton_data_->data.data(), baton_data_->data.size())) @@ -701,10 +694,10 @@ struct LocalizeWorker : Napi::AsyncWorker bool skip_feature = false; // will be searching for the class with lowest index in class_key_precedence - std::uint32_t class_key_idx = class_key_precedence.size(); + std::uint32_t class_key_idx = static_cast(class_key_precedence.size()); vtzero::property_value class_value; - std::uint32_t language_key_idx = language_key_precedence.size(); + std::uint32_t language_key_idx = static_cast(language_key_precedence.size()); vtzero::property_value language_value; vtzero::property_value original_language_value; @@ -722,7 +715,7 @@ struct LocalizeWorker : Napi::AsyncWorker { if (property.value().type() == vtzero::property_value_type::string_value) { - if (property.value() == "all") + if (property.value().string_value() == "all") { // do nothing - keep this feature but don't need to preserve this property continue; @@ -753,7 +746,9 @@ struct LocalizeWorker : Napi::AsyncWorker else { // keep only the feature in selected worldview or in 'all' worldview - if (property.value() == "all" || property.value().contains(baton_data_->worldviews[0])) + std::string const& property_value = static_cast(property.value().string_value()); + std::vector property_value_list = utils::split(property_value); + if (property_value == "all" || std::find(property_value_list.begin(), property_value_list.end(), baton_data_->worldviews[0]) != property_value_list.end()) { final_properties.emplace_back(baton_data_->worldview_property, property.value()); continue; @@ -779,7 +774,7 @@ struct LocalizeWorker : Napi::AsyncWorker ) { // check if the property is of higher precedence that class key encountered so far - std::uint32_t idx = std::find(class_key_precedence.begin(), class_key_precedence.end(), property_key) + std::uint32_t idx = static_cast(std::find(class_key_precedence.begin(), class_key_precedence.end(), property_key) - class_key_precedence.begin()); if (idx < class_key_idx) { class_key_idx = idx; @@ -796,7 +791,7 @@ struct LocalizeWorker : Napi::AsyncWorker ) { // check if the property is of higher precedence that class key encountered so far - std::uint32_t idx = std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key) + std::uint32_t idx = static_cast(std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key) - language_key_precedence.begin()); if (idx < language_key_idx) { language_key_idx = idx; @@ -820,7 +815,7 @@ struct LocalizeWorker : Napi::AsyncWorker } else { - final_properties.emplace(property_key, property.value()); + final_properties.emplace_back(property_key, property.value()); continue; } } @@ -834,7 +829,7 @@ struct LocalizeWorker : Napi::AsyncWorker // all other properties else { - properties.emplace_back(property_key, property.value()); + final_properties.emplace_back(property_key, property.value()); continue; } @@ -845,15 +840,15 @@ struct LocalizeWorker : Napi::AsyncWorker // use the class value of highest precedence if (class_value.valid()) { - final_properties.emplace_back(baton_data->class_property, class_value); + final_properties.emplace_back(baton_data_->class_property, class_value); } // use the language value of highest precedence if (language_value.valid()) { - final_properties.emplace_back(baton_data->language_property, language_value); + final_properties.emplace_back(baton_data_->language_property, language_value); } - if (return_localized_tile) { + if (baton_data_->return_localized_tile) { final_properties.emplace_back(baton_data_->language_property + "_local", original_language_value); } @@ -950,6 +945,7 @@ Napi::Value localize(Napi::CallbackInfo const& info) std::string class_property = "class"; std::string class_prefix = "_mbx_"; bool compress = false; + bool return_localized_tile = false; // validate params object Napi::Value params_val = info[0]; @@ -1001,6 +997,7 @@ Napi::Value localize(Napi::CallbackInfo const& info) std::string language_item = language_item_val.As(); languages.push_back(language_item); } + return_localized_tile = true; } } @@ -1050,6 +1047,7 @@ Napi::Value localize(Napi::CallbackInfo const& info) std::string worldview_item = worldview_item_val.As(); worldviews.push_back(worldview_item); } + return_localized_tile = true; } } @@ -1118,6 +1116,7 @@ Napi::Value localize(Napi::CallbackInfo const& info) worldview_prefix, class_property, class_prefix, + return_localized_tile, compress); auto* worker = new LocalizeWorker{std::move(baton_data), callback}; From 2b1f1491c4311b16a9a6a751e8a40cae7dba9d32 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 19 Oct 2022 11:25:53 -0700 Subject: [PATCH 16/41] fix class test --- src/vtcomposite.cpp | 2 +- test/vtcomposite-localize-class.test.js | 950 +++++++++++++++++++----- 2 files changed, 759 insertions(+), 193 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index f39e22e4..10cbc11d 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -848,7 +848,7 @@ struct LocalizeWorker : Napi::AsyncWorker final_properties.emplace_back(baton_data_->language_property, language_value); } - if (baton_data_->return_localized_tile) { + if (baton_data_->return_localized_tile && original_language_value.valid()) { final_properties.emplace_back(baton_data_->language_property + "_local", original_language_value); } diff --git a/test/vtcomposite-localize-class.test.js b/test/vtcomposite-localize-class.test.js index a2c4f7f7..3d137ff5 100644 --- a/test/vtcomposite-localize-class.test.js +++ b/test/vtcomposite-localize-class.test.js @@ -5,76 +5,217 @@ const { vtinfo } = require('./test-utils.js'); const mvtFixtures = require('@mapbox/mvt-fixtures'); const test = require('tape'); -test('[localize class] _mbx_class is assigned to class when worldview filtering is provided', (assert) => { +/****************************************************************************** + * TEST SET 1: + * - request non-localized tile + * - layer has worldview + * - layer has class vs. _mbx_class differentiation + ******************************************************************************/ +test('[localize class] requesting non-localized tile; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // worldview + 1, 1, // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'worldview', 'class' ], + values: [ + { string_value: 'all' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // _mbx_worldview - 1, 1 // _mbx_class - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ '_mbx_worldview', '_mbx_class' ], - values: [ - { string_value: 'US' }, - { string_value: 'affogato' }, - ], - extent: 4096 - } - ] - }).buffer, - worldviews: ['US'] + buffer: feature + // no languages or worldviews = requesting non-localized tile }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', class: 'fancy_affogato' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize class] requesting non-localized tile; feature with compatible worldview key in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // worldview + 1, 1, // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'worldview', 'class' ], + values: [ + { string_value: 'US' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature + // no languages or worldviews = requesting non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US', class: 'fancy_affogato' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize class] requesting non-localized tile; feature with incompatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1, // worldview, + 2, 2 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', 'worldview', 'class' ], + values: [ + { string_value: 'all' }, + { string_value: 'every_wv' }, // use a different value from _mbx_worldview to test that localize indeed returns this one + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature + // no languages or worldviews = requesting non-localized tile + }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'US', - class: 'affogato' - }, 'expected properties'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'every_wv', class: 'fancy_affogato' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize class] requesting non-localized tile; feature with incompatible worldview key in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1 // _mbx_class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class'], + values: [ + { string_value: 'US' }, + { string_value: 'affogato' }, + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature + // no languages or worldviews = requesting non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); assert.end(); }); }); -test('[localize worldview] _mbx_class is assigned to class when worldview is `all`', (assert) => { + +/****************************************************************************** + * TEST SET 2: + * - request localized worldview + * - layer has worldview + * - layer has class vs. _mbx_class differentiation + ******************************************************************************/ +test('[localize class] requesting localized worldview; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1 // _mbx_class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class' ], + values: [ + { string_value: 'all' }, + { string_value: 'affogato' }, + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // _mbx_worldview - 1, 1 // _mbx_class - ], - type: 1, // point - geometry: [9, 54, 38] - } - ], - keys: ['_mbx_worldview', '_mbx_class'], - values: [ - { string_value: 'all' }, - { string_value: 'affogato' } - ], - extent: 4096 - } - ] - }).buffer, - worldviews: [] + buffer: feature, + worldviews: ['US'] }; localize(params, (err, vtBuffer) => { @@ -82,182 +223,607 @@ test('[localize worldview] _mbx_class is assigned to class when worldview is `al const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'all', - class: 'affogato' - }); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', class: 'affogato' }, 'expected properties'); assert.end(); }); }); -test('[localize class] _mbx_class is assigned to class when multiple worldviews are provided', (assert) => { +test('[localize class] requesting localized worldview; feature with compatible worldview key in the requested worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1 // _mbx_class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class' ], + values: [ + { string_value: 'US' }, + { string_value: 'affogato' }, + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // _mbx_worldview - 1, 1 // _mbx_class - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ '_mbx_worldview', '_mbx_class' ], - values: [ - { string_value: 'US,CN' }, - { string_value: 'affogato' } - ], - extent: 4096 - } - ] - }).buffer, - worldviews: ['US', 'CN'], + buffer: feature, + worldviews: ['US'] }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'CN', - class: 'affogato' - }, 'expected properties'); - assert.deepEqual(tile.layers.admin.feature(1).properties, { - worldview: 'US', - class: 'affogato' - }, 'expected properties'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US', class: 'affogato' }, 'expected properties'); assert.end(); }); }); -test('[localize class] _mbx_class is dropped with no worldview filtering', (assert) => { +test('[localize class] requesting localized worldview; feature with compatible worldview key in an irrelevant worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1 // _mbx_class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class' ], + values: [ + { string_value: 'US' }, + { string_value: 'affogato' }, + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // _mbx_worldview - type: 1, // point - geometry: [ 9, 54, 38 ] - }, - { - id: 11, - tags: [ 1, 1 ], // _mbx_class - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ '_mbx_worldview', '_mbx_class' ], - values: [ - { string_value: 'US' }, - { string_value: 'affogato' }, - ], - extent: 4096 - } - ] - }).buffer, - worldviews: [] + buffer: feature, + worldviews: ['JP'] }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + +test('[localize class] requesting localized worldview; feature with incompatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1, // _mbx_class + 2, 0, // worldview + 3, 2 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class', 'worldview', 'class' ], + values: [ + { string_value: 'all' }, + { string_value: 'affogato' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, {}, 'expected properties'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', class: 'affogato' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize class] requesting localized worldview; feature with incompatible worldview key in the requested worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // worldview + 1, 1 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'worldview', 'class' ], + values: [ + { string_value: 'US' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); assert.end(); }); }); -test('[localize class] _mbx_class is assigned to class when language is provided and there is no worldview filtering', (assert) => { +test('[localize class] requesting localized worldview; feature with incompatible worldview key in an irrelevant worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // worldview + 1, 1 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'worldview', 'class' ], + values: [ + { string_value: 'US' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // name - 1, 1, // _mbx_name_de - 2, 2, // _mbx_class - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'name', '_mbx_name_de', '_mbx_class' ], - values: [ - { string_value: 'France' }, - { string_value: 'Frankreich' }, - { string_value: 'affogato' }, - ], - extent: 4096 - } - ] - }).buffer, - language: 'de', + buffer: feature, + worldviews: ['JP'] }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + +/****************************************************************************** + * TEST SET 3: + * - request localized language + * - layer has worldview + * - layer has class vs. _mbx_class differentiation + ******************************************************************************/ +test('[localize class] requesting localized language; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1 // _mbx_class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class' ], + values: [ + { string_value: 'all' }, + { string_value: 'affogato' }, + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - name: 'Frankreich', - name_local: 'France', - class: 'affogato' - }, 'expected properties'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', class: 'affogato' }, 'expected properties'); assert.end(); }); }); -test('[localize class] Invalid _mbx_class value should be dropped', (assert) => { +test('[localize class] requesting localized languages; feature with compatible worldview key in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1 // _mbx_class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class' ], + values: [ + { string_value: 'US' }, + { string_value: 'affogato' }, + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // _mbx_worldview - 1, 1 // _mbx_class - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ '_mbx_worldview', '_mbx_class' ], - values: [ - { string_value: 'US' }, - { int_value: 42 }, - ], - extent: 4096 - } - ] - }).buffer, - worldviews: ['US'] + buffer: feature, + worldviews: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + +test('[localize class] requesting localized language; feature with incompatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1, // _mbx_class + 2, 0, // worldview + 3, 2 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', '_mbx_class', 'worldview', 'class' ], + values: [ + { string_value: 'all' }, + { string_value: 'affogato' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['ja'] }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'US', - }, 'expected properties'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', class: 'affogato' }, 'expected properties'); assert.end(); }); }); + +test('[localize class] requesting localized language; feature with incompatible worldview key in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // worldview + 1, 1 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'worldview', 'class' ], + values: [ + { string_value: 'US' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['ja'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has one feature'); + assert.end(); + }); +}); + +/****************************************************************************** + * TEST SET 4: + * - layer has no worldview + * - layer has no class vs. _mbx_class differentiation + ******************************************************************************/ + test('[localize class] requesting non-localized tile; feature has class', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'class' ], + values: [ + { string_value: 'affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature + // no languages or worldviews = requesting non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize class] requesting localized worldview; feature has class', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'class' ], + values: [ + { string_value: 'affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize class] requesting localized language; feature has class', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 // class + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'class' ], + values: [ + { string_value: 'affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); + assert.end(); + }); + }); + + /****************************************************************************** + * TEST SET 5: + * - test custom class_property and class_prefix + ******************************************************************************/ + test('[localize class] requesting non-localized tile; feature with custom class property and prefix but compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // worldview + 1, 1, // ccllaassss + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ 'worldview', 'ccllaassss' ], + values: [ + { string_value: 'all' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + class_property: "ccllaassss", + class_prefix: "mmbbxx_" + // no languages or worldviews = requesting non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', ccllaassss: 'fancy_affogato' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize class] requesting localized language; feature with custom class property and prefix but incompatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1, // mmbbxx_ccllaassss + 2, 0, // worldview + 3, 2 // ccllaassss + ], + type: 1, // point + geometry: [ 9, 55, 38 ] + } + ], + keys: [ '_mbx_worldview', 'mmbbxx_ccllaassss', 'worldview', 'ccllaassss' ], + values: [ + { string_value: 'all' }, + { string_value: 'affogato' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + class_property: 'ccllaassss', + class_prefix: 'mmbbxx_', + languages: ['ja'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', ccllaassss: 'affogato' }, 'expected properties'); + assert.end(); + }); + }); From dd809d29ade2aa62a58d307d78dbaf949db633ff Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 19 Oct 2022 17:51:03 -0700 Subject: [PATCH 17/41] fix worldview test --- src/vtcomposite.cpp | 44 +- test/vtcomposite-localize-worldview.test.js | 967 ++++++++++++++------ 2 files changed, 727 insertions(+), 284 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 10cbc11d..740ed9bf 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -9,6 +9,7 @@ #include // vtzero #include +#include #include #include // geometry.hpp @@ -738,25 +739,52 @@ struct LocalizeWorker : Napi::AsyncWorker { if (property.value().type() == vtzero::property_value_type::string_value) { - if (keep_every_worldview) + std::string property_value = static_cast(property.value().string_value()); + + // keep only the feature in selected worldview or in 'all' worldview + if (property_value == "all") { final_properties.emplace_back(baton_data_->worldview_property, property.value()); continue; } else { - // keep only the feature in selected worldview or in 'all' worldview - std::string const& property_value = static_cast(property.value().string_value()); - std::vector property_value_list = utils::split(property_value); - if (property_value == "all" || std::find(property_value_list.begin(), property_value_list.end(), baton_data_->worldviews[0]) != property_value_list.end()) + if (keep_every_worldview) { - final_properties.emplace_back(baton_data_->worldview_property, property.value()); + // For now just return the first one; + // TODO: explode feature into multiple features, one for each worldview, if property.value() is a comma-separated list + std::vector property_worldviews = utils::split(property_value); + vtzero::encoded_property_value epv{property_worldviews[0]}; + vtzero::property_value pv{epv.data()}; + final_properties.emplace_back(baton_data_->worldview_property, pv); continue; } else { - skip_feature = true; - continue; + std::vector matching_worldviews; + std::vector property_worldviews = utils::split(property_value); + utils::intersection(property_worldviews, baton_data_->worldviews, matching_worldviews); + + // For now just process the first requested worldview; + if (matching_worldviews.empty()) { + skip_feature = true; + continue; + } + else if (std::find(matching_worldviews.begin(), matching_worldviews.end(), baton_data_->worldviews[0]) == matching_worldviews.end()) + { + // TODO when support multiple worldviews: remove this else if block + skip_feature = true; + continue; + } + else + { + // TODO: support multiple worldviews + std::string const& first_wv{baton_data_->worldviews[0]}; + vtzero::encoded_property_value epv{first_wv}; + vtzero::property_value pv{epv.data()}; + final_properties.emplace_back(baton_data_->worldview_property, pv); + continue; + } } } } diff --git a/test/vtcomposite-localize-worldview.test.js b/test/vtcomposite-localize-worldview.test.js index 1c3641c6..884bea80 100644 --- a/test/vtcomposite-localize-worldview.test.js +++ b/test/vtcomposite-localize-worldview.test.js @@ -5,148 +5,523 @@ const { vtinfo, getFeatureById } = require('./test-utils.js'); const mvtFixtures = require('@mapbox/mvt-fixtures'); const test = require('tape'); -const fixtureDefaults = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // _mbx_worldview: US,CN,JP,IN - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'US,CN,JP,IN' } - ], - extent: 4096 - } - ] -}).buffer; - -const fixtureWithAll = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // _mbx_worldview: all - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'all' } - ], - extent: 4096 - } - ] -}).buffer; - -test('[localize worldview] defaults - worldview: US specified, only US created', (assert) => { + +/****************************************************************************** + * TEST SET 1: + * - request non-localized tile + * - layer has worldview + ******************************************************************************/ +test('[localize worldview] requesting nonlocalized tiles; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], // worldview: all + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'worldview' ], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: fixtureDefaults, - worldviews: ['US'], - // worldview_property: '_mbx_worldview' + buffer: feature + // no languages or worldviews = requesting nonlocalized tiles }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'US' - }, 'expected properties'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); assert.end(); }); }); -test('[localize worldview] defaults - worldview: null filters out all features with a valid "worldview_property" property', (assert) => { +test('[localize worldview] requesting nonlocalized tiles; feature with compatible worldview key in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'worldview' ], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // _mbx_worldview: US - type: 1, // point - geometry: [ 9, 54, 38 ] - }, - { - id: 10, - tags: [ 1, 0 ], // something_else: US - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ '_mbx_worldview', 'something_else' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer, - worldviews: [] + buffer: feature + // no languages or worldviews = requesting nonlocalized tiles }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - something_else: 'US' - }, 'expected properties'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); assert.end(); }); }); -test('[localize worldview] no worldview specified, feature with "all" value is retained', (assert) => { +test('[localize worldview] requesting nonlocalized tiles; feature with an incompatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview, + 1, 1 // worldview + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview', 'worldview' ], + values: [ + { string_value: 'all' }, + { string_value: 'every' } // use a different value from _mbx_worldview to test that localize indeed returns this value + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: fixtureWithAll, - worldviews: [] + buffer: feature + // no languages or worldviews = requesting nonlocalized tiles }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); assert.ok('admin' in tile.layers, 'has admin layer'); assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'all' - }); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'every' }, 'expected properties'); assert.end(); }); }); -test('[localize worldview] worldview: US specified, feature with _mbx_worldview: "all" is retained', (assert) => { +test('[localize worldview] requesting nonlocalized tiles; feature with an incompatible worldview key in several worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'CN,JP,US' } + ], + extent: 4096 + } + ] + }).buffer; + const params = { - buffer: fixtureWithAll, - worldviews: ['US'] + buffer: feature + // no languages or worldviews = requesting nonlocalized tiles }; + localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'all' - }); + assert.deepEqual(tile.layers, {}, 'has no feature'); assert.end(); }); }); -test('[localize worldview] property with non-string value, feature is dropped', (assert) => { - const params = { - buffer: mvtFixtures.create({ +/****************************************************************************** + * TEST SET 2: + * - request localized worldview + * - layer has worldview + ******************************************************************************/ + test('[localize worldview] requesting localized worldview; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], // _mbx_worldview: all + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized worldview; feature with compatible worldview key in several worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'CN,JP,TR,US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in "all" worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 0 // worldview + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview', 'worldview' ], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in the requested worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'worldview' ], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in an irrelevant worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'worldview' ], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['JP'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); + }); + + +/****************************************************************************** + * TEST SET 3: + * - request localized language + * - layer has worldview + ******************************************************************************/ + test('[localize worldview] requesting localized language; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], // _mbx_worldview: all + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized langauge; feature with compatible worldview key in several worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'CN,JP,TR,US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized language; feature with incompatible worldview key in "all" worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 0 // worldview + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview', 'worldview' ], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized language; feature with incompatible worldview key in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'worldview' ], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); + }); + + /****************************************************************************** + * TEST SET 4: + * - custom worldview_property and worldview_prefix + ******************************************************************************/ + test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ layers: [ { version: 2, @@ -154,32 +529,42 @@ test('[localize worldview] property with non-string value, feature is dropped', features: [ { id: 10, - tags: [ 0, 0 ], // _mbx_worldview: 100 + tags: [ + 0, 0, + 1, 0 + ], type: 1, // point geometry: [ 9, 54, 38 ] } ], - keys: [ '_mbx_worldview' ], + keys: [ 'mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww' ], values: [ - { int_value: 100 } + { string_value: 'all' } ], extent: 4096 } ] - }).buffer, - worldviews: ['US'] - } - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.equal(Object.keys(tile.layers).length, 0, 'no feature or layers retained'); - assert.end(); + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_' + // no languages or worldviews = request non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'all' }, 'expected properties'); + assert.end(); + }); }); -}); -test('[localize worldview] feature with _mbx_worldview and worldview properties reassigns worldview', (assert) => { - const params = { - buffer: mvtFixtures.create({ + test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in a worldview', (assert) => { + const feature = mvtFixtures.create({ layers: [ { version: 2, @@ -188,174 +573,163 @@ test('[localize worldview] feature with _mbx_worldview and worldview properties { id: 10, tags: [ - 0, 0, // _mbx_worldview: US - 1, 1 // worldview: RU <-- this is overwritten + 0, 0 ], type: 1, // point geometry: [ 9, 54, 38 ] } ], - keys: [ - '_mbx_worldview', - 'worldview' - ], + keys: [ 'wwoorrllddvviieeww' ], values: [ - { string_value: 'US' }, - { string_value: 'RU' } + { string_value: 'US' } ], extent: 4096 } ] - }).buffer, - worldviews: ['US'] - }; + }).buffer; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'US' + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_' + // no languages or worldviews = request non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'US' }, 'expected properties'); + assert.end(); }); - assert.end(); }); -}); -test('[localize worldview] custom worldview_property', (assert) => { - const tile = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // custom_worldview: US - type: 1, // point - geometry: [ 9, 54, 38 ] - }, - { - id: 20, - tags: [ 0, 1 ], // custom_worldview: RU - type: 1, // point - geometry: [ 9, 56, 38 ] - } - ], - keys: [ - 'custom_worldview' - ], - values: [ - { string_value: 'US' }, - { string_value: 'RU' } - ], - extent: 4096 - } - ] - }).buffer; + test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix but has an incompatible worldview key', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, + 1, 0 + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww' ], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; - const params = { - buffer: tile, - worldviews: ['US'], - worldview_property: 'custom_worldview' - }; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'expected number of features'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'US' - }, 'expected properties retained'); - assert.end(); - }); -}); + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_' + // no languages or worldviews = request non-localized tile + }; -test('[localize worldview] feature split into multiple worldviews', (assert) => { - const params = { - buffer: fixtureDefaults, - worldviews: ['CN', 'IN', 'JP', 'US'], - worldview_property: '_mbx_worldview' - }; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 4, 'has four features'); - assert.equal(tile.layers.admin.feature(0).properties.worldview, 'CN', 'expected CN worldview'); - assert.equal(tile.layers.admin.feature(1).properties.worldview, 'IN', 'expected IN worldview'); - assert.equal(tile.layers.admin.feature(2).properties.worldview, 'JP', 'expected JP worldview'); - assert.equal(tile.layers.admin.feature(3).properties.worldview, 'US', 'expected US worldview'); - assert.end(); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); }); -}); -test('[localize worldview] worldviews not specified are not split into features', (assert) => { - const params = { - buffer: fixtureDefaults, - worldviews: ['US', 'JP'], - worldview_property: '_mbx_worldview' - }; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 2, 'has two features'); - assert.equal(tile.layers.admin.feature(0).properties.worldview, 'JP', 'expected JP worldview'); - assert.equal(tile.layers.admin.feature(1).properties.worldview, 'US', 'expected US worldview'); - assert.end(); + test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'mmbbxx_wwoorrllddvviieeww' ], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'all' }, 'expected properties'); + assert.end(); + }); }); -}); -test('[localize worldview] custom worldview_defaults array', (assert) => { - const tile = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // custom_worldview: US - type: 1, // point - geometry: [ 9, 54, 38 ] - }, - { - id: 20, - tags: [ 0, 1 ], // custom_worldview: RU - type: 1, // point - geometry: [ 9, 56, 38 ] - } - ], - keys: [ 'custom_worldview' ], - values: [ - { string_value: 'US' }, - { string_value: 'RU' } - ], - extent: 4096 - } - ] - }).buffer; + test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is the requested worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'mmbbxx_wwoorrllddvviieeww' ], + values: [ + { string_value: 'JP,US' } + ], + extent: 4096 + } + ] + }).buffer; - const params = { - buffer: tile, - worldviews: ['RU'], - worldview_property: 'custom_worldview' - }; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'expected number of features'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { - worldview: 'RU' - }, 'expected properties retained'); - assert.end(); + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'US' }, 'expected properties'); + assert.end(); + }); }); -}); -test('[localize worldview] partial matching worldviews are not considered matches, only perfect matches after splitting by comma', (assert) => { - const params = { - buffer: mvtFixtures.create({ + test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is in a irrelevant worldview', (assert) => { + const feature = mvtFixtures.create({ layers: [ { version: 2, @@ -364,32 +738,73 @@ test('[localize worldview] partial matching worldviews are not considered matche { id: 10, tags: [ - 0, 0, // name: Some place - 1, 1 // _mbx_worldview: USAAAA,CN,JP,INdia + 0, 0 ], type: 1, // point geometry: [ 9, 54, 38 ] } ], - keys: [ - 'name', - '_mbx_worldview' + keys: [ 'mmbbxx_wwoorrllddvviieeww' ], + values: [ + { string_value: 'CN,TR' } ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix but has an incompatible worldview key', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, + 1, 0 + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww' ], values: [ - { string_value: 'Some place' }, - { string_value: 'USAAAA,CN,JP,INdia' } + { string_value: 'US' } ], extent: 4096 } ] - }).buffer, - worldviews: ['US'] - }; + }).buffer; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.equal(Object.keys(tile.layers).length, 0, 'no feature or layers retained since US does not match USAAAA'); - assert.end(); + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['JP'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); }); -}); \ No newline at end of file From 8d62e753cf638c12d70e5c1a0e926c542235439e Mon Sep 17 00:00:00 2001 From: lily-chai Date: Fri, 21 Oct 2022 10:54:25 -0700 Subject: [PATCH 18/41] add worldview_default; enfore non-empty array of languages and worldviews --- src/vtcomposite.cpp | 106 ++++++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 29 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 740ed9bf..feff90c4 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -119,6 +119,9 @@ struct LocalizeBatonType return_localized_tile{std::move(return_localized_tile_)}, compress{compress_} { + // Note: for LocalizeBatonType, return_localized_tile_ dictates whether it + // will return a localized or non-localized tile; the existence and value of + // languages_ and worldviews_ does not matter. } ~LocalizeBatonType() noexcept @@ -963,17 +966,21 @@ Napi::Value localize(Napi::CallbackInfo const& info) // mandatory params Napi::Buffer buffer; - // default param values + // optional params and their default values std::vector languages; // default is undefined std::string language_property = "name"; std::string language_prefix = "_mbx_"; std::vector worldviews; // default is undefined std::string worldview_property = "worldview"; std::string worldview_prefix = "_mbx_"; + std::string worldview_default = "US"; std::string class_property = "class"; std::string class_prefix = "_mbx_"; bool compress = false; - bool return_localized_tile = false; + + // param that'll be deduced from other params + bool return_localized_tile = false; // true only if languages or worldviews exist + bool worldviews_provided = false; // true if worldviews exist // validate params object Napi::Value params_val = info[0]; @@ -1005,27 +1012,34 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "languages"))) { Napi::Value language_val = params.Get(Napi::String::New(info.Env(), "languages")); - if (!language_val.IsArray() && !language_val.IsNull() && !language_val.IsUndefined()) - { - return utils::CallbackError("params.languages must be an array or null", info); - } - if (language_val.IsArray()) { Napi::Array language_array = language_val.As(); std::uint32_t num_languages = language_array.Length(); - languages.reserve(num_languages); - for (std::uint32_t lg = 0; lg < num_languages; ++lg) + if (num_languages > 0) { - Napi::Value language_item_val = language_array.Get(lg); - if (!language_item_val.IsString()) + languages.reserve(num_languages); + + for (std::uint32_t lg = 0; lg < num_languages; ++lg) { - return utils::CallbackError("params.languages must be an array of strings", info); + Napi::Value language_item_val = language_array.Get(lg); + if (!language_item_val.IsString()) + { + return utils::CallbackError("params.languages must be an array of strings", info); + } + std::string language_item = language_item_val.As(); + languages.push_back(language_item); } - std::string language_item = language_item_val.As(); - languages.push_back(language_item); + return_localized_tile = true; + } + else + { + return utils::CallbackError("params.languages must be a non-empty array", info); } - return_localized_tile = true; + } + else + { + return utils::CallbackError("params.languages must be a non-empty array", info); } } @@ -1055,27 +1069,34 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "worldviews"))) { Napi::Value worldview_val = params.Get(Napi::String::New(info.Env(), "worldviews")); - if (!worldview_val.IsArray() && !worldview_val.IsNull() && !worldview_val.IsUndefined()) - { - return utils::CallbackError("params.worldviews must be an array or null", info); - } - if (worldview_val.IsArray()) { Napi::Array worldview_array = worldview_val.As(); std::uint32_t num_worldviews = worldview_array.Length(); - worldviews.reserve(num_worldviews); - for (std::uint32_t wv = 0; wv < num_worldviews; ++wv) - { - Napi::Value worldview_item_val = worldview_array.Get(wv); - if (!worldview_item_val.IsString()) + if (num_worldviews > 0) { + worldviews.reserve(num_worldviews); + + for (std::uint32_t wv = 0; wv < num_worldviews; ++wv) { - return utils::CallbackError("params.worldviews must be an array of strings", info); + Napi::Value worldview_item_val = worldview_array.Get(wv); + if (!worldview_item_val.IsString()) + { + return utils::CallbackError("params.worldviews must be an array of strings", info); + } + std::string worldview_item = worldview_item_val.As(); + worldviews.push_back(worldview_item); } - std::string worldview_item = worldview_item_val.As(); - worldviews.push_back(worldview_item); + return_localized_tile = true; + worldviews_provided = true; + } + else + { + return utils::CallbackError("params.worldviews must be a non-empty array", info); } - return_localized_tile = true; + } + else + { + return utils::CallbackError("params.worldviews must be a non-empty array", info); } } @@ -1101,6 +1122,17 @@ Napi::Value localize(Napi::CallbackInfo const& info) worldview_prefix = worldview_prefix_val.As(); } + // params.worldview_default (optional) + if (params.Has(Napi::String::New(info.Env(), "worldview_default"))) + { + Napi::Value worldview_default_val = params.Get(Napi::String::New(info.Env(), "worldview_default")); + if (!worldview_default_val.IsString()) + { + return utils::CallbackError("params.worldview_default must be a string", info); + } + worldview_default = worldview_default_val.As(); + } + // params.class_property (optional) if (params.Has(Napi::String::New(info.Env(), "class_property"))) { @@ -1134,6 +1166,22 @@ Napi::Value localize(Napi::CallbackInfo const& info) compress = comp_value.As().Value(); } + // This if block must be validated *after* params.languages and params.worldviews + // because it checks return_localized_tile which is dictated by the + // value of both params.languages and params.worldviews. + if (return_localized_tile) + { + if (!worldviews_provided) + { + worldviews.reserve(1); + worldviews.push_back(worldview_default); + } + else + { + // do nothing – already knows which worldview to return + } + } + std::unique_ptr baton_data = std::make_unique( buffer, languages, From a5b94bb221b236f87b42c6fb0c084bb097a95a05 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Fri, 21 Oct 2022 12:33:32 -0700 Subject: [PATCH 19/41] test default worldview --- test/vtcomposite-localize-worldview.test.js | 385 ++++++++++++++++++++ 1 file changed, 385 insertions(+) diff --git a/test/vtcomposite-localize-worldview.test.js b/test/vtcomposite-localize-worldview.test.js index 884bea80..a4be1974 100644 --- a/test/vtcomposite-localize-worldview.test.js +++ b/test/vtcomposite-localize-worldview.test.js @@ -165,6 +165,44 @@ test('[localize worldview] requesting nonlocalized tiles; feature with an incomp }); }); +test('[localize worldview] requesting nonlocalized tiles; feature with no worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'whatever' ], + values: [ + { string_value: 'blah' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature + // no languages or worldviews = requesting nonlocalized tiles + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { whatever: 'blah' }, 'expected properties'); + assert.end(); + }); +}); + /****************************************************************************** * TEST SET 2: * - request localized worldview @@ -359,6 +397,44 @@ test('[localize worldview] requesting nonlocalized tiles; feature with an incomp }); }); + test('[localize worldview] requesting localized worldview; feature with no worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'whatever' ], + values: [ + { string_value: 'blah' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { whatever: 'blah' }, 'expected properties'); + assert.end(); + }); + }); + /****************************************************************************** * TEST SET 3: @@ -516,6 +592,44 @@ test('[localize worldview] requesting nonlocalized tiles; feature with an incomp }); }); + test('[localize worldview] requesting localized language; feature with no worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'whatever' ], + values: [ + { string_value: 'blah' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { whatever: 'blah' }, 'expected properties'); + assert.end(); + }); + }); + /****************************************************************************** * TEST SET 4: * - custom worldview_property and worldview_prefix @@ -808,3 +922,274 @@ test('[localize worldview] requesting nonlocalized tiles; feature with an incomp assert.end(); }); }); + + /****************************************************************************** + * TEST SET 5: + * - custom worldview_default + ******************************************************************************/ + test('[localize worldview] requesting non-localized tiles; feature in the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'worldview' ], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_default: 'US' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting non-localized tiles; feature in a worldview other than the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'worldview' ], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_default: 'US' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localize worldview; feature in the default worldview but not in the reqeusted worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: [ 'JP' ], + worldview_default: 'US' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized worldview; feature not in the default worldview but in the reqeusted worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: [ 'JP' ], + worldview_default: 'US' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized worldview; feature in the default worldview and in the reqeusted worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: [ 'JP' ], + worldview_default: 'JP' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localized language; feature in the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: [ 'en' ], + worldview_default: 'JP' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); + }); + }); + + test('[localize worldview] requesting localize langauge; feature not in the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ 0, 0 ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ '_mbx_worldview' ], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: [ 'en' ], + worldview_default: 'US' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); + }); From ff8849214e604248deda6ad72f0b5dac1114fcd2 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Fri, 21 Oct 2022 12:47:46 -0700 Subject: [PATCH 20/41] test invalid params.worldviews values --- test/vtcomposite-localize-worldview.test.js | 1968 ++++++++++--------- 1 file changed, 1001 insertions(+), 967 deletions(-) diff --git a/test/vtcomposite-localize-worldview.test.js b/test/vtcomposite-localize-worldview.test.js index a4be1974..dea46ffb 100644 --- a/test/vtcomposite-localize-worldview.test.js +++ b/test/vtcomposite-localize-worldview.test.js @@ -1,12 +1,12 @@ 'use strict'; const localize = require('../lib/index.js').localize; -const { vtinfo, getFeatureById } = require('./test-utils.js'); +const { vtinfo } = require('./test-utils.js'); const mvtFixtures = require('@mapbox/mvt-fixtures'); const test = require('tape'); -/****************************************************************************** +/** **************************************************************************** * TEST SET 1: * - request non-localized tile * - layer has worldview @@ -20,12 +20,12 @@ test('[localize worldview] requesting nonlocalized tiles; feature with compatibl features: [ { id: 10, - tags: [ 0, 0 ], // worldview: all + tags: [0, 0], // worldview: all type: 1, // point - geometry: [ 9, 54, 38 ] + geometry: [9, 54, 38] } ], - keys: [ 'worldview' ], + keys: ['worldview'], values: [ { string_value: 'all' } ], @@ -58,12 +58,12 @@ test('[localize worldview] requesting nonlocalized tiles; feature with compatibl features: [ { id: 10, - tags: [ 0, 0 ], + tags: [0, 0], type: 1, // point - geometry: [ 9, 54, 38 ] + geometry: [9, 54, 38] } ], - keys: [ 'worldview' ], + keys: ['worldview'], values: [ { string_value: 'US' } ], @@ -101,10 +101,10 @@ test('[localize worldview] requesting nonlocalized tiles; feature with an incomp 1, 1 // worldview ], type: 1, // point - geometry: [ 9, 54, 38 ] + geometry: [9, 54, 38] } ], - keys: [ '_mbx_worldview', 'worldview' ], + keys: ['_mbx_worldview', 'worldview'], values: [ { string_value: 'all' }, { string_value: 'every' } // use a different value from _mbx_worldview to test that localize indeed returns this value @@ -138,12 +138,12 @@ test('[localize worldview] requesting nonlocalized tiles; feature with an incomp features: [ { id: 10, - tags: [ 0, 0 ], + tags: [0, 0], type: 1, // point - geometry: [ 9, 54, 38 ] + geometry: [9, 54, 38] } ], - keys: [ '_mbx_worldview' ], + keys: ['_mbx_worldview'], values: [ { string_value: 'CN,JP,US' } ], @@ -174,12 +174,12 @@ test('[localize worldview] requesting nonlocalized tiles; feature with no worldv features: [ { id: 10, - tags: [ 0, 0 ], + tags: [0, 0], type: 1, // point - geometry: [ 9, 54, 38 ] + geometry: [9, 54, 38] } ], - keys: [ 'whatever' ], + keys: ['whatever'], values: [ { string_value: 'blah' } ], @@ -203,993 +203,1027 @@ test('[localize worldview] requesting nonlocalized tiles; feature with no worldv }); }); -/****************************************************************************** +/** **************************************************************************** * TEST SET 2: * - request localized worldview * - layer has worldview ******************************************************************************/ - test('[localize worldview] requesting localized worldview; feature with compatible worldview key in "all" worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // _mbx_worldview: all - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'all' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized worldview; feature with compatible worldview key in several worldviews', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'CN,JP,TR,US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in "all" worldviews', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // _mbx_worldview - 1, 0 // worldview - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview', 'worldview' ], - values: [ - { string_value: 'all' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in the requested worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'worldview' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in an irrelevant worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'worldview' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['JP'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized worldview; feature with no worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'whatever' ], - values: [ - { string_value: 'blah' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { whatever: 'blah' }, 'expected properties'); - assert.end(); - }); - }); - - -/****************************************************************************** +test('[localize worldview] requesting localized worldview; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], // _mbx_worldview: all + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized worldview; feature with compatible worldview key in several worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'CN,JP,TR,US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in "all" worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 0 // worldview + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview', 'worldview'], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in the requested worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['worldview'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in an irrelevant worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['worldview'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['JP'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized worldview; feature with no worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['whatever'], + values: [ + { string_value: 'blah' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { whatever: 'blah' }, 'expected properties'); + assert.end(); + }); +}); + + +/** **************************************************************************** * TEST SET 3: * - request localized language * - layer has worldview ******************************************************************************/ - test('[localize worldview] requesting localized language; feature with compatible worldview key in "all" worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], // _mbx_worldview: all - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'all' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - languages: ['en'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized langauge; feature with compatible worldview key in several worldviews', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'CN,JP,TR,US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['en'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized language; feature with incompatible worldview key in "all" worldviews', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // _mbx_worldview - 1, 0 // worldview - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview', 'worldview' ], - values: [ - { string_value: 'all' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - languages: ['en'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized language; feature with incompatible worldview key in a worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'worldview' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - languages: ['en'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); - }); - - test('[localize worldview] requesting localized language; feature with no worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'whatever' ], - values: [ - { string_value: 'blah' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - languages: ['en'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { whatever: 'blah' }, 'expected properties'); - assert.end(); - }); - }); - - /****************************************************************************** +test('[localize worldview] requesting localized language; feature with compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], // _mbx_worldview: all + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized langauge; feature with compatible worldview key in several worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'CN,JP,TR,US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized language; feature with incompatible worldview key in "all" worldviews', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 0 // worldview + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview', 'worldview'], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized language; feature with incompatible worldview key in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['worldview'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + +test('[localize worldview] requesting localized language; feature with no worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['whatever'], + values: [ + { string_value: 'blah' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { whatever: 'blah' }, 'expected properties'); + assert.end(); + }); +}); + +/** **************************************************************************** * TEST SET 4: * - custom worldview_property and worldview_prefix ******************************************************************************/ - test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, - 1, 0 - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww' ], - values: [ - { string_value: 'all' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_' - // no languages or worldviews = request non-localized tile - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'all' }, 'expected properties'); - assert.end(); - }); +test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, + 1, 0 + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww'], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_' + // no languages or worldviews = request non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'all' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in a worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['wwoorrllddvviieeww'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_' + // no languages or worldviews = request non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'US' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix but has an incompatible worldview key', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, + 1, 0 + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_' + // no languages or worldviews = request non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); }); +}); + +test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['mmbbxx_wwoorrllddvviieeww'], + values: [ + { string_value: 'all' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['US'] + }; - test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in a worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0 - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'wwoorrllddvviieeww' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_' - // no languages or worldviews = request non-localized tile - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'US' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'all' }, 'expected properties'); + assert.end(); }); +}); + +test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is the requested worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['mmbbxx_wwoorrllddvviieeww'], + values: [ + { string_value: 'JP,US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['US'] + }; - test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix but has an incompatible worldview key', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, - 1, 0 - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_' - // no languages or worldviews = request non-localized tile - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'US' }, 'expected properties'); + assert.end(); }); +}); + +test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is in a irrelevant worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['mmbbxx_wwoorrllddvviieeww'], + values: [ + { string_value: 'CN,TR' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['US'] + }; - test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'mmbbxx_wwoorrllddvviieeww' ], - values: [ - { string_value: 'all' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'all' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); }); +}); - test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is the requested worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0 - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'mmbbxx_wwoorrllddvviieeww' ], - values: [ - { string_value: 'JP,US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { wwoorrllddvviieeww: 'US' }, 'expected properties'); - assert.end(); - }); - }); +test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix but has an incompatible worldview key', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, + 1, 0 + ], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; - test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix and is in a irrelevant worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0 - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'mmbbxx_wwoorrllddvviieeww' ], - values: [ - { string_value: 'CN,TR' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); - }); + const params = { + buffer: feature, + worldview_property: 'wwoorrllddvviieeww', + worldview_prefix: 'mmbbxx_', + worldviews: ['JP'] + }; - test('[localize worldview] requesting localized worldview; feature has custom worldview property key and prefix but has an incompatible worldview key', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, - 1, 0 - ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'mmbbxx_wwoorrllddvviieeww', 'wwoorrllddvviieeww' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', - worldviews: ['JP'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); }); +}); - /****************************************************************************** +/** **************************************************************************** * TEST SET 5: * - custom worldview_default ******************************************************************************/ - test('[localize worldview] requesting non-localized tiles; feature in the default worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'worldview' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_default: 'US' - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); - assert.end(); - }); +test('[localize worldview] requesting non-localized tiles; feature in the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['worldview'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_default: 'US' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); + assert.end(); }); +}); + +test('[localize worldview] requesting non-localized tiles; feature in a worldview other than the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['worldview'], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldview_default: 'US' + }; - test('[localize worldview] requesting non-localized tiles; feature in a worldview other than the default worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ 'worldview' ], - values: [ - { string_value: 'JP' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldview_default: 'US' - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); }); +}); - test('[localize worldview] requesting localize worldview; feature in the default worldview but not in the reqeusted worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'US' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: [ 'JP' ], - worldview_default: 'US' - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); +test('[localize worldview] requesting localize worldview; feature in the default worldview but not in the reqeusted worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'US' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['JP'], + worldview_default: 'US' + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); }); +}); + +test('[localize worldview] requesting localized worldview; feature not in the default worldview but in the reqeusted worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['JP'], + worldview_default: 'US' + }; - test('[localize worldview] requesting localized worldview; feature not in the default worldview but in the reqeusted worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'JP' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: [ 'JP' ], - worldview_default: 'US' - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); }); +}); + +test('[localize worldview] requesting localized worldview; feature in the default worldview and in the reqeusted worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['JP'], + worldview_default: 'JP' + }; - test('[localize worldview] requesting localized worldview; feature in the default worldview and in the reqeusted worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'JP' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: [ 'JP' ], - worldview_default: 'JP' - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); }); +}); + +test('[localize worldview] requesting localized language; feature in the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'], + worldview_default: 'JP' + }; - test('[localize worldview] requesting localized language; feature in the default worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'JP' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - languages: [ 'en' ], - worldview_default: 'JP' - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'JP' }, 'expected properties'); + assert.end(); }); +}); + +test('[localize worldview] requesting localize langauge; feature not in the default worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'], + worldview_default: 'US' + }; - test('[localize worldview] requesting localize langauge; feature not in the default worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ 0, 0 ], - type: 1, // point - geometry: [ 9, 54, 38 ] - } - ], - keys: [ '_mbx_worldview' ], - values: [ - { string_value: 'JP' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - languages: [ 'en' ], - worldview_default: 'US' - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); }); +}); + +/** **************************************************************************** +* TEST SET 6: +* - test invalid params +******************************************************************************/ + +test('[localize worldview] invalid params', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'JP' } + ], + extent: 4096 + } + ] + }).buffer; + + assert.throws(() => { localize({ buffer: feature, worldviews: [] }); }); + assert.throws(() => { localize({ buffer: feature, worldviews: null }); }); + assert.throws(() => { localize({ buffer: feature, worldviews: undefined }); }); + assert.end(); +}); From 3bd5f32a323184ba46857640b1f8a07cf154e836 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Fri, 21 Oct 2022 12:53:57 -0700 Subject: [PATCH 21/41] lint --- test/vtcomposite-localize-class.test.js | 524 ++++++++++---------- test/vtcomposite-localize-worldview.test.js | 18 +- 2 files changed, 271 insertions(+), 271 deletions(-) diff --git a/test/vtcomposite-localize-class.test.js b/test/vtcomposite-localize-class.test.js index 3d137ff5..a4787ab8 100644 --- a/test/vtcomposite-localize-class.test.js +++ b/test/vtcomposite-localize-class.test.js @@ -5,7 +5,7 @@ const { vtinfo } = require('./test-utils.js'); const mvtFixtures = require('@mapbox/mvt-fixtures'); const test = require('tape'); -/****************************************************************************** +/** **************************************************************************** * TEST SET 1: * - request non-localized tile * - layer has worldview @@ -25,10 +25,10 @@ test('[localize class] requesting non-localized tile; feature with compatible wo 1, 1, // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ 'worldview', 'class' ], + keys: ['worldview', 'class'], values: [ { string_value: 'all' }, { string_value: 'fancy_affogato' } @@ -43,14 +43,14 @@ test('[localize class] requesting non-localized tile; feature with compatible wo // no languages or worldviews = requesting non-localized tile }; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', class: 'fancy_affogato' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', class: 'fancy_affogato' }, 'expected properties'); + assert.end(); + }); }); test('[localize class] requesting non-localized tile; feature with compatible worldview key in a worldview', (assert) => { @@ -67,10 +67,10 @@ test('[localize class] requesting non-localized tile; feature with compatible wo 1, 1, // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ 'worldview', 'class' ], + keys: ['worldview', 'class'], values: [ { string_value: 'US' }, { string_value: 'fancy_affogato' } @@ -85,14 +85,14 @@ test('[localize class] requesting non-localized tile; feature with compatible wo // no languages or worldviews = requesting non-localized tile }; - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US', class: 'fancy_affogato' }, 'expected properties'); - assert.end(); - }); + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US', class: 'fancy_affogato' }, 'expected properties'); + assert.end(); + }); }); test('[localize class] requesting non-localized tile; feature with incompatible worldview key in "all" worldview', (assert) => { @@ -110,10 +110,10 @@ test('[localize class] requesting non-localized tile; feature with incompatible 2, 2 // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', 'worldview', 'class' ], + keys: ['_mbx_worldview', 'worldview', 'class'], values: [ { string_value: 'all' }, { string_value: 'every_wv' }, // use a different value from _mbx_worldview to test that localize indeed returns this one @@ -153,10 +153,10 @@ test('[localize class] requesting non-localized tile; feature with incompatible 1, 1 // _mbx_class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class'], + keys: ['_mbx_worldview', '_mbx_class'], values: [ { string_value: 'US' }, { string_value: 'affogato' }, @@ -180,7 +180,7 @@ test('[localize class] requesting non-localized tile; feature with incompatible }); -/****************************************************************************** +/** **************************************************************************** * TEST SET 2: * - request localized worldview * - layer has worldview @@ -200,10 +200,10 @@ test('[localize class] requesting localized worldview; feature with compatible w 1, 1 // _mbx_class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class' ], + keys: ['_mbx_worldview', '_mbx_class'], values: [ { string_value: 'all' }, { string_value: 'affogato' }, @@ -242,10 +242,10 @@ test('[localize class] requesting localized worldview; feature with compatible w 1, 1 // _mbx_class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class' ], + keys: ['_mbx_worldview', '_mbx_class'], values: [ { string_value: 'US' }, { string_value: 'affogato' }, @@ -284,10 +284,10 @@ test('[localize class] requesting localized worldview; feature with compatible w 1, 1 // _mbx_class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class' ], + keys: ['_mbx_worldview', '_mbx_class'], values: [ { string_value: 'US' }, { string_value: 'affogato' }, @@ -326,10 +326,10 @@ test('[localize class] requesting localized worldview; feature with incompatible 3, 2 // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class', 'worldview', 'class' ], + keys: ['_mbx_worldview', '_mbx_class', 'worldview', 'class'], values: [ { string_value: 'all' }, { string_value: 'affogato' }, @@ -369,10 +369,10 @@ test('[localize class] requesting localized worldview; feature with incompatible 1, 1 // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ 'worldview', 'class' ], + keys: ['worldview', 'class'], values: [ { string_value: 'US' }, { string_value: 'fancy_affogato' } @@ -409,10 +409,10 @@ test('[localize class] requesting localized worldview; feature with incompatible 1, 1 // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ 'worldview', 'class' ], + keys: ['worldview', 'class'], values: [ { string_value: 'US' }, { string_value: 'fancy_affogato' } @@ -435,7 +435,7 @@ test('[localize class] requesting localized worldview; feature with incompatible }); }); -/****************************************************************************** +/** **************************************************************************** * TEST SET 3: * - request localized language * - layer has worldview @@ -455,10 +455,10 @@ test('[localize class] requesting localized language; feature with compatible wo 1, 1 // _mbx_class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class' ], + keys: ['_mbx_worldview', '_mbx_class'], values: [ { string_value: 'all' }, { string_value: 'affogato' }, @@ -497,10 +497,10 @@ test('[localize class] requesting localized languages; feature with compatible w 1, 1 // _mbx_class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class' ], + keys: ['_mbx_worldview', '_mbx_class'], values: [ { string_value: 'US' }, { string_value: 'affogato' }, @@ -539,10 +539,10 @@ test('[localize class] requesting localized language; feature with incompatible 3, 2 // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ '_mbx_worldview', '_mbx_class', 'worldview', 'class' ], + keys: ['_mbx_worldview', '_mbx_class', 'worldview', 'class'], values: [ { string_value: 'all' }, { string_value: 'affogato' }, @@ -582,10 +582,10 @@ test('[localize class] requesting localized language; feature with incompatible 1, 1 // class ], type: 1, // point - geometry: [ 9, 55, 38 ] + geometry: [9, 55, 38] } ], - keys: [ 'worldview', 'class' ], + keys: ['worldview', 'class'], values: [ { string_value: 'US' }, { string_value: 'fancy_affogato' } @@ -608,222 +608,222 @@ test('[localize class] requesting localized language; feature with incompatible }); }); -/****************************************************************************** +/** **************************************************************************** * TEST SET 4: * - layer has no worldview * - layer has no class vs. _mbx_class differentiation ******************************************************************************/ - test('[localize class] requesting non-localized tile; feature has class', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0 // class - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ 'class' ], - values: [ - { string_value: 'affogato' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature - // no languages or worldviews = requesting non-localized tile - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize class] requesting localized worldview; feature has class', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0 // class - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ 'class' ], - values: [ - { string_value: 'affogato' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - worldviews: ['US'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize class] requesting localized language; feature has class', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0 // class - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ 'class' ], - values: [ - { string_value: 'affogato' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - languages: ['en'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); - assert.end(); - }); - }); - - /****************************************************************************** - * TEST SET 5: - * - test custom class_property and class_prefix - ******************************************************************************/ - test('[localize class] requesting non-localized tile; feature with custom class property and prefix but compatible worldview key in "all" worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // worldview - 1, 1, // ccllaassss - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ 'worldview', 'ccllaassss' ], - values: [ - { string_value: 'all' }, - { string_value: 'fancy_affogato' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - class_property: "ccllaassss", - class_prefix: "mmbbxx_" - // no languages or worldviews = requesting non-localized tile - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', ccllaassss: 'fancy_affogato' }, 'expected properties'); - assert.end(); - }); - }); - - test('[localize class] requesting localized language; feature with custom class property and prefix but incompatible worldview key in "all" worldview', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [ - 0, 0, // _mbx_worldview - 1, 1, // mmbbxx_ccllaassss - 2, 0, // worldview - 3, 2 // ccllaassss - ], - type: 1, // point - geometry: [ 9, 55, 38 ] - } - ], - keys: [ '_mbx_worldview', 'mmbbxx_ccllaassss', 'worldview', 'ccllaassss' ], - values: [ - { string_value: 'all' }, - { string_value: 'affogato' }, - { string_value: 'fancy_affogato' } - ], - extent: 4096 - } - ] - }).buffer; - - const params = { - buffer: feature, - class_property: 'ccllaassss', - class_prefix: 'mmbbxx_', - languages: ['ja'] - }; - - localize(params, (err, vtBuffer) => { - assert.ifError(err); - const tile = vtinfo(vtBuffer); - assert.ok('admin' in tile.layers, 'has admin layer'); - assert.equal(tile.layers.admin.length, 1, 'has one feature'); - assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', ccllaassss: 'affogato' }, 'expected properties'); - assert.end(); - }); - }); +test('[localize class] requesting non-localized tile; feature has class', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 // class + ], + type: 1, // point + geometry: [9, 55, 38] + } + ], + keys: ['class'], + values: [ + { string_value: 'affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature + // no languages or worldviews = requesting non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize class] requesting localized worldview; feature has class', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 // class + ], + type: 1, // point + geometry: [9, 55, 38] + } + ], + keys: ['class'], + values: [ + { string_value: 'affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize class] requesting localized language; feature has class', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0 // class + ], + type: 1, // point + geometry: [9, 55, 38] + } + ], + keys: ['class'], + values: [ + { string_value: 'affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + languages: ['en'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { class: 'affogato' }, 'expected properties'); + assert.end(); + }); +}); + +/** **************************************************************************** + * TEST SET 5: + * - test custom class_property and class_prefix + ******************************************************************************/ +test('[localize class] requesting non-localized tile; feature with custom class property and prefix but compatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // worldview + 1, 1, // ccllaassss + ], + type: 1, // point + geometry: [9, 55, 38] + } + ], + keys: ['worldview', 'ccllaassss'], + values: [ + { string_value: 'all' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + class_property: 'ccllaassss', + class_prefix: 'mmbbxx_' + // no languages or worldviews = requesting non-localized tile + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', ccllaassss: 'fancy_affogato' }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize class] requesting localized language; feature with custom class property and prefix but incompatible worldview key in "all" worldview', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [ + 0, 0, // _mbx_worldview + 1, 1, // mmbbxx_ccllaassss + 2, 0, // worldview + 3, 2 // ccllaassss + ], + type: 1, // point + geometry: [9, 55, 38] + } + ], + keys: ['_mbx_worldview', 'mmbbxx_ccllaassss', 'worldview', 'ccllaassss'], + values: [ + { string_value: 'all' }, + { string_value: 'affogato' }, + { string_value: 'fancy_affogato' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + class_property: 'ccllaassss', + class_prefix: 'mmbbxx_', + languages: ['ja'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'all', ccllaassss: 'affogato' }, 'expected properties'); + assert.end(); + }); +}); diff --git a/test/vtcomposite-localize-worldview.test.js b/test/vtcomposite-localize-worldview.test.js index dea46ffb..51b0dc53 100644 --- a/test/vtcomposite-localize-worldview.test.js +++ b/test/vtcomposite-localize-worldview.test.js @@ -631,9 +631,9 @@ test('[localize worldview] requesting localized language; feature with no worldv }); /** **************************************************************************** - * TEST SET 4: - * - custom worldview_property and worldview_prefix - ******************************************************************************/ + * TEST SET 4: + * - custom worldview_property and worldview_prefix + ******************************************************************************/ test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { const feature = mvtFixtures.create({ layers: [ @@ -924,9 +924,9 @@ test('[localize worldview] requesting localized worldview; feature has custom wo }); /** **************************************************************************** - * TEST SET 5: - * - custom worldview_default - ******************************************************************************/ + * TEST SET 5: + * - custom worldview_default + ******************************************************************************/ test('[localize worldview] requesting non-localized tiles; feature in the default worldview', (assert) => { const feature = mvtFixtures.create({ layers: [ @@ -1195,9 +1195,9 @@ test('[localize worldview] requesting localize langauge; feature not in the defa }); /** **************************************************************************** -* TEST SET 6: -* - test invalid params -******************************************************************************/ + * TEST SET 6: + * - test invalid params + ******************************************************************************/ test('[localize worldview] invalid params', (assert) => { const feature = mvtFixtures.create({ From 84f5baa61a8c1bda138b805f06622f7f8e7675d4 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Fri, 21 Oct 2022 13:19:09 -0700 Subject: [PATCH 22/41] split up vtcomposite-param-validation.test.js by function --- ...mposite-composite-param-validation.test.js | 511 +++++++++++++ ...omposite-localize-param-validation.test.js | 193 +++++ test/vtcomposite-param-validation.test.js | 700 ------------------ 3 files changed, 704 insertions(+), 700 deletions(-) create mode 100644 test/vtcomposite-composite-param-validation.test.js create mode 100644 test/vtcomposite-localize-param-validation.test.js delete mode 100644 test/vtcomposite-param-validation.test.js diff --git a/test/vtcomposite-composite-param-validation.test.js b/test/vtcomposite-composite-param-validation.test.js new file mode 100644 index 00000000..c879b1c6 --- /dev/null +++ b/test/vtcomposite-composite-param-validation.test.js @@ -0,0 +1,511 @@ +'use strict'; + +const test = require('tape'); +const vt = require('../lib/index.js'); + +const composite = vt.composite; + +test('[composite] failure: fails without callback function', (assert) => { + try { + composite(); + } catch (err) { + assert.ok(/last argument must be a callback function/.test(err.message), 'expected error message'); + assert.end(); + } +}); + +test('[composite] failure: buffers is not an array', (assert) => { + composite('i am not an array', { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'first arg \'tiles\' must be an array of tile objects'); + assert.end(); + }); +}); + +test('[composite] failure: buffers array is empty', (assert) => { + const buffs = []; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'tiles\' array must be of length greater than 0'); + assert.end(); + }); +}); + +test('[composite] failure: item in buffers array is not an object', (assert) => { + const buffs = [ + 'not an object' + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'items in \'tiles\' array must be objects'); + assert.end(); + }); +}); + +test('[composite] failure: buffer value does not exist', (assert) => { + const buffs = [ + { + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'item in \'tiles\' array does not include a buffer value'); + assert.end(); + }); +}); + +test('[composite] failure: buffer value is null', (assert) => { + const buffs = [ + { + buffer: null, + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'buffer value in \'tiles\' array item is null or undefined'); + assert.end(); + }); +}); + +test('[composite] failure: buffer value is not a buffer', (assert) => { + const buffs = [ + { + buffer: 'not a buffer', + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'buffer value in \'tiles\' array item is not a true buffer'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object missing z value', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + // z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'item in \'tiles\' array does not include a \'z\' value'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object missing x value', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + // x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'item in \'tiles\' array does not include a \'x\' value'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object missing y value', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + // y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'item in \'tiles\' array does not include a \'y\' value'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object z value is not an int32', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 'zero', + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'z\' value in \'tiles\' array item is not an int32'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object x value is not an int32', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 'zero', + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'x\' value in \'tiles\' array item is not an int32'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object y value is not an int32', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 'zero' + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'y\' value in \'tiles\' array item is not an int32'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object z value is negative', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: -10, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'z\' value must not be less than zero'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object x value is negative', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: -5, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'x\' value must not be less than zero'); + assert.end(); + }); +}); + +test('[composite] failure: buffer object y value is negative', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: -4 + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'y\' value must not be less than zero'); + assert.end(); + }); +}); + +test('[composite] failure: layers option is not an array', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0, + layers: 'not an array' + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'layers\' value in the \'tiles\' array must be an array'); + assert.end(); + }); +}); + +test('[composite] failure: layers option is an empty array', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0, + layers: [] + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'layers\' array must be of length greater than 0'); + assert.end(); + }); +}); + +test('[composite] failure: layers option is an array with invalid types (not strings)', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0, + layers: [1, 2, 3, 'correct'] + } + ]; + composite(buffs, { z:3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'items in \'layers\' array must be strings'); + assert.end(); + }); +}); + +// TESTS FOR ZXY MAP REQUEST! + +test('[composite] failure: map request zxy missing z value', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'item in \'tiles\' array does not include a \'z\' value'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy missing x value', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'item in \'tiles\' array does not include a \'x\' value'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy missing y value', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, 'item in \'tiles\' array does not include a \'y\' value'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy z value is not an int32', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:'zero', x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'z\' value in \'tiles\' array item is not an int32'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy x value is not an int32', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:'zero', y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'x\' value in \'tiles\' array item is not an int32'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy y value is not an int32', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:'zero' }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'y\' value in \'tiles\' array item is not an int32'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy z value is negative', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 10, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:-3, x:1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'z\' value must not be less than zero'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy x value is negative', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:-1, y:0 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'x\' value must not be less than zero'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy y value is negative', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:3, x:1, y:-4 }, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'y\' value must not be less than zero'); + assert.end(); + }); +}); + +test('[composite] failure: map request zxy is not an object', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, true, {}, (err) => { + assert.ok(err); + assert.equal(err.message, '\'zxy_maprequest\' must be an object'); + assert.end(); + }); +}); + +test('[composite] failure: compress must be a boolean', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:0, x:0, y:0 }, { compress:'hi' }, (err) => { + assert.ok(err); + assert.equal(err.message, '\'compress\' must be a boolean'); + assert.end(); + }); +}); + +test('[composite] failure: options must be an object', (assert) => { + const buffs = [ + { + buffer: Buffer.from('hey'), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:0, x:0, y:0 }, true, (err) => { + assert.ok(err); + assert.equal(err.message, '\'options\' arg must be an object'); + assert.end(); + }); +}); + +test('[composite] failure: buffer size is not int32', (assert) => { + const buffs = [ + { + buffer: new Buffer.alloc(10), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:0, x:0, y:0 }, { buffer_size:'hi' }, (err) => { + assert.ok(err); + assert.equal(err.message, '\'buffer_size\' must be an int32'); + assert.end(); + }); +}); + +test('[composite] failure: buffer size is not positive int32', (assert) => { + const buffs = [ + { + buffer: new Buffer.alloc(10), + z: 0, + x: 0, + y: 0 + } + ]; + composite(buffs, { z:0, x:0, y:0 }, { buffer_size:-10 }, (err) => { + assert.ok(err); + assert.equal(err.message, '\'buffer_size\' must be a positive int32'); + assert.end(); + }); +}); diff --git a/test/vtcomposite-localize-param-validation.test.js b/test/vtcomposite-localize-param-validation.test.js new file mode 100644 index 00000000..d03bb1cf --- /dev/null +++ b/test/vtcomposite-localize-param-validation.test.js @@ -0,0 +1,193 @@ +'use strict'; + +const test = require('tape'); +const vt = require('../lib/index.js'); +const mvtFixtures = require('@mapbox/mvt-fixtures'); + +const localize = vt.localize; + +test('[localize] success with all parameters', (assert) => { + localize({ + buffer: mvtFixtures.get('064').buffer, + language: 'en', + worldviews: ['US'], + worldview_property: '_mbx_worldview', + compress: true + }, (err, buffer) => { + assert.ifError(err); + assert.ok(buffer); + assert.end(); + }); +}); + +test('[localize] parameter validation', (assert) => { + assert.throws(() => { + localize(); + }, /expected params and callback arguments/); + assert.throws(() => { + localize(Object(), Function(), 'something extra'); + }, /expected params and callback arguments/); + assert.throws(() => { + localize('not an object', Function()); + }, /first argument must be an object/); + assert.throws(() => { + localize(Object(), 'not a function'); + }, /second argument must be a callback function/); + assert.end(); +}); + +test('[localize] params.buffer', (assert) => { + localize({ + // buffer: // not defined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.buffer is required', 'expected error message'); + }); + + localize({ + buffer: 1, // not a buffer + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.buffer must be a Buffer', 'expected error message'); + }); + + localize({ + buffer: null, // set to "null" + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.buffer must be a Buffer', 'expected error message'); + }); + + localize({ + buffer: undefined, // set to "undefined" + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.buffer must be a Buffer', 'expected error message'); + }); + + localize({ + buffer: Object(), // not a true buffer + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.buffer is not a true Buffer', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.language', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + language: 1 + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language must be null or a string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + language: '' // empty string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language cannot be an empty string', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.language_property', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + language: 'es', + language_property: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_property must be a string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + language: 'es', + language_property: null // null value + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_property must be a string', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.language_prefix', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + language: 'es', + language_prefix: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_prefix must be a string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + language: 'es', + language_prefix: null // null value + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_prefix must be a string', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.worldviews', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + worldviews: 1 // not an array + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview must be an array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: [1, 2, 3] // array with non-strings + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview must be an array of strings', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['USA'] // array with >2 char strings + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview items must be strings of 2 characters', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.worldview_property', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['US'], + worldview_property: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_property must be a string', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.compress', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + compress: 1 // not a boolean + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.compress must be a boolean', 'expected error message'); + }); + + assert.end(); +}); diff --git a/test/vtcomposite-param-validation.test.js b/test/vtcomposite-param-validation.test.js deleted file mode 100644 index 6aeb1b32..00000000 --- a/test/vtcomposite-param-validation.test.js +++ /dev/null @@ -1,700 +0,0 @@ -var test = require('tape'); -var vt = require('../lib/index.js'); -var fs = require('fs'); -var path = require('path'); -var zlib = require('zlib'); -var mvtFixtures = require('@mapbox/mvt-fixtures'); - -const composite = vt.composite; -const localize = vt.localize; - -test('[composite] failure: fails without callback function', assert => { - try { - composite(); - } catch(err) { - assert.ok(/last argument must be a callback function/.test(err.message), 'expected error message'); - assert.end(); - } -}); - -test('[composite] failure: buffers is not an array', assert => { - composite('i am not an array', {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'first arg \'tiles\' must be an array of tile objects'); - assert.end(); - }); -}); - -test('[composite] failure: buffers array is empty', assert => { - const buffs = []; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'tiles\' array must be of length greater than 0'); - assert.end(); - }); -}); - -test('[composite] failure: item in buffers array is not an object', assert => { - const buffs = [ - 'not an object' - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'items in \'tiles\' array must be objects'); - assert.end(); - }); -}); - -test('[composite] failure: buffer value does not exist', assert => { - const buffs = [ - { - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'item in \'tiles\' array does not include a buffer value'); - assert.end(); - }); -}); - -test('[composite] failure: buffer value is null', assert => { - const buffs = [ - { - buffer: null, - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'buffer value in \'tiles\' array item is null or undefined'); - assert.end(); - }); -}); - -test('[composite] failure: buffer value is not a buffer', assert => { - const buffs = [ - { - buffer: 'not a buffer', - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'buffer value in \'tiles\' array item is not a true buffer'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object missing z value', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - // z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'item in \'tiles\' array does not include a \'z\' value'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object missing x value', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - // x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'item in \'tiles\' array does not include a \'x\' value'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object missing y value', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - // y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'item in \'tiles\' array does not include a \'y\' value'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object z value is not an int32', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 'zero', - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'z\' value in \'tiles\' array item is not an int32'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object x value is not an int32', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 'zero', - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'x\' value in \'tiles\' array item is not an int32'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object y value is not an int32', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 'zero' - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'y\' value in \'tiles\' array item is not an int32'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object z value is negative', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: -10, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'z\' value must not be less than zero'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object x value is negative', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: -5, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'x\' value must not be less than zero'); - assert.end(); - }); -}); - -test('[composite] failure: buffer object y value is negative', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: -4 - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'y\' value must not be less than zero'); - assert.end(); - }); -}); - -test('[composite] failure: layers option is not an array', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0, - layers: 'not an array' - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'layers\' value in the \'tiles\' array must be an array'); - assert.end(); - }); -}); - -test('[composite] failure: layers option is an empty array', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0, - layers: [] - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'layers\' array must be of length greater than 0'); - assert.end(); - }); -}); - -test('[composite] failure: layers option is an array with invalid types (not strings)', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0, - layers: [1, 2, 3, 'correct'] - } - ]; - composite(buffs, {z:3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'items in \'layers\' array must be strings'); - assert.end(); - }); -}); - -// TESTS FOR ZXY MAP REQUEST! - -test('[composite] failure: map request zxy missing z value', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'item in \'tiles\' array does not include a \'z\' value'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy missing x value', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'item in \'tiles\' array does not include a \'x\' value'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy missing y value', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, 'item in \'tiles\' array does not include a \'y\' value'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy z value is not an int32', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:'zero', x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'z\' value in \'tiles\' array item is not an int32'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy x value is not an int32', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:'zero', y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'x\' value in \'tiles\' array item is not an int32'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy y value is not an int32', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:'zero'}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'y\' value in \'tiles\' array item is not an int32'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy z value is negative', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 10, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:-3, x:1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'z\' value must not be less than zero'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy x value is negative', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:-1, y:0}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'x\' value must not be less than zero'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy y value is negative', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:3, x:1, y:-4}, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'y\' value must not be less than zero'); - assert.end(); - }); -}); - -test('[composite] failure: map request zxy is not an object', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, true, {}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'zxy_maprequest\' must be an object'); - assert.end(); - }); -}); - -test('[composite] failure: compress must be a boolean', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:0, x:0, y:0}, {compress:'hi'}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'compress\' must be a boolean'); - assert.end(); - }); -}); - -test('[composite] failure: options must be an object', assert => { - const buffs = [ - { - buffer: Buffer.from('hey'), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:0, x:0, y:0}, true, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'options\' arg must be an object'); - assert.end(); - }); -}); - -test('[composite] failure: buffer size is not int32', assert => { - const buffs = [ - { - buffer: new Buffer.alloc(10), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:0, x:0, y:0}, {buffer_size:'hi'}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'buffer_size\' must be an int32'); - assert.end(); - }); -}); - -test('[composite] failure: buffer size is not positive int32', assert => { - const buffs = [ - { - buffer: new Buffer.alloc(10), - z: 0, - x: 0, - y: 0 - } - ]; - composite(buffs, {z:0, x:0, y:0}, {buffer_size:-10}, function(err, result) { - assert.ok(err); - assert.equal(err.message, '\'buffer_size\' must be a positive int32'); - assert.end(); - }); -}); - -test('[localize] success with all parameters', (assert) => { - localize({ - buffer: mvtFixtures.get('064').buffer, - language: 'en', - worldviews: ['US'], - worldview_property: '_mbx_worldview', - compress: true - }, (err, buffer) => { - assert.ifError(err); - assert.ok(buffer); - assert.end(); - }); -}); - -test('[localize] parameter validation', (assert) => { - assert.throws(() => { - localize(); - }, /expected params and callback arguments/); - assert.throws(() => { - localize(Object(), Function(), 'something extra'); - }, /expected params and callback arguments/); - assert.throws(() => { - localize('not an object', Function()); - }, /first argument must be an object/); - assert.throws(() => { - localize(Object(), 'not a function'); - }, /second argument must be a callback function/); - assert.end(); -}); - -test('[localize] params.buffer', assert => { - localize({ - // buffer: // not defined - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.buffer is required', 'expected error message'); - }); - - localize({ - buffer: 1, // not a buffer - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.buffer must be a Buffer', 'expected error message'); - }); - - localize({ - buffer: null, // set to "null" - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.buffer must be a Buffer', 'expected error message'); - }); - - localize({ - buffer: undefined, // set to "undefined" - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.buffer must be a Buffer', 'expected error message'); - }); - - localize({ - buffer: Object(), // not a true buffer - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.buffer is not a true Buffer', 'expected error message'); - }); - - assert.end(); -}); - -test('[localize] params.language', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - language: 1 - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.language must be null or a string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('hi'), - language: '' // empty string - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.language cannot be an empty string', 'expected error message'); - }); - - assert.end(); -}); - -test('[localize] params.language_property', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - language: 'es', - language_property: 1 // not a string - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.language_property must be a string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - language: 'es', - language_property: null // null value - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.language_property must be a string', 'expected error message'); - }); - - assert.end(); -}); - -test('[localize] params.language_prefix', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - language: 'es', - language_prefix: 1 // not a string - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - language: 'es', - language_prefix: null // null value - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a string', 'expected error message'); - }); - - assert.end(); -}); - -test('[localize] params.worldviews', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - worldviews: 1 // not an array - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.worldview must be an array', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - worldviews: [1, 2, 3] // array with non-strings - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.worldview must be an array of strings', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - worldviews: ['USA'] // array with >2 char strings - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.worldview items must be strings of 2 characters', 'expected error message'); - }); - - assert.end(); -}); - -test('[localize] params.worldview_property', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - worldviews: ['US'], - worldview_property: 1 // not a string - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.worldview_property must be a string', 'expected error message'); - }); - - assert.end(); -}); - -test('[localize] params.compress', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - compress: 1 // not a boolean - }, function (err) { - assert.ok(err); - assert.equal(err.message, 'params.compress must be a boolean', 'expected error message'); - }); - - assert.end(); -}); \ No newline at end of file From c8750a214056be507170be2d07509406dfd8c0fd Mon Sep 17 00:00:00 2001 From: lily-chai Date: Fri, 21 Oct 2022 13:58:13 -0700 Subject: [PATCH 23/41] fix localize param validation tests --- src/vtcomposite.cpp | 40 +- ...omposite-localize-param-validation.test.js | 384 ++++++++++++++++-- test/vtcomposite-localize-worldview.test.js | 34 -- 3 files changed, 381 insertions(+), 77 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index feff90c4..15cffa94 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -991,6 +991,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) } Napi::Object params = info[0].As(); + // empty string to check against + Napi::String empty_string = Napi::String::New(info.Env(), ""); + // params.buffer (required) if (!params.Has(Napi::String::New(info.Env(), "buffer"))) { @@ -1023,9 +1026,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) for (std::uint32_t lg = 0; lg < num_languages; ++lg) { Napi::Value language_item_val = language_array.Get(lg); - if (!language_item_val.IsString()) + if (!language_item_val.IsString() || language_item_val == empty_string) { - return utils::CallbackError("params.languages must be an array of strings", info); + return utils::CallbackError("params.languages must be an array of non-empty strings", info); } std::string language_item = language_item_val.As(); languages.push_back(language_item); @@ -1047,9 +1050,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "language_property"))) { Napi::Value language_property_val = params.Get(Napi::String::New(info.Env(), "language_property")); - if (!language_property_val.IsString()) + if (!language_property_val.IsString() || language_property_val == empty_string) { - return utils::CallbackError("params.language_property must be a string", info); + return utils::CallbackError("params.language_property must be a non-empty string", info); } language_property = language_property_val.As(); } @@ -1058,9 +1061,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "language_prefix"))) { Napi::Value language_prefix_val = params.Get(Napi::String::New(info.Env(), "language_prefix")); - if (!language_prefix_val.IsString()) + if (!language_prefix_val.IsString() || language_prefix_val == empty_string) { - return utils::CallbackError("params.language_prefix must be a string", info); + return utils::CallbackError("params.language_prefix must be a non-empty string", info); } language_prefix = language_prefix_val.As(); } @@ -1079,9 +1082,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) for (std::uint32_t wv = 0; wv < num_worldviews; ++wv) { Napi::Value worldview_item_val = worldview_array.Get(wv); - if (!worldview_item_val.IsString()) + if (!worldview_item_val.IsString() || worldview_item_val == empty_string) { - return utils::CallbackError("params.worldviews must be an array of strings", info); + return utils::CallbackError("params.worldviews must be an array of non-empty strings", info); } std::string worldview_item = worldview_item_val.As(); worldviews.push_back(worldview_item); @@ -1104,9 +1107,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "worldview_property"))) { Napi::Value worldview_property_val = params.Get(Napi::String::New(info.Env(), "worldview_property")); - if (!worldview_property_val.IsString()) + if (!worldview_property_val.IsString() || worldview_property_val == empty_string) { - return utils::CallbackError("params.worldview_property must be a string", info); + return utils::CallbackError("params.worldview_property must be a non-empty string", info); } worldview_property = worldview_property_val.As(); } @@ -1115,9 +1118,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "worldview_prefix"))) { Napi::Value worldview_prefix_val = params.Get(Napi::String::New(info.Env(), "worldview_prefix")); - if (!worldview_prefix_val.IsString()) + if (!worldview_prefix_val.IsString() || worldview_prefix_val == empty_string) { - return utils::CallbackError("params.worldview_prefix must be a string", info); + return utils::CallbackError("params.worldview_prefix must be a non-empty string", info); } worldview_prefix = worldview_prefix_val.As(); } @@ -1126,20 +1129,19 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "worldview_default"))) { Napi::Value worldview_default_val = params.Get(Napi::String::New(info.Env(), "worldview_default")); - if (!worldview_default_val.IsString()) + if (!worldview_default_val.IsString() || worldview_default_val == empty_string) { - return utils::CallbackError("params.worldview_default must be a string", info); + return utils::CallbackError("params.worldview_default must be a non-empty string", info); } - worldview_default = worldview_default_val.As(); } // params.class_property (optional) if (params.Has(Napi::String::New(info.Env(), "class_property"))) { Napi::Value class_property_val = params.Get(Napi::String::New(info.Env(), "class_property")); - if (!class_property_val.IsString()) + if (!class_property_val.IsString() || class_property_val == empty_string) { - return utils::CallbackError("params.class_property must be a string", info); + return utils::CallbackError("params.class_property must be a non-empty string", info); } class_property = class_property_val.As(); } @@ -1148,9 +1150,9 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "class_prefix"))) { Napi::Value class_prefix_val = params.Get(Napi::String::New(info.Env(), "class_prefix")); - if (!class_prefix_val.IsString()) + if (!class_prefix_val.IsString() || class_prefix_val == empty_string) { - return utils::CallbackError("params.class_prefix must be a string", info); + return utils::CallbackError("params.class_prefix must be a non-empty string", info); } class_prefix = class_prefix_val.As(); } diff --git a/test/vtcomposite-localize-param-validation.test.js b/test/vtcomposite-localize-param-validation.test.js index d03bb1cf..73ce2a80 100644 --- a/test/vtcomposite-localize-param-validation.test.js +++ b/test/vtcomposite-localize-param-validation.test.js @@ -9,9 +9,14 @@ const localize = vt.localize; test('[localize] success with all parameters', (assert) => { localize({ buffer: mvtFixtures.get('064').buffer, - language: 'en', + languages: ['en'], + language_property: 'lang', + language_prefix: 'blah', worldviews: ['US'], - worldview_property: '_mbx_worldview', + worldview_property: 'wv', + worldview_prefix: 'whatever', + class_property: 'klass', + class_prefix: 'sth', compress: true }, (err, buffer) => { assert.ifError(err); @@ -75,21 +80,85 @@ test('[localize] params.buffer', (assert) => { assert.end(); }); -test('[localize] params.language', (assert) => { +test('[localize] params.languages', (assert) => { + localize({ + buffer: Buffer.from('hi'), + languages: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be a non-empty array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + languages: null + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be a non-empty array', 'expected error message'); + }); + localize({ buffer: Buffer.from('howdy'), - language: 1 + languages: 1 + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be a non-empty array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + languages: '' // empty string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be a non-empty array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + languages: 'hi' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be a non-empty array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + languages: [] }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.language must be null or a string', 'expected error message'); + assert.equal(err.message, 'params.languages must be a non-empty array', 'expected error message'); }); localize({ buffer: Buffer.from('hi'), - language: '' // empty string + languages: [1, 2, 3] }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.language cannot be an empty string', 'expected error message'); + assert.equal(err.message, 'params.languages must be an array of non-empty strings', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + languages: ['hi', null] + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be an array of non-empty strings', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + languages: [undefined, 'hi'] + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be an array of non-empty strings', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('hi'), + languages: ['hi', ''] + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.languages must be an array of non-empty strings', 'expected error message'); }); assert.end(); @@ -98,20 +167,38 @@ test('[localize] params.language', (assert) => { test('[localize] params.language_property', (assert) => { localize({ buffer: Buffer.from('howdy'), - language: 'es', + languages: ['es'], language_property: 1 // not a string }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.language_property must be a string', 'expected error message'); + assert.equal(err.message, 'params.language_property must be a non-empty string', 'expected error message'); }); localize({ buffer: Buffer.from('howdy'), - language: 'es', + languages: ['es'], language_property: null // null value }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.language_property must be a string', 'expected error message'); + assert.equal(err.message, 'params.language_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + languages: ['es'], + language_property: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + languages: ['es'], + language_property: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_property must be a non-empty string', 'expected error message'); }); assert.end(); @@ -120,48 +207,122 @@ test('[localize] params.language_property', (assert) => { test('[localize] params.language_prefix', (assert) => { localize({ buffer: Buffer.from('howdy'), - language: 'es', + languages: ['es'], language_prefix: 1 // not a string }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a string', 'expected error message'); + assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); }); localize({ buffer: Buffer.from('howdy'), - language: 'es', + languages: ['es'], language_prefix: null // null value }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a string', 'expected error message'); + assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + languages: ['es'], + language_prefix: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + languages: ['es'], + language_prefix: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); }); assert.end(); }); test('[localize] params.worldviews', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + worldviews: null + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldviews must be a non-empty array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldviews must be a non-empty array', 'expected error message'); + }); + localize({ buffer: Buffer.from('howdy'), worldviews: 1 // not an array }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.worldview must be an array', 'expected error message'); + assert.equal(err.message, 'params.worldviews must be a non-empty array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldviews must be a non-empty array', 'expected error message'); }); localize({ buffer: Buffer.from('howdy'), - worldviews: [1, 2, 3] // array with non-strings + worldviews: 'US' }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.worldview must be an array of strings', 'expected error message'); + assert.equal(err.message, 'params.worldviews must be a non-empty array', 'expected error message'); }); localize({ buffer: Buffer.from('howdy'), - worldviews: ['USA'] // array with >2 char strings + worldviews: [] }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.worldview items must be strings of 2 characters', 'expected error message'); + assert.equal(err.message, 'params.worldviews must be a non-empty array', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: [1, 2, 3] + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldviews must be an array of non-empty strings', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['hi', null] + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldviews must be an array of non-empty strings', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: [undefined, 'howdy'] + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldviews must be an array of non-empty strings', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['howdy', ''] + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldviews must be an array of non-empty strings', 'expected error message'); }); assert.end(); @@ -174,20 +335,195 @@ test('[localize] params.worldview_property', (assert) => { worldview_property: 1 // not a string }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.worldview_property must be a string', 'expected error message'); + assert.equal(err.message, 'params.worldview_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['US'], + worldview_property: null + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['US'], + worldview_property: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['US'], + worldview_property: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_property must be a non-empty string', 'expected error message'); }); assert.end(); }); -test('[localize] params.compress', (assert) => { +test('[localize] params.worldview_prefix', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['US'], + worldview_prefix: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['US'], + worldview_prefix: null + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldviews: ['US'], + worldview_prefix: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); + }); + localize({ buffer: Buffer.from('howdy'), - compress: 1 // not a boolean + worldviews: ['US'], + worldview_prefix: '' }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.compress must be a boolean', 'expected error message'); + assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); }); assert.end(); }); + +test('[localize] params.worldview_default', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + worldview_default: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_default must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldview_default: null + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_default must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldview_default: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_default must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + worldview_default: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.worldview_default must be a non-empty string', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.class_property', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + class_property: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + class_property: null + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + class_property: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_property must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + class_property: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_property must be a non-empty string', 'expected error message'); + }); + + assert.end(); +}); + +test('[localize] params.class_prefix', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + class_prefix: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + class_prefix: null + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + class_prefix: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + class_prefix: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); + }); + + assert.end(); +}); + +// test('[localize] params.compress', (assert) => { +// localize({ +// buffer: Buffer.from('howdy'), +// compress: 1 // not a boolean +// }, (err) => { +// assert.ok(err); +// assert.equal(err.message, 'params.compress must be a boolean', 'expected error message'); +// }); +// +// assert.end(); +// }); diff --git a/test/vtcomposite-localize-worldview.test.js b/test/vtcomposite-localize-worldview.test.js index 51b0dc53..19df501c 100644 --- a/test/vtcomposite-localize-worldview.test.js +++ b/test/vtcomposite-localize-worldview.test.js @@ -1193,37 +1193,3 @@ test('[localize worldview] requesting localize langauge; feature not in the defa assert.end(); }); }); - -/** **************************************************************************** - * TEST SET 6: - * - test invalid params - ******************************************************************************/ - -test('[localize worldview] invalid params', (assert) => { - const feature = mvtFixtures.create({ - layers: [ - { - version: 2, - name: 'admin', - features: [ - { - id: 10, - tags: [0, 0], - type: 1, // point - geometry: [9, 54, 38] - } - ], - keys: ['_mbx_worldview'], - values: [ - { string_value: 'JP' } - ], - extent: 4096 - } - ] - }).buffer; - - assert.throws(() => { localize({ buffer: feature, worldviews: [] }); }); - assert.throws(() => { localize({ buffer: feature, worldviews: null }); }); - assert.throws(() => { localize({ buffer: feature, worldviews: undefined }); }); - assert.end(); -}); From 938ef20803f8009837c3bf9194d1e8d695dee613 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Fri, 21 Oct 2022 14:45:30 -0700 Subject: [PATCH 24/41] fix default worldview code, add partial string match test --- src/vtcomposite.cpp | 10 ++--- test/vtcomposite-localize-worldview.test.js | 43 ++++++++++++++++++++- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 15cffa94..e4d98ac0 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -980,7 +980,6 @@ Napi::Value localize(Napi::CallbackInfo const& info) // param that'll be deduced from other params bool return_localized_tile = false; // true only if languages or worldviews exist - bool worldviews_provided = false; // true if worldviews exist // validate params object Napi::Value params_val = info[0]; @@ -1090,7 +1089,6 @@ Napi::Value localize(Napi::CallbackInfo const& info) worldviews.push_back(worldview_item); } return_localized_tile = true; - worldviews_provided = true; } else { @@ -1133,6 +1131,7 @@ Napi::Value localize(Napi::CallbackInfo const& info) { return utils::CallbackError("params.worldview_default must be a non-empty string", info); } + worldview_default = worldview_default_val.As(); } // params.class_property (optional) @@ -1173,15 +1172,12 @@ Napi::Value localize(Napi::CallbackInfo const& info) // value of both params.languages and params.worldviews. if (return_localized_tile) { - if (!worldviews_provided) + if (worldviews.empty()) { worldviews.reserve(1); worldviews.push_back(worldview_default); } - else - { - // do nothing – already knows which worldview to return - } + // else do nothing – already knows which worldview to return } std::unique_ptr baton_data = std::make_unique( diff --git a/test/vtcomposite-localize-worldview.test.js b/test/vtcomposite-localize-worldview.test.js index 19df501c..05fd4f75 100644 --- a/test/vtcomposite-localize-worldview.test.js +++ b/test/vtcomposite-localize-worldview.test.js @@ -284,6 +284,42 @@ test('[localize worldview] requesting localized worldview; feature with compatib }); }); +test('[localize worldview] requesting localized worldview; feature with compatible worldview key an irrelevant worldview (test partial matching)', (assert) => { + const feature = mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'admin', + features: [ + { + id: 10, + tags: [0, 0], + type: 1, // point + geometry: [9, 54, 38] + } + ], + keys: ['_mbx_worldview'], + values: [ + { string_value: 'USSR' } + ], + extent: 4096 + } + ] + }).buffer; + + const params = { + buffer: feature, + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.ifError(err); + const tile = vtinfo(vtBuffer); + assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.end(); + }); +}); + test('[localize worldview] requesting localized worldview; feature with incompatible worldview key in "all" worldviews', (assert) => { const feature = mvtFixtures.create({ layers: [ @@ -504,13 +540,16 @@ test('[localize worldview] requesting localized langauge; feature with compatibl const params = { buffer: feature, - worldviews: ['en'] + languages: ['en'] + // worldivew_default is US }; localize(params, (err, vtBuffer) => { assert.ifError(err); const tile = vtinfo(vtBuffer); - assert.deepEqual(tile.layers, {}, 'has no feature'); + assert.ok('admin' in tile.layers, 'has admin layer'); + assert.equal(tile.layers.admin.length, 1, 'has one feature'); + assert.deepEqual(tile.layers.admin.feature(0).properties, { worldview: 'US' }, 'expected properties'); assert.end(); }); }); From 7825b6b14fd498bf482025cf3774ff73d432c59b Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 24 Oct 2022 15:06:36 -0700 Subject: [PATCH 25/41] fix language test --- test/vtcomposite-localize-language.test.js | 270 ++++++++++++++++----- 1 file changed, 205 insertions(+), 65 deletions(-) diff --git a/test/vtcomposite-localize-language.test.js b/test/vtcomposite-localize-language.test.js index 4650b75a..8dd4b9fb 100644 --- a/test/vtcomposite-localize-language.test.js +++ b/test/vtcomposite-localize-language.test.js @@ -8,11 +8,12 @@ const mvtFixtures = require('@mapbox/mvt-fixtures'); const test = require('tape'); const zlib = require('zlib'); + test('[localize] success: buffer size stays the same when no changes needed', (assert) => { const singlePointBuffer = mvtFixtures.get('002').buffer; const params = { buffer: singlePointBuffer, - language: 'piglatin' + languages: ['piglatin'] }; localize(params, (err, vtBuffer) => { @@ -29,7 +30,7 @@ test('[localize] success: single gzipped VT', (assert) => { const gzipped_buffer = zlib.gzipSync(singlePointBuffer); const params = { buffer: gzipped_buffer, - language: 'piglatin' + languages: ['piglatin'] }; localize(params, (err, vtBuffer) => { assert.notOk(err); @@ -42,7 +43,7 @@ test('[localize] success: gzipped output', (assert) => { const singlePointBuffer = mvtFixtures.get('002').buffer; const params = { buffer: singlePointBuffer, - language: 'piglatin', + languages: ['piglatin'], compress: true }; localize(params, (err, vtBuffer) => { @@ -57,7 +58,7 @@ test('[localize] success: gzipped input and output', (assert) => { const gzipped_buffer = zlib.gzipSync(singlePointBuffer); const params = { buffer: gzipped_buffer, - language: 'piglatin', + languages: ['piglatin'], compress: true }; localize(params, (err, vtBuffer) => { @@ -77,7 +78,7 @@ test('[localize] success - same layer names, same features, same extents', (asse const bottomLayerExtent = initialOutputInfo.layers.bottom.extent; const params = { buffer: initialBuffer, - language: 'es' + languages: ['es'] }; localize(params, (err, localizedBuffer) => { @@ -96,13 +97,13 @@ test('[localize] success - same layer names, same features, same extents', (asse }); }); -test('[localize] success - feature without name_ or _mbx prefixed properties has same properties', (assert) => { +test('[localize] success - feature without name_* or _mbx_name_* prefixed properties has same properties', (assert) => { const initialBuffer = mvtFixtures.get('063').buffer; const initialOutputInfo = vtinfo(initialBuffer); const initialFeature = initialOutputInfo.layers.top.feature(0); const params = { buffer: initialBuffer, - language: 'es' + languages: ['es'] // initialFeature does not have name_es }; localize(params, (err, vtBuffer) => { @@ -117,19 +118,17 @@ test('[localize] success - feature without name_ or _mbx prefixed properties has test('[localize] success - feature with specified language in name_{language} property', (assert) => { const initialBuffer = mvtFixtures.get('063').buffer; const initialProperties = { - 'name': 'Espana', - '_mbx_name_de': 'Spanien', - 'name_fr': 'Espagne', - '_mbx_name_fr': 'Espagne', - 'name_en': 'Spain', - 'population': 20 + name: 'Espana', + _mbx_name_de: 'Spanien', + name_fr: 'Espagne', + _mbx_name_fr: 'Espagne', + name_en: 'Spain', + population: 20 }; const localizedProperties = { - 'name': 'Spain', - 'name_local': 'Espana', - 'name_fr': 'Espagne', - 'name_en': 'Spain', - 'population': 20 + name: 'Spain', + name_local: 'Espana', + population: 20 }; const initialOutputInfo = vtinfo(initialBuffer); const feature = initialOutputInfo.layers.bottom.feature(1); @@ -137,7 +136,7 @@ test('[localize] success - feature with specified language in name_{language} pr const params = { buffer: initialBuffer, - language: 'en' + languages: ['en'] }; localize(params, (err, vtBuffer) => { @@ -152,18 +151,17 @@ test('[localize] success - feature with specified language in name_{language} pr test('[localize] success - feature with specified language in _mbx_name_{language} property', (assert) => { const initialBuffer = mvtFixtures.get('063').buffer; const initialProperties = { - 'name': 'Germany', - 'name_en': 'Germany', - 'name_fr': 'Allemagne', - '_mbx_name_fr': 'La Allemagne', - '_mbx_name_de': 'Deutschland', - '_mbx_other': 'Alemania' + name: 'Germany', + name_en: 'Germany', + name_fr: 'Allemagne', + _mbx_name_fr: 'La Allemagne', + _mbx_name_de: 'Deutschland', + _mbx_other: 'Alemania' }; const localizedProperties = { - 'name': 'Deutschland', - 'name_local': 'Germany', - 'name_en': 'Germany', - 'name_fr': 'Allemagne', + name: 'Deutschland', + name_local: 'Germany', + _mbx_other: 'Alemania', }; const initialOutputInfo = vtinfo(initialBuffer); const feature = initialOutputInfo.layers.bottom.feature(0); @@ -171,14 +169,14 @@ test('[localize] success - feature with specified language in _mbx_name_{languag const params = { buffer: initialBuffer, - language: 'de' + languages: ['de'] }; localize(params, (err, vtBuffer) => { assert.notOk(err); const outputInfo = vtinfo(vtBuffer); const localizedFeature = outputInfo.layers.bottom.feature(0); - assert.deepEqual(localizedFeature.properties, localizedProperties, 'expected name_local, updated name, dropped _mbx properties'); + assert.deepEqual(localizedFeature.properties, localizedProperties, 'expected name_local, updated name, dropped _mbx_name_* properties'); assert.end(); }); }); @@ -186,18 +184,17 @@ test('[localize] success - feature with specified language in _mbx_name_{languag test('[localize] success - feature with specified language in both name_{language} and _mbx_name_{language} properties', (assert) => { const initialBuffer = mvtFixtures.get('063').buffer; const initialProperties = { - 'name': 'Germany', - 'name_en': 'Germany', - 'name_fr': 'Allemagne', - '_mbx_name_fr': 'La Allemagne', - '_mbx_name_de': 'Deutschland', - '_mbx_other': 'Alemania' + name: 'Germany', + name_en: 'Germany', + _mbx_name_fr: 'La Allemagne', + name_fr: 'Allemagne', + _mbx_name_de: 'Deutschland', + _mbx_other: 'Alemania' }; const localizedProperties = { - 'name': 'Allemagne', - 'name_local': 'Germany', - 'name_en': 'Germany', - 'name_fr': 'Allemagne', // choosing first encountered property in (name_fr, _mbx_name_fr) + name: 'Allemagne', // name_{language} takes precedence over _mbx_name_{language} + name_local: 'Germany', + _mbx_other: 'Alemania' }; const initialOutputInfo = vtinfo(initialBuffer); const feature = initialOutputInfo.layers.bottom.feature(0); @@ -205,14 +202,14 @@ test('[localize] success - feature with specified language in both name_{languag const params = { buffer: initialBuffer, - language: 'fr' + languages: ['fr'] }; localize(params, (err, vtBuffer) => { assert.notOk(err); const outputInfo = vtinfo(vtBuffer); const localizedFeature = outputInfo.layers.bottom.feature(0); - assert.deepEqual(localizedFeature.properties, localizedProperties, 'expected name_local, updated name, dropped _mbx properties'); + assert.deepEqual(localizedFeature.properties, localizedProperties, 'expected name_local, updated name, dropped _mbx_name_* properties'); assert.end(); }); }); @@ -230,7 +227,7 @@ test('[localize] success - _mbx prefixed property keys removed from all layers', 'population' ]; const topLayerKeysExpected = topLayerKeys; - const bottomLayerKeysExpected = ['name_en', 'name_fr', 'name_local', 'name', 'population']; + const bottomLayerKeysExpected = ['_mbx_other', 'name', 'name_local', 'population']; const initialOutputInfo = vtinfo(initialBuffer); assert.deepEqual(initialOutputInfo.layers.top._keys, topLayerKeys, 'expected initial keys'); @@ -238,19 +235,19 @@ test('[localize] success - _mbx prefixed property keys removed from all layers', const params = { buffer: initialBuffer, - language: 'gr' + languages: ['gr'] }; localize(params, (err, vtBuffer) => { assert.notOk(err); const outputInfo = vtinfo(vtBuffer); assert.deepEqual(outputInfo.layers.top._keys, topLayerKeysExpected, 'expected same keys'); - assert.deepEqual(outputInfo.layers.bottom._keys, bottomLayerKeysExpected, 'expected added name_local, dropped _mbx keys'); + assert.deepEqual(outputInfo.layers.bottom._keys, bottomLayerKeysExpected, 'expected added name_local, dropped _mbx_name_* keys'); assert.end(); }); }); -test('[localize] success - no language specified', (assert) => { +test('[localize] success - no language specified but has worldview specified (i.e. return localized features)', (assert) => { const initialBuffer = mvtFixtures.get('063').buffer; const topLayerKeys = ['population']; const bottomLayerKeys = [ @@ -263,20 +260,17 @@ test('[localize] success - no language specified', (assert) => { 'population' ]; const topLayerKeysExpected = topLayerKeys; - const bottomLayerKeysExpected = ['name', 'name_en', 'name_fr', 'name_local', 'population']; + const bottomLayerKeysExpected = ['_mbx_other', 'name', 'name_local', 'population']; const localizedProperties0 = { - 'name': 'Germany', - 'name_local': 'Germany', - 'name_en': 'Germany', - 'name_fr': 'Allemagne', + name: 'Germany', + name_local: 'Germany', + _mbx_other: 'Alemania' }; const localizedProperties1 = { - 'name': 'Espana', - 'name_local': 'Espana', - 'name_fr': 'Espagne', - 'name_en': 'Spain', - 'population': 20 + name: 'Espana', + name_local: 'Espana', + population: 20 }; const initialOutputInfo = vtinfo(initialBuffer); @@ -285,7 +279,57 @@ test('[localize] success - no language specified', (assert) => { const params = { buffer: initialBuffer, - language: null + worldviews: ['US'] + }; + + localize(params, (err, vtBuffer) => { + assert.notOk(err); + const outputInfo = vtinfo(vtBuffer); + const localizedFeature0 = outputInfo.layers.bottom.feature(0); + const localizedFeature1 = outputInfo.layers.bottom.feature(1); + assert.deepEqual(localizedFeature0.properties, localizedProperties0, 'expected same name, dropped _mbx_name_* properties'); + assert.deepEqual(localizedFeature1.properties, localizedProperties1, 'expected same name, dropped _mbx_name_* properties'); + assert.deepEqual(outputInfo.layers.top._keys, topLayerKeysExpected, 'expected same keys'); + assert.deepEqual(outputInfo.layers.bottom._keys, bottomLayerKeysExpected, 'expected dropped _mbx_name_* keys'); + assert.end(); + }); +}); + +test('[localize] success - no language specified, no worldview specified (i.e. return non-localized features)', (assert) => { + const initialBuffer = mvtFixtures.get('063').buffer; + const topLayerKeys = ['population']; + const bottomLayerKeys = [ + 'name', + 'name_en', + 'name_fr', + '_mbx_name_fr', + '_mbx_name_de', + '_mbx_other', + 'population' + ]; + const topLayerKeysExpected = topLayerKeys; + const bottomLayerKeysExpected = ['name_en', 'name_fr', '_mbx_other', 'name', 'population']; + + const localizedProperties0 = { + name: 'Germany', + name_en: 'Germany', + name_fr: 'Allemagne', + _mbx_other: 'Alemania' + }; + const localizedProperties1 = { + name: 'Espana', + name_fr: 'Espagne', + name_en: 'Spain', + population: 20 + }; + + const initialOutputInfo = vtinfo(initialBuffer); + assert.deepEqual(initialOutputInfo.layers.top._keys, topLayerKeys, 'expected initial keys'); + assert.deepEqual(initialOutputInfo.layers.bottom._keys, bottomLayerKeys, 'expected initial keys'); + + const params = { + buffer: initialBuffer + // no lanugages and worldviews = return non-localized tile }; localize(params, (err, vtBuffer) => { @@ -293,10 +337,10 @@ test('[localize] success - no language specified', (assert) => { const outputInfo = vtinfo(vtBuffer); const localizedFeature0 = outputInfo.layers.bottom.feature(0); const localizedFeature1 = outputInfo.layers.bottom.feature(1); - assert.deepEqual(localizedFeature0.properties, localizedProperties0, 'expected same name, dropped _mbx properties'); - assert.deepEqual(localizedFeature1.properties, localizedProperties1, 'expected same name, dropped _mbx properties'); + assert.deepEqual(localizedFeature0.properties, localizedProperties0, 'expected same name, dropped _mbx_name_* properties'); + assert.deepEqual(localizedFeature1.properties, localizedProperties1, 'expected same name, dropped _mbx_name_* properties'); assert.deepEqual(outputInfo.layers.top._keys, topLayerKeysExpected, 'expected same keys'); - assert.deepEqual(outputInfo.layers.bottom._keys, bottomLayerKeysExpected, 'expected dropped _mbx keys'); + assert.deepEqual(outputInfo.layers.bottom._keys, bottomLayerKeysExpected, 'expected dropped _mbx_name_* keys'); assert.end(); }); }); @@ -330,7 +374,7 @@ test('[localize language] custom params.language_property and params.language_pr } ] }).buffer, - language: 'jp', + languages: ['jp'], language_property: 'language', language_prefix: '_drop_me_' }; @@ -341,7 +385,6 @@ test('[localize language] custom params.language_property and params.language_pr assert.equal(info.layers.places.length, 1, 'expected number of features'); assert.deepEqual(info.layers.places.feature(0).properties, { language: 'kon\'nichiwa', - language_es: 'hola', language_local: 'hello' }, 'expected properties'); assert.end(); @@ -375,7 +418,7 @@ test('[localize language] language codes >2 characters are viable translations', } ] }).buffer, - language: 'zh-Hant', + languages: ['zh-Hant'], language_property: 'language', language_prefix: '_pre_' }; @@ -390,4 +433,101 @@ test('[localize language] language codes >2 characters are viable translations', }, 'expected properties'); assert.end(); }); -}); \ No newline at end of file +}); + +test('[localize language] fallback to second language if the first does not exist', (assert) => { + const params = { + buffer: mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'places', + features: [ + { + id: 10, + tags: [ + 0, 0, // language: hello + 1, 1, // _pre_language_zh-Hant: Nǐ hǎo + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'language', '_pre_language_zh-Hant' ], + values: [ + { string_value: 'hello' }, + { string_value: 'Nǐ hǎo' } + ], + extent: 4096 + } + ] + }).buffer, + languages: ['en', 'zh-Hant'], + language_property: 'language', + language_prefix: '_pre_' + }; + + localize(params, (err, buffer) => { + assert.notOk(err); + const info = vtinfo(buffer); + assert.equal(info.layers.places.length, 1, 'expected number of features'); + assert.deepEqual(info.layers.places.feature(0).properties, { + language: 'Nǐ hǎo', + language_local: 'hello' + }, 'expected properties'); + assert.end(); + }); +}); + +test('[localize language] _mbx_worldview and _mbx_class dropped; other _mbx_* are kept', (assert) => { + const params = { + buffer: mvtFixtures.create({ + layers: [ + { + version: 2, + name: 'places', + features: [ + { + id: 10, + tags: [ + 0, 0, // name: hello + 1, 1, // _mbx_name_zh-Hant: Nǐ hǎo, + 2, 2, + 3, 3, + 4, 4 + ], + type: 1, // point + geometry: [ 9, 54, 38 ] + } + ], + keys: [ 'name', '_mbx_name_zh-Hant', '_mbx_worldview', '_mbx_class', '_mbx_other'], + values: [ + { string_value: 'hello' }, + { string_value: 'Nǐ hǎo' }, + { string_value: 'CN' }, + { string_value: 'sea'}, + { string_value: 'blah'}, + ], + extent: 4096 + } + ] + }).buffer, + languages: ['fr', 'de', 'zh-Hant'], + // use default name-, worldview- and class-related params, except for worldview_default + worldview_default: 'CN' + }; + + localize(params, (err, buffer) => { + assert.notOk(err); + const info = vtinfo(buffer); + assert.equal(info.layers.places.length, 1, 'expected number of features'); + assert.deepEqual(info.layers.places.feature(0).properties, { + name: 'Nǐ hǎo', + name_local: 'hello', + worldview: 'CN', + class: 'sea', + _mbx_other: 'blah' + }, 'expected properties'); + assert.end(); + }); +}); From 287fc2086c72e30304cb9fcf7c36bcfbae2c573e Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 24 Oct 2022 15:15:55 -0700 Subject: [PATCH 26/41] add checks for legacy params --- src/vtcomposite.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index e4d98ac0..35801edc 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -1010,6 +1010,11 @@ Napi::Value localize(Napi::CallbackInfo const& info) } buffer = buffer_obj.As>(); + // params.language is an invalid param + if (params.Has(Napi::String::New(info.Env(), "language"))) + { + return utils::CallbackError("params.language is an invalid param... do you mean params.languages?", info); + } // params.languages (optional) if (params.Has(Napi::String::New(info.Env(), "languages"))) { @@ -1067,6 +1072,11 @@ Napi::Value localize(Napi::CallbackInfo const& info) language_prefix = language_prefix_val.As(); } + // params.worldview is an invalid param + if (params.Has(Napi::String::New(info.Env(), "worldview"))) + { + return utils::CallbackError("params.worldview is an invalid param... do you mean params.worldviews?", info); + } // params.worldviews (optional) if (params.Has(Napi::String::New(info.Env(), "worldviews"))) { From 32665e7ab3393b31fbb162015f00afde15ccd359 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 24 Oct 2022 15:22:15 -0700 Subject: [PATCH 27/41] Revert "[revert me] comment out make_global_settings" This reverts commit 05dc619937b9a3fef6ee4c3d493dcb786a06778c. --- CONTRIBUTING.md | 2 +- binding.gyp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e44656d6..8bf37a4a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ npm install -g documentation npm run docs ``` -* Note for MacOS: if you're having problem building on MacOS, try commenting out [`make_global_settings` in binding.gyp](https://github.com/mapbox/vtcomposite/blob/main/binding.gyp#L5-L9) (for more info see [this](https://github.com/mapbox/node-cpp-skel/pull/169#issuecomment-1068127191)). +* Note for MacOS: if you're having issue building on MacOS, try commenting out [`make_global_settings` in binding.gyp](https://github.com/mapbox/vtcomposite/blob/main/binding.gyp#L5-L9) (for more info see [this](https://github.com/mapbox/node-cpp-skel/pull/169#issuecomment-1068127191)). # Benchmarks diff --git a/binding.gyp b/binding.gyp index c5ea5f42..d85699c2 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,11 +2,11 @@ { # https://github.com/springmeyer/gyp/blob/master/test/make_global_settings/wrapper/wrapper.gyp 'make_global_settings': [ - # ['CXX', '<(module_root_dir)/mason_packages/.link/bin/clang++'], - # ['CC', '<(module_root_dir)/mason_packages/.link/bin/clang'], - # ['LINK', '<(module_root_dir)/mason_packages/.link/bin/clang++'], - # ['AR', '<(module_root_dir)/mason_packages/.link/bin/llvm-ar'], - # ['NM', '<(module_root_dir)/mason_packages/.link/bin/llvm-nm'] + ['CXX', '<(module_root_dir)/mason_packages/.link/bin/clang++'], + ['CC', '<(module_root_dir)/mason_packages/.link/bin/clang'], + ['LINK', '<(module_root_dir)/mason_packages/.link/bin/clang++'], + ['AR', '<(module_root_dir)/mason_packages/.link/bin/llvm-ar'], + ['NM', '<(module_root_dir)/mason_packages/.link/bin/llvm-nm'] ], 'includes': [ 'common.gypi'], # brings in a default set of options that are inherited from gyp 'variables': { # custom variables we use specific to this file From ae376792ff0aa817b3dd2f81cfc0178f3a30e029 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 24 Oct 2022 16:21:18 -0700 Subject: [PATCH 28/41] update readme --- README.md | 96 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 1eb47922..3a50171f 100644 --- a/README.md +++ b/README.md @@ -73,29 +73,81 @@ A filtering function for modifying a tile's features and properties to support l #### Parameters - `params` **Object** - - `params.buffer` **Buffer** a vector tile buffer, gzip compressed or not - - `params.compress` **Boolean** a boolean value indicating whether or not to return a compressed buffer. Default is to return an uncompressed buffer. (optional, default `false`) - - `params.language` **String** the IETF BCP 47 language code used to search for matching translations available in a feature's properties. All language-related properties must match the following format: `{language_prefix}{language_property}_{language}`. Default properties are `_mbx_name_{language}`. Example `_mbx_name_jp` property includes the Japanese translation for the value in `name`. - - If a feature has a matching translation, the feature redefines the `{language_property}` to the value of the discovered translation. - - If a feature has a matching translation, the original value from `{language_property}` is retained in a newly created value `{language_property}_local`. - - If a translation is from a property _without_ a `{language_prefix}`, the property is retained in the final tile. - - If a translation is from a property _with_ a `{language_prefix}`, the property is dropped in the final tile. This allows users to add as many languages as desired to the vector tile and eventually drop them before using the tile. - - If a feature does not have a matching translation, no fields are modified. - - In any case, all fields with a property that matches `{language_prefix}` are dropped from the final tile, even if they do not pertain to language translations. - - `params.language_property` **String** the primary property in features that identifies the feature in a language. Default `name`. This values is used to search for additional translations that match the following format `{language_property}_{language}` - - `params.language_prefix` **String** prefix for any additional translation properties. The value is used to search for additional translations that match the following format: `{language_prefix}{language_property}_{language}`. - - `params.worldview` **Array** array of ISO 3166-1 alpha-2 country codes used to filter out features of different worldviews. Worldview data must be included in the vector tile. See `params.worldview_property` for more details on encoding data. - - If a feature matches one of the requested worldviews, the feature is kept. It will have a property `worldview` equal to the matching worldview value and the `params.worldview_property` property will be dropped. If the original feature contained a `worldview` property, it is overwritten. - - If a feature has a worldview value of `all` it is considered a match and `worldview: all` is added to the feature's properties and the `params.worldview_property` property is dropped. If the original feature contains a `worldview` property, it is ovewritten. - - If a feature does not match the request worldview the entire feature is dropped. - - If a feature does not have a `params.worldview_property` property it is retained. - - `params.worldview_property` **String** the name of the property that specifies in which worldview a feature belongs. The vector tile encoded property must contain a comma-separated string of ISO 3166-1 alpha-2 country codes that define which worldviews the feature represents (example: `US,RU,IN`). Default value: `_mbx_worldview`. - - If a feature contains multiple values that match multiple given values in the `params.worldview` array, it will be split into multiple features in the final vector tile, one for each matching worldview. - - `params.class_property` **String** the name of the property that specifies the class of a feature (examples: `sea`, `canal`, `village`). Default value: `_mbx_class`. + - `params.buffer` **Buffer** a vector tile buffer, gzip compressed or not. + - `params.compress` **Boolean** a boolean value indicating whether or not to return a compressed buffer. + - Default value: `false` (i.e. return an uncompressed buffer). + - `params.languages` **Array** array of IETF BCP 47 language codes used to search for matching translations available in a feature's properties. + - Optional parameter. + - All language-related properties must match the following format: `{language_prefix}{language_property}_{language}`. + - Default properties are `_mbx_name_{language}`; for example, the `_mbx_name_jp` property contains the Japanese translation for the value in `name`. + - `params.language_property` **String** the primary property in features that identifies the feature in a language. + - Default value: `name`. + - This values is used to search for additional translations that match the following format `{language_property}_{language}`. + - `params.language_prefix` **String** prefix for any additional translation properties. + - Default value: `_mbx_`. + - The value is used to search for additional translations that match the following format: `{language_prefix}{language_property}_{language}`. + - `params.worldviews` **Array** array of ISO 3166-1 alpha-2 country codes used to filter out features of different worldviews. + - Optional parameter. + - For now, only the first item in the array will be processed; the rest are discarded. (*TODO in the future*: expand support for more than one worldviews.) + -`params.worldview_property` **String** the name of the property that specifies in which worldview a feature belongs. + - Default value: `worldview`. + - The vector tile encoded property must contain a single ISO 3166-1 alpha-2 country code or a comma-separated string of country codes that define which worldviews the feature represents (for example: `JP`, `IN,RU,US`). + - `params.worldview_prefix` **String** prefix for the worldview property. + - Default value: `_mbx_`. + - `params.worldview_default` **String** default worldview to assume when `params.worldviews` is not provided. + - Default value: `US`. + - `params.class_property` **String** the name of the property that specifies the class category of a feature. + - Default value: `class`. + - `params.class_prefix` **String** prefix for the class property. + - Default value: `_mbx_`. - `callback` **Function** callback function that returns `err` and `buffer` parameters +The existence of the parameters `params.languages` and `params.worldviews` determines the type of features that will be returned: + +- Non-localized feature: when `params.languages` and `params.worldviews` both do not exist. + - No new language property. + - The property `{language_property}` retains its original value. + - Properties like `{language_property}_{language}` are kept. + - Properties like `{language_prefix}{language_property}_{language}` are dropped. + - All features with `{worldview_property}` are kept. + - All features with `{worldview_prefix}{worldview_property}` are dropped except for those that have the value `all`. + - Property `{class_property}` retains its original value. + - Property `{class_prefix}{class_property}` is dropped. + +- Localized feature: when either `params.languages` or `params.worldviews` exists. + - A new `{language_property}_local` property is created to keep the original value of `{language_property}` + - The value of `{language_property}` is replaced with the first translation found by looping through `params.languages`. + - First searches for `{language_property}_{language}` and then `{language_prefix}{language_property}_{langauge}` before moving on to the next language in `params.languages`. + - Properties like `{language_property}_{language}` are dropped. + - Properties like `{language_prefix}{language_property}_{language}` are dropped. + - All features with `{worldview_property}` are dropped except for those that have the value `all`. + - Features with `{worldview_prefix}{worldview_property}` are kept if their `{worldview_prefix}{worldview_property}` value is + - `all`, or + - a comma-separated list that contains the first item of `parmas.worldviews`, in which case the value of `{worldview_prefix}{worldview_property}` is replaced by that one single worldview country code. + - If `{class_prefix}{class_property}` exists, + - Property `{class_property}` is replaced with the value in `{class_prefix}{class_property}`. + - Property `{class_prefix}{class_property}` is dropped. + #### Example +Example 1: a tile of non-localized features + +```js +const { localize } = require('@mapbox/vtcomposite'); + +const params = { + // REQUIRED + buffer: require('fs').readFileSync('./path/to/tile.mvt'), +}; + +localize(params, function(err, result) { + if (err) throw err; + console.log(result); // tile buffer +}); +``` + +Example 2: a tile of localized features in Japan worldview + ```js const { localize } = require('@mapbox/vtcomposite'); @@ -103,10 +155,8 @@ const params = { // REQUIRED buffer: require('fs').readFileSync('./path/to/tile.mvt'), // OPTIONAL (defaults) - language: null, - worldview: [], - worldview_property: '_mbx_worldview', - class_property: '_mbx_class', + languages: ['ja'] + worldviews: ['JP'], compress: true }; From 30eac5f5d46f92cbd64a6b8cc4e961ef0ea07c25 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 24 Oct 2022 16:44:57 -0700 Subject: [PATCH 29/41] fix format --- src/module_utils.hpp | 6 ++-- src/vtcomposite.cpp | 67 ++++++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/module_utils.hpp b/src/module_utils.hpp index bb1f86e2..45952592 100644 --- a/src/module_utils.hpp +++ b/src/module_utils.hpp @@ -30,9 +30,9 @@ inline std::vector split(std::string const& input) } // checks if a string starts with a given substring -inline bool startswith(std::string const& s, std::string const& ss) { - return ss.length() <= s.length() - && std::equal(ss.begin(), ss.end(), s.begin()); +inline bool startswith(std::string const& s, std::string const& ss) +{ + return ss.length() <= s.length() && std::equal(ss.begin(), ss.end(), s.begin()); } // finds the intersection of two vectors of strings diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 35801edc..5dc46455 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -617,9 +617,9 @@ struct LocalizeWorker : Napi::AsyncWorker // create a feature with new properties from a template feature static void create_new_feature( - vtzero::feature const& template_feature, - std::vector> const& properties, - vtzero::layer_builder& lbuilder) + vtzero::feature const& template_feature, + std::vector> const& properties, + vtzero::layer_builder& lbuilder) { vtzero::geometry_feature_builder fbuilder{lbuilder}; fbuilder.copy_id(template_feature); // TODO: deduplicate this (vector tile spec says SHOULD be unique) @@ -628,7 +628,7 @@ struct LocalizeWorker : Napi::AsyncWorker // add property to feature for (auto const& property : properties) { - fbuilder.add_property(property.first, property.second); + fbuilder.add_property(property.first, property.second); } fbuilder.commit(); @@ -660,8 +660,9 @@ struct LocalizeWorker : Napi::AsyncWorker language_key_precedence.push_back(baton_data_->language_prefix + baton_data_->language_property + "_" + lang); } language_key_precedence.push_back(baton_data_->language_property); - - } else { + } + else + { keep_every_worldview = true; incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; @@ -710,7 +711,10 @@ struct LocalizeWorker : Napi::AsyncWorker while (auto property = feature.next_property()) { // if already know we'll be skipping this feature, don't need to comb through its properties - if (skip_feature) continue; + if (skip_feature) + { + continue; + } std::string property_key = property.key().to_string(); @@ -769,7 +773,8 @@ struct LocalizeWorker : Napi::AsyncWorker utils::intersection(property_worldviews, baton_data_->worldviews, matching_worldviews); // For now just process the first requested worldview; - if (matching_worldviews.empty()) { + if (matching_worldviews.empty()) + { skip_feature = true; continue; } @@ -798,11 +803,9 @@ struct LocalizeWorker : Napi::AsyncWorker } } - else if - ( + else if ( utils::startswith(property_key, baton_data_->class_property) || - utils::startswith(property_key, baton_data_->class_prefix + baton_data_->class_property) - ) + utils::startswith(property_key, baton_data_->class_prefix + baton_data_->class_property)) { // check if the property is of higher precedence that class key encountered so far std::uint32_t idx = static_cast(std::find(class_key_precedence.begin(), class_key_precedence.end(), property_key) - class_key_precedence.begin()); @@ -815,11 +818,9 @@ struct LocalizeWorker : Napi::AsyncWorker continue; } - else if - ( + else if ( utils::startswith(property_key, baton_data_->language_property) || - utils::startswith(property_key, baton_data_->language_prefix + baton_data_->language_property) - ) + utils::startswith(property_key, baton_data_->language_prefix + baton_data_->language_property)) { // check if the property is of higher precedence that class key encountered so far std::uint32_t idx = static_cast(std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key) - language_key_precedence.begin()); @@ -859,7 +860,8 @@ struct LocalizeWorker : Napi::AsyncWorker } // all other properties - else { + else + { final_properties.emplace_back(property_key, property.value()); continue; } @@ -867,26 +869,32 @@ struct LocalizeWorker : Napi::AsyncWorker } // end of properties loop // if skip feature, proceed to next feature - if (skip_feature) continue; + if (skip_feature) + { + continue; + } // use the class value of highest precedence - if (class_value.valid()) { + if (class_value.valid()) + { final_properties.emplace_back(baton_data_->class_property, class_value); } // use the language value of highest precedence - if (language_value.valid()) { + if (language_value.valid()) + { final_properties.emplace_back(baton_data_->language_property, language_value); } - if (baton_data_->return_localized_tile && original_language_value.valid()) { + if (baton_data_->return_localized_tile && original_language_value.valid()) + { final_properties.emplace_back(baton_data_->language_property + "_local", original_language_value); } create_new_feature(feature, final_properties, lbuilder); } // end of features loop - } // end of layers loop + } // end of layers loop std::string& tile_buffer = *output_buffer_; if (baton_data_->compress) @@ -967,10 +975,10 @@ Napi::Value localize(Napi::CallbackInfo const& info) Napi::Buffer buffer; // optional params and their default values - std::vector languages; // default is undefined + std::vector languages; // default is undefined std::string language_property = "name"; std::string language_prefix = "_mbx_"; - std::vector worldviews; // default is undefined + std::vector worldviews; // default is undefined std::string worldview_property = "worldview"; std::string worldview_prefix = "_mbx_"; std::string worldview_default = "US"; @@ -979,7 +987,7 @@ Napi::Value localize(Napi::CallbackInfo const& info) bool compress = false; // param that'll be deduced from other params - bool return_localized_tile = false; // true only if languages or worldviews exist + bool return_localized_tile = false; // true only if languages or worldviews exist // validate params object Napi::Value params_val = info[0]; @@ -1019,7 +1027,8 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "languages"))) { Napi::Value language_val = params.Get(Napi::String::New(info.Env(), "languages")); - if (language_val.IsArray()) { + if (language_val.IsArray()) + { Napi::Array language_array = language_val.As(); std::uint32_t num_languages = language_array.Length(); @@ -1081,11 +1090,13 @@ Napi::Value localize(Napi::CallbackInfo const& info) if (params.Has(Napi::String::New(info.Env(), "worldviews"))) { Napi::Value worldview_val = params.Get(Napi::String::New(info.Env(), "worldviews")); - if (worldview_val.IsArray()) { + if (worldview_val.IsArray()) + { Napi::Array worldview_array = worldview_val.As(); std::uint32_t num_worldviews = worldview_array.Length(); - if (num_worldviews > 0) { + if (num_worldviews > 0) + { worldviews.reserve(num_worldviews); for (std::uint32_t wv = 0; wv < num_worldviews; ++wv) From fd5479e577ce062256bfc61d8ae386ac15eef8fb Mon Sep 17 00:00:00 2001 From: lily-chai Date: Mon, 24 Oct 2022 17:23:03 -0700 Subject: [PATCH 30/41] tidy up --- src/vtcomposite.cpp | 50 ++++++++++----------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 5dc46455..7bec6f89 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -116,7 +116,7 @@ struct LocalizeBatonType worldview_prefix{std::move(worldview_prefix_)}, class_property{std::move(class_property_)}, class_prefix{std::move(class_prefix_)}, - return_localized_tile{std::move(return_localized_tile_)}, + return_localized_tile{return_localized_tile_}, compress{compress_} { // Note: for LocalizeBatonType, return_localized_tile_ dictates whether it @@ -638,11 +638,11 @@ struct LocalizeWorker : Napi::AsyncWorker { try { - bool keep_every_worldview; + bool keep_every_worldview = true; std::string incompatible_worldview_key; std::string compatible_worldview_key; std::vector class_key_precedence; - bool keep_every_language; + bool keep_every_language = true; std::vector language_key_precedence; if (baton_data_->return_localized_tile) { @@ -699,10 +699,10 @@ struct LocalizeWorker : Napi::AsyncWorker bool skip_feature = false; // will be searching for the class with lowest index in class_key_precedence - std::uint32_t class_key_idx = static_cast(class_key_precedence.size()); + auto class_key_idx = static_cast(class_key_precedence.size()); vtzero::property_value class_value; - std::uint32_t language_key_idx = static_cast(language_key_precedence.size()); + auto language_key_idx = static_cast(language_key_precedence.size()); vtzero::property_value language_value; vtzero::property_value original_language_value; @@ -723,21 +723,15 @@ struct LocalizeWorker : Napi::AsyncWorker { if (property.value().type() == vtzero::property_value_type::string_value) { - if (property.value().string_value() == "all") - { - // do nothing - keep this feature but don't need to preserve this property - continue; - } - else + if (property.value().string_value() != "all") { skip_feature = true; - continue; } + // else do nothing - keep this feature but don't need to preserve this property. } else { skip_feature = true; - continue; } } @@ -752,7 +746,6 @@ struct LocalizeWorker : Napi::AsyncWorker if (property_value == "all") { final_properties.emplace_back(baton_data_->worldview_property, property.value()); - continue; } else { @@ -764,7 +757,6 @@ struct LocalizeWorker : Napi::AsyncWorker vtzero::encoded_property_value epv{property_worldviews[0]}; vtzero::property_value pv{epv.data()}; final_properties.emplace_back(baton_data_->worldview_property, pv); - continue; } else { @@ -773,16 +765,10 @@ struct LocalizeWorker : Napi::AsyncWorker utils::intersection(property_worldviews, baton_data_->worldviews, matching_worldviews); // For now just process the first requested worldview; - if (matching_worldviews.empty()) - { - skip_feature = true; - continue; - } - else if (std::find(matching_worldviews.begin(), matching_worldviews.end(), baton_data_->worldviews[0]) == matching_worldviews.end()) + if (std::find(matching_worldviews.begin(), matching_worldviews.end(), baton_data_->worldviews[0]) == matching_worldviews.end()) { // TODO when support multiple worldviews: remove this else if block skip_feature = true; - continue; } else { @@ -791,7 +777,6 @@ struct LocalizeWorker : Napi::AsyncWorker vtzero::encoded_property_value epv{first_wv}; vtzero::property_value pv{epv.data()}; final_properties.emplace_back(baton_data_->worldview_property, pv); - continue; } } } @@ -799,7 +784,6 @@ struct LocalizeWorker : Napi::AsyncWorker else { skip_feature = true; - continue; } } @@ -815,7 +799,6 @@ struct LocalizeWorker : Napi::AsyncWorker class_value = property.value(); } // wait till we are done looping through all properties to add class value to final_properties - continue; } else if ( @@ -834,28 +817,18 @@ struct LocalizeWorker : Napi::AsyncWorker if (property_key == baton_data_->language_property) { original_language_value = property.value(); - continue; } else { if (keep_every_language) { - if (utils::startswith(property_key, baton_data_->language_prefix)) - { - // drop properties that start with a prefix - continue; - } - else + if (!utils::startswith(property_key, baton_data_->language_prefix)) { final_properties.emplace_back(property_key, property.value()); - continue; } + // else – drop properties that start with a prefix } - else - { - // wait till we are done looping through all properties to add class value to final_properties - continue; - } + // else – wait till we are done looping through all properties to add class value to final_properties } } @@ -863,7 +836,6 @@ struct LocalizeWorker : Napi::AsyncWorker else { final_properties.emplace_back(property_key, property.value()); - continue; } } // end of properties loop From 3aa513e4ae5e8d2953dff2aae72a34fbdc5ac0d0 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 25 Oct 2022 10:33:04 -0700 Subject: [PATCH 31/41] preserve ability to create clones of a feature --- src/vtcomposite.cpp | 117 +++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 46 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 7bec6f89..d5c15539 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -616,9 +616,11 @@ struct LocalizeWorker : Napi::AsyncWorker output_buffer_{std::make_unique()} {} // create a feature with new properties from a template feature - static void create_new_feature( + static void build_new_feature( vtzero::feature const& template_feature, std::vector> const& properties, + std::string const& worldview_key, + std::string const& worldview_val, vtzero::layer_builder& lbuilder) { vtzero::geometry_feature_builder fbuilder{lbuilder}; @@ -628,12 +630,31 @@ struct LocalizeWorker : Napi::AsyncWorker // add property to feature for (auto const& property : properties) { + if (property.first == worldview_key) // safeguard – should always evaluate to false + { + continue; + } fbuilder.add_property(property.first, property.second); } + if (!worldview_key.empty()) + { + fbuilder.add_property(worldview_key, worldview_val); + } + fbuilder.commit(); } + // returns a vector of requested worldviews that the feature exists in + static std::vector worldviews_for_feature(std::vector available_worldviews, std::vector target_worldviews) + { + target_worldviews.emplace_back("all"); + + std::vector matching_worldviews; + utils::intersection(available_worldviews, target_worldviews, matching_worldviews); + return matching_worldviews; + } + void Execute() override { try @@ -698,6 +719,10 @@ struct LocalizeWorker : Napi::AsyncWorker // will be skipping features with incompatible worldview bool skip_feature = false; + // will be creating one clone of the feature for each worldview if worldview property exists + bool has_worldview_key = false; + std::vector worldviews_to_create; + // will be searching for the class with lowest index in class_key_precedence auto class_key_idx = static_cast(class_key_precedence.size()); vtzero::property_value class_value; @@ -718,70 +743,58 @@ struct LocalizeWorker : Napi::AsyncWorker std::string property_key = property.key().to_string(); - // skip feature only if the value of incompatible worldview key is not 'all' - if (property_key == incompatible_worldview_key) + if ( + utils::startswith(property_key, baton_data_->worldview_property) || + utils::startswith(property_key, baton_data_->worldview_prefix + baton_data_->worldview_property)) { - if (property.value().type() == vtzero::property_value_type::string_value) + // skip feature only if the value of incompatible worldview key is not 'all' + if (property_key == incompatible_worldview_key) { - if (property.value().string_value() != "all") + if (property.value().type() == vtzero::property_value_type::string_value) + { + if (property.value().string_value() != "all") + { + skip_feature = true; + } + // else do nothing - keep this feature but don't need to preserve this property. + } + else { skip_feature = true; } - // else do nothing - keep this feature but don't need to preserve this property. } - else - { - skip_feature = true; - } - } - // keep feature and retain its compatible worldview value - else if (property_key == compatible_worldview_key) - { - if (property.value().type() == vtzero::property_value_type::string_value) + // keep feature and retain its compatible worldview value + else if (property_key == compatible_worldview_key) { - std::string property_value = static_cast(property.value().string_value()); + has_worldview_key = true; - // keep only the feature in selected worldview or in 'all' worldview - if (property_value == "all") - { - final_properties.emplace_back(baton_data_->worldview_property, property.value()); - } - else + if (property.value().type() == vtzero::property_value_type::string_value) { + std::string property_value = static_cast(property.value().string_value()); + + std::vector available_worldviews = utils::split(property_value); + + // determine which worldviews to create a clone of the feature if (keep_every_worldview) { - // For now just return the first one; - // TODO: explode feature into multiple features, one for each worldview, if property.value() is a comma-separated list - std::vector property_worldviews = utils::split(property_value); - vtzero::encoded_property_value epv{property_worldviews[0]}; - vtzero::property_value pv{epv.data()}; - final_properties.emplace_back(baton_data_->worldview_property, pv); + worldviews_to_create = worldviews_for_feature(available_worldviews, available_worldviews); } else { - std::vector matching_worldviews; - std::vector property_worldviews = utils::split(property_value); - utils::intersection(property_worldviews, baton_data_->worldviews, matching_worldviews); - - // For now just process the first requested worldview; - if (std::find(matching_worldviews.begin(), matching_worldviews.end(), baton_data_->worldviews[0]) == matching_worldviews.end()) + worldviews_to_create = worldviews_for_feature(available_worldviews, baton_data_->worldviews); + if (worldviews_to_create.empty()) { - // TODO when support multiple worldviews: remove this else if block skip_feature = true; } - else - { - // TODO: support multiple worldviews - std::string const& first_wv{baton_data_->worldviews[0]}; - vtzero::encoded_property_value epv{first_wv}; - vtzero::property_value pv{epv.data()}; - final_properties.emplace_back(baton_data_->worldview_property, pv); - } } } + else + { + skip_feature = true; + } } - else + else // safeguard – should never reach here { skip_feature = true; } @@ -798,7 +811,7 @@ struct LocalizeWorker : Napi::AsyncWorker class_key_idx = idx; class_value = property.value(); } - // wait till we are done looping through all properties to add class value to final_properties + // wait till we are done looping through all properties before we add class value to final_properties } else if ( @@ -863,7 +876,19 @@ struct LocalizeWorker : Napi::AsyncWorker final_properties.emplace_back(baton_data_->language_property + "_local", original_language_value); } - create_new_feature(feature, final_properties, lbuilder); + // build new feature(s) + if (has_worldview_key) + { + if (!worldviews_to_create.empty()) + { // safeguard – should always evalute to true + // Take just the first worldview. TODO: support all worldviews. + build_new_feature(feature, final_properties, baton_data_->worldview_property, worldviews_to_create[0], lbuilder); + } + } + else + { + build_new_feature(feature, final_properties, "", "", lbuilder); + } } // end of features loop } // end of layers loop From 06255f45045b17adf7693bc12e9451c1cdf6a8c0 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 25 Oct 2022 14:05:26 -0700 Subject: [PATCH 32/41] increment version, update changelog --- CHANGELOG.md | 4 ++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb63708e..fcf59712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.2.0 + +- Updates the `localize` function to return features with either all properties localized or features with no properties localized; nothing in between [#129](https://github.com/mapbox/vtcomposite/pull/129). + # 1.1.0 - Updates the `localize` function to translate `_mbx_class` to `class` when `_mbx_worldview` is provided along with matching worldview filtering diff --git a/package-lock.json b/package-lock.json index 92dec98b..1b7805ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mapbox/vtcomposite", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index c2b171d6..44f5b0b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/vtcomposite", - "version": "1.1.0", + "version": "1.2.0", "description": "Compositing operations on Vector Tiles (c++ bindings using N-API)", "url": "http://github.com/mapbox/vtcomposite", "main": "./lib/index.js", From 4931c25cc701b20cc2852380bed69ffa57c8757f Mon Sep 17 00:00:00 2001 From: lily-chai Date: Tue, 25 Oct 2022 14:12:48 -0700 Subject: [PATCH 33/41] 1.2.0-dev0 [publish binary] --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b7805ab..3b5599c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mapbox/vtcomposite", - "version": "1.2.0", + "version": "1.2.0-dev0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 44f5b0b2..2cbb14cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/vtcomposite", - "version": "1.2.0", + "version": "1.2.0-dev0", "description": "Compositing operations on Vector Tiles (c++ bindings using N-API)", "url": "http://github.com/mapbox/vtcomposite", "main": "./lib/index.js", From cf0cee6105d4ecf995a640075e10ced9cc1df626 Mon Sep 17 00:00:00 2001 From: Lily Chai Date: Thu, 27 Oct 2022 05:12:34 +0800 Subject: [PATCH 34/41] fix comment Co-authored-by: Sam Matthews --- src/vtcomposite.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index d5c15539..53bcb806 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -818,7 +818,7 @@ struct LocalizeWorker : Napi::AsyncWorker utils::startswith(property_key, baton_data_->language_property) || utils::startswith(property_key, baton_data_->language_prefix + baton_data_->language_property)) { - // check if the property is of higher precedence that class key encountered so far + // check if the property is of higher precedence that language key encountered so far std::uint32_t idx = static_cast(std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key) - language_key_precedence.begin()); if (idx < language_key_idx) { From 328092ebb711545abc53be9f1a6b0fed4ffec2d7 Mon Sep 17 00:00:00 2001 From: Lily Chai Date: Thu, 27 Oct 2022 05:25:47 +0800 Subject: [PATCH 35/41] fix README.md Co-authored-by: John Klancer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a50171f..8c4d76e0 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ The existence of the parameters `params.languages` and `params.worldviews` deter - Localized feature: when either `params.languages` or `params.worldviews` exists. - A new `{language_property}_local` property is created to keep the original value of `{language_property}` - The value of `{language_property}` is replaced with the first translation found by looping through `params.languages`. - - First searches for `{language_property}_{language}` and then `{language_prefix}{language_property}_{langauge}` before moving on to the next language in `params.languages`. + - First searches for `{language_property}_{language}` and then `{language_prefix}{language_property}_{language}` before moving on to the next language in `params.languages`. - Properties like `{language_property}_{language}` are dropped. - Properties like `{language_prefix}{language_property}_{language}` are dropped. - All features with `{worldview_property}` are dropped except for those that have the value `all`. From 3711e05918f95ff3494302a0c36e07fe1bdfad08 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 26 Oct 2022 16:19:01 -0700 Subject: [PATCH 36/41] address feedback --- README.md | 6 +++--- package-lock.json | 2 +- package.json | 2 +- src/module_utils.hpp | 4 ++-- src/vtcomposite.cpp | 17 ++++++++--------- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8c4d76e0..5f7288e4 100644 --- a/README.md +++ b/README.md @@ -88,8 +88,8 @@ A filtering function for modifying a tile's features and properties to support l - The value is used to search for additional translations that match the following format: `{language_prefix}{language_property}_{language}`. - `params.worldviews` **Array** array of ISO 3166-1 alpha-2 country codes used to filter out features of different worldviews. - Optional parameter. - - For now, only the first item in the array will be processed; the rest are discarded. (*TODO in the future*: expand support for more than one worldviews.) - -`params.worldview_property` **String** the name of the property that specifies in which worldview a feature belongs. + - For now, only the first item in the array will be processed; the rest are discarded (*TODO in the future*: expand support for more than one worldviews). + - `params.worldview_property` **String** the name of the property that specifies in which worldview a feature belongs. - Default value: `worldview`. - The vector tile encoded property must contain a single ISO 3166-1 alpha-2 country code or a comma-separated string of country codes that define which worldviews the feature represents (for example: `JP`, `IN,RU,US`). - `params.worldview_prefix` **String** prefix for the worldview property. @@ -123,7 +123,7 @@ The existence of the parameters `params.languages` and `params.worldviews` deter - All features with `{worldview_property}` are dropped except for those that have the value `all`. - Features with `{worldview_prefix}{worldview_property}` are kept if their `{worldview_prefix}{worldview_property}` value is - `all`, or - - a comma-separated list that contains the first item of `parmas.worldviews`, in which case the value of `{worldview_prefix}{worldview_property}` is replaced by that one single worldview country code. + - a comma-separated list that contains the first item of `parmas.worldviews`, in which a property `{worldview_property}` is created from that one single worldview country code and the property `{worldview_prefix}{worldview_property}` is dropped. - If `{class_prefix}{class_property}` exists, - Property `{class_property}` is replaced with the value in `{class_prefix}{class_property}`. - Property `{class_prefix}{class_property}` is dropped. diff --git a/package-lock.json b/package-lock.json index 3b5599c7..7d34f010 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@mapbox/vtcomposite", - "version": "1.2.0-dev0", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 2cbb14cd..013b215d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/vtcomposite", - "version": "1.2.0-dev0", + "version": "2.0.0", "description": "Compositing operations on Vector Tiles (c++ bindings using N-API)", "url": "http://github.com/mapbox/vtcomposite", "main": "./lib/index.js", diff --git a/src/module_utils.hpp b/src/module_utils.hpp index 45952592..ffdea67d 100644 --- a/src/module_utils.hpp +++ b/src/module_utils.hpp @@ -30,9 +30,9 @@ inline std::vector split(std::string const& input) } // checks if a string starts with a given substring -inline bool startswith(std::string const& s, std::string const& ss) +inline bool startswith(std::string const& astring, std::string const& substring) { - return ss.length() <= s.length() && std::equal(ss.begin(), ss.end(), s.begin()); + return substring.length() <= astring.length() && std::equal(substring.begin(), substring.end(), astring.begin()); } // finds the intersection of two vectors of strings diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 53bcb806..2e6b761e 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -684,13 +684,13 @@ struct LocalizeWorker : Napi::AsyncWorker } else { - keep_every_worldview = true; + keep_every_worldview = true; // reassign to the same value as default for clarity incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; class_key_precedence.push_back(baton_data_->class_property); - keep_every_language = true; + keep_every_language = true; // reassign to the same value as default for clarity language_key_precedence.push_back(baton_data_->language_property); } @@ -733,14 +733,14 @@ struct LocalizeWorker : Napi::AsyncWorker // collect final properties std::vector> final_properties; - while (auto property = feature.next_property()) + while (auto property = feature.next_property() &&) { // if already know we'll be skipping this feature, don't need to comb through its properties if (skip_feature) { continue; } - + std::string property_key = property.key().to_string(); if ( @@ -805,7 +805,7 @@ struct LocalizeWorker : Napi::AsyncWorker utils::startswith(property_key, baton_data_->class_prefix + baton_data_->class_property)) { // check if the property is of higher precedence that class key encountered so far - std::uint32_t idx = static_cast(std::find(class_key_precedence.begin(), class_key_precedence.end(), property_key) - class_key_precedence.begin()); + std::uint32_t idx = static_cast(std::distance(class_key_precedence.begin(), std::find(class_key_precedence.begin(), class_key_precedence.end(), property_key))); if (idx < class_key_idx) { class_key_idx = idx; @@ -819,14 +819,14 @@ struct LocalizeWorker : Napi::AsyncWorker utils::startswith(property_key, baton_data_->language_prefix + baton_data_->language_property)) { // check if the property is of higher precedence that language key encountered so far - std::uint32_t idx = static_cast(std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key) - language_key_precedence.begin()); + std::uint32_t idx = static_cast(std::distance(language_key_precedence.begin(), std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key))); if (idx < language_key_idx) { language_key_idx = idx; language_value = property.value(); } - // preserve original language value and wait till finish looping through all properties to assign a value + // preserve original language value, and wait till finish looping through all properties to assign a value if (property_key == baton_data_->language_property) { original_language_value = property.value(); @@ -841,7 +841,7 @@ struct LocalizeWorker : Napi::AsyncWorker } // else – drop properties that start with a prefix } - // else – wait till we are done looping through all properties to add class value to final_properties + // else – wait till we are done looping through all properties to add {language} value to final_properties } } @@ -1192,7 +1192,6 @@ Napi::Value localize(Napi::CallbackInfo const& info) { if (worldviews.empty()) { - worldviews.reserve(1); worldviews.push_back(worldview_default); } // else do nothing – already knows which worldview to return From f7236b3f13f8e587e0c78876d948489bd4a64913 Mon Sep 17 00:00:00 2001 From: lily-chai Date: Wed, 26 Oct 2022 16:29:21 -0700 Subject: [PATCH 37/41] remove stray && --- src/vtcomposite.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 2e6b761e..3968384e 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -684,13 +684,13 @@ struct LocalizeWorker : Napi::AsyncWorker } else { - keep_every_worldview = true; // reassign to the same value as default for clarity + keep_every_worldview = true; // reassign to the same value as default for clarity incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; class_key_precedence.push_back(baton_data_->class_property); - keep_every_language = true; // reassign to the same value as default for clarity + keep_every_language = true; // reassign to the same value as default for clarity language_key_precedence.push_back(baton_data_->language_property); } @@ -733,14 +733,14 @@ struct LocalizeWorker : Napi::AsyncWorker // collect final properties std::vector> final_properties; - while (auto property = feature.next_property() &&) + while (auto property = feature.next_property()) { // if already know we'll be skipping this feature, don't need to comb through its properties if (skip_feature) { continue; } - + std::string property_key = property.key().to_string(); if ( From 47f5e1000766328462b6bacb1831519332d07eee Mon Sep 17 00:00:00 2001 From: lily-chai Date: Thu, 27 Oct 2022 09:12:08 -0700 Subject: [PATCH 38/41] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf59712..92c51331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 1.2.0 +# 2.0.0 - Updates the `localize` function to return features with either all properties localized or features with no properties localized; nothing in between [#129](https://github.com/mapbox/vtcomposite/pull/129). From 759e7099809cd65f787ea92c3b7af739da7d2b7c Mon Sep 17 00:00:00 2001 From: lily-chai Date: Thu, 27 Oct 2022 09:28:07 -0700 Subject: [PATCH 39/41] elaborate comment --- src/vtcomposite.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 3968384e..14f1df25 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -716,7 +716,9 @@ struct LocalizeWorker : Napi::AsyncWorker vtzero::layer_builder lbuilder{tbuilder, layer.name(), layer.version(), layer.extent()}; while (auto feature = layer.next_feature()) { - // will be skipping features with incompatible worldview + // a flag to indicate whether This feature will be dropped; will set this flag + // to true when we encounter a property that suggests this feature should be + // discarded (for example, if the feature has an incompatible worldview key/value). bool skip_feature = false; // will be creating one clone of the feature for each worldview if worldview property exists @@ -735,7 +737,9 @@ struct LocalizeWorker : Napi::AsyncWorker std::vector> final_properties; while (auto property = feature.next_property()) { - // if already know we'll be skipping this feature, don't need to comb through its properties + // if true, we've already encounterd a property that indicates + // we will be discard this feature, so we can fast forward this while loop and + // don't need to comb through the rest of its properties. if (skip_feature) { continue; From 00b939803e3f9647113e01a84a9ebb2d382fcabb Mon Sep 17 00:00:00 2001 From: lily-chai Date: Thu, 27 Oct 2022 10:27:10 -0700 Subject: [PATCH 40/41] one hidden_prefix for all; drop anything with hidden_prefix --- README.md | 34 ++-- src/vtcomposite.cpp | 84 +++------ test/vtcomposite-localize-class.test.js | 8 +- test/vtcomposite-localize-language.test.js | 29 ++-- ...omposite-localize-param-validation.test.js | 162 +++++------------- test/vtcomposite-localize-worldview.test.js | 16 +- 6 files changed, 106 insertions(+), 227 deletions(-) diff --git a/README.md b/README.md index 5f7288e4..87f68245 100644 --- a/README.md +++ b/README.md @@ -76,31 +76,27 @@ A filtering function for modifying a tile's features and properties to support l - `params.buffer` **Buffer** a vector tile buffer, gzip compressed or not. - `params.compress` **Boolean** a boolean value indicating whether or not to return a compressed buffer. - Default value: `false` (i.e. return an uncompressed buffer). + - `params.hidden_prefix` **String** prefix for any additional properties that will be used to override non-prefixed properties. + - Default value: `_mbx_`. + - Any property that starts with this prefix are considered hidden properties and thus will be dropped. - `params.languages` **Array** array of IETF BCP 47 language codes used to search for matching translations available in a feature's properties. - Optional parameter. - - All language-related properties must match the following format: `{language_prefix}{language_property}_{language}`. + - All language-related properties must match the following format: `{hidden_prefix}{language_property}_{language}`. - Default properties are `_mbx_name_{language}`; for example, the `_mbx_name_jp` property contains the Japanese translation for the value in `name`. - `params.language_property` **String** the primary property in features that identifies the feature in a language. - Default value: `name`. - This values is used to search for additional translations that match the following format `{language_property}_{language}`. - - `params.language_prefix` **String** prefix for any additional translation properties. - - Default value: `_mbx_`. - - The value is used to search for additional translations that match the following format: `{language_prefix}{language_property}_{language}`. - `params.worldviews` **Array** array of ISO 3166-1 alpha-2 country codes used to filter out features of different worldviews. - Optional parameter. - For now, only the first item in the array will be processed; the rest are discarded (*TODO in the future*: expand support for more than one worldviews). - `params.worldview_property` **String** the name of the property that specifies in which worldview a feature belongs. - Default value: `worldview`. - The vector tile encoded property must contain a single ISO 3166-1 alpha-2 country code or a comma-separated string of country codes that define which worldviews the feature represents (for example: `JP`, `IN,RU,US`). - - `params.worldview_prefix` **String** prefix for the worldview property. - - Default value: `_mbx_`. - `params.worldview_default` **String** default worldview to assume when `params.worldviews` is not provided. - Default value: `US`. - `params.class_property` **String** the name of the property that specifies the class category of a feature. - Default value: `class`. - - `params.class_prefix` **String** prefix for the class property. - - Default value: `_mbx_`. -- `callback` **Function** callback function that returns `err` and `buffer` parameters + - `callback` **Function** callback function that returns `err` and `buffer` parameters The existence of the parameters `params.languages` and `params.worldviews` determines the type of features that will be returned: @@ -108,25 +104,25 @@ The existence of the parameters `params.languages` and `params.worldviews` deter - No new language property. - The property `{language_property}` retains its original value. - Properties like `{language_property}_{language}` are kept. - - Properties like `{language_prefix}{language_property}_{language}` are dropped. + - Properties like `{hidden_prefix}{language_property}_{language}` are dropped. - All features with `{worldview_property}` are kept. - - All features with `{worldview_prefix}{worldview_property}` are dropped except for those that have the value `all`. + - All features with `{hidden_prefix}{worldview_property}` are dropped except for those that have the value `all`. - Property `{class_property}` retains its original value. - - Property `{class_prefix}{class_property}` is dropped. + - Property `{hidden_prefix}{class_property}` is dropped. - Localized feature: when either `params.languages` or `params.worldviews` exists. - A new `{language_property}_local` property is created to keep the original value of `{language_property}` - The value of `{language_property}` is replaced with the first translation found by looping through `params.languages`. - - First searches for `{language_property}_{language}` and then `{language_prefix}{language_property}_{language}` before moving on to the next language in `params.languages`. + - First searches for `{language_property}_{language}` and then `{hidden_prefix}{language_property}_{language}` before moving on to the next language in `params.languages`. - Properties like `{language_property}_{language}` are dropped. - - Properties like `{language_prefix}{language_property}_{language}` are dropped. + - Properties like `{hidden_prefix}{language_property}_{language}` are dropped. - All features with `{worldview_property}` are dropped except for those that have the value `all`. - - Features with `{worldview_prefix}{worldview_property}` are kept if their `{worldview_prefix}{worldview_property}` value is + - Features with `{hidden_prefix}{worldview_property}` are kept if their `{hidden_prefix}{worldview_property}` value is - `all`, or - - a comma-separated list that contains the first item of `parmas.worldviews`, in which a property `{worldview_property}` is created from that one single worldview country code and the property `{worldview_prefix}{worldview_property}` is dropped. - - If `{class_prefix}{class_property}` exists, - - Property `{class_property}` is replaced with the value in `{class_prefix}{class_property}`. - - Property `{class_prefix}{class_property}` is dropped. + - a comma-separated list that contains the first item of `parmas.worldviews`, in which a property `{worldview_property}` is created from that one single worldview country code and the property `{hidden_prefix}{worldview_property}` is dropped. + - If `{hidden_prefix}{class_property}` exists, + - Property `{class_property}` is replaced with the value in `{hidden_prefix}{class_property}`. + - Property `{hidden_prefix}{class_property}` is dropped. #### Example diff --git a/src/vtcomposite.cpp b/src/vtcomposite.cpp index 14f1df25..087a5713 100644 --- a/src/vtcomposite.cpp +++ b/src/vtcomposite.cpp @@ -96,26 +96,22 @@ struct BatonType struct LocalizeBatonType { LocalizeBatonType(Napi::Buffer const& buffer, + std::string hidden_prefix_, std::vector languages_, std::string language_property_, - std::string language_prefix_, std::vector worldviews_, std::string worldview_property_, - std::string worldview_prefix_, std::string class_property_, - std::string class_prefix_, bool return_localized_tile_, bool compress_) : data{buffer.Data(), buffer.Length()}, buffer_ref{Napi::Persistent(buffer)}, + hidden_prefix{std::move(hidden_prefix_)}, languages{std::move(languages_)}, language_property{std::move(language_property_)}, - language_prefix{std::move(language_prefix_)}, worldviews{std::move(worldviews_)}, worldview_property{std::move(worldview_property_)}, - worldview_prefix{std::move(worldview_prefix_)}, class_property{std::move(class_property_)}, - class_prefix{std::move(class_prefix_)}, return_localized_tile{return_localized_tile_}, compress{compress_} { @@ -144,14 +140,12 @@ struct LocalizeBatonType // members vtzero::data_view data; Napi::Reference> buffer_ref; + std::string hidden_prefix; std::vector languages; std::string language_property; - std::string language_prefix; std::vector worldviews; std::string worldview_property; - std::string worldview_prefix; std::string class_property; - std::string class_prefix; bool return_localized_tile; bool compress; }; @@ -669,23 +663,23 @@ struct LocalizeWorker : Napi::AsyncWorker { keep_every_worldview = false; incompatible_worldview_key = baton_data_->worldview_property; - compatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; + compatible_worldview_key = baton_data_->hidden_prefix + baton_data_->worldview_property; - class_key_precedence.push_back(baton_data_->class_prefix + baton_data_->class_property); + class_key_precedence.push_back(baton_data_->hidden_prefix + baton_data_->class_property); class_key_precedence.push_back(baton_data_->class_property); keep_every_language = false; for (auto const& lang : baton_data_->languages) { language_key_precedence.push_back(baton_data_->language_property + "_" + lang); - language_key_precedence.push_back(baton_data_->language_prefix + baton_data_->language_property + "_" + lang); + language_key_precedence.push_back(baton_data_->hidden_prefix + baton_data_->language_property + "_" + lang); } language_key_precedence.push_back(baton_data_->language_property); } else { keep_every_worldview = true; // reassign to the same value as default for clarity - incompatible_worldview_key = baton_data_->worldview_prefix + baton_data_->worldview_property; + incompatible_worldview_key = baton_data_->hidden_prefix + baton_data_->worldview_property; compatible_worldview_key = baton_data_->worldview_property; class_key_precedence.push_back(baton_data_->class_property); @@ -749,7 +743,7 @@ struct LocalizeWorker : Napi::AsyncWorker if ( utils::startswith(property_key, baton_data_->worldview_property) || - utils::startswith(property_key, baton_data_->worldview_prefix + baton_data_->worldview_property)) + utils::startswith(property_key, baton_data_->hidden_prefix + baton_data_->worldview_property)) { // skip feature only if the value of incompatible worldview key is not 'all' if (property_key == incompatible_worldview_key) @@ -806,7 +800,7 @@ struct LocalizeWorker : Napi::AsyncWorker else if ( utils::startswith(property_key, baton_data_->class_property) || - utils::startswith(property_key, baton_data_->class_prefix + baton_data_->class_property)) + utils::startswith(property_key, baton_data_->hidden_prefix + baton_data_->class_property)) { // check if the property is of higher precedence that class key encountered so far std::uint32_t idx = static_cast(std::distance(class_key_precedence.begin(), std::find(class_key_precedence.begin(), class_key_precedence.end(), property_key))); @@ -820,7 +814,7 @@ struct LocalizeWorker : Napi::AsyncWorker else if ( utils::startswith(property_key, baton_data_->language_property) || - utils::startswith(property_key, baton_data_->language_prefix + baton_data_->language_property)) + utils::startswith(property_key, baton_data_->hidden_prefix + baton_data_->language_property)) { // check if the property is of higher precedence that language key encountered so far std::uint32_t idx = static_cast(std::distance(language_key_precedence.begin(), std::find(language_key_precedence.begin(), language_key_precedence.end(), property_key))); @@ -839,7 +833,7 @@ struct LocalizeWorker : Napi::AsyncWorker { if (keep_every_language) { - if (!utils::startswith(property_key, baton_data_->language_prefix)) + if (!utils::startswith(property_key, baton_data_->hidden_prefix)) { final_properties.emplace_back(property_key, property.value()); } @@ -850,11 +844,13 @@ struct LocalizeWorker : Napi::AsyncWorker } // all other properties - else + else if (!utils::startswith(property_key, baton_data_->hidden_prefix)) { final_properties.emplace_back(property_key, property.value()); } + // else – drop property key that starts with {hidden_prefix} + } // end of properties loop // if skip feature, proceed to next feature @@ -976,15 +972,13 @@ Napi::Value localize(Napi::CallbackInfo const& info) Napi::Buffer buffer; // optional params and their default values + std::string hidden_prefix = "_mbx_"; std::vector languages; // default is undefined std::string language_property = "name"; - std::string language_prefix = "_mbx_"; std::vector worldviews; // default is undefined std::string worldview_property = "worldview"; - std::string worldview_prefix = "_mbx_"; std::string worldview_default = "US"; std::string class_property = "class"; - std::string class_prefix = "_mbx_"; bool compress = false; // param that'll be deduced from other params @@ -1019,6 +1013,17 @@ Napi::Value localize(Napi::CallbackInfo const& info) } buffer = buffer_obj.As>(); + // params.hidden_prefix (optional) + if (params.Has(Napi::String::New(info.Env(), "hidden_prefix"))) + { + Napi::Value hidden_prefix_val = params.Get(Napi::String::New(info.Env(), "hidden_prefix")); + if (!hidden_prefix_val.IsString() || hidden_prefix_val == empty_string) + { + return utils::CallbackError("params.hidden_prefix must be a non-empty string", info); + } + hidden_prefix = hidden_prefix_val.As(); + } + // params.language is an invalid param if (params.Has(Napi::String::New(info.Env(), "language"))) { @@ -1071,17 +1076,6 @@ Napi::Value localize(Napi::CallbackInfo const& info) language_property = language_property_val.As(); } - // params.language_prefix (optional) - if (params.Has(Napi::String::New(info.Env(), "language_prefix"))) - { - Napi::Value language_prefix_val = params.Get(Napi::String::New(info.Env(), "language_prefix")); - if (!language_prefix_val.IsString() || language_prefix_val == empty_string) - { - return utils::CallbackError("params.language_prefix must be a non-empty string", info); - } - language_prefix = language_prefix_val.As(); - } - // params.worldview is an invalid param if (params.Has(Napi::String::New(info.Env(), "worldview"))) { @@ -1134,17 +1128,6 @@ Napi::Value localize(Napi::CallbackInfo const& info) worldview_property = worldview_property_val.As(); } - // params.worldview_prefix (optional) - if (params.Has(Napi::String::New(info.Env(), "worldview_prefix"))) - { - Napi::Value worldview_prefix_val = params.Get(Napi::String::New(info.Env(), "worldview_prefix")); - if (!worldview_prefix_val.IsString() || worldview_prefix_val == empty_string) - { - return utils::CallbackError("params.worldview_prefix must be a non-empty string", info); - } - worldview_prefix = worldview_prefix_val.As(); - } - // params.worldview_default (optional) if (params.Has(Napi::String::New(info.Env(), "worldview_default"))) { @@ -1167,17 +1150,6 @@ Napi::Value localize(Napi::CallbackInfo const& info) class_property = class_property_val.As(); } - // params.class_prefix (optional) - if (params.Has(Napi::String::New(info.Env(), "class_prefix"))) - { - Napi::Value class_prefix_val = params.Get(Napi::String::New(info.Env(), "class_prefix")); - if (!class_prefix_val.IsString() || class_prefix_val == empty_string) - { - return utils::CallbackError("params.class_prefix must be a non-empty string", info); - } - class_prefix = class_prefix_val.As(); - } - // params.compress (optional) if (params.Has(Napi::String::New(info.Env(), "compress"))) { @@ -1203,14 +1175,12 @@ Napi::Value localize(Napi::CallbackInfo const& info) std::unique_ptr baton_data = std::make_unique( buffer, + hidden_prefix, languages, language_property, - language_prefix, worldviews, worldview_property, - worldview_prefix, class_property, - class_prefix, return_localized_tile, compress); diff --git a/test/vtcomposite-localize-class.test.js b/test/vtcomposite-localize-class.test.js index a4787ab8..67b1802e 100644 --- a/test/vtcomposite-localize-class.test.js +++ b/test/vtcomposite-localize-class.test.js @@ -735,7 +735,7 @@ test('[localize class] requesting localized language; feature has class', (asser /** **************************************************************************** * TEST SET 5: - * - test custom class_property and class_prefix + * - test custom class_property and hidden_prefix ******************************************************************************/ test('[localize class] requesting non-localized tile; feature with custom class property and prefix but compatible worldview key in "all" worldview', (assert) => { const feature = mvtFixtures.create({ @@ -767,7 +767,7 @@ test('[localize class] requesting non-localized tile; feature with custom class const params = { buffer: feature, class_property: 'ccllaassss', - class_prefix: 'mmbbxx_' + hidden_prefix: 'mmbbxx_' // no languages or worldviews = requesting non-localized tile }; @@ -800,7 +800,7 @@ test('[localize class] requesting localized language; feature with custom class geometry: [9, 55, 38] } ], - keys: ['_mbx_worldview', 'mmbbxx_ccllaassss', 'worldview', 'ccllaassss'], + keys: ['_mmbbxx_worldview', '_mmbbxx_ccllaassss', 'worldview', 'ccllaassss'], values: [ { string_value: 'all' }, { string_value: 'affogato' }, @@ -814,7 +814,7 @@ test('[localize class] requesting localized language; feature with custom class const params = { buffer: feature, class_property: 'ccllaassss', - class_prefix: 'mmbbxx_', + hidden_prefix: '_mmbbxx_', languages: ['ja'] }; diff --git a/test/vtcomposite-localize-language.test.js b/test/vtcomposite-localize-language.test.js index 8dd4b9fb..6740895b 100644 --- a/test/vtcomposite-localize-language.test.js +++ b/test/vtcomposite-localize-language.test.js @@ -161,7 +161,6 @@ test('[localize] success - feature with specified language in _mbx_name_{languag const localizedProperties = { name: 'Deutschland', name_local: 'Germany', - _mbx_other: 'Alemania', }; const initialOutputInfo = vtinfo(initialBuffer); const feature = initialOutputInfo.layers.bottom.feature(0); @@ -193,8 +192,7 @@ test('[localize] success - feature with specified language in both name_{languag }; const localizedProperties = { name: 'Allemagne', // name_{language} takes precedence over _mbx_name_{language} - name_local: 'Germany', - _mbx_other: 'Alemania' + name_local: 'Germany' }; const initialOutputInfo = vtinfo(initialBuffer); const feature = initialOutputInfo.layers.bottom.feature(0); @@ -227,7 +225,7 @@ test('[localize] success - _mbx prefixed property keys removed from all layers', 'population' ]; const topLayerKeysExpected = topLayerKeys; - const bottomLayerKeysExpected = ['_mbx_other', 'name', 'name_local', 'population']; + const bottomLayerKeysExpected = ['name', 'name_local', 'population']; const initialOutputInfo = vtinfo(initialBuffer); assert.deepEqual(initialOutputInfo.layers.top._keys, topLayerKeys, 'expected initial keys'); @@ -260,12 +258,11 @@ test('[localize] success - no language specified but has worldview specified (i. 'population' ]; const topLayerKeysExpected = topLayerKeys; - const bottomLayerKeysExpected = ['_mbx_other', 'name', 'name_local', 'population']; + const bottomLayerKeysExpected = ['name', 'name_local', 'population']; const localizedProperties0 = { name: 'Germany', - name_local: 'Germany', - _mbx_other: 'Alemania' + name_local: 'Germany' }; const localizedProperties1 = { name: 'Espana', @@ -308,13 +305,12 @@ test('[localize] success - no language specified, no worldview specified (i.e. r 'population' ]; const topLayerKeysExpected = topLayerKeys; - const bottomLayerKeysExpected = ['name_en', 'name_fr', '_mbx_other', 'name', 'population']; + const bottomLayerKeysExpected = ['name_en', 'name_fr', 'name', 'population']; const localizedProperties0 = { name: 'Germany', name_en: 'Germany', - name_fr: 'Allemagne', - _mbx_other: 'Alemania' + name_fr: 'Allemagne' }; const localizedProperties1 = { name: 'Espana', @@ -345,7 +341,7 @@ test('[localize] success - no language specified, no worldview specified (i.e. r }); }); -test('[localize language] custom params.language_property and params.language_prefix properties', (assert) => { +test('[localize language] custom params.language_property and params.hidden_prefix properties', (assert) => { const params = { buffer: mvtFixtures.create({ layers: [ @@ -376,7 +372,7 @@ test('[localize language] custom params.language_property and params.language_pr }).buffer, languages: ['jp'], language_property: 'language', - language_prefix: '_drop_me_' + hidden_prefix: '_drop_me_' }; localize(params, (err, buffer) => { @@ -420,7 +416,7 @@ test('[localize language] language codes >2 characters are viable translations', }).buffer, languages: ['zh-Hant'], language_property: 'language', - language_prefix: '_pre_' + hidden_prefix: '_pre_' }; localize(params, (err, buffer) => { @@ -464,7 +460,7 @@ test('[localize language] fallback to second language if the first does not exis }).buffer, languages: ['en', 'zh-Hant'], language_property: 'language', - language_prefix: '_pre_' + hidden_prefix: '_pre_' }; localize(params, (err, buffer) => { @@ -479,7 +475,7 @@ test('[localize language] fallback to second language if the first does not exis }); }); -test('[localize language] _mbx_worldview and _mbx_class dropped; other _mbx_* are kept', (assert) => { +test('[localize language] _mbx_worldview and _mbx_class dropped; other _mbx_* also dropped', (assert) => { const params = { buffer: mvtFixtures.create({ layers: [ @@ -525,8 +521,7 @@ test('[localize language] _mbx_worldview and _mbx_class dropped; other _mbx_* ar name: 'Nǐ hǎo', name_local: 'hello', worldview: 'CN', - class: 'sea', - _mbx_other: 'blah' + class: 'sea' }, 'expected properties'); assert.end(); }); diff --git a/test/vtcomposite-localize-param-validation.test.js b/test/vtcomposite-localize-param-validation.test.js index 73ce2a80..4597676a 100644 --- a/test/vtcomposite-localize-param-validation.test.js +++ b/test/vtcomposite-localize-param-validation.test.js @@ -9,14 +9,12 @@ const localize = vt.localize; test('[localize] success with all parameters', (assert) => { localize({ buffer: mvtFixtures.get('064').buffer, + hidden_prefix: 'whatever', languages: ['en'], language_property: 'lang', - language_prefix: 'blah', worldviews: ['US'], worldview_property: 'wv', - worldview_prefix: 'whatever', class_property: 'klass', - class_prefix: 'sth', compress: true }, (err, buffer) => { assert.ifError(err); @@ -80,6 +78,42 @@ test('[localize] params.buffer', (assert) => { assert.end(); }); +test('[localize] params.hidden_prefix', (assert) => { + localize({ + buffer: Buffer.from('howdy'), + hidden_prefix: 1 // not a string + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.hidden_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + hidden_prefix: null // null value + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.hidden_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + hidden_prefix: undefined + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.hidden_prefix must be a non-empty string', 'expected error message'); + }); + + localize({ + buffer: Buffer.from('howdy'), + hidden_prefix: '' + }, (err) => { + assert.ok(err); + assert.equal(err.message, 'params.hidden_prefix must be a non-empty string', 'expected error message'); + }); + + assert.end(); +}); + test('[localize] params.languages', (assert) => { localize({ buffer: Buffer.from('hi'), @@ -204,46 +238,6 @@ test('[localize] params.language_property', (assert) => { assert.end(); }); -test('[localize] params.language_prefix', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - languages: ['es'], - language_prefix: 1 // not a string - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - languages: ['es'], - language_prefix: null // null value - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - languages: ['es'], - language_prefix: undefined - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - languages: ['es'], - language_prefix: '' - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.language_prefix must be a non-empty string', 'expected error message'); - }); - - assert.end(); -}); - test('[localize] params.worldviews', (assert) => { localize({ buffer: Buffer.from('howdy'), @@ -368,46 +362,6 @@ test('[localize] params.worldview_property', (assert) => { assert.end(); }); -test('[localize] params.worldview_prefix', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - worldviews: ['US'], - worldview_prefix: 1 // not a string - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - worldviews: ['US'], - worldview_prefix: null - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - worldviews: ['US'], - worldview_prefix: undefined - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - worldviews: ['US'], - worldview_prefix: '' - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.worldview_prefix must be a non-empty string', 'expected error message'); - }); - - assert.end(); -}); - test('[localize] params.worldview_default', (assert) => { localize({ buffer: Buffer.from('howdy'), @@ -480,50 +434,14 @@ test('[localize] params.class_property', (assert) => { assert.end(); }); -test('[localize] params.class_prefix', (assert) => { - localize({ - buffer: Buffer.from('howdy'), - class_prefix: 1 // not a string - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); - }); - +test('[localize] params.compress', (assert) => { localize({ buffer: Buffer.from('howdy'), - class_prefix: null + compress: 1 // not a boolean }, (err) => { assert.ok(err); - assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - class_prefix: undefined - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); - }); - - localize({ - buffer: Buffer.from('howdy'), - class_prefix: '' - }, (err) => { - assert.ok(err); - assert.equal(err.message, 'params.class_prefix must be a non-empty string', 'expected error message'); + assert.equal(err.message, 'params.compress must be a boolean', 'expected error message'); }); assert.end(); }); - -// test('[localize] params.compress', (assert) => { -// localize({ -// buffer: Buffer.from('howdy'), -// compress: 1 // not a boolean -// }, (err) => { -// assert.ok(err); -// assert.equal(err.message, 'params.compress must be a boolean', 'expected error message'); -// }); -// -// assert.end(); -// }); diff --git a/test/vtcomposite-localize-worldview.test.js b/test/vtcomposite-localize-worldview.test.js index 05fd4f75..f25c1279 100644 --- a/test/vtcomposite-localize-worldview.test.js +++ b/test/vtcomposite-localize-worldview.test.js @@ -671,7 +671,7 @@ test('[localize worldview] requesting localized language; feature with no worldv /** **************************************************************************** * TEST SET 4: - * - custom worldview_property and worldview_prefix + * - custom worldview_property and hidden_prefix ******************************************************************************/ test('[localize worldview] requesting non-localized tile; feature has custom worldview property key and prefix and is in "all" worldview', (assert) => { const feature = mvtFixtures.create({ @@ -702,7 +702,7 @@ test('[localize worldview] requesting non-localized tile; feature has custom wor const params = { buffer: feature, worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_' + hidden_prefix: 'mmbbxx_' // no languages or worldviews = request non-localized tile }; @@ -744,7 +744,7 @@ test('[localize worldview] requesting non-localized tile; feature has custom wor const params = { buffer: feature, worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_' + hidden_prefix: 'mmbbxx_' // no languages or worldviews = request non-localized tile }; @@ -787,7 +787,7 @@ test('[localize worldview] requesting non-localized tile; feature has custom wor const params = { buffer: feature, worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_' + hidden_prefix: 'mmbbxx_' // no languages or worldviews = request non-localized tile }; @@ -825,7 +825,7 @@ test('[localize worldview] requesting localized worldview; feature has custom wo const params = { buffer: feature, worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', + hidden_prefix: 'mmbbxx_', worldviews: ['US'] }; @@ -867,7 +867,7 @@ test('[localize worldview] requesting localized worldview; feature has custom wo const params = { buffer: feature, worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', + hidden_prefix: 'mmbbxx_', worldviews: ['US'] }; @@ -909,7 +909,7 @@ test('[localize worldview] requesting localized worldview; feature has custom wo const params = { buffer: feature, worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', + hidden_prefix: 'mmbbxx_', worldviews: ['US'] }; @@ -950,7 +950,7 @@ test('[localize worldview] requesting localized worldview; feature has custom wo const params = { buffer: feature, worldview_property: 'wwoorrllddvviieeww', - worldview_prefix: 'mmbbxx_', + hidden_prefix: 'mmbbxx_', worldviews: ['JP'] }; From bd7647f6df33422dc1addffe082384f5ef9a00ba Mon Sep 17 00:00:00 2001 From: lily-chai Date: Thu, 27 Oct 2022 10:58:08 -0700 Subject: [PATCH 41/41] 2.0.0 [publish binary]