diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..211de33 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vector-tile-spec"] + path = vector-tile-spec + url = https://github.com/mapbox/vector-tile-spec.git diff --git a/.npmignore b/.npmignore index d3fd5c7..9daa824 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,2 @@ -_reserve +.DS_Store +node_modules diff --git a/API.md b/API.md new file mode 100644 index 0000000..03b564d --- /dev/null +++ b/API.md @@ -0,0 +1,82 @@ +# create + +Create a tile fixture from a protocol buffer schema object representing the +Mapbox Vector Tile schema. + +**Parameters** + +- `object` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** the json schema object to generate against the Mapbox Vector Tile Specification protocol (see src/ for examples) +- `json` + +**Examples** + +```javascript +const mvtf = require('@mapbox/mvt-fixtures'); + +const fixture = { + layers: [ + { + version: 2, + name: 'hello', + features: [ + { + id: 1, + tags: [], + type: 1, + geometry: [ 9, 50, 34 ] + } + ], + keys: {}, + values: {}, + extent: 4096 + } + ] +} + +const buffer = mvtf.create(fixture); +``` + +Returns **[Buffer](https://nodejs.org/api/buffer.html)** buffer - a protocol buffer representing a Mapbox Vector Tile + +# each + +Loops through all fixtures and provides the fixture object from get() + +**Parameters** + +- `function` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** a synchronously running function to execute on each fixture +- `fn` + +**Examples** + +```javascript +const mvtf = require('mvt-fixtures'); +const assert = require('assert'); + +mvtf.each(function(fixture) { + assert.ok(Buffer.isBuffer(fixture.buffer), 'is a buffer'); +}); +``` + +# get + +Get a fixture by name + +**Parameters** + +- `id` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)\|[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number))** the id of the fixture as specified in [FIXTURES.md](FIXTURES.md) + +**Examples** + +```javascript +const mvtf = require('mvt-fixtures'); + +const fixture = mvtf.get('001'); +console.log(fixture.id); // => '001' +console.log(fixture.description); // => ... +console.log(fixture.specification_reference); // => url to Mapbox Vector Tile specification reference +console.log(fixture.buffer); // => Buffer object +console.log(fixture.json); // => json representation of the fixture +``` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** fixture - a fixture object diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c63ee..fecdcd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -## 2.1.0 (release candidate 1) +## 2.1.0 (work in progress) - Rename project to `mvt-fixtures` - Break fixtures into `valid` and `invalid` directories - Match version with that of the Mapbox Vector Tile Specification +- Fixtures only include descriptions and no names, are now ordered by numerical id - Include more valid fixtures: TODO ## 1.0.0 @@ -11,4 +12,4 @@ ## 0.0.1 -- first \ No newline at end of file +- first diff --git a/FIXTURES.md b/FIXTURES.md new file mode 100644 index 0000000..1493254 --- /dev/null +++ b/FIXTURES.md @@ -0,0 +1,5 @@ +id|description|valid v1|valid v2 +---|---|---|--- +001|A vector tile without any layers, which essentially results in a completely empty buffer. - [link](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L75)|true|true +002|A single layer with a single point feature that has no id field. According to the specification, "A feature MAY contain an id field. If a feature has an id field, the value of the id SHOULD be unique among the features of the parent layer." - [link](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/README.md#42-features)|true|true +003|A single point feature with a missing geometry type. From the spec, "A feature MUST contain a type field as described in the Geometry Types section." - [link](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L41) - recommended error handling `recoverable`|false|false diff --git a/Makefile b/Makefile deleted file mode 100644 index 31b2845..0000000 --- a/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -MODULE_NAME := $(shell node -e "console.log(require('./package.json').name)") - -default: node_modules - ./node_modules/.bin/node-pre-gyp build --loglevel=error --clang=1 - -debug: - npm install --build-from-source=$(MODULE_NAME) --verbose --clang=1 - -clean: - rm -rf node_modules - rm -rf lib/binding - rm -rf build - -node_modules: - npm install --build-from-source=$(MODULE_NAME) --clang=1 - -docs: - npm run docs - -test: - npm test - -.PHONY: test docs \ No newline at end of file diff --git a/README.md b/README.md index 585b810..0b7f0b6 100644 --- a/README.md +++ b/README.md @@ -2,78 +2,109 @@ [![Build Status](https://travis-ci.org/mapbox/mvt-fixtures.svg?branch=master)](https://travis-ci.org/mapbox/mvt-fixtures) -A [`require()`able](#require-fixtures) suite of [valid](#valid-fixtures) & [invalid](#invalid-fixtures) vector tile fixtures for testing [Mapbox Vector Tile](https://github.com/mapbox/vector-tile-spec) decoding. *Previously called `evilmvt` but eventually `happymvt` prevailed.* +A `require()`able suite of valid and invalid vector tile fixtures for testing [Mapbox Vector Tile](https://github.com/mapbox/vector-tile-spec) encoders and decoders. You can view a list of all fixtures at [FIXTURES.md](FIXTURES.md). -All fixtures are included in the `/fixtures` directory. They are named to be as descriptive as possible, but the tables below gives us more space to describe the underlying data. +# Usage -**Version**: The version of `mvt-fixtures` stays in sync with the version of the Mapbox Vector Tile Specification. For instance `mvt-fixtures@2.1.0` references the Mapbox `vector-tile-spec@2.1`, reserving the patch versions for any unexpected bug fixes in this project. +mvt-fixtures can be used in two distinct ways -# Usage +1. **javascript interface**: use the javascript interface to generate fixtures on the fly +1. **raw fixtures** use the raw fixtures directly via the /fixtures directory. + +The Javascript API is recommended if you are working in Javascript or Node.js. The raw fixtures are provided for those using this outside of a Javascript application. The recommended workflow is to have your encoder or decoder loop through every fixture and either expect to successfully decode/encode valid fixtures, or fail to decode/encode invalid fixtures. When new fixtures are added to this repository, you simply need to update the version of the module (or your submodule) to get the new fixtures and re-run tests. + +**Validity:** each fixture includes information about whether they are valid according to the specification versions and possible error outcomes if they are invalid. If any of the fixtures are invalid, they must include an `error` field describing how to recover (or not) from the error. These can be found in the `validity` field of the fixture and info.json files. The following checks: -### `require('@mapbox/mvt-fixtures')` +* `v1` (Boolean): is this fixture valid according to Version 1.x of the Mapbox Vector Tile spec +* `v2` (Boolean): is this fixture valid according to Version 2.x of the Mapbox Vector Tile spec +* `error` (String): describes if the encoder/decoder should recover from this error or stop completely. THis is only present if the fixture is invalid according to one or more spec revisions. Values are + * `recoverable`: should the encoder/decoder continue move on and continue its work? For instance, if invalid geometry is found, can the encoder safely move to the next feature? + * `fatal`: the encoder should completely stop its process -Install +### Javascript usage +Check out the full Javascript interface over at [API.md](API.md) + +```shell +npm install @mapbox/mvt-fixtures --save-dev ``` -npm install @mapbox/mvt-fixtures --save + +```javascript +const mvtf = require('@mapbox/mvt-fixtures'); +const decoder = require('your-mvt-decoder'); + +mvtf.each(function(fixture) { + let output = decoder(fixture.buffer); + assert.equal(output.layers.length, fixture.json.layers.length, 'expected number of layers'); + // ... more tests +}); ``` -You can require the fixtures directly from the `evilmvt` module using the name of the fixture. +### Non-JS interface + +You can access all of the fixtures and their metadata in the /fixtures directory. You can download this repository and get them manually, or use this repository as a submodule. Each fixture is named by the directory /fixtures/{name} and has the following files: + +1. tile.mvt - the protocol buffer that represents (or intentionally breaks) the Mapbox Vector Tile specification +1. tile.json - a JSON representation of the tile and its properties +1. info.json - information about the fixture including `name`, `description`, and `specification_reference`. + +# Develop + +### Adding a new fixture + +All fixtures have a source file in the /src directory. This file is a module that exports an object with the following parameters: ```javascript -var fixtures = require('@mapbox/mvt-fixtures').fixtures; -var buffer = fixtures.invalid['Tags-nonexistant-values']; -// do something with bufer +module.exports = function(schema) { + return { + description: 'DESCRIPTION', + specification_reference: 'SPECIFICATION_URL', + validity: { + v1: true, + v2: false, + error: 'ERROR_TYPE' + }, + json: {...}, + manipulate: function(buffer) { + // function to further manipulate the buffer + return buffer; + } + } +}; ``` -### Vendor or Submodule - -Alternatively, you can [download the repository](https://github.com/mapbox/evilmvt/archive/master.zip) and use the fixtures manually, OR include this repository as a [Git Submodule](https://github.com/blog/2104-working-with-submodules). - -# Invalid fixtures `fixtures/invalid` - -Fixture name | Description ----|--- -`Feature-missing-GeomType.mvt` | The `Feature` message is missing a [`GeomType`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L41) message. -`Feature-multiple-geometries.mvt` | The `Feature` message as multiple [`geometry`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L46) fields encoded, when there should only be one. -`Feature-no-geometry.mvt` | The `Feature` message has no [`geometry`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L46). -`Feature-odd_number_tags.mvt` | Only has a single [`tag`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L38) where multiples of 2 are required. -`Feature-unknown_field_type.mvt` | Has a field value of `10`, which is [not listed as an enum](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L8-L13) and therefore invalid. -`GeomType-type.mvt` | The tag for GeomType is `10`, which is invalid. -`Key-mistyped_uint32.mvt` | Has a [`key`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L63) property incorrectly encoded as a type `std::uint32_t`. | n/a -`Layer-extent-mistyped_string.mvt` | Layer [`extent`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L70) is incorrectly encoded as a type `std::string`. -`Layer-extent-none.mvt` | Missing the [`extent`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L70) type -`Layer-name-duplicates.mvt` | Includes two layer [`name`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L57)s with the same value: "layer_name". -`Layer-name-mistyped_uint32.mvt` | Has a layer name incorrectly encoded as `std::uint32_t`. -`Layer-name-none.mvt` | Does not include a layer name. -`Layer-name-none-version1.mvt` | Same as above, but version 1 tile. -`Layer-no-features.mvt` | Layer has no repeated [`Features`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L60) tags. -`Layer-unknow_value_type.mvt` | Includes a Layer value tag of `20`, which is not defined in the spec. -`Layer-invalid-version.mvt` | Layer version is `99`, which is invalid according to the specification. -`Layer-version-mistyped_string.mvt` | Layer version is incorrectly typed as a `std::string`. -`Layer-version-none.mvt` | Layer does not have a [`version`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L55) property. -`Tags-nonexistant-values.mvt` | Feature has [`tags`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L38) that point to non-existent [`Keys`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L63) and [`Values`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) in the layer. -`Tile-unknown-tag.mvt` | Tile message has an unknown tag value. The only accepted tag value here is `3`, but this tile encodes a `Feature` with the tag value of `10`. -`Value-no-fields.mvt` | includes a [`Value`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) without any fields encoded within it. -`Value-string-mistyped_int64.mvt` | A Layer value property is listed as "string" but encoded as `std::int64_t`. -`Value-unknown-field-type.mvt` | The [`Value`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) has a field with an [unknown type](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L17-L28). - -# Valid fixtures `fixtures/valid` - -Fixture name | Description ----|--- -`Feature-single-linestring.mvt` | Single layer with a valid [linestring geometry](https://github.com/mapbox/vector-tile-spec/tree/master/2.1#4353-example-linestring) from the spec docs. -`Feature-single-multilinestring.mvt` | Single layer with a valid [multilinestring geometry](https://github.com/mapbox/vector-tile-spec/tree/master/2.1#4354-example-multi-linestring) from the spec docs. -`Feature-single-multipoint.mvt` | Single layer with a valid [multipoint geometry](https://github.com/mapbox/vector-tile-spec/tree/master/2.1#4352-example-multi-point) from the spec docs. -`Feature-single-point.mvt` | Single layer with a valid [point geometry](https://github.com/mapbox/vector-tile-spec/tree/master/2.1#4351-example-point) from the spec docs. -`Feature-single-polygon.mvt` | Single layer with a valid [polygon geometry](https://github.com/mapbox/vector-tile-spec/tree/master/2.1#4355-example-polygon) from the spec docs. -`Feature-unknown-GeomType.mvt` | Single geometry with [`UNKNOWN` type](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L9). This is considered "valid" in the lens of the specification. Encoders/decoders can choose to use or throw on this goemetry type. -`Value-multiple-fields.mvt` | The [`Value`](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) message has two different string entries. -`Value-single-bool-point.mvt` | Single [Value](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) with `bool` type and a single Point feature. -`Value-single-double-point.mvt` | Single [Value](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) with `double` type and a single Point feature. -`Value-single-float-point.mvt` | Single [Value](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) with `float` type and a single Point feature. -`Value-single-int64-point.mvt` | Single [Value](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) with `int64` type and a single Point feature. -`Value-single-sint64-point.mvt` | Single [Value](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) with `sint64` type and a single Point feature. -`Value-single-string-point.mvt` | Single [Value](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) with `string` type and a single Point feature. -`Value-single-uint64-point.mvt` | Single [Value](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L66) with `uint64` type and a single Point feature. -`Values-all.mvt` | A buffer with all possible [`Value` types](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L17-L28) encoded in the layer and single Feature. +A new fixture can be created by running the command, which will auto-increment the ID: + +```shell +npm run new +# New file created: /src/003.js. +``` + +### Building fixtures + +To rebuild all of the raw fixtures (including the tile.mvt, tile.json, and info.json files) in /fixtures you can run: + +``` +npm run build +``` + +### Building docs + +Documentation takes two forms... + +1. Javascript API docs in API.md +1. Fixture reference in FIXTURES.md + +These can be generated by running: + +``` +npm run docs +``` + +### Running tests + +All tests can be run with: + +``` +npm test +``` diff --git a/_reserve/Makefile b/_reserve/Makefile deleted file mode 100644 index 31b2845..0000000 --- a/_reserve/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -MODULE_NAME := $(shell node -e "console.log(require('./package.json').name)") - -default: node_modules - ./node_modules/.bin/node-pre-gyp build --loglevel=error --clang=1 - -debug: - npm install --build-from-source=$(MODULE_NAME) --verbose --clang=1 - -clean: - rm -rf node_modules - rm -rf lib/binding - rm -rf build - -node_modules: - npm install --build-from-source=$(MODULE_NAME) --clang=1 - -docs: - npm run docs - -test: - npm test - -.PHONY: test docs \ No newline at end of file diff --git a/_reserve/bin/create.js b/_reserve/bin/create.js deleted file mode 100755 index 433d20d..0000000 --- a/_reserve/bin/create.js +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node - -var test = require('tape'); -var path = require('path'); -var evilmvt = require('../lib/index.js'); -var fs = require('fs'); -var vtinfo = require('@mapbox/vtinfo'); - -var fixturesPath = '../../fixtures/new'; - -var buffer = evilmvt.create(); -console.log(buffer); -console.log(vtinfo(buffer)); - -if (process.argv[2]) { - fs.writeFileSync(path.resolve(__dirname, fixturesPath, process.argv[2]), buffer); -} \ No newline at end of file diff --git a/_reserve/binding.gyp b/_reserve/binding.gyp deleted file mode 100644 index 063b865..0000000 --- a/_reserve/binding.gyp +++ /dev/null @@ -1,38 +0,0 @@ -{ - 'includes': [ 'common.gypi' ], - 'targets': [ - { - 'target_name': 'evilmvt', - 'sources': [ './src/evilmvt.cpp' ], - 'include_dirs': [ - ' -#include -#include -#include -#include - -/** - * Create a Mapbox Vector Tile with evil, non-compliant values. - * - * @name evil - * @param {Buffer} buffer - Vector Tile PBF - * - */ -NAN_METHOD(create) -{ - // prepare new pbf_writer to create new tile - std::string final_tile; - protozero::pbf_writer final_tile_writer(final_tile); - - - { - protozero::pbf_writer layer_writer(final_tile_writer, 3); - - // add layer version - std::uint32_t version = 2; // default to 2 - layer_writer.add_uint32(15, version); // version - - std::string name = "single_point"; - layer_writer.add_string(1, name.data(), name.size()); // name - - // keys - // layer_writer.add_string(3, "string"); - // layer_writer.add_string(3, "float"); - // layer_writer.add_string(3, "double"); - // layer_writer.add_string(3, "int64"); - // layer_writer.add_string(3, "uint64"); - // layer_writer.add_string(3, "sint64"); - layer_writer.add_string(3, "bool"); - - - // { // string - // protozero::pbf_writer value_writer_string(layer_writer, 4); - // value_writer_string.add_string(1, "hello"); - // } - // { // float - // protozero::pbf_writer value_writer_string(layer_writer, 4); - // float float_value = 9.000023; - // value_writer_string.add_float(2, float_value); - // } - // { // double - // protozero::pbf_writer value_writer_string(layer_writer, 4); - // double double_value = 8.99999999999996; - // value_writer_string.add_double(3, double_value); - // } - // { // int64 - // protozero::pbf_writer value_writer_string(layer_writer, 4); - // std::int64_t int64_value = 9223372036854775807; - // value_writer_string.add_int64(4, int64_value); - // } - // { // uint64 - // protozero::pbf_writer value_writer_string(layer_writer, 4); - // std::uint64_t uint64_value = -922337203685477580; - // value_writer_string.add_uint64(5, uint64_value); - // } - // { // sint64 - // protozero::pbf_writer value_writer_string(layer_writer, 4); - // std::int64_t sint64_value = 9123372036854775807; - // value_writer_string.add_sint64(6, sint64_value); - // } - { // bool - protozero::pbf_writer value_writer_string(layer_writer, 4); - bool bool_value = true; - value_writer_string.add_bool(7, bool_value); - } - - // extent - std::uint32_t extent = 4096; - layer_writer.add_uint32(5, extent); - - // features - { - protozero::pbf_writer feature_writer(layer_writer, 2); - std::uint64_t id = 123; - feature_writer.add_uint64(1, id); // feature id - - std::vector tags = { 0, 0 }; // 'hello': 'world' - feature_writer.add_packed_int32(2, std::begin(tags), std::end(tags)); // feature tags - - // std::uint32_t geom_type = 0; // UNKNOWN - std::uint32_t geom_type = 1; // POINT - // std::uint32_t geom_type = 2; // LINESTRING - // std::uint32_t geom_type = 3; // POLYGON - feature_writer.add_enum(3, geom_type); - - std::vector geom = { 9, 50, 34 }; // point - // std::vector geom = { 17, 10, 14, 3, 9 }; // multipoint - // std::vector geom = { 9, 4, 4, 18, 0, 16, 16, 0 }; // linestring - // std::vector geom = { 9, 4, 4, 18, 0, 16, 16, 0, 9, 17, 17, 10, 4, 8 }; // multilinestring - // std::vector geom = { 9, 6, 12, 18, 10, 12, 24, 44, 15 }; // polygon - feature_writer.add_packed_int32(4, std::begin(geom), std::end(geom)); // feature geometry - } - } - - // return the new buffer - info.GetReturnValue().Set(Nan::CopyBuffer((char*)final_tile.data(), final_tile.size()).ToLocalChecked()); - return; -} - -extern "C" { - static void init(v8::Handle target) { - Nan::HandleScope scope; - Nan::SetMethod(target, "create", create); - } - #define MAKE_MODULE(_modname) NODE_MODULE( _modname, init); - MAKE_MODULE(MODULE_NAME); -} \ No newline at end of file diff --git a/_reserve/src/evilmvt.hpp b/_reserve/src/evilmvt.hpp deleted file mode 100644 index b3b18cc..0000000 --- a/_reserve/src/evilmvt.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#pragma GCC diagnostic push -// #pragma GCC diagnostic ignored "-Wunused-parameter" -// #pragma GCC diagnostic ignored "-Wshadow" -#include -#pragma GCC diagnostic pop - -// evilmvt solo method -NAN_METHOD(create); \ No newline at end of file diff --git a/_reserve/src/reserve/valid-values-all.cpp b/_reserve/src/reserve/valid-values-all.cpp deleted file mode 100644 index 2a81a0c..0000000 --- a/_reserve/src/reserve/valid-values-all.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "evilmvt.hpp" - -#include -#include -#include -#include -#include - -/** - * Create a Mapbox Vector Tile with evil, non-compliant values. - * - * @name evil - * @param {Buffer} buffer - Vector Tile PBF - * - */ -NAN_METHOD(create) -{ - // v8::Local buffer = info[0]->ToObject(); - // if (buffer->IsNull() || buffer->IsUndefined() || !node::Buffer::HasInstance(buffer)) { - // Nan::ThrowTypeError("First argument must be a valid buffer."); - // return; - // } - - // prepare original buffer if it exists - // const char *original_tile = node::Buffer::Data(buffer); - // std::size_t dataLength = node::Buffer::Length(buffer); - // protozero::pbf_reader original_tile_reader(original_tile, dataLength); - - // prepare new pbf_writer to create new tile - std::string final_tile; - protozero::pbf_writer final_tile_writer(final_tile); - - // prepare layer pbf_writer to add to final_tile_writer - { - protozero::pbf_writer layer_writer(final_tile_writer, 3); - - // add layer attributes - std::uint32_t version = 2; - layer_writer.add_uint32(15, version); // version - std::string name = "layer_name"; - layer_writer.add_string(1, name.data(), name.size()); // name - - // keys - layer_writer.add_string(3, "string"); - layer_writer.add_string(3, "float"); - layer_writer.add_string(3, "double"); - layer_writer.add_string(3, "int64"); - layer_writer.add_string(3, "uint64"); - layer_writer.add_string(3, "sint64"); - layer_writer.add_string(3, "bool"); - - - { // string - protozero::pbf_writer value_writer_string(layer_writer, 4); - value_writer_string.add_string(1, "hello"); - } - { // float - protozero::pbf_writer value_writer_string(layer_writer, 4); - float float_value = 9.000023; - value_writer_string.add_float(2, float_value); - } - { // double - protozero::pbf_writer value_writer_string(layer_writer, 4); - double double_value = 8.99999999999996; - value_writer_string.add_double(3, double_value); - } - { // int64 - protozero::pbf_writer value_writer_string(layer_writer, 4); - std::int64_t int64_value = 9223372036854775807; - value_writer_string.add_int64(4, int64_value); - } - { // uint64 - protozero::pbf_writer value_writer_string(layer_writer, 4); - std::uint64_t uint64_value = -922337203685477580; - value_writer_string.add_uint64(5, uint64_value); - } - { // sint64 - protozero::pbf_writer value_writer_string(layer_writer, 4); - std::int64_t sint64_value = 9123372036854775807; - value_writer_string.add_sint64(6, sint64_value); - } - { // bool - protozero::pbf_writer value_writer_string(layer_writer, 4); - bool bool_value = true; - value_writer_string.add_bool(7, bool_value); - } - - - // extent - std::uint32_t extent = 4096; - layer_writer.add_uint32(5, extent); - - // features - { - protozero::pbf_writer feature_writer(layer_writer, 2); - std::uint64_t id = 123; - feature_writer.add_uint64(1, id); // feature id - - std::vector tags = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 }; // 'hello': 'world' - feature_writer.add_packed_int32(2, std::begin(tags), std::end(tags)); // feature tags - - // std::uint32_t geom_type = 0; // UNKNOWN - std::uint32_t geom_type = 1; // POINT - // std::uint32_t geom_type = 2; // LINESTRING - // std::uint32_t geom_type = 3; // POLYGON - feature_writer.add_enum(3, geom_type); - - std::vector geom = { 9, 50, 34 }; // point - // std::vector geom = { 17, 10, 14, 3, 9 }; // multipoint - // std::vector geom = { 9, 4, 4, 18, 0, 16, 16, 0 }; // linestring - // std::vector geom = { 9, 4, 4, 18, 0, 16, 16, 0, 9, 17, 17, 10, 4, 8 }; // multilinestring - // std::vector geom = { 9, 6, 12, 18, 10, 12, 24, 44, 15 }; // polygon - feature_writer.add_packed_int32(4, std::begin(geom), std::end(geom)); // feature geometry - } - } - - // return the new buffer - info.GetReturnValue().Set(Nan::CopyBuffer((char*)final_tile.data(), final_tile.size()).ToLocalChecked()); - return; -} - -extern "C" { - static void init(v8::Handle target) { - Nan::HandleScope scope; - Nan::SetMethod(target, "create", create); - } - #define MAKE_MODULE(_modname) NODE_MODULE( _modname, init); - MAKE_MODULE(MODULE_NAME); -} \ No newline at end of file diff --git a/fixtures/001/info.json b/fixtures/001/info.json new file mode 100644 index 0000000..eb6728c --- /dev/null +++ b/fixtures/001/info.json @@ -0,0 +1,8 @@ +{ + "description": "A vector tile without any layers, which essentially results in a completely empty buffer.", + "specification_reference": "https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L75", + "validity": { + "v1": true, + "v2": true + } +} \ No newline at end of file diff --git a/fixtures/001/tile.json b/fixtures/001/tile.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/fixtures/001/tile.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/fixtures/001/tile.mvt b/fixtures/001/tile.mvt new file mode 100644 index 0000000..e69de29 diff --git a/fixtures/002/info.json b/fixtures/002/info.json new file mode 100644 index 0000000..67e0c3e --- /dev/null +++ b/fixtures/002/info.json @@ -0,0 +1,8 @@ +{ + "description": "A single layer with a single point feature that has no id field. According to the specification, \"A feature MAY contain an id field. If a feature has an id field, the value of the id SHOULD be unique among the features of the parent layer.\"", + "specification_reference": "https://github.com/mapbox/vector-tile-spec/blob/master/2.1/README.md#42-features", + "validity": { + "v1": true, + "v2": true + } +} \ No newline at end of file diff --git a/fixtures/002/tile.json b/fixtures/002/tile.json new file mode 100644 index 0000000..8ad3d92 --- /dev/null +++ b/fixtures/002/tile.json @@ -0,0 +1,22 @@ +{ + "layers": [ + { + "version": 2, + "name": "hello", + "features": [ + { + "tags": [], + "type": 1, + "geometry": [ + 9, + 50, + 34 + ] + } + ], + "keys": {}, + "values": {}, + "extent": 4096 + } + ] +} \ No newline at end of file diff --git a/fixtures/002/tile.mvt b/fixtures/002/tile.mvt new file mode 100644 index 0000000..4ecce68 Binary files /dev/null and b/fixtures/002/tile.mvt differ diff --git a/fixtures/003/info.json b/fixtures/003/info.json new file mode 100644 index 0000000..ff0b016 --- /dev/null +++ b/fixtures/003/info.json @@ -0,0 +1,9 @@ +{ + "description": "A single point feature with a missing geometry type. From the spec, \"A feature MUST contain a type field as described in the Geometry Types section.\"", + "specification_reference": "https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L41", + "validity": { + "v1": false, + "v2": false, + "error": "recoverable" + } +} \ No newline at end of file diff --git a/fixtures/003/tile.json b/fixtures/003/tile.json new file mode 100644 index 0000000..b0c82b2 --- /dev/null +++ b/fixtures/003/tile.json @@ -0,0 +1,22 @@ +{ + "layers": [ + { + "version": 2, + "name": "hello", + "features": [ + { + "id": 1, + "tags": [], + "geometry": [ + 9, + 50, + 34 + ] + } + ], + "keys": {}, + "values": {}, + "extent": 4096 + } + ] +} \ No newline at end of file diff --git a/fixtures/003/tile.mvt b/fixtures/003/tile.mvt new file mode 100644 index 0000000..295130d Binary files /dev/null and b/fixtures/003/tile.mvt differ diff --git a/fixtures/invalid/Feature-missing-GeomType.mvt b/fixtures/invalid/Feature-missing-GeomType.mvt deleted file mode 100644 index d2090a4..0000000 Binary files a/fixtures/invalid/Feature-missing-GeomType.mvt and /dev/null differ diff --git a/fixtures/invalid/Feature-multiple-geometries.mvt b/fixtures/invalid/Feature-multiple-geometries.mvt deleted file mode 100644 index 4a9bfd3..0000000 Binary files a/fixtures/invalid/Feature-multiple-geometries.mvt and /dev/null differ diff --git a/fixtures/invalid/Feature-no-geometry.mvt b/fixtures/invalid/Feature-no-geometry.mvt deleted file mode 100644 index 0c8a9c0..0000000 Binary files a/fixtures/invalid/Feature-no-geometry.mvt and /dev/null differ diff --git a/fixtures/invalid/Feature-odd_number_tags.mvt b/fixtures/invalid/Feature-odd_number_tags.mvt deleted file mode 100644 index 8d04a6a..0000000 Binary files a/fixtures/invalid/Feature-odd_number_tags.mvt and /dev/null differ diff --git a/fixtures/invalid/Feature-unknown_field_type.mvt b/fixtures/invalid/Feature-unknown_field_type.mvt deleted file mode 100644 index 47ca4c5..0000000 Binary files a/fixtures/invalid/Feature-unknown_field_type.mvt and /dev/null differ diff --git a/fixtures/invalid/GeomType-invalid-type.mvt b/fixtures/invalid/GeomType-invalid-type.mvt deleted file mode 100644 index 5bbd7d8..0000000 Binary files a/fixtures/invalid/GeomType-invalid-type.mvt and /dev/null differ diff --git a/fixtures/invalid/Key-mistyped_uint32.mvt b/fixtures/invalid/Key-mistyped_uint32.mvt deleted file mode 100644 index 6c4a728..0000000 Binary files a/fixtures/invalid/Key-mistyped_uint32.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-extent-mistyped_string.mvt b/fixtures/invalid/Layer-extent-mistyped_string.mvt deleted file mode 100644 index a0b4c41..0000000 Binary files a/fixtures/invalid/Layer-extent-mistyped_string.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-extent-none.mvt b/fixtures/invalid/Layer-extent-none.mvt deleted file mode 100644 index 6d71280..0000000 Binary files a/fixtures/invalid/Layer-extent-none.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-name-duplicates.mvt b/fixtures/invalid/Layer-name-duplicates.mvt deleted file mode 100644 index 685f3e5..0000000 Binary files a/fixtures/invalid/Layer-name-duplicates.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-name-mistyped_uint32.mvt b/fixtures/invalid/Layer-name-mistyped_uint32.mvt deleted file mode 100644 index c5f22cc..0000000 Binary files a/fixtures/invalid/Layer-name-mistyped_uint32.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-name-none-version1.mvt b/fixtures/invalid/Layer-name-none-version1.mvt deleted file mode 100644 index 5eb6a0e..0000000 Binary files a/fixtures/invalid/Layer-name-none-version1.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-name-none.mvt b/fixtures/invalid/Layer-name-none.mvt deleted file mode 100644 index 94d9bd9..0000000 Binary files a/fixtures/invalid/Layer-name-none.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-no-features.mvt b/fixtures/invalid/Layer-no-features.mvt deleted file mode 100644 index 995308b..0000000 --- a/fixtures/invalid/Layer-no-features.mvt +++ /dev/null @@ -1,4 +0,0 @@ -!x - -layer_namehello" -world(€ \ No newline at end of file diff --git a/fixtures/invalid/Layer-unknown_field_type.mvt b/fixtures/invalid/Layer-unknown_field_type.mvt deleted file mode 100644 index 67e53c3..0000000 Binary files a/fixtures/invalid/Layer-unknown_field_type.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-version-invalid.mvt b/fixtures/invalid/Layer-version-invalid.mvt deleted file mode 100644 index b189730..0000000 Binary files a/fixtures/invalid/Layer-version-invalid.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-version-mistyped_string.mvt b/fixtures/invalid/Layer-version-mistyped_string.mvt deleted file mode 100644 index 42d38a5..0000000 Binary files a/fixtures/invalid/Layer-version-mistyped_string.mvt and /dev/null differ diff --git a/fixtures/invalid/Layer-version-none.mvt b/fixtures/invalid/Layer-version-none.mvt deleted file mode 100644 index fa26ed0..0000000 Binary files a/fixtures/invalid/Layer-version-none.mvt and /dev/null differ diff --git a/fixtures/invalid/Tags-nonexistant-values.mvt b/fixtures/invalid/Tags-nonexistant-values.mvt deleted file mode 100644 index 4b7c2d4..0000000 Binary files a/fixtures/invalid/Tags-nonexistant-values.mvt and /dev/null differ diff --git a/fixtures/invalid/Tile-unknown-tag.mvt b/fixtures/invalid/Tile-unknown-tag.mvt deleted file mode 100644 index 7208dd4..0000000 Binary files a/fixtures/invalid/Tile-unknown-tag.mvt and /dev/null differ diff --git a/fixtures/invalid/Value-no-fields.mvt b/fixtures/invalid/Value-no-fields.mvt deleted file mode 100644 index 4994a5d..0000000 Binary files a/fixtures/invalid/Value-no-fields.mvt and /dev/null differ diff --git a/fixtures/invalid/Value-string-mistyped_int64.mvt b/fixtures/invalid/Value-string-mistyped_int64.mvt deleted file mode 100644 index 7422a73..0000000 Binary files a/fixtures/invalid/Value-string-mistyped_int64.mvt and /dev/null differ diff --git a/fixtures/invalid/Value-unknown-field-type.mvt b/fixtures/invalid/Value-unknown-field-type.mvt deleted file mode 100644 index 8af5c8e..0000000 Binary files a/fixtures/invalid/Value-unknown-field-type.mvt and /dev/null differ diff --git a/fixtures/valid/Feature-single-linestring.mvt b/fixtures/valid/Feature-single-linestring.mvt deleted file mode 100644 index 2bc1302..0000000 Binary files a/fixtures/valid/Feature-single-linestring.mvt and /dev/null differ diff --git a/fixtures/valid/Feature-single-multilinestring.mvt b/fixtures/valid/Feature-single-multilinestring.mvt deleted file mode 100644 index 9e8d230..0000000 Binary files a/fixtures/valid/Feature-single-multilinestring.mvt and /dev/null differ diff --git a/fixtures/valid/Feature-single-multipoint.mvt b/fixtures/valid/Feature-single-multipoint.mvt deleted file mode 100644 index 4c8fa28..0000000 Binary files a/fixtures/valid/Feature-single-multipoint.mvt and /dev/null differ diff --git a/fixtures/valid/Feature-single-point.mvt b/fixtures/valid/Feature-single-point.mvt deleted file mode 100644 index 79133bd..0000000 Binary files a/fixtures/valid/Feature-single-point.mvt and /dev/null differ diff --git a/fixtures/valid/Feature-single-polygon.mvt b/fixtures/valid/Feature-single-polygon.mvt deleted file mode 100644 index 9e14343..0000000 Binary files a/fixtures/valid/Feature-single-polygon.mvt and /dev/null differ diff --git a/fixtures/valid/Feature-unknown-GeomType.mvt b/fixtures/valid/Feature-unknown-GeomType.mvt deleted file mode 100644 index f02ed05..0000000 Binary files a/fixtures/valid/Feature-unknown-GeomType.mvt and /dev/null differ diff --git a/fixtures/valid/Value-multiple-fields.mvt b/fixtures/valid/Value-multiple-fields.mvt deleted file mode 100644 index b1e9e74..0000000 Binary files a/fixtures/valid/Value-multiple-fields.mvt and /dev/null differ diff --git a/fixtures/valid/Value-single-bool-point.mvt b/fixtures/valid/Value-single-bool-point.mvt deleted file mode 100644 index ea36ee7..0000000 Binary files a/fixtures/valid/Value-single-bool-point.mvt and /dev/null differ diff --git a/fixtures/valid/Value-single-double-point.mvt b/fixtures/valid/Value-single-double-point.mvt deleted file mode 100644 index c4cd80d..0000000 Binary files a/fixtures/valid/Value-single-double-point.mvt and /dev/null differ diff --git a/fixtures/valid/Value-single-float-point.mvt b/fixtures/valid/Value-single-float-point.mvt deleted file mode 100644 index 46ac821..0000000 Binary files a/fixtures/valid/Value-single-float-point.mvt and /dev/null differ diff --git a/fixtures/valid/Value-single-int64-point.mvt b/fixtures/valid/Value-single-int64-point.mvt deleted file mode 100644 index df647bc..0000000 Binary files a/fixtures/valid/Value-single-int64-point.mvt and /dev/null differ diff --git a/fixtures/valid/Value-single-sint64-point.mvt b/fixtures/valid/Value-single-sint64-point.mvt deleted file mode 100644 index 22ab9c0..0000000 Binary files a/fixtures/valid/Value-single-sint64-point.mvt and /dev/null differ diff --git a/fixtures/valid/Value-single-string-point.mvt b/fixtures/valid/Value-single-string-point.mvt deleted file mode 100644 index ed8044a..0000000 Binary files a/fixtures/valid/Value-single-string-point.mvt and /dev/null differ diff --git a/fixtures/valid/Value-single-uint64-point.mvt b/fixtures/valid/Value-single-uint64-point.mvt deleted file mode 100644 index 27d2baa..0000000 Binary files a/fixtures/valid/Value-single-uint64-point.mvt and /dev/null differ diff --git a/fixtures/valid/Values-all-types.mvt b/fixtures/valid/Values-all-types.mvt deleted file mode 100644 index 3c976a9..0000000 Binary files a/fixtures/valid/Values-all-types.mvt and /dev/null differ diff --git a/index.js b/index.js new file mode 100644 index 0000000..b7a27da --- /dev/null +++ b/index.js @@ -0,0 +1,144 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const d3 = require('d3-queue'); +const schema = require('protocol-buffers-schema'); +const Pbf = require('pbf'); +const Compile = require('pbf/compile'); + +const proto_mvt = schema.parse(fs.readFileSync('vector-tile-spec/2.1/vector_tile.proto', 'utf8')); +const mvt = Compile(proto_mvt); + +function generateBuffer(json) { + const pbf = new Pbf(); + mvt.Tile.write(json, pbf); + return new Buffer(pbf.finish()); +}; + +/** + * Get a fixture by name + * @param {String|Number} id - the id of the fixture as specified in [FIXTURES.md](FIXTURES.md) + * @returns {Object} fixture - a fixture object + * @example + * const mvtf = require('mvt-fixtures'); + * + * const fixture = mvtf.get('001'); + * console.log(fixture.id); // => '001' + * console.log(fixture.description); // => ... + * console.log(fixture.specification_reference); // => url to Mapbox Vector Tile specification reference + * console.log(fixture.buffer); // => Buffer object + * console.log(fixture.json); // => json representation of the fixture + */ +function get(id) { + if (!id) throw new Error('No fixture id provided'); + + // add prefix zeros if they don't exist + id = (typeof id === 'number') ? getID(id) : id; + + let final = {}; + let fixture; + try { + fixture = require(`./src/${id}.js`)(mvt); + } catch(err) { + throw new Error(`${id} is not a fixture`); + } + + final.id = id; + final.description = fixture.description; + final.specification_reference = fixture.specification_reference; + final.json = fixture.json; + final.buffer = generateBuffer(fixture.json); + final.validity = fixture.validity; + if (fixture.manipulate) { + final.buffer = fixture.manipulate(final.buffer); + } + + return final; +}; + +/** + * Loops through all fixtures and provides the fixture object from get() + * @param {Function} function - a synchronously running function to execute on each fixture + * @example + * const mvtf = require('mvt-fixtures'); + * const assert = require('assert'); + * + * mvtf.each(function(fixture) { + * assert.ok(Buffer.isBuffer(fixture.buffer), 'is a buffer'); + * }); + */ +function each(fn) { + if (!fn) throw new Error('must provide a function argument in .each()'); + if (typeof fn !== 'function') throw new Error('argument is not a function'); + + const files = fs.readdirSync('./src'); + const queue = d3.queue(1); + files.forEach(function(file) { + queue.defer(function(next) { + let name = path.parse(file).name; + let fixture = get(name); + fn(fixture); + next(); + }); + }); + + queue.awaitAll(function(err) { + if (err) throw err; + // do nothing? + }); +} +/** + * Create a tile fixture from a protocol buffer schema object representing the + * Mapbox Vector Tile schema. + * @param {Object} object - the json schema object to generate against the Mapbox Vector Tile Specification protocol (see src/ for examples) + * @returns {Buffer} buffer - a protocol buffer representing a Mapbox Vector Tile + * @example + * const mvtf = require('@mapbox/mvt-fixtures'); + * + * const fixture = { + * layers: [ + * { + * version: 2, + * name: 'hello', + * features: [ + * { + * id: 1, + * tags: [], + * type: 1, + * geometry: [ 9, 50, 34 ] + * } + * ], + * keys: {}, + * values: {}, + * extent: 4096 + * } + * ] + * } + * + * const buffer = mvtf.create(fixture); + */ +function create(json) { + if (!json) throw new Error('No specification provided'); + if (typeof json !== 'object') throw new Error('Specification parameter must be an object'); + + return generateBuffer(json); +} + +function getID(number) { + if (number < 10) { + return `00${number}`; + } else if (number < 100 && number > 9) { + return `0${number}`; + } else { + return `${number}`; + } +} + +module.exports = { + get: get, + each: each, + create: create, + getID, getID, + schema: mvt +}; diff --git a/lib/fixtures.js b/lib/fixtures.js deleted file mode 100644 index f14b5d3..0000000 --- a/lib/fixtures.js +++ /dev/null @@ -1,19 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var validFixturePath = path.resolve(__dirname, '..', 'fixtures', 'valid'); -var invalidFixturePath = path.resolve(__dirname, '..', 'fixtures', 'invalid'); - -var fixtures = { - valid: {}, - invalid: {} -}; - -fs.readdirSync(validFixturePath).forEach(function(file) { - fixtures.valid[path.basename(file, '.mvt')] = fs.readFileSync(path.resolve(validFixturePath, file)); -}); - -fs.readdirSync(invalidFixturePath).forEach(function(file) { - fixtures.invalid[path.basename(file, '.mvt')] = fs.readFileSync(path.resolve(invalidFixturePath, file)); -}); - -module.exports = fixtures; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 2053f26..0000000 --- a/lib/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - fixtures: require('./fixtures.js') -}; \ No newline at end of file diff --git a/package.json b/package.json index 973d1a1..03e9e4a 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,12 @@ "name": "@mapbox/mvt-fixtures", "version": "2.1.0", "description": "A require-able test fixture suite of valid and invalid Mapbox Vector Tiles", - "main": "./lib/index.js", + "main": "index.js", "scripts": { - "test": "tape test/*.test.js" + "test": "tape test/*.test.js", + "docs": "node scripts/docs.js && documentation build index.js -f md > API.md --shallow", + "new": "node scripts/new.js", + "build": "node scripts/build.js" }, "homepage": "https://github.com/mapbox/mvt-fixtures", "repository": { @@ -14,6 +17,12 @@ "author": "Mapbox", "license": "ISC", "devDependencies": { + "@mapbox/vector-tile": "^1.3.0", "tape": "^4.5.1" + }, + "dependencies": { + "d3-queue": "^3.0.7", + "pbf": "^3.0.5", + "protocol-buffers-schema": "^3.3.1" } -} \ No newline at end of file +} diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..e69b5a9 --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,24 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const mvtf = require('..'); + +mvtf.each(function(fixture) { + + let dir = path.resolve('./fixtures/' + fixture.id); + if (!fs.existsSync(dir)){ + fs.mkdirSync(dir); + } + + let mvt = dir + '/tile.mvt'; + let json = dir + '/tile.json'; + let info = dir + '/info.json'; + fs.writeFileSync(mvt, fixture.buffer); + fs.writeFileSync(json, JSON.stringify(fixture.json, null, 2)); + fs.writeFileSync(info, JSON.stringify({ + description: fixture.description, + specification_reference: fixture.specification_reference, + validity: fixture.validity + }, null, 2)); +}); diff --git a/scripts/docs.js b/scripts/docs.js new file mode 100644 index 0000000..65594e1 --- /dev/null +++ b/scripts/docs.js @@ -0,0 +1,18 @@ +'use strict'; + +const fs = require('fs'); +const mvtf = require('..'); + +let docs = `id|description|valid v1|valid v2 +---|---|---|--- +`; + +mvtf.each(function(fixture) { + let description = `${fixture.description} - [link](${fixture.specification_reference})`; + if (!fixture.validity.v1 || !fixture.validity.v2) { + description += ` - recommended error handling \`${fixture.validity.error}\``; + } + docs+=`${fixture.id}|${description}|${fixture.validity.v1}|${fixture.validity.v2}\n`; +}); + +fs.writeFileSync('./FIXTURES.md', docs); diff --git a/scripts/new.js b/scripts/new.js new file mode 100644 index 0000000..2ef9961 --- /dev/null +++ b/scripts/new.js @@ -0,0 +1,44 @@ +const fs = require('fs'); +const getID = require('..').getID; + +const files = fs.readdirSync('./src'); +const id = getID(files.length + 1); // get the next available number + +const template = `module.exports = function(schema) { + return { + description: 'DESCRIPTION', + specification_reference: 'https://github.com/mapbox/vector-tile-spec/blob/master/2.1/README.md', + validity: { + v1: true, + v2: true, + error: 'IF INVALID, ERROR TYPE HERE' + }, + json: { + layers: [ + { + version: 2, + name: 'hello', + features: [ + { + id: 1, + tags: [], + type: schema.Tile.GeomType.POINT.value, + geometry: [ 9, 50, 34 ] + } + ], + keys: {}, + values: {}, + extent: 4096 + } + ] + }, + manipulate: function(buffer) { + // stuff here + return buffer; + } + } +}; +` + +fs.writeFileSync(`./src/${id}.js`, template); +console.log(`New file created: /src/${id}.js.\nMake sure to run "npm run build" to generate the raw fixtures.`); diff --git a/src/001.js b/src/001.js new file mode 100644 index 0000000..3a47d81 --- /dev/null +++ b/src/001.js @@ -0,0 +1,11 @@ +module.exports = function(schema) { + return { + description: 'A vector tile without any layers, which essentially results in a completely empty buffer.', + specification_reference: 'https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L75', + validity: { + v1: true, + v2: true + }, + json: {} + } +}; diff --git a/src/002.js b/src/002.js new file mode 100644 index 0000000..31eee16 --- /dev/null +++ b/src/002.js @@ -0,0 +1,33 @@ +module.exports = function(schema) { + return { + description: 'A single layer with a single point feature that has no id field. According to the specification, "A feature MAY contain an id field. If a feature has an id field, the value of the id SHOULD be unique among the features of the parent layer."', + specification_reference: 'https://github.com/mapbox/vector-tile-spec/blob/master/2.1/README.md#42-features', + validity: { + v1: true, + v2: true + }, + json: { + layers: [ + { + version: 2, + name: 'hello', + features: [ + { + // without id + // id: 1, + tags: [], + type: schema.Tile.GeomType.POINT.value, + geometry: [ 9, 50, 34 ] + } + ], + keys: {}, + values: {}, + extent: 4096 + } + ] + }, + manipulate: function(buffer) { + return buffer; + } + } +}; diff --git a/src/003.js b/src/003.js new file mode 100644 index 0000000..6454adf --- /dev/null +++ b/src/003.js @@ -0,0 +1,34 @@ +module.exports = function(schema) { + return { + description: 'A single point feature with a missing geometry type. From the spec, "A feature MUST contain a type field as described in the Geometry Types section."', + specification_reference: 'https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L41', + validity: { + v1: false, + v2: false, + error: 'recoverable' + }, + json: { + layers: [ + { + version: 2, + name: 'hello', + features: [ + { + id: 1, + tags: [], + // type: schema.Tile.GeomType.POINT.value, + geometry: [ 9, 50, 34 ] + } + ], + keys: {}, + values: {}, + extent: 4096 + } + ] + }, + manipulate: function(buffer) { + // stuff here + return buffer; + } + } +}; diff --git a/test/create.test.js b/test/create.test.js new file mode 100644 index 0000000..7bddcf5 --- /dev/null +++ b/test/create.test.js @@ -0,0 +1,73 @@ +const test = require('tape'); +const fs = require('fs'); +const vt = require('@mapbox/vector-tile').VectorTile; +const pbf = require('pbf'); +const schema = require('protocol-buffers-schema'); +const Compile = require('pbf/compile'); +const mvtf = require('..'); + +const mvt_proto = schema.parse(fs.readFileSync(__dirname + '/../vector-tile-spec/2.1/vector_tile.proto', 'utf8')); + +test('[create] failure, throws error when no spec is provided', (assert) => { + try { + const buffer = mvtf.create(); + } catch(err) { + assert.ok(err); + assert.ok(/No specification provided/.test(err.message)); + assert.end(); + } +}); + +test('[create] failure, throws error when specification is not an object', (assert) => { + try { + const buffer = mvtf.create('not-an-object'); + } catch(err) { + assert.ok(err); + assert.ok(/Specification parameter must be an object/.test(err.message)); + assert.end(); + } +}); + +test('[create] success, creates a protocol that is not compliant with the MVT spec', (assert) => { + const template = { + hello: 'world' + }; + + const buffer = mvtf.create(template); + assert.ok(Buffer.isBuffer(buffer), 'is a buffer'); + + const info = new vt(new pbf(buffer)); + assert.notOk(Object.keys(info.layers).length, 'no layers'); + assert.end(); +}); + +test('[create] success, creates a compliant protocol buffer', (assert) => { + const spec = Compile(mvt_proto); + const template = { + layers: [ + { + version: 2, + name: 'hello', + features: [ + { + id: 1, + tags: [], + type: spec.Tile.GeomType.POINT, + geometry: [ 9, 50, 34 ] + } + ], + keys: {}, + values: {}, + extent: 4096 + } + ] + }; + + const buffer = mvtf.create(template); + assert.equal(typeof buffer, 'object', 'returns a buffer'); + + const info = new vt(new pbf(buffer)); + assert.equal(Object.keys(info.layers).length, 1, 'expected number of layers'); + assert.ok(info.layers.hello, 'expected layer name'); + assert.end(); +}); diff --git a/test/docs.test.js b/test/docs.test.js new file mode 100644 index 0000000..04a35b7 --- /dev/null +++ b/test/docs.test.js @@ -0,0 +1,22 @@ +'use strict'; + +const test = require('tape'); +const fs = require('fs'); +const path = require('path'); +const exec = require('child_process').exec; + +test('[docs] FIXTURES.md and API.md have been generated', (assert) => { + const apiBefore = fs.readFileSync(path.resolve('./API.md')); + const fixBefore = fs.readFileSync(path.resolve('./FIXTURES.md')); + const script = path.resolve('./scripts/docs.js'); + exec(`node ${script}`, function(err, stdin, stdout) { + assert.ifError(err); + const apiAfter = fs.readFileSync(path.resolve('./API.md')); + const fixAfter = fs.readFileSync(path.resolve('./FIXTURES.md')); + assert.deepEqual(fixBefore, fixAfter, 'FIXTURE.md buffers are equal'); + assert.deepEqual(apiBefore, apiAfter, 'API.md buffers are equal'); + assert.equal(fixBefore.toString(), fixAfter.toString(), 'FIXTURE.md strings match regenerated docs'); + assert.equal(apiBefore.toString(), apiAfter.toString(), 'API.md strings match regenerated docs'); + assert.end(); + }); +}); diff --git a/test/each.test.js b/test/each.test.js new file mode 100644 index 0000000..41fcf36 --- /dev/null +++ b/test/each.test.js @@ -0,0 +1,33 @@ +'use strict'; + +const test = require('tape'); +const fs = require('fs'); +const mvtf = require('..'); + +test('[each] loads all fixtures', (assert) => { + const numFixtures = fs.readdirSync(__dirname + '/../src').length; + + let count = 0; + mvtf.each(function(fixture) { + count++; + assert.ok(fixture.id); + assert.ok(fixture.description); + assert.ok(fixture.buffer); + assert.ok(fixture.specification_reference); + assert.ok(fixture.validity); + }); + + assert.equal(numFixtures, count, 'expected number of fixtures'); + assert.end(); +}); + +test('[each] failure, throws error if no function provided', (assert) => { + try { + mvtf.each(); + assert.fail(); + } catch(err) { + assert.ok(err); + assert.ok(/must provide a function argument in \.each()/.test(err.message), 'expected error message'); + assert.end(); + } +}); diff --git a/test/fixtures.test.js b/test/fixtures.test.js index 2cb2e77..6107c4e 100644 --- a/test/fixtures.test.js +++ b/test/fixtures.test.js @@ -1,16 +1,49 @@ -var test = require('tape'); -var fixtures = require('../lib/').fixtures; - -test('valid fixtures are buffers', function(t) { - for (f in fixtures.valid) { - t.ok(Buffer.isBuffer(fixtures.valid[f]), f + ' is a buffer'); - } - t.end(); +'use strict'; + +const test = require('tape'); +const fs = require('fs'); +const path = require('path'); +const mvtf = require('..'); + +test('[fixtures] validate all raw fixtures are present', (assert) => { + mvtf.each(function(f) { + assert.ok(fs.existsSync(path.resolve(`${__dirname}/../fixtures/${f.id}`)), 'directory exists'); + assert.ok(fs.existsSync(path.resolve(`${__dirname}/../fixtures/${f.id}/tile.mvt`))); + assert.ok(fs.existsSync(path.resolve(`${__dirname}/../fixtures/${f.id}/tile.json`))); + assert.ok(fs.existsSync(path.resolve(`${__dirname}/../fixtures/${f.id}/info.json`))); + }); + assert.end(); }); -test('invalid fixtures are buffers', function(t) { - for (f in fixtures.invalid) { - t.ok(Buffer.isBuffer(fixtures.invalid[f]), f + ' is a buffer'); - } - t.end(); -}); \ No newline at end of file +test('[fixtures] validate all raw fixtures info matches that of the source fixture', (assert) => { + mvtf.each(function(f) { + // read info file and check name, description, and spec url match + let info = JSON.parse(fs.readFileSync(path.resolve(`${__dirname}/../fixtures/${f.id}/info.json`))); + assert.equal(info.description, f.description, 'descriptions match'); + assert.equal(info.specification_reference, f.specification_reference, 'specification_references match'); + + let buffer = fs.readFileSync(path.resolve(`${__dirname}/../fixtures/${f.id}/tile.mvt`)); + assert.deepEqual(buffer, f.buffer, 'buffers are equal'); + assert.equal(buffer.length, f.buffer.length, 'buffer lengths are equal'); + + let json = JSON.parse(fs.readFileSync(path.resolve(`${__dirname}/../fixtures/${f.id}/tile.json`))); + assert.deepEqual(json, f.json, 'jsons are equal'); + }); + + assert.end(); +}); + +test('[fixtures] validate all source fixtures to make sure they have all required properties', (assert) => { + const files = fs.readdirSync(path.resolve(`${__dirname}/../src`)); + files.forEach(function(file) { + let fixture = require(path.resolve(`src/${file}`))(mvtf.schema); + assert.ok(fixture.description, `${file} has property description`); + assert.ok(fixture.specification_reference, `${file} has property specification_reference`); + assert.ok(fixture.json, `${file} has property json`); + if (fixture.manipulate) { + assert.equal(typeof fixture.manipulate, 'function', `${file} property manipulate is a function`); + } + }); + + assert.end(); +}); diff --git a/test/get.test.js b/test/get.test.js new file mode 100644 index 0000000..9b32c8d --- /dev/null +++ b/test/get.test.js @@ -0,0 +1,47 @@ +const test = require('tape'); +const vt = require('@mapbox/vector-tile').VectorTile; +const pbf = require('pbf'); +const mvtf = require('..'); + +test('[get] failure, throws error if no id provided', (assert) => { + try { + var buffer = mvtf.get(); + assert.fail(); + } catch(err) { + assert.ok(err); + assert.ok(/No fixture id provided/.test(err.message)); + assert.end(); + } +}); + +test('[get] failure, throws error if fixture does not exist', (assert) => { + try { + var buffer = mvtf.get('beep-boop'); + assert.fail(); + } catch(err) { + assert.ok(err); + assert.ok(/beep-boop is not a fixture/.test(err.message)); + assert.end(); + } +}); + +test('[get] success, gets a fixture and its properties/buffer', (assert) => { + const fixture = mvtf.get('002'); + assert.ok(fixture.buffer); + assert.ok(fixture.id); + assert.ok(fixture.json); + assert.ok(fixture.description); + assert.ok(fixture.specification_reference); + assert.ok(fixture.validity); + assert.notOk(fixture.manipulate); + assert.equal(typeof fixture.buffer, 'object', 'returns a buffer'); + + const info = new vt(new pbf(fixture.buffer)); + assert.equal(Object.keys(info.layers).length, 1, 'expected number of layers'); + assert.ok(info.layers.hello, 'expected layer name'); + + assert.ok(mvtf.get(1), 'works with a number too'); + assert.ok(mvtf.get(2), 'works with a number too'); + + assert.end(); +}); diff --git a/vector-tile-spec b/vector-tile-spec new file mode 160000 index 0000000..452ef14 --- /dev/null +++ b/vector-tile-spec @@ -0,0 +1 @@ +Subproject commit 452ef14841f3e1288de8b355f722682b63c1b13f