diff --git a/CHANGELOG.md b/CHANGELOG.md index 108e7303..6d54c32c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.81.0 + +* Add --distinguish-duplicates option + # 2.80.0 * Remove undocumented command-line options diff --git a/README.md b/README.md index 5c6ffec5..66df275d 100644 --- a/README.md +++ b/README.md @@ -430,6 +430,7 @@ be reduced to the maximum that can be used with the specified _maxzoom_. * `-j` *filter* or `--feature-filter`=*filter*: Check features against a per-layer filter (as defined in the [Mapbox GL Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/#other-filter) or in a Felt filter specification still to be finalized) and only include those that match. Any features in layers that have no filter specified will be passed through. Filters for the layer `"*"` apply to all layers. The special variable `$zoom` refers to the current zoom level. * `-J` *filter-file* or `--feature-filter-file`=*filter-file*: Like `-j`, but read the filter from a file. + * `--unidecode-data` *file*: Specify transliterations that will be used when evaluating expressions, in the format from https://metacpan.org/pod/Text::Unidecode Example: to find the Natural Earth countries with low `scalerank` but high `LABELRANK`: @@ -468,14 +469,16 @@ the same layer, enclose them in an `all` expression so they will all be evaluate compensate for the larger marker, or `-Bf`*number* to allow at most *number* features in the densest tile. * `--retain-points-multiplier=`_multiple_: Retain the specified multiple of points instead of just the number of points that would ordinarily be retained by the drop rate. These can be thinned out later with the `-m` option to `tippecanoe-overzoom`. The start of each cluster is marked in the feature sequence by the `tippecanoe:retain_points_multiplier_first` attribute. The `--tile-size-limit` will also be extended at low zoom levels to allow for the multiplied features. * `--drop-denser=`_percentage_: When dropping dots at zoom levels below the base zoom, give the specified _percentage_ - preference to retaining points in sparse areas and dropping points in dense areas. + preference to retaining points in sparse areas and dropping points in dense areas. Use `--preserve-point-density-threshold` instead. * `--limit-base-zoom-to-maximum-zoom` or `-Pb`: Limit the guessed base zoom not to exceed the maxzoom, even if this would put more than the requested number of features in a base zoom tile. * `-al` or `--drop-lines`: Let "dot" dropping at lower zooms apply to lines too * `-ap` or `--drop-polygons`: Let "dot" dropping at lower zooms apply to polygons too * `-K` _distance_ or `--cluster-distance=`_distance_: Cluster points (as with `--cluster-densest-as-needed`, but without the experimental discovery process) that are approximately within _distance_ of each other. The units are tile coordinates within a nominally 256-pixel tile, so the maximum value of 255 allows only one feature per tile. Values around 10 are probably appropriate for typical marker sizes. See `--cluster-densest-as-needed` below for behavior. * `-k` _zoom_ or `--cluster-maxzoom=`_zoom_: Max zoom on which to cluster points if clustering is enabled. * `-kg` or `--cluster-maxzoom=g`: Set `--cluster-maxzoom=` to `maxzoom - 1` so that all features are visible at the maximum zoom level. + * `--keep-point-cluster-position`: Do not average the location of points as they are clustered. * `--preserve-point-density-threshold=`_level_: At the low zoom levels, do not reduce point density below the specified _level_, even if the specified drop rate would normally call for it, so that low-density areas of the map do not appear blank. The unit is the distance between preserved points, as a fraction of the size of a tile. Values of 32 or 64 are probably appropriate for typical marker sizes. + * `--preserve-multiplier-density-threshold=`_level_: Preserve a minimum point density, but as `--retain-points-multiplier` features, not primary features. ### Dropping a fraction of features to keep under tile size limits @@ -488,6 +491,7 @@ the same layer, enclose them in an `all` expression so they will all be evaluate * `-aS` or `--coalesce-fraction-as-needed`: Dynamically combine a fraction of features from each zoom level into other nearby features to keep large tiles under the 500K size limit. (Again, mostly useful for polygons.) * `-pd` or `--force-feature-limit`: Dynamically drop some fraction of features from large tiles to keep them under the 500K size limit. It will probably look ugly at the tile boundaries. (This is like `-ad` but applies to each tile individually, not to the entire zoom level.) You probably don't want to use this. * `-aC` or `--cluster-densest-as-needed`: If a tile is too large, try to reduce its size by increasing the minimum spacing between features, and leaving one placeholder feature from each group. The remaining feature will be given a `"clustered": true` attribute to indicate that it represents a cluster, a `"point_count"` attribute to indicate the number of features that were clustered into it, and a `"sqrt_point_count"` attribute to indicate the relative width of a feature to represent the cluster. If the features being clustered are points, the representative feature will be located at the average of the original points' locations; otherwise, one of the original features will be left as the representative. + * `--distinguish-duplicates`: Treat sets of up to 50 exact duplicate locations as sub-layers, which will be accumulated or coalesced into other features of their own sub-layer instead of into their duplicates. ### Dropping tightly overlapping features diff --git a/main.cpp b/main.cpp index 1f8b8f83..b916b8b2 100644 --- a/main.cpp +++ b/main.cpp @@ -98,6 +98,7 @@ long long extend_zooms_max = 0; int retain_points_multiplier = 1; std::vector unidecode_data; size_t maximum_string_attribute_length = 0; +const char *tmpdir = "/tmp"; std::string drop_by_attribute_as_needed_attribute; bool drop_by_attribute_descending = false; @@ -741,7 +742,7 @@ void start_parsing(int fd, STREAM *fp, long long offset, long long len, std::ato parser_created = true; } -void radix1(int *geomfds_in, int *indexfds_in, int inputs, int prefix, int splits, long long mem, const char *tmpdir, long long *availfiles, FILE *geomfile, FILE *indexfile, std::atomic *geompos_out, long long *progress, long long *progress_max, long long *progress_reported, int maxzoom, int basezoom, double droprate, double gamma, struct drop_state *ds) { +void radix1(int *geomfds_in, int *indexfds_in, int inputs, int prefix, int splits, long long mem, long long *availfiles, FILE *geomfile, FILE *indexfile, std::atomic *geompos_out, long long *progress, long long *progress_max, long long *progress_reported, int maxzoom, int basezoom, double droprate, double gamma, struct drop_state *ds) { // Arranged as bits to facilitate subdividing again if a subdivided file is still huge int splitbits = log(splits) / log(2); splits = 1 << splitbits; @@ -1029,7 +1030,7 @@ void radix1(int *geomfds_in, int *indexfds_in, int inputs, int prefix, int split // counter backward but will be an honest estimate of the work remaining. *progress_max += geomst.st_size / 4; - radix1(&geomfds[i], &indexfds[i], 1, prefix + splitbits, *availfiles / 4, mem, tmpdir, availfiles, geomfile, indexfile, geompos_out, progress, progress_max, progress_reported, maxzoom, basezoom, droprate, gamma, ds); + radix1(&geomfds[i], &indexfds[i], 1, prefix + splitbits, *availfiles / 4, mem, availfiles, geomfile, indexfile, geompos_out, progress, progress_max, progress_reported, maxzoom, basezoom, droprate, gamma, ds); already_closed = 1; } } @@ -1064,7 +1065,7 @@ void prep_drop_states(struct drop_state *ds, int maxzoom, int basezoom, double d } } -void radix(std::vector &readers, int nreaders, FILE *geomfile, FILE *indexfile, const char *tmpdir, std::atomic *geompos, int maxzoom, int basezoom, double droprate, double gamma) { +void radix(std::vector &readers, int nreaders, FILE *geomfile, FILE *indexfile, std::atomic *geompos, int maxzoom, int basezoom, double droprate, double gamma) { // Run through the index and geometry for each reader, // splitting the contents out by index into as many // sub-files as we can write to simultaneously. @@ -1114,7 +1115,7 @@ void radix(std::vector &readers, int nreaders, FILE *geomfile, FI long long progress = 0, progress_max = geom_total, progress_reported = -1; long long availfiles_before = availfiles; - radix1(geomfds, indexfds, nreaders, 0, splits, mem, tmpdir, &availfiles, geomfile, indexfile, geompos, &progress, &progress_max, &progress_reported, maxzoom, basezoom, droprate, gamma, ds); + radix1(geomfds, indexfds, nreaders, 0, splits, mem, &availfiles, geomfile, indexfile, geompos, &progress, &progress_max, &progress_reported, maxzoom, basezoom, droprate, gamma, ds); if (availfiles - 2 * nreaders != availfiles_before) { fprintf(stderr, "Internal error: miscounted available file descriptors: %lld vs %lld\n", availfiles - 2 * nreaders, availfiles); @@ -1215,7 +1216,7 @@ double round_droprate(double r) { return std::round(r * 100000.0) / 100000.0; } -std::pair read_input(std::vector &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set *exclude, std::set *include, int exclude_all, json_object *filter, double droprate, int buffer, const char *tmpdir, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, long long *file_bbox1, long long *file_bbox2, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, bool guess_cluster_maxzoom, std::unordered_map const *attribute_types, const char *pgm, std::unordered_map const *attribute_accum, std::map const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) { +std::pair read_input(std::vector &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set *exclude, std::set *include, int exclude_all, json_object *filter, double droprate, int buffer, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, long long *file_bbox1, long long *file_bbox2, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, bool guess_cluster_maxzoom, std::unordered_map const *attribute_types, const char *pgm, std::unordered_map const *attribute_accum, std::map const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) { int ret = EXIT_SUCCESS; std::vector readers; @@ -2208,7 +2209,7 @@ std::pair read_input(std::vector &sources, char *fname, i serialize_uint(geomfile, ix, &geompos, fname); serialize_uint(geomfile, iy, &geompos, fname); - radix(readers, CPUS, geomfile, indexfile, tmpdir, &geompos, maxzoom, basezoom, droprate, gamma); + radix(readers, CPUS, geomfile, indexfile, &geompos, maxzoom, basezoom, droprate, gamma); /* end of tile */ serialize_ulong_long(geomfile, 0, &geompos, fname); // EOF @@ -2756,7 +2757,7 @@ std::pair read_input(std::vector &sources, char *fname, i std::atomic midx(0); std::atomic midy(0); std::vector strategies; - int written = traverse_zooms(fd, size, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, tmpdir, gamma, full_detail, low_detail, min_detail, pool_off, initial_x, initial_y, simplification, maxzoom_simplification, layermaps, prefilter, postfilter, attribute_accum, filter, strategies, iz, shared_nodes_map, nodepos, shared_nodes_bloom, basezoom, droprate, unidecode_data, &drop_by_attribute_as_needed_attribute, drop_by_attribute_descending); + int written = traverse_zooms(fd, size, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, gamma, full_detail, low_detail, min_detail, pool_off, initial_x, initial_y, simplification, maxzoom_simplification, layermaps, prefilter, postfilter, attribute_accum, filter, strategies, iz, shared_nodes_map, nodepos, shared_nodes_bloom, basezoom, droprate, unidecode_data, &drop_by_attribute_as_needed_attribute, drop_by_attribute_descending); if (maxzoom != written) { if (written > minzoom) { @@ -2992,7 +2993,6 @@ int main(int argc, char **argv) { double droprate = 2.5; double gamma = 0; int buffer = 5; - const char *tmpdir = "/tmp"; const char *attribution = NULL; std::vector sources; const char *prefilter = NULL; @@ -3100,6 +3100,7 @@ int main(int argc, char **argv) { {"force-feature-limit", no_argument, &prevent[P_DYNAMIC_DROP], 1}, {"cluster-densest-as-needed", no_argument, &additional[A_CLUSTER_DENSEST_AS_NEEDED], 1}, {"keep-point-cluster-position", no_argument, &additional[A_KEEP_POINT_CLUSTER_POSITION], 1}, + {"distinguish-duplicates", no_argument, &additional[A_DISTINGUISH_DUPLICATES], 1}, {"Dropping tightly overlapping features", 0, 0, 0}, {"gamma", required_argument, 0, 'g'}, @@ -3853,7 +3854,7 @@ int main(int argc, char **argv) { auto input_ret = read_input(sources, name ? name : out_mbtiles ? out_mbtiles : out_dir, - maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, out_dir, &exclude, &include, exclude_all, filter, droprate, buffer, tmpdir, gamma, read_parallel, forcetable, attribution, gamma != 0, file_bbox, file_bbox1, file_bbox2, prefilter, postfilter, description, guess_maxzoom, guess_cluster_maxzoom, &attribute_types, argv[0], &attribute_accum, attribute_descriptions, commandline, minimum_maxzoom); + maxzoom, minzoom, basezoom, basezoom_marker_width, outdb, out_dir, &exclude, &include, exclude_all, filter, droprate, buffer, gamma, read_parallel, forcetable, attribution, gamma != 0, file_bbox, file_bbox1, file_bbox2, prefilter, postfilter, description, guess_maxzoom, guess_cluster_maxzoom, &attribute_types, argv[0], &attribute_accum, attribute_descriptions, commandline, minimum_maxzoom); ret = std::get<0>(input_ret); diff --git a/main.hpp b/main.hpp index 368016e9..8c1806f4 100644 --- a/main.hpp +++ b/main.hpp @@ -53,6 +53,7 @@ extern long long extend_zooms_max; extern int retain_points_multiplier; extern size_t maximum_string_attribute_length; extern unsigned long long preserve_multiplier_density_threshold; +extern const char *tmpdir; struct order_field { std::string name; diff --git a/man/tippecanoe.1 b/man/tippecanoe.1 index e83fb104..63cff977 100644 --- a/man/tippecanoe.1 +++ b/man/tippecanoe.1 @@ -518,10 +518,10 @@ If the type is \fB\fCint\fR and the original attribute was floating\-point, it i that are dropped, coalesced\-as\-needed, or clustered. The \fIoperation\fP may be \fB\fCsum\fR, \fB\fCproduct\fR, \fB\fCmean\fR, \fB\fCmax\fR, \fB\fCmin\fR, \fB\fCconcat\fR, or \fB\fCcomma\fR to specify how the named \fIattribute\fP is accumulated onto the attribute of the same name in a feature that does survive. -The attributes and operations may also be specified as JSON keys and values: \fB\fC\-\-accumulate\-attribute='{"attr": "operation", "attr2", "operation2"}'\fR\&. +The attributes and operations may also be specified as JSON keys and values: \fB\fC\-\-accumulate\-attribute='{"attr": "operation", "attr2": "operation2"}'\fR\&. .IP \(bu 2 \fB\fC\-\-set\-attribute\fR \fIattribute\fP\fB\fC:\fR\fIvalue\fP: Set the value of the specified \fIattribute\fP in each feature to the specified \fIvalue\fP\&. This is mostly useful to give an attribute in each feature an initial value for \fB\fC\-\-accumulate\-attribute\fR\&. -The attributes and values may also be specified as JSON keys and values: \fB\fC\-\-set\-attribute='{"attr": value, "attr2", value}'\fR\&. +The attributes and values may also be specified as JSON keys and values: \fB\fC\-\-set\-attribute='{"attr": value, "attr2": value}'\fR\&. .IP \(bu 2 \fB\fC\-pe\fR or \fB\fC\-\-empty\-csv\-columns\-are\-null\fR: Treat empty CSV columns as nulls rather than as empty strings. .IP \(bu 2 @@ -539,6 +539,8 @@ The attributes and values may also be specified as JSON keys and values: \fB\fC\ \fB\fC\-j\fR \fIfilter\fP or \fB\fC\-\-feature\-filter\fR=\fIfilter\fP: Check features against a per\-layer filter (as defined in the Mapbox GL Style Specification \[la]https://docs.mapbox.com/mapbox-gl-js/style-spec/#other-filter\[ra] or in a Felt filter specification still to be finalized) and only include those that match. Any features in layers that have no filter specified will be passed through. Filters for the layer \fB\fC"*"\fR apply to all layers. The special variable \fB\fC$zoom\fR refers to the current zoom level. .IP \(bu 2 \fB\fC\-J\fR \fIfilter\-file\fP or \fB\fC\-\-feature\-filter\-file\fR=\fIfilter\-file\fP: Like \fB\fC\-j\fR, but read the filter from a file. +.IP \(bu 2 +\fB\fC\-\-unidecode\-data\fR \fIfile\fP: Specify transliterations that will be used when evaluating expressions, in the format from \[la]https://metacpan.org/pod/Text::Unidecode\[ra] .RE .PP Example: to find the Natural Earth countries with low \fB\fCscalerank\fR but high \fB\fCLABELRANK\fR: @@ -587,7 +589,7 @@ compensate for the larger marker, or \fB\fC\-Bf\fR\fInumber\fP to allow at most \fB\fC\-\-retain\-points\-multiplier=\fR\fImultiple\fP: Retain the specified multiple of points instead of just the number of points that would ordinarily be retained by the drop rate. These can be thinned out later with the \fB\fC\-m\fR option to \fB\fCtippecanoe\-overzoom\fR\&. The start of each cluster is marked in the feature sequence by the \fB\fCtippecanoe:retain_points_multiplier_first\fR attribute. The \fB\fC\-\-tile\-size\-limit\fR will also be extended at low zoom levels to allow for the multiplied features. .IP \(bu 2 \fB\fC\-\-drop\-denser=\fR\fIpercentage\fP: When dropping dots at zoom levels below the base zoom, give the specified \fIpercentage\fP -preference to retaining points in sparse areas and dropping points in dense areas. +preference to retaining points in sparse areas and dropping points in dense areas. Use \fB\fC\-\-preserve\-point\-density\-threshold\fR instead. .IP \(bu 2 \fB\fC\-\-limit\-base\-zoom\-to\-maximum\-zoom\fR or \fB\fC\-Pb\fR: Limit the guessed base zoom not to exceed the maxzoom, even if this would put more than the requested number of features in a base zoom tile. .IP \(bu 2 @@ -601,7 +603,11 @@ preference to retaining points in sparse areas and dropping points in dense area .IP \(bu 2 \fB\fC\-kg\fR or \fB\fC\-\-cluster\-maxzoom=g\fR: Set \fB\fC\-\-cluster\-maxzoom=\fR to \fB\fCmaxzoom \- 1\fR so that all features are visible at the maximum zoom level. .IP \(bu 2 +\fB\fC\-\-keep\-point\-cluster\-position\fR: Do not average the location of points as they are clustered. +.IP \(bu 2 \fB\fC\-\-preserve\-point\-density\-threshold=\fR\fIlevel\fP: At the low zoom levels, do not reduce point density below the specified \fIlevel\fP, even if the specified drop rate would normally call for it, so that low\-density areas of the map do not appear blank. The unit is the distance between preserved points, as a fraction of the size of a tile. Values of 32 or 64 are probably appropriate for typical marker sizes. +.IP \(bu 2 +\fB\fC\-\-preserve\-multiplier\-density\-threshold=\fR\fIlevel\fP: Preserve a minimum point density, but as \fB\fC\-\-retain\-points\-multiplier\fR features, not primary features. .RE .SS Dropping a fraction of features to keep under tile size limits .RS @@ -621,11 +627,13 @@ preference to retaining points in sparse areas and dropping points in dense area \fB\fC\-pd\fR or \fB\fC\-\-force\-feature\-limit\fR: Dynamically drop some fraction of features from large tiles to keep them under the 500K size limit. It will probably look ugly at the tile boundaries. (This is like \fB\fC\-ad\fR but applies to each tile individually, not to the entire zoom level.) You probably don't want to use this. .IP \(bu 2 \fB\fC\-aC\fR or \fB\fC\-\-cluster\-densest\-as\-needed\fR: If a tile is too large, try to reduce its size by increasing the minimum spacing between features, and leaving one placeholder feature from each group. The remaining feature will be given a \fB\fC"clustered": true\fR attribute to indicate that it represents a cluster, a \fB\fC"point_count"\fR attribute to indicate the number of features that were clustered into it, and a \fB\fC"sqrt_point_count"\fR attribute to indicate the relative width of a feature to represent the cluster. If the features being clustered are points, the representative feature will be located at the average of the original points' locations; otherwise, one of the original features will be left as the representative. +.IP \(bu 2 +\fB\fC\-\-distinguish\-duplicates\fR: Treat sets of up to 50 exact duplicate locations as sub\-layers, which will be accumulated or coalesced into other features of their own sub\-layer instead of into their duplicates. .RE .SS Dropping tightly overlapping features .RS .IP \(bu 2 -\fB\fC\-g\fR \fIgamma\fP or \fB\fC\-\-gamma=_gamma\fR_: Rate at which especially dense dots are dropped (default 0, for no effect). A gamma of 2 reduces the number of dots less than a pixel apart to the square root of their original number. +\fB\fC\-g\fR \fIgamma\fP or \fB\fC\-\-gamma=\fR\fIgamma\fP: Rate at which especially dense dots are dropped (default 0, for no effect). A gamma of 2 reduces the number of dots less than a pixel apart to the square root of their original number. .IP \(bu 2 \fB\fC\-aG\fR or \fB\fC\-\-increase\-gamma\-as\-needed\fR: If a tile is too large, try to reduce it to under 500K by increasing the \fB\fC\-g\fR gamma. The discovered gamma applies to the entire zoom level. You probably want to use \fB\fC\-\-drop\-densest\-as\-needed\fR instead. .RE diff --git a/options.hpp b/options.hpp index 0543f820..91bdd22b 100644 --- a/options.hpp +++ b/options.hpp @@ -29,6 +29,7 @@ #define A_VISVALINGAM ((int) 'v') #define A_DETECT_WRAPAROUND ((int) 'w') #define A_KEEP_POINT_CLUSTER_POSITION ((int) 'a') +#define A_DISTINGUISH_DUPLICATES ((int) 'u') #define A_DROP_BY_ATTRIBUTE_AS_NEEDED ((int) 'A') #define P_TILE_COMPRESSION ((int) 'C') diff --git a/serial.cpp b/serial.cpp index 5afc2ed2..2aaa2d7c 100644 --- a/serial.cpp +++ b/serial.cpp @@ -743,6 +743,7 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf, std:: prevent[P_DYNAMIC_DROP] || additional[A_INCREASE_GAMMA_AS_NEEDED] || additional[A_GENERATE_POLYGON_LABEL_POINTS] || + additional[A_DISTINGUISH_DUPLICATES] || sst->uses_gamma || retain_points_multiplier > 1 || preserve_multiplier_density_threshold > 0 || diff --git a/tests/distinguish-duplicates/in.json b/tests/distinguish-duplicates/in.json new file mode 100644 index 00000000..684aa0ef --- /dev/null +++ b/tests/distinguish-duplicates/in.json @@ -0,0 +1,183 @@ +{"type":"Feature","properties":{"a":0},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":0},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":0},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":1},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":1},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":1},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":2},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":2},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":2},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":3},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":3},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":3},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":4},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":4},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":4},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":5},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":5},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":5},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":6},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":6},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":6},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":7},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":7},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":7},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":8},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":8},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":8},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":9},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":9},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":9},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":10},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":10},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":10},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":11},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":11},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":11},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":12},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":12},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":12},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":13},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":13},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":13},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":14},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":14},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":14},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":15},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":15},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":15},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":16},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":16},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":16},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":17},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":17},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":17},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":18},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":18},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":18},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":19},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":19},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":19},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":20},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":20},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":20},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":21},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":21},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":21},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":22},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":22},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":22},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":23},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":23},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":23},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":24},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":24},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":24},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":25},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":25},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":25},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":26},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":26},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":26},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":27},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":27},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":27},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":28},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":28},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":28},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":29},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":29},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":29},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":30},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":30},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":30},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":31},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":31},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":31},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":32},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":32},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":32},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":33},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":33},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":33},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":34},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":34},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":34},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":35},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":35},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":35},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":36},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":36},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":36},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":37},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":37},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":37},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":38},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":38},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":38},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":39},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":39},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":39},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":40},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":40},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":40},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":41},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":41},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":41},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":42},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":42},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":42},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":43},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":43},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":43},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":44},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":44},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":44},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":45},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":45},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":45},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":46},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":46},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":46},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":47},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":47},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":47},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":48},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":48},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":48},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":49},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":49},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":49},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":50},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":50},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":50},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":51},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":51},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":51},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":52},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":52},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":52},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":53},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":53},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":53},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":54},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":54},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":54},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":55},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":55},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":55},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":56},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":56},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":56},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":57},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":57},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":57},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":58},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":58},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":58},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":59},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":59},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":59},"geometry":{"type":"Point","coordinates":[3,1]}} +{"type":"Feature","properties":{"a":60},"geometry":{"type":"Point","coordinates":[1,1]}} +{"type":"Feature","properties":{"b":60},"geometry":{"type":"Point","coordinates":[2,1]}} +{"type":"Feature","properties":{"c":60},"geometry":{"type":"Point","coordinates":[3,1]}} diff --git a/tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json b/tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json new file mode 100644 index 00000000..4f4dcaca --- /dev/null +++ b/tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json @@ -0,0 +1,755 @@ +{ "type": "FeatureCollection", "properties": { +"antimeridian_adjusted_bounds": "1.000000,1.000000,3.000000,1.000000", +"bounds": "1.000000,1.000000,3.000000,1.000000", +"center": "3.000000,1.000000,1", +"description": "tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json.check.mbtiles", +"format": "pbf", +"generator_options": "./tippecanoe -q -a@ -f -o tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json.check.mbtiles -z1 -r1 -b0 --distinguish-duplicates tests/distinguish-duplicates/in.json", +"json": "{\"vector_layers\":[{\"id\":\"in\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":1,\"fields\":{\"a\":\"Number\",\"b\":\"Number\",\"c\":\"Number\"}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"in\",\"count\":183,\"geometry\":\"Point\",\"attributeCount\":3,\"attributes\":[{\"attribute\":\"a\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60},{\"attribute\":\"b\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60},{\"attribute\":\"c\",\"count\":61,\"type\":\"number\",\"values\":[0,1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,35,36,37,38,39,4,40,41,42,43,44,45,46,47,48,49,5,50,51,52,53,54,55,56,57,58,59,6,60,7,8,9],\"min\":0,\"max\":60}]}]}}", +"maxzoom": "1", +"minzoom": "0", +"name": "tests/distinguish-duplicates/out/-z1_-r1_-b0_--distinguish-duplicates.json.check.mbtiles", +"tippecanoe_decisions": "{\"basezoom\":1,\"droprate\":1,\"retain_points_multiplier\":1}", +"type": "overlay", +"version": "2" +}, "features": [ +{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "properties": { "a": 0 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 1 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 2 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 3 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 4 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 5 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 6 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 7 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 8 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 9 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 10 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 11 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 12 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 13 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 14 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 15 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 16 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 17 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 18 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 19 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 20 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 21 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 22 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 23 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 24 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 25 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 26 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 27 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 28 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 29 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 30 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 31 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 32 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 33 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 34 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 35 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 36 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 37 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 38 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 39 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 40 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 41 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 42 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 43 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 44 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 45 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 46 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 47 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 48 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 49 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 50 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 51 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 52 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 53 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 54 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 55 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 56 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 57 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 58 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 59 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "a": 60 }, "geometry": { "type": "Point", "coordinates": [ 1.054688, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "b": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +, +{ "type": "Feature", "properties": { "c": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 0.966751 ] } } +] } +] } +, +{ "type": "FeatureCollection", "properties": { "zoom": 1, "x": 1, "y": 0 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "properties": { "a": 0 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 0 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 1 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 1 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 2 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 2 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 3 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 3 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 4 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 4 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 5 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 5 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 6 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 6 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 7 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 7 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 8 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 8 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 9 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 9 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 10 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 10 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 11 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 11 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 12 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 12 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 13 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 13 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 14 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 14 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 15 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 15 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 16 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 16 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 17 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 17 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 18 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 18 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 19 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 19 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 20 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 20 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 21 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 21 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 22 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 22 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 23 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 23 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 24 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 24 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 25 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 25 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 26 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 26 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 27 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 27 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 28 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 28 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 29 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 29 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 30 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 30 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 31 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 31 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 32 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 32 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 33 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 33 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 34 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 34 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 35 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 35 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 36 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 36 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 37 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 37 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 38 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 38 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 39 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 39 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 40 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 40 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 41 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 41 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 42 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 42 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 43 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 43 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 44 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 44 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 45 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 45 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 46 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 46 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 47 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 47 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 48 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 48 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 49 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 49 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 50 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 50 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 51 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 52 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 53 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 54 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 55 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 56 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 57 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 58 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 59 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "a": 60 }, "geometry": { "type": "Point", "coordinates": [ 1.010742, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "b": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.021484, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 51 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 52 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 53 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 54 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 55 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 56 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 57 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 58 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 59 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +, +{ "type": "Feature", "properties": { "c": 60 }, "geometry": { "type": "Point", "coordinates": [ 2.988281, 1.010690 ] } } +] } +] } +] } diff --git a/tile.cpp b/tile.cpp index ede49e89..4deb19f5 100644 --- a/tile.cpp +++ b/tile.cpp @@ -947,6 +947,7 @@ struct write_tile_args { atomic_strategy *strategy = NULL; int zoom = -1; bool compressed; + bool first_zoom; node *shared_nodes_map; size_t nodepos; std::string const *shared_nodes_bloom; @@ -1100,42 +1101,128 @@ struct next_feature_state { // set when a feature excluded by tippecanoe_minzoom first appears beyond the next // zoom, so variable-depth pyramids don't leaf above the feature's minzoom and prune it away bool minzoom_feature_pending = false; + + std::vector deferrals; + size_t which_deferral = 0; + bool doing_deferrals = false; }; // This function is called repeatedly from write_tile() to retrieve the next feature // from the input stream. If the stream is at an end, it returns a feature with the // geometry type set to -2. -static serial_feature next_feature(decompressor *geoms, std::atomic *geompos_in, int z, unsigned tx, unsigned ty, unsigned *initial_x, unsigned *initial_y, long long *original_features, long long *unclipped_features, int nextzoom, int maxzoom, int minzoom, int max_zoom_increment, size_t pass, std::atomic *along, long long alongminus, int buffer, std::atomic *within, compressor **geomfile, std::atomic *geompos, long long start_geompos[], std::atomic *oprogress, double todo, const char *fname, int child_shards, json_object *filter, const char *global_stringpool, long long *pool_off, std::vector> *layer_unmaps, bool first_time, bool compressed, multiplier_state *multiplier_state, std::shared_ptr &tile_stringpool, std::vector const &unidecode_data, next_feature_state &next_feature_state, double droprate) { +static serial_feature next_feature(decompressor *geoms, std::atomic *geompos_in, int z, unsigned tx, unsigned ty, unsigned *initial_x, unsigned *initial_y, long long *original_features, long long *unclipped_features, int nextzoom, int maxzoom, int minzoom, int max_zoom_increment, size_t pass, std::atomic *along, long long alongminus, int buffer, std::atomic *within, compressor **geomfile, std::atomic *geompos, long long start_geompos[], std::atomic *oprogress, double todo, const char *fname, int child_shards, json_object *filter, const char *global_stringpool, long long *pool_off, std::vector> *layer_unmaps, bool first_time, bool compressed, bool first_zoom, multiplier_state *multiplier_state, std::shared_ptr &tile_stringpool, std::vector const &unidecode_data, next_feature_state &next_feature_state, double droprate) { double extra_multiplier_zooms = log(retain_points_multiplier) / log(droprate); while (1) { serial_feature sf; long long len; + std::string s; - if (geoms->deserialize_long_long(&len, geompos_in) == 0) { - fprintf(stderr, "Unexpected physical EOF in feature stream\n"); - exit(EXIT_READ); - } - if (len <= 0) { - if (compressed) { - geoms->end(geompos_in); + if (next_feature_state.doing_deferrals) { + if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { + // done with direct deserialization, done with deferred deserialization + for (auto const fp : next_feature_state.deferrals) { + if (fclose(fp) != 0) { + perror("fclose deferral"); + exit(EXIT_CLOSE); + } + } + + sf.t = -2; + return sf; } - sf.t = -2; - return sf; - } + if (fread(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]) != 1) { + if (ferror(next_feature_state.deferrals[next_feature_state.which_deferral])) { + perror("read deferral"); + exit(EXIT_READ); + } + next_feature_state.which_deferral++; + continue; + } - std::string s; - s.resize(len); - size_t n = geoms->fread((void *) s.c_str(), sizeof(char), s.size(), geompos_in); - if (n != s.size()) { - fprintf(stderr, "Short read (%zu for %zu) from geometry\n", n, s.size()); - exit(EXIT_READ); + s.resize(len); + if (fread((void *) s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]) != (size_t) len) { + fprintf(stderr, "short read in deferred deserialization: %s\n", strerror(errno)); + exit(EXIT_READ); + } + } else { + if (geoms->deserialize_long_long(&len, geompos_in) == 0) { + fprintf(stderr, "Unexpected physical EOF in feature stream\n"); + exit(EXIT_READ); + } + if (len <= 0) { + if (compressed) { + geoms->end(geompos_in); + } + + next_feature_state.doing_deferrals = true; + next_feature_state.which_deferral = 0; + + for (auto const fp : next_feature_state.deferrals) { + errno = 0; + rewind(fp); + if (errno != 0) { + perror("rewind deferral"); + exit(EXIT_SEEK); + } + } + + continue; + } + + s.resize(len); + size_t n = geoms->fread((void *) s.c_str(), sizeof(char), s.size(), geompos_in); + if (n != s.size()) { + fprintf(stderr, "Short read (%zu for %zu) from geometry\n", n, s.size()); + exit(EXIT_READ); + } } sf = deserialize_feature(s, z, tx, ty, initial_x, initial_y); sf.stringpool = global_stringpool + pool_off[sf.segment]; + if (!next_feature_state.doing_deferrals && *original_features > 0 && first_zoom && additional[A_DISTINGUISH_DUPLICATES] && sf.index == next_feature_state.previndex) { + if (next_feature_state.which_deferral >= next_feature_state.deferrals.size()) { + std::string tmpname = std::string(tmpdir) + "/deferral.XXXXXX"; + std::vector tmpl(tmpname.begin(), tmpname.end()); + tmpl.push_back('\0'); + + int fd = mkstemp_cloexec(tmpl.data()); + if (fd < 0) { + fprintf(stderr, "Can't create temporary file %zu for feature deferral: %s\n", next_feature_state.which_deferral, strerror(errno)); + exit(EXIT_OPEN); + } + unlink(tmpl.data()); + FILE *fp = fdopen(fd, "wb+"); + if (fp == NULL) { + fprintf(stderr, "Can't reopen temporary file for feature deferral: %s\n", strerror(errno)); + exit(EXIT_OPEN); + } + next_feature_state.deferrals.resize(next_feature_state.which_deferral + 1); + next_feature_state.deferrals[next_feature_state.which_deferral] = fp; + } + + if (fwrite(&len, sizeof(len), 1, next_feature_state.deferrals[next_feature_state.which_deferral]) != 1) { + perror("write deferral length"); + exit(EXIT_WRITE); + } + if (fwrite(s.c_str(), 1, len, next_feature_state.deferrals[next_feature_state.which_deferral]) != (size_t) len) { + perror("write deferral"); + exit(EXIT_WRITE); + } + + // limit the maximum depth of deferral, + // because even at z0 we will run out of file descriptors + // (and tile space) if there are large numbers of duplicates + if (next_feature_state.which_deferral < 50) { + next_feature_state.which_deferral++; + } + continue; + } else { + next_feature_state.which_deferral = 0; + } + // with fractional zoom level, so we can target a specific number // of features to keep with retain-points-multiplier, not just the // powers of the drop rate @@ -1360,6 +1447,7 @@ struct run_prefilter_args { std::vector const *unidecode_data; bool first_time = false; bool compressed = false; + bool first_zoom = false; double droprate = 1; }; @@ -1371,7 +1459,7 @@ void *run_prefilter(void *v) { next_feature_state next_feature_state; while (1) { - serial_feature sf = next_feature(rpa->geoms, rpa->geompos_in, rpa->z, rpa->tx, rpa->ty, rpa->initial_x, rpa->initial_y, rpa->original_features, rpa->unclipped_features, rpa->nextzoom, rpa->maxzoom, rpa->minzoom, rpa->max_zoom_increment, rpa->pass, rpa->along, rpa->alongminus, rpa->buffer, rpa->within, rpa->geomfile, rpa->geompos, rpa->start_geompos, rpa->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->global_stringpool, rpa->pool_off, rpa->layer_unmaps, rpa->first_time, rpa->compressed, &multiplier_state, tile_stringpool, *(rpa->unidecode_data), next_feature_state, rpa->droprate); + serial_feature sf = next_feature(rpa->geoms, rpa->geompos_in, rpa->z, rpa->tx, rpa->ty, rpa->initial_x, rpa->initial_y, rpa->original_features, rpa->unclipped_features, rpa->nextzoom, rpa->maxzoom, rpa->minzoom, rpa->max_zoom_increment, rpa->pass, rpa->along, rpa->alongminus, rpa->buffer, rpa->within, rpa->geomfile, rpa->geompos, rpa->start_geompos, rpa->oprogress, rpa->todo, rpa->fname, rpa->child_shards, rpa->filter, rpa->global_stringpool, rpa->pool_off, rpa->layer_unmaps, rpa->first_time, rpa->compressed, rpa->first_zoom, &multiplier_state, tile_stringpool, *(rpa->unidecode_data), next_feature_state, rpa->droprate); if (sf.t < 0) { break; } @@ -1647,7 +1735,7 @@ void skip_tile(decompressor *geoms, std::atomic *geompos_in, bool com } } -long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *global_stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, std::atomic *geompos, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, unsigned long long mindrop_sequence, double minattribute, const char *prefilter, const char *postfilter, json_object *filter, write_tile_args *arg, atomic_strategy *strategy_out, bool compressed_input, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, std::vector const &unidecode_data, long long estimated_complexity, std::set &skip_children_out) { +long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *global_stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, std::atomic *geompos, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, unsigned long long mindrop_sequence, double minattribute, const char *prefilter, const char *postfilter, json_object *filter, write_tile_args *arg, atomic_strategy *strategy_out, bool compressed_input, bool first_zoom, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, std::vector const &unidecode_data, long long estimated_complexity, std::set &skip_children_out) { double merge_fraction = 1; double mingap_fraction = 1; double minextent_fraction = 1; @@ -1829,6 +1917,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch rpa.unidecode_data = &unidecode_data; rpa.first_time = first_time; rpa.compressed = compressed_input; + rpa.first_zoom = first_zoom; rpa.droprate = arg->droprate; // this does need to be a real thread, so we can pipe both to and from it @@ -1861,7 +1950,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch ssize_t which_serial_feature = -1; if (prefilter == NULL) { - sf = next_feature(geoms, geompos_in, z, tx, ty, initial_x, initial_y, &original_features, &unclipped_features, nextzoom, maxzoom, minzoom, max_zoom_increment, pass, along, alongminus, buffer, within, geomfile, geompos, start_geompos, &oprogress, todo, fname, child_shards, filter, global_stringpool, pool_off, layer_unmaps, first_time, compressed_input, &multiplier_state, tile_stringpool, unidecode_data, next_feature_state, arg->droprate); + sf = next_feature(geoms, geompos_in, z, tx, ty, initial_x, initial_y, &original_features, &unclipped_features, nextzoom, maxzoom, minzoom, max_zoom_increment, pass, along, alongminus, buffer, within, geomfile, geompos, start_geompos, &oprogress, todo, fname, child_shards, filter, global_stringpool, pool_off, layer_unmaps, first_time, compressed_input, first_zoom, &multiplier_state, tile_stringpool, unidecode_data, next_feature_state, arg->droprate); } else { sf = parse_feature(prefilter_jp, z, tx, ty, layermaps, tiling_seg, layer_unmaps, postfilter != NULL, key_pool); } @@ -3154,7 +3243,7 @@ exit(EXIT_IMPOSSIBLE); len = 1; } else { arg->wrote_zoom = z; - len = write_tile(&dc, &geompos, arg->global_stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->geompos, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->mindrop_sequence, arg->minattribute, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed, arg->shared_nodes_map, arg->nodepos, *(arg->shared_nodes_bloom), (*arg->unidecode_data), estimated_complexity, arg->skip_children_out); + len = write_tile(&dc, &geompos, arg->global_stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->geompos, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->mindrop_sequence, arg->minattribute, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed, arg->first_zoom, arg->shared_nodes_map, arg->nodepos, *(arg->shared_nodes_bloom), (*arg->unidecode_data), estimated_complexity, arg->skip_children_out); } if (pthread_mutex_lock(&var_lock) != 0) { @@ -3220,7 +3309,7 @@ exit(EXIT_IMPOSSIBLE); return err_or_null; } -int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, json_object *filter, std::vector &strategies, int iz, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending) { +int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, json_object *filter, std::vector &strategies, int iz, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending) { last_progress = 0; // The existing layermaps are one table per input thread. @@ -3417,6 +3506,7 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std:: args[thread].strategy = &strategy; args[thread].zoom = z; args[thread].compressed = (z != iz); + args[thread].first_zoom = (z == iz); args[thread].shared_nodes_map = shared_nodes_map; args[thread].nodepos = nodepos; args[thread].shared_nodes_bloom = &shared_nodes_bloom; diff --git a/tile.hpp b/tile.hpp index 8f266c0b..368f45e5 100644 --- a/tile.hpp +++ b/tile.hpp @@ -62,7 +62,7 @@ struct strategy { // long long write_tile(char **geom, char *stringpool, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int min_detail, int basezoom, sqlite3 *outdb, const char *outdir, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along, double gamma, int nlayers, std::atomic *strategy); -int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending); +int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending); int manage_gap(unsigned long long index, unsigned long long *previndex, double scale, double gamma, double *gap); diff --git a/version.hpp b/version.hpp index 8974b68f..71b353ad 100644 --- a/version.hpp +++ b/version.hpp @@ -1,6 +1,6 @@ #ifndef VERSION_HPP #define VERSION_HPP -#define VERSION "v2.80.0" +#define VERSION "v2.81.0" #endif