diff --git a/test/expected/serialize.json b/test/expected/serialize.json new file mode 100644 index 0000000..2ef850d --- /dev/null +++ b/test/expected/serialize.json @@ -0,0 +1,341 @@ +{ + "layers": { + "landuse": { + "version": 1, + "name": "landuse", + "extent": 4096, + "length": 3, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "osm_id" + ], + "_values": [ + "park", + 0, + 0, + "sand", + 0 + ], + "_features": [ + 13, + 225, + 325 + ] + }, + "waterway": { + "version": 1, + "name": "waterway", + "extent": 4096, + "length": 11, + "shaping": {}, + "faces": [], + "_keys": [ + "osm_id", + "type" + ], + "_values": [ + 102639147, + "stream", + 28483936, + 28544165, + 28544279, + 28544488, + 28544489, + 28544658, + 28544491, + 28544660, + 28544661, + 28544659 + ], + "_features": [ + 556, + 629, + 925, + 973, + 1049, + 1146, + 1193, + 1234, + 1272, + 1298, + 1357 + ] + }, + "water": { + "version": 1, + "name": "water", + "extent": 4096, + "length": 5, + "shaping": {}, + "faces": [], + "_keys": [ + "osm_id" + ], + "_values": [ + 0, + 0, + 0, + 0, + 0 + ], + "_features": [ + 1555, + 2026, + 2107, + 2153, + 2201 + ] + }, + "road": { + "version": 1, + "name": "road", + "extent": 4096, + "length": 37, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "oneway", + "osm_id", + "type" + ], + "_values": [ + "street_limited", + 0, + 12176014, + "residential", + "street", + 102638413, + 12190176, + 125871922, + "unclassified", + 12172969, + 12171395, + 12172974, + 12171388, + 12176906, + 12184872, + 12188141, + 82660225, + 12185663, + 232292986, + 236784059, + 12187833, + 12180093, + 12181692, + 12179250, + 12182785, + 207142176, + 232292987, + 12181251, + 112429240, + 12172937, + 12178793, + 12184634, + 12175654, + 12187257, + 12183699, + "main", + 112926169, + "primary", + 239213058, + 174215004, + 157338248, + 174215024, + 88159974, + "tertiary", + 12177297, + 12177319 + ], + "_features": [ + 2402, + 2524, + 2619, + 2653, + 2767, + 2836, + 2870, + 2903, + 2981, + 3009, + 3042, + 3070, + 3107, + 3144, + 3214, + 3245, + 3367, + 3427, + 3470, + 3501, + 3546, + 3615, + 3660, + 3689, + 3719, + 3772, + 3822, + 3857, + 3889, + 3923, + 4101, + 4159, + 4281, + 4570, + 4611, + 4919, + 4958 + ] + }, + "bridge": { + "version": 1, + "name": "bridge", + "extent": 4096, + "length": 2, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "oneway", + "osm_id", + "type" + ], + "_values": [ + "service", + 0, + 207142221, + "track", + "street", + 207142175, + "residential" + ], + "_features": [ + 5723, + 5752 + ] + }, + "place_label": { + "version": 1, + "name": "place_label", + "extent": 4096, + "length": 3, + "shaping": {}, + "faces": [], + "_keys": [ + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "type", + "ldir", + "scalerank" + ], + "_values": [ + "Mill Valley", + 0, + "town", + "", + "Tamalpais Valley", + 0, + 0, + "hamlet", + "Muir Beach", + 0 + ], + "_features": [ + 6058, + 6096, + 6138 + ] + }, + "poi_label": { + "version": 1, + "name": "poi_label", + "extent": 4096, + "length": 1, + "shaping": {}, + "faces": [], + "_keys": [ + "localrank", + "maki", + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "ref", + "scalerank", + "type" + ], + "_values": [ + 1, + "park", + "Muir Woods National Monument", + 0, + "", + "Park" + ], + "_features": [ + 6406 + ] + }, + "road_label": { + "version": 1, + "name": "road_label", + "extent": 4096, + "length": 7, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "len", + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "ref", + "reflen" + ], + "_values": [ + "main", + 7396.168723572097, + "Panoramic Hwy", + "Panoramic Highway", + 88159974, + "", + 0, + 4357.193150547803, + "Shoreline Hwy", + "Shoreline Highway", + 157338250, + 3528.44005555027, + 174215024, + 2928.2679350617123, + 125871918, + 7600.032638046431, + 12177319, + 2513.4221036393283, + 174215004, + 1716.9016582189813, + 12188378 + ], + "_features": [ + 6622, + 6783, + 6879, + 7328, + 7512, + 8269, + 8570 + ] + } + }, + "faces": {} +} \ No newline at end of file diff --git a/test/expected/shape.json b/test/expected/shape.json new file mode 100644 index 0000000..bffb8f0 --- /dev/null +++ b/test/expected/shape.json @@ -0,0 +1,1137 @@ +{ + "layers": { + "landuse": { + "version": 1, + "name": "landuse", + "extent": 4096, + "length": 3, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "osm_id" + ], + "_values": [ + "park", + 0, + 0, + "sand", + 0 + ], + "_features": [ + 13, + 225, + 325 + ] + }, + "waterway": { + "version": 1, + "name": "waterway", + "extent": 4096, + "length": 11, + "shaping": {}, + "faces": [], + "_keys": [ + "osm_id", + "type" + ], + "_values": [ + 102639147, + "stream", + 28483936, + 28544165, + 28544279, + 28544488, + 28544489, + 28544658, + 28544491, + 28544660, + 28544661, + 28544659 + ], + "_features": [ + 567, + 640, + 936, + 984, + 1060, + 1157, + 1204, + 1245, + 1283, + 1309, + 1368 + ] + }, + "water": { + "version": 1, + "name": "water", + "extent": 4096, + "length": 5, + "shaping": {}, + "faces": [], + "_keys": [ + "osm_id" + ], + "_values": [ + 0, + 0, + 0, + 0, + 0 + ], + "_features": [ + 1577, + 2048, + 2129, + 2175, + 2223 + ] + }, + "road": { + "version": 1, + "name": "road", + "extent": 4096, + "length": 37, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "oneway", + "osm_id", + "type" + ], + "_values": [ + "street_limited", + 0, + 12176014, + "residential", + "street", + 102638413, + 12190176, + 125871922, + "unclassified", + 12172969, + 12171395, + 12172974, + 12171388, + 12176906, + 12184872, + 12188141, + 82660225, + 12185663, + 232292986, + 236784059, + 12187833, + 12180093, + 12181692, + 12179250, + 12182785, + 207142176, + 232292987, + 12181251, + 112429240, + 12172937, + 12178793, + 12184634, + 12175654, + 12187257, + 12183699, + "main", + 112926169, + "primary", + 239213058, + 174215004, + 157338248, + 174215024, + 88159974, + "tertiary", + 12177297, + 12177319 + ], + "_features": [ + 2490, + 2612, + 2707, + 2741, + 2855, + 2924, + 2958, + 2991, + 3069, + 3097, + 3130, + 3158, + 3195, + 3232, + 3302, + 3333, + 3455, + 3515, + 3558, + 3589, + 3634, + 3703, + 3748, + 3777, + 3807, + 3860, + 3910, + 3945, + 3977, + 4011, + 4189, + 4247, + 4369, + 4658, + 4699, + 5007, + 5046 + ] + }, + "bridge": { + "version": 1, + "name": "bridge", + "extent": 4096, + "length": 2, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "oneway", + "osm_id", + "type" + ], + "_values": [ + "service", + 0, + 207142221, + "track", + "street", + 207142175, + "residential" + ], + "_features": [ + 5822, + 5851 + ] + }, + "place_label": { + "version": 1, + "name": "place_label", + "extent": 4096, + "length": 3, + "shaping": { + "Open Sans": { + "Mill Valley": [ + { + "face": 0, + "glyph": 48, + "x": 0, + "y": 26 + }, + { + "face": 0, + "glyph": 76, + "x": 22, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 28, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 34, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 40, + "y": 26 + }, + { + "face": 0, + "glyph": 57, + "x": 46, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 60, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 73, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 79, + "y": 26 + }, + { + "face": 0, + "glyph": 72, + "x": 85, + "y": 26 + }, + { + "face": 0, + "glyph": 92, + "x": 98, + "y": 26 + } + ], + "Tamalpais Valley": [ + { + "face": 0, + "glyph": 55, + "x": 0, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 11, + "y": 26 + }, + { + "face": 0, + "glyph": 80, + "x": 24, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 46, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 59, + "y": 26 + }, + { + "face": 0, + "glyph": 83, + "x": 65, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 80, + "y": 26 + }, + { + "face": 0, + "glyph": 76, + "x": 93, + "y": 26 + }, + { + "face": 0, + "glyph": 86, + "x": 99, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 110, + "y": 26 + }, + { + "face": 0, + "glyph": 57, + "x": 116, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 130, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 143, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 149, + "y": 26 + }, + { + "face": 0, + "glyph": 72, + "x": 155, + "y": 26 + }, + { + "face": 0, + "glyph": 92, + "x": 168, + "y": 26 + } + ], + "Muir Beach": [ + { + "face": 0, + "glyph": 48, + "x": 0, + "y": 26 + }, + { + "face": 0, + "glyph": 88, + "x": 22, + "y": 26 + }, + { + "face": 0, + "glyph": 76, + "x": 37, + "y": 26 + }, + { + "face": 0, + "glyph": 85, + "x": 43, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 53, + "y": 26 + }, + { + "face": 0, + "glyph": 37, + "x": 59, + "y": 26 + }, + { + "face": 0, + "glyph": 72, + "x": 75, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 88, + "y": 26 + }, + { + "face": 0, + "glyph": 70, + "x": 101, + "y": 26 + }, + { + "face": 0, + "glyph": 75, + "x": 112, + "y": 26 + } + ] + } + }, + "faces": [ + "Open Sans Regular" + ], + "_keys": [ + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "type", + "ldir", + "scalerank" + ], + "_values": [ + "Mill Valley", + 0, + "town", + "", + "Tamalpais Valley", + 0, + 0, + "hamlet", + "Muir Beach", + 0 + ], + "_features": [ + 6223, + 6261, + 6303 + ] + }, + "poi_label": { + "version": 1, + "name": "poi_label", + "extent": 4096, + "length": 1, + "shaping": { + "Open Sans": { + "Muir Woods National Monument": [ + { + "face": 0, + "glyph": 48, + "x": 0, + "y": 26 + }, + { + "face": 0, + "glyph": 88, + "x": 22, + "y": 26 + }, + { + "face": 0, + "glyph": 76, + "x": 37, + "y": 26 + }, + { + "face": 0, + "glyph": 85, + "x": 43, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 53, + "y": 26 + }, + { + "face": 0, + "glyph": 58, + "x": 59, + "y": 26 + }, + { + "face": 0, + "glyph": 82, + "x": 81, + "y": 26 + }, + { + "face": 0, + "glyph": 82, + "x": 96, + "y": 26 + }, + { + "face": 0, + "glyph": 71, + "x": 111, + "y": 26 + }, + { + "face": 0, + "glyph": 86, + "x": 126, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 137, + "y": 26 + }, + { + "face": 0, + "glyph": 49, + "x": 143, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 161, + "y": 26 + }, + { + "face": 0, + "glyph": 87, + "x": 174, + "y": 26 + }, + { + "face": 0, + "glyph": 76, + "x": 182, + "y": 26 + }, + { + "face": 0, + "glyph": 82, + "x": 188, + "y": 26 + }, + { + "face": 0, + "glyph": 81, + "x": 203, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 218, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 231, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 237, + "y": 26 + }, + { + "face": 0, + "glyph": 48, + "x": 243, + "y": 26 + }, + { + "face": 0, + "glyph": 82, + "x": 265, + "y": 26 + }, + { + "face": 0, + "glyph": 81, + "x": 280, + "y": 26 + }, + { + "face": 0, + "glyph": 88, + "x": 295, + "y": 26 + }, + { + "face": 0, + "glyph": 80, + "x": 310, + "y": 26 + }, + { + "face": 0, + "glyph": 72, + "x": 332, + "y": 26 + }, + { + "face": 0, + "glyph": 81, + "x": 345, + "y": 26 + }, + { + "face": 0, + "glyph": 87, + "x": 360, + "y": 26 + } + ] + } + }, + "faces": [ + "Open Sans Regular" + ], + "_keys": [ + "localrank", + "maki", + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "ref", + "scalerank", + "type" + ], + "_values": [ + 1, + "park", + "Muir Woods National Monument", + 0, + "", + "Park" + ], + "_features": [ + 6807 + ] + }, + "road_label": { + "version": 1, + "name": "road_label", + "extent": 4096, + "length": 7, + "shaping": { + "Open Sans": { + "Panoramic Hwy": [ + { + "face": 0, + "glyph": 51, + "x": 0, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 14, + "y": 26 + }, + { + "face": 0, + "glyph": 81, + "x": 27, + "y": 26 + }, + { + "face": 0, + "glyph": 82, + "x": 42, + "y": 26 + }, + { + "face": 0, + "glyph": 85, + "x": 57, + "y": 26 + }, + { + "face": 0, + "glyph": 68, + "x": 67, + "y": 26 + }, + { + "face": 0, + "glyph": 80, + "x": 80, + "y": 26 + }, + { + "face": 0, + "glyph": 76, + "x": 102, + "y": 26 + }, + { + "face": 0, + "glyph": 70, + "x": 108, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 119, + "y": 26 + }, + { + "face": 0, + "glyph": 43, + "x": 125, + "y": 26 + }, + { + "face": 0, + "glyph": 90, + "x": 143, + "y": 26 + }, + { + "face": 0, + "glyph": 92, + "x": 162, + "y": 26 + } + ], + "Shoreline Hwy": [ + { + "face": 0, + "glyph": 54, + "x": 0, + "y": 26 + }, + { + "face": 0, + "glyph": 75, + "x": 13, + "y": 26 + }, + { + "face": 0, + "glyph": 82, + "x": 28, + "y": 26 + }, + { + "face": 0, + "glyph": 85, + "x": 43, + "y": 26 + }, + { + "face": 0, + "glyph": 72, + "x": 53, + "y": 26 + }, + { + "face": 0, + "glyph": 79, + "x": 66, + "y": 26 + }, + { + "face": 0, + "glyph": 76, + "x": 72, + "y": 26 + }, + { + "face": 0, + "glyph": 81, + "x": 78, + "y": 26 + }, + { + "face": 0, + "glyph": 72, + "x": 93, + "y": 26 + }, + { + "face": 0, + "glyph": 3, + "x": 106, + "y": 26 + }, + { + "face": 0, + "glyph": 43, + "x": 112, + "y": 26 + }, + { + "face": 0, + "glyph": 90, + "x": 130, + "y": 26 + }, + { + "face": 0, + "glyph": 92, + "x": 149, + "y": 26 + } + ] + } + }, + "faces": [ + "Open Sans Regular" + ], + "_keys": [ + "class", + "len", + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "ref", + "reflen" + ], + "_values": [ + "main", + 7396.168723572097, + "Panoramic Hwy", + "Panoramic Highway", + 88159974, + "", + 0, + 4357.193150547803, + "Shoreline Hwy", + "Shoreline Highway", + 157338250, + 3528.44005555027, + 174215024, + 2928.2679350617123, + 125871918, + 7600.032638046431, + 12177319, + 2513.4221036393283, + 174215004, + 1716.9016582189813, + 12188378 + ], + "_features": [ + 7198, + 7359, + 7455, + 7904, + 8088, + 8845, + 9146 + ] + } + }, + "faces": { + "Open Sans Regular": { + "glyphs": { + "3": { + "id": 3, + "width": 0, + "height": 0, + "left": 0, + "top": 0, + "advance": 6 + }, + "37": { + "id": 37, + "width": 13, + "height": 18, + "left": 2, + "top": 18, + "advance": 15 + }, + "43": { + "id": 43, + "width": 14, + "height": 18, + "left": 2, + "top": 18, + "advance": 17 + }, + "48": { + "id": 48, + "width": 18, + "height": 18, + "left": 2, + "top": 18, + "advance": 21 + }, + "49": { + "id": 49, + "width": 14, + "height": 18, + "left": 2, + "top": 18, + "advance": 18 + }, + "51": { + "id": 51, + "width": 12, + "height": 18, + "left": 2, + "top": 18, + "advance": 14 + }, + "54": { + "id": 54, + "width": 12, + "height": 19, + "left": 1, + "top": 18, + "advance": 13 + }, + "55": { + "id": 55, + "width": 14, + "height": 18, + "left": 0, + "top": 18, + "advance": 13 + }, + "57": { + "id": 57, + "width": 15, + "height": 18, + "left": 0, + "top": 18, + "advance": 14 + }, + "58": { + "id": 58, + "width": 22, + "height": 18, + "left": 0, + "top": 18, + "advance": 22 + }, + "68": { + "id": 68, + "width": 11, + "height": 15, + "left": 1, + "top": 14, + "advance": 13 + }, + "70": { + "id": 70, + "width": 10, + "height": 15, + "left": 1, + "top": 14, + "advance": 11 + }, + "71": { + "id": 71, + "width": 12, + "height": 20, + "left": 1, + "top": 19, + "advance": 14 + }, + "72": { + "id": 72, + "width": 12, + "height": 15, + "left": 1, + "top": 14, + "advance": 13 + }, + "75": { + "id": 75, + "width": 11, + "height": 19, + "left": 2, + "top": 19, + "advance": 14 + }, + "76": { + "id": 76, + "width": 4, + "height": 18, + "left": 1, + "top": 18, + "advance": 6 + }, + "79": { + "id": 79, + "width": 3, + "height": 19, + "left": 2, + "top": 19, + "advance": 6 + }, + "80": { + "id": 80, + "width": 19, + "height": 14, + "left": 2, + "top": 14, + "advance": 22 + }, + "81": { + "id": 81, + "width": 11, + "height": 14, + "left": 2, + "top": 14, + "advance": 14 + }, + "82": { + "id": 82, + "width": 13, + "height": 15, + "left": 1, + "top": 14, + "advance": 14 + }, + "83": { + "id": 83, + "width": 12, + "height": 20, + "left": 2, + "top": 14, + "advance": 14 + }, + "85": { + "id": 85, + "width": 8, + "height": 14, + "left": 2, + "top": 14, + "advance": 9 + }, + "86": { + "id": 86, + "width": 10, + "height": 15, + "left": 1, + "top": 14, + "advance": 11 + }, + "87": { + "id": 87, + "width": 8, + "height": 17, + "left": 0, + "top": 16, + "advance": 8 + }, + "88": { + "id": 88, + "width": 12, + "height": 14, + "left": 1, + "top": 13, + "advance": 14 + }, + "90": { + "id": 90, + "width": 19, + "height": 13, + "left": 0, + "top": 13, + "advance": 18 + }, + "92": { + "id": 92, + "width": 13, + "height": 19, + "left": 0, + "top": 13, + "advance": 12 + } + }, + "family": "Open Sans", + "style": "Regular" + } + } +} \ No newline at end of file diff --git a/test/expected/simplify.json b/test/expected/simplify.json new file mode 100644 index 0000000..ae98ad4 --- /dev/null +++ b/test/expected/simplify.json @@ -0,0 +1,337 @@ +{ + "layers": { + "landuse": { + "version": 2, + "name": "landuse", + "extent": 4096, + "length": 3, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "osm_id" + ], + "_values": [ + "park", + 0, + 0, + "sand", + 0 + ], + "_features": [ + 13, + 223, + 314 + ] + }, + "waterway": { + "version": 2, + "name": "waterway", + "extent": 4096, + "length": 11, + "shaping": {}, + "faces": [], + "_keys": [ + "osm_id", + "type" + ], + "_values": [ + 102639147, + "stream", + 28483936, + 28544165, + 28544279, + 28544488, + 28544489, + 28544658, + 28544491, + 28544660, + 28544661, + 28544659 + ], + "_features": [ + 543, + 616, + 912, + 960, + 1036, + 1133, + 1180, + 1221, + 1259, + 1285, + 1344 + ] + }, + "water": { + "version": 2, + "name": "water", + "extent": 4096, + "length": 1, + "shaping": {}, + "faces": [], + "_keys": [ + "osm_id" + ], + "_values": [ + 0, + 0, + 0, + 0, + 0 + ], + "_features": [ + 1542 + ] + }, + "road": { + "version": 2, + "name": "road", + "extent": 4096, + "length": 37, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "oneway", + "osm_id", + "type" + ], + "_values": [ + "street_limited", + 0, + 12176014, + "residential", + "street", + 102638413, + 12190176, + 125871922, + "unclassified", + 12172969, + 12171395, + 12172974, + 12171388, + 12176906, + 12184872, + 12188141, + 82660225, + 12185663, + 232292986, + 236784059, + 12187833, + 12180093, + 12181692, + 12179250, + 12182785, + 207142176, + 232292987, + 12181251, + 112429240, + 12172937, + 12178793, + 12184634, + 12175654, + 12187257, + 12183699, + "main", + 112926169, + "primary", + 239213058, + 174215004, + 157338248, + 174215024, + 88159974, + "tertiary", + 12177297, + 12177319 + ], + "_features": [ + 2190, + 2312, + 2407, + 2441, + 2555, + 2624, + 2658, + 2691, + 2769, + 2797, + 2830, + 2858, + 2895, + 2932, + 3002, + 3033, + 3155, + 3215, + 3258, + 3289, + 3334, + 3403, + 3448, + 3477, + 3507, + 3560, + 3610, + 3645, + 3677, + 3711, + 3889, + 3947, + 4069, + 4358, + 4399, + 4707, + 4746 + ] + }, + "bridge": { + "version": 2, + "name": "bridge", + "extent": 4096, + "length": 2, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "oneway", + "osm_id", + "type" + ], + "_values": [ + "service", + 0, + 207142221, + "track", + "street", + 207142175, + "residential" + ], + "_features": [ + 5511, + 5540 + ] + }, + "place_label": { + "version": 2, + "name": "place_label", + "extent": 4096, + "length": 3, + "shaping": {}, + "faces": [], + "_keys": [ + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "type", + "ldir", + "scalerank" + ], + "_values": [ + "Mill Valley", + 0, + "town", + "", + "Tamalpais Valley", + 0, + 0, + "hamlet", + "Muir Beach", + 0 + ], + "_features": [ + 5682, + 5720, + 5762 + ] + }, + "poi_label": { + "version": 2, + "name": "poi_label", + "extent": 4096, + "length": 1, + "shaping": {}, + "faces": [], + "_keys": [ + "localrank", + "maki", + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "ref", + "scalerank", + "type" + ], + "_values": [ + 1, + "park", + "Muir Woods National Monument", + 0, + "", + "Park" + ], + "_features": [ + 6010 + ] + }, + "road_label": { + "version": 2, + "name": "road_label", + "extent": 4096, + "length": 7, + "shaping": {}, + "faces": [], + "_keys": [ + "class", + "len", + "name", + "name_de", + "name_en", + "name_es", + "name_fr", + "osm_id", + "ref", + "reflen" + ], + "_values": [ + "main", + 7396.168723572097, + "Panoramic Hwy", + "Panoramic Highway", + 88159974, + "", + 0, + 4357.193150547803, + "Shoreline Hwy", + "Shoreline Highway", + 157338250, + 3528.44005555027, + 174215024, + 2928.2679350617123, + 125871918, + 7600.032638046431, + 12177319, + 2513.4221036393283, + 174215004, + 1716.9016582189813, + 12188378 + ], + "_features": [ + 6226, + 6387, + 6483, + 6932, + 7116, + 7873, + 8174 + ] + } + }, + "faces": {} +} \ No newline at end of file diff --git a/test/fixtures/mapbox-streets-v4.13.1306.3163.vector.pbf b/test/fixtures/mapbox-streets-v4.13.1306.3163.vector.pbf new file mode 100644 index 0000000..a0d8c85 Binary files /dev/null and b/test/fixtures/mapbox-streets-v4.13.1306.3163.vector.pbf differ diff --git a/test/format/ieee754.js b/test/format/ieee754.js new file mode 100644 index 0000000..47d18da --- /dev/null +++ b/test/format/ieee754.js @@ -0,0 +1,118 @@ +'use strict'; + +// Copyright (c) 2008, Fair Oaks Labs, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// +// Modifications to writeIEEE754 to support negative zeroes made by Brian White + +exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) { + var e, m, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = isBE ? 0 : (nBytes - 1), + d = isBE ? 1 : -1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; + +exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isBE ? (nBytes - 1) : 0, + d = isBE ? -1 : 1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; +}; diff --git a/test/format/protobuf.js b/test/format/protobuf.js new file mode 100644 index 0000000..c31a9e9 --- /dev/null +++ b/test/format/protobuf.js @@ -0,0 +1,116 @@ +'use strict'; + +var ieee754 = require('./ieee754.js'); + +module.exports = Protobuf; +function Protobuf(buf) { + this.buf = buf; + this.length = buf.length; + this.pos = 0; +} + +Protobuf.prototype.destroy = function() { + this.buf = null; +}; + +Protobuf.prototype.readUInt32 = function() { + var val = this.readUInt32LE(this.pos); + this.pos += 4; + return val; +}; + +Protobuf.prototype.readUInt64 = function() { + var val = this.readUInt64LE(this.pos); + this.pos += 8; + return val; +}; + +Protobuf.prototype.readDouble = function() { + var val = ieee754.readIEEE754(this.buf, this.pos, false, 52, 8); + this.pos += 8; + return val; +}; + +Protobuf.prototype.readVarint = function() { + // TODO: bounds checking + var pos = this.pos; + if (this.buf[pos] <= 0x7f) { + this.pos++; + return this.buf[pos]; + } else if (this.buf[pos + 1] <= 0x7f) { + this.pos += 2; + return (this.buf[pos] & 0x7f) | (this.buf[pos + 1] << 7); + } else if (this.buf[pos + 2] <= 0x7f) { + this.pos += 3; + return (this.buf[pos] & 0x7f) | (this.buf[pos + 1] & 0x7f) << 7 | (this.buf[pos + 2]) << 14; + } else if (this.buf[pos + 3] <= 0x7f) { + this.pos += 4; + return (this.buf[pos] & 0x7f) | (this.buf[pos + 1] & 0x7f) << 7 | (this.buf[pos + 2] & 0x7f) << 14 | (this.buf[pos + 3]) << 21; + } else if (this.buf[pos + 4] <= 0x7f) { + this.pos += 5; + return ((this.buf[pos] & 0x7f) | (this.buf[pos + 1] & 0x7f) << 7 | (this.buf[pos + 2] & 0x7f) << 14 | (this.buf[pos + 3]) << 21) + (this.buf[pos + 4] * 268435456); + } else { + this.skip(0); + return 0; + // throw new Error("TODO: Handle 6+ byte varints"); + } +}; + +Protobuf.prototype.readSVarint = function() { + var num = this.readVarint(); + if (num > 2147483647) throw new Error('TODO: Handle numbers >= 2^30'); + // zigzag encoding + return ((num >> 1) ^ -(num & 1)); +}; + +Protobuf.prototype.readString = function() { + var bytes = this.readVarint(); + // TODO: bounds checking + var chr = String.fromCharCode; + var b = this.buf; + var p = this.pos; + var end = this.pos + bytes; + var str = ''; + while (p < end) { + if (b[p] <= 0x7F) str += chr(b[p++]); + else if (b[p] <= 0xBF) throw new Error('Invalid UTF-8 codepoint: ' + b[p]); + else if (b[p] <= 0xDF) str += chr((b[p++] & 0x1F) << 6 | (b[p++] & 0x3F)); + else if (b[p] <= 0xEF) str += chr((b[p++] & 0x1F) << 12 | (b[p++] & 0x3F) << 6 | (b[p++] & 0x3F)); + else if (b[p] <= 0xF7) p += 4; // We can't handle these codepoints in JS, so skip. + else if (b[p] <= 0xFB) p += 5; + else if (b[p] <= 0xFD) p += 6; + else throw new Error('Invalid UTF-8 codepoint: ' + b[p]); + } + this.pos += bytes; + return str; +}; + +Protobuf.prototype.readBuffer = function() { + var bytes = this.readVarint(); + var buffer = this.buf.subarray(this.pos, this.pos + bytes); + this.pos += bytes; + return buffer; +}; + +Protobuf.prototype.readPacked = function(type) { + // TODO: bounds checking + var bytes = this.readVarint(); + var end = this.pos + bytes; + var array = []; + while (this.pos < end) { + array.push(this['read' + type]()); + } + return array; +}; + +Protobuf.prototype.skip = function(val) { + // TODO: bounds checking + var type = val & 0x7; + switch (type) { + /* varint */ case 0: while (this.buf[this.pos++] > 0x7f); break; + /* 64 bit */ case 1: this.pos += 8; break; + /* length */ case 2: var bytes = this.readVarint(); this.pos += bytes; break; + /* 32 bit */ case 5: this.pos += 4; break; + default: throw new Error('Unimplemented type: ' + type); + } +}; diff --git a/test/format/vectortile.js b/test/format/vectortile.js new file mode 100644 index 0000000..fd0fd5e --- /dev/null +++ b/test/format/vectortile.js @@ -0,0 +1,102 @@ +'use strict'; + +var VectorTileLayer = require('./vectortilelayer'); + +module.exports = VectorTile; +function VectorTile(buffer, end) { + // Public + this.layers = {}; + this.faces = {}; + + // Private + this._buffer = buffer; + + var val, tag; + if (typeof end === 'undefined') end = buffer.length; + while (buffer.pos < end) { + val = buffer.readVarint(); + tag = val >> 3; + if (tag == 3) { + var layer = this.readLayer(); + if (layer.length) { + this.layers[layer.name] = layer; + } + } else if (tag == 4) { + var face = this.readFace(); + this.faces[face.family + ' ' + face.style] = face; + } else { + // console.warn('skipping tile tag ' + tag); + buffer.skip(val); + } + } +} + +VectorTile.prototype.readLayer = function() { + var buffer = this._buffer; + + var bytes = buffer.readVarint(); + var end = buffer.pos + bytes; + var layer = new VectorTileLayer(buffer, end); + buffer.pos = end; + return layer; +}; + +VectorTile.prototype.readFace = function() { + var buffer = this._buffer; + var face = { glyphs: {} }; + + var bytes = buffer.readVarint(); + var val, tag; + var end = buffer.pos + bytes; + while (buffer.pos < end) { + val = buffer.readVarint(); + tag = val >> 3; + + if (tag == 1) { + face.family = buffer.readString(); + } else if (tag == 2) { + face.style = buffer.readString(); + } else if (tag == 5) { + var glyph = this.readGlyph(); + face.glyphs[glyph.id] = glyph; + } else { + buffer.skip(val); + } + } + + return face; +}; + + +VectorTile.prototype.readGlyph = function() { + var buffer = this._buffer; + var glyph = {}; + + var bytes = buffer.readVarint(); + var val, tag; + var end = buffer.pos + bytes; + while (buffer.pos < end) { + val = buffer.readVarint(); + tag = val >> 3; + + if (tag == 1) { + glyph.id = buffer.readVarint(); + } else if (tag == 2) { + glyph.bitmap = buffer.readBuffer(); + } else if (tag == 3) { + glyph.width = buffer.readVarint(); + } else if (tag == 4) { + glyph.height = buffer.readVarint(); + } else if (tag == 5) { + glyph.left = buffer.readSVarint(); + } else if (tag == 6) { + glyph.top = buffer.readSVarint(); + } else if (tag == 7) { + glyph.advance = buffer.readVarint(); + } else { + buffer.skip(val); + } + } + + return glyph; +}; diff --git a/test/format/vectortilefeature.js b/test/format/vectortilefeature.js new file mode 100644 index 0000000..4691f43 --- /dev/null +++ b/test/format/vectortilefeature.js @@ -0,0 +1,145 @@ +'use strict'; + +var VectorTilePoint = require('./vectortilepoint.js'); + +/* + * Construct a new vector tile feature given a buffer. + * + * @param {object} buffer + * @param {number} [end] + * @param {extent} + * @param {object} keys + * @param {object} values + */ +module.exports = VectorTileFeature; +function VectorTileFeature(buffer, end, extent, keys, values) { + // Public + this._extent = extent; + this._type = 0; + + // Private + this._buffer = buffer; + this._geometry = -1; + + if (typeof end === 'undefined') { + end = buffer.length; + } + + var val, tag; + while (buffer.pos < end) { + val = buffer.readVarint(); + tag = val >> 3; + + if (tag == 1) { + this._id = buffer.readVarint(); + } else if (tag == 2) { + var tag_end = buffer.pos + buffer.readVarint(); + while (buffer.pos < tag_end) { + var key = keys[buffer.readVarint()]; + var value = values[buffer.readVarint()]; + this[key] = value; + } + } else if (tag == 3) { + this._type = buffer.readVarint(); + } else if (tag == 4) { + this._geometry = buffer.pos; + buffer.skip(val); + } else { + buffer.skip(val); + } + } +} + +VectorTileFeature.Unknown = 0; +VectorTileFeature.Point = 1; +VectorTileFeature.LineString = 2; +VectorTileFeature.Polygon = 3; + + +VectorTileFeature.mapping = []; +VectorTileFeature.mapping[VectorTileFeature.Point] = 'point'; +VectorTileFeature.mapping[VectorTileFeature.LineString] = 'line'; +VectorTileFeature.mapping[VectorTileFeature.Polygon] = 'fill'; + + +VectorTileFeature.prototype.loadGeometry = function() { + var buffer = this._buffer; + buffer.pos = this._geometry; + + var bytes = buffer.readVarint(); + var end = buffer.pos + bytes; + var cmd = 1, length = 0, x = 0, y = 0; + + var lines = []; + var line = null; + + while (buffer.pos < end) { + if (!length) { + var cmd_length = buffer.readVarint(); + cmd = cmd_length & 0x7; + length = cmd_length >> 3; + } + + length--; + + if (cmd == 1 || cmd == 2) { + x += buffer.readSVarint(); + y += buffer.readSVarint(); + + if (cmd == 1) { + // moveTo + if (line) { + lines.push(line); + } + line = []; + } + + line.push(new VectorTilePoint(x, y)); + } else if (cmd == 7) { + // closePolygon + line.push(line[0]); + } else { + throw new Error('unknown command ' + cmd); + } + } + + if (line) { + lines.push(line); + } + + return lines; +}; + +VectorTileFeature.prototype.bbox = function() { + var buffer = this._buffer; + buffer.pos = this._geometry; + + var bytes = buffer.readVarint(); + var end = buffer.pos + bytes; + var cmd = 1, length = 0, x = 0, y = 0; + + var x1 = Infinity, x2 = -Infinity, y1 = Infinity, y2 = -Infinity; + + while (buffer.pos < end) { + if (!length) { + var cmd_length = buffer.readVarint(); + cmd = cmd_length & 0x7; + length = cmd_length >> 3; + } + + length--; + + if (cmd == 1 || cmd == 2) { + x += buffer.readSVarint(); + y += buffer.readSVarint(); + if (x < x1) x1 = x; + if (x > x2) x2 = x; + if (y < y1) y1 = y; + if (y > y2) y2 = y; + } else if (cmd != 7) { + throw new Error('unknown command ' + cmd); + } + } + + return { x1: x1, y1: y1, x2: x2, y2: y2 }; +}; diff --git a/test/format/vectortilelayer.js b/test/format/vectortilelayer.js new file mode 100644 index 0000000..76b0520 --- /dev/null +++ b/test/format/vectortilelayer.js @@ -0,0 +1,150 @@ +'use strict'; + +var VectorTileFeature = require('./vectortilefeature.js'); + +module.exports = VectorTileLayer; +function VectorTileLayer(buffer, end) { + // Public + this.version = 1; + this.name = null; + this.extent = 4096; + this.length = 0; + this.shaping = {}; + this.faces = []; + + // Private + this._buffer = buffer; + this._keys = []; + this._values = []; + this._features = []; + + var stack_index = []; + var labels = []; + + var val, tag; + if (typeof end === 'undefined') end = buffer.length; + while (buffer.pos < end) { + val = buffer.readVarint(); + tag = val >> 3; + if (tag == 15) { + this.version = buffer.readVarint(); + } else if (tag == 1) { + this.name = buffer.readString(); + } else if (tag == 5) { + this.extent = buffer.readVarint(); + } else if (tag == 2) { + this.length++; + this._features.push(buffer.pos); + buffer.skip(val); + } else if (tag == 3) { + this._keys.push(buffer.readString()); + } else if (tag == 4) { + this._values.push(this.readFeatureValue()); + } else if (tag == 7) { + this.faces.push(buffer.readString()); + } else if (tag == 8) { + labels.push(this.readLabel()); + } else if (tag == 9) { + stack_index.push(buffer.readString()); + } else { + console.warn('skipping layer tag ' + tag); + buffer.skip(val); + } + } + + // Build index of [stack][text] => shaping information + var shaping = this.shaping; + for (var i = 0; i < labels.length; i++) { + var label = labels[i]; + var text = this._values[label.text]; + var stack = stack_index[label.stack]; + + if (!(stack in shaping)) shaping[stack] = {}; + shaping[stack][text] = label.glyphs; + } +} + +VectorTileLayer.prototype.readFeatureValue = function() { + var buffer = this._buffer; + var value = null; + + var bytes = buffer.readVarint(); + var val, tag; + var end = buffer.pos + bytes; + while (buffer.pos < end) { + val = buffer.readVarint(); + tag = val >> 3; + + if (tag == 1) { + value = buffer.readString(); + } else if (tag == 2) { + throw new Error('read float'); + } else if (tag == 3) { + value = buffer.readDouble(); + } else if (tag == 4) { + value = buffer.readVarint(); + } else if (tag == 5) { + throw new Error('read uint'); + } else if (tag == 6) { + value = buffer.readSVarint(); + } else if (tag == 7) { + value = Boolean(buffer.readVarint()); + } else { + buffer.skip(val); + } + } + + return value; +}; + +VectorTileLayer.prototype.readLabel = function() { + var label = { glyphs: [] }; + var faces, glyphs, x, y; + + var buffer = this._buffer; + var bytes = buffer.readVarint(); + var val, tag; + var end = buffer.pos + bytes; + while (buffer.pos < end) { + val = buffer.readVarint(); + tag = val >> 3; + + if (tag == 1) { + label.text = buffer.readVarint(); + } else if (tag == 2) { + label.stack = buffer.readVarint(); + } else if (tag == 3) { + faces = buffer.readPacked('Varint'); + } else if (tag == 4) { + glyphs = buffer.readPacked('Varint'); + } else if (tag == 5) { + x = buffer.readPacked('Varint'); + } else if (tag == 6) { + y = buffer.readPacked('Varint'); + } else { + buffer.skip(val); + } + } + + for (var i = 0; i < glyphs.length; i++) { + label.glyphs.push({ face: faces[i], glyph: glyphs[i], x: x[i], y: y[i] }); + } + + return label; +}; + +/* + * Return feature `i` from this layer as a `VectorTileFeature` + * + * @param {number} i + * @returns {VectorTileFeature} + */ +VectorTileLayer.prototype.feature = function(i) { + if (i < 0 || i >= this._features.length) { + throw new Error('feature index out of bounds'); + } + + this._buffer.pos = this._features[i]; + var end = this._buffer.readVarint() + this._buffer.pos; + return new VectorTileFeature(this._buffer, end, this.extent, this._keys, this._values); +}; diff --git a/test/format/vectortilepoint.js b/test/format/vectortilepoint.js new file mode 100644 index 0000000..dd0797b --- /dev/null +++ b/test/format/vectortilepoint.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = VectorTilePoint; +function VectorTilePoint(x, y) { + this.x = x; + this.y = y; +} + +VectorTilePoint.prototype.toString = function() { + return "Point(" + this.x + ", " + this.y + ")"; +}; diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..7d4b776 --- /dev/null +++ b/test/test.js @@ -0,0 +1,88 @@ +var fontserver = require('../index.js'); +var assert = require('assert'); +var zlib = require('zlib'); +var fs = require('fs'); +var zdata = fs.readFileSync(__dirname + '/fixtures/mapbox-streets-v4.13.1306.3163.vector.pbf'); +var Protobuf = require('./format/protobuf'); +var VectorTile = require('./format/vectortile'); + +function nobuffer(key, val) { + return key !== '_buffer' && key !== 'bitmap' ? val : undefined; +} + +function jsonEqual(key, json) { + fs.writeFileSync(__dirname + '/expected/'+key+'.json', JSON.stringify(json, null, 2)); + assert.deepEqual(json, require('./expected/'+key+'.json')); +} + +describe('convert', function() { + var data; + before(function(done) { + zlib.inflate(zdata, function(err, d) { + assert.ifError(err); + data = d; + done(); + }); + }); + + it('serialize', function(done) { + var tile = new fontserver.Tile(data); + var vt = new VectorTile(new Protobuf(new Uint8Array(tile.serialize()))); + var json = JSON.parse(JSON.stringify(vt, nobuffer)); + jsonEqual('serialize', json); + done(); + }); + + it('simplify', function(done) { + var tile = new fontserver.Tile(data); + tile.simplify(function(err) { + assert.ifError(err); + var vt = new VectorTile(new Protobuf(new Uint8Array(tile.serialize()))); + var json = JSON.parse(JSON.stringify(vt, nobuffer)); + jsonEqual('simplify', json); + done(); + }); + }); + + it('simplify (x10)', function(done) { + this.timeout(10000); + var remaining = 10; + for (var i = 0; i < 10; i++) (function() { + var tile = new fontserver.Tile(data); + tile.simplify(function(err) { + assert.ifError(err); + var vt = new VectorTile(new Protobuf(new Uint8Array(tile.serialize()))); + var json = JSON.parse(JSON.stringify(vt, nobuffer)); + jsonEqual('simplify', json); + if (!--remaining) return done(); + }); + })(); + }); + + it('shape', function(done) { + var tile = new fontserver.Tile(data); + tile.shape('Open Sans', function(err) { + assert.ifError(err); + var vt = new VectorTile(new Protobuf(new Uint8Array(tile.serialize()))); + var json = JSON.parse(JSON.stringify(vt, nobuffer)); + jsonEqual('shape', json); + done(); + }); + }); + + it('shape (x10)', function(done) { + this.timeout(10000); + var remaining = 10; + for (var i = 0; i < 10; i++) (function() { + var tile = new fontserver.Tile(data); + tile.shape('Open Sans', function(err) { + assert.ifError(err); + var vt = new VectorTile(new Protobuf(new Uint8Array(tile.serialize()))); + var json = JSON.parse(JSON.stringify(vt, nobuffer)); + jsonEqual('shape', json); + if (!--remaining) return done(); + }); + })(); + }); +}); +