Skip to content

Commit

Permalink
Merge pull request mapbox#271 from mapbox/empty-features
Browse files Browse the repository at this point in the history
Test that an empty feature is no longer generated (here in tile 11/328/791)
  • Loading branch information
e-n-f authored Jul 13, 2016
2 parents 09ab013 + 9908db5 commit 26e21b0
Show file tree
Hide file tree
Showing 12 changed files with 4,209 additions and 13 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 1.12.5

* Add an option to vary the level of line and polygon simplification
* Be careful not to produce an empty tile if there was a feature with
empty geometry.

## 1.12.4

* Be even more careful not to produce features with empty geometry
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ resolution is obtained than by using a smaller _maxzoom_ or _detail_.
compensate for the larger marker, or -rf*number* to allow at most *number* features in the densest tile.
* -g _gamma_ or --gamma=_gamma_: 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.

### Line and polygon simplification

* -S _scale_ or --simplify=_scale_: Multiply the tolerance for line and polygon simplification by _scale_. The standard tolerance tries to keep
the line or polygon within one tile unit of its proper location. You can probably go up to about 10 without too much visible difference.

### Doing more

* -ac or --coalesce: Coalesce adjacent line and polygon features that have the same properties.
Expand Down
4 changes: 2 additions & 2 deletions geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,7 @@ drawvec impose_tile_boundaries(drawvec &geom, long long extent) {
return out;
}

drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds) {
drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds, double simplification) {
int res = 1 << (32 - detail - z);
long long area = 1LL << (32 - z);

Expand Down Expand Up @@ -1081,7 +1081,7 @@ drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds)
geom[j - 1].necessary = 1;

if (j - i > 1) {
douglas_peucker(geom, i, j - i, res);
douglas_peucker(geom, i, j - i, res * simplification);
}
i = j - 1;
}
Expand Down
2 changes: 1 addition & 1 deletion geometry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer);
bool point_within_tile(long long x, long long y, int z, int detail, long long buffer);
int quick_check(long long *bbox, int z, int detail, long long buffer);
drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds);
drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds, double simplification);
drawvec reorder_lines(drawvec &geom);
drawvec fix_polygon(drawvec &geom);
std::vector<drawvec> chop_polygon(std::vector<drawvec> &geoms);
Expand Down
14 changes: 12 additions & 2 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ static int min_detail = 7;

int quiet = 0;
int geometry_scale = 0;
double simplification = 1;

int prevent[256];
int additional[256];
Expand Down Expand Up @@ -1606,7 +1607,7 @@ int read_input(std::vector<source> &sources, char *fname, const char *layername,
}

unsigned midx = 0, midy = 0;
int written = traverse_zooms(fd, size, meta, stringpool, &midx, &midy, layernames, maxzoom, minzoom, basezoom, outdb, droprate, buffer, fname, tmpdir, gamma, nlayers, full_detail, low_detail, min_detail, meta_off, pool_off, initial_x, initial_y);
int written = traverse_zooms(fd, size, meta, stringpool, &midx, &midy, layernames, maxzoom, minzoom, basezoom, outdb, droprate, buffer, fname, tmpdir, gamma, nlayers, full_detail, low_detail, min_detail, meta_off, pool_off, initial_x, initial_y, simplification);

if (maxzoom != written) {
fprintf(stderr, "\n\n\n*** NOTE TILES ONLY COMPLETE THROUGH ZOOM %d ***\n\n\n", written);
Expand Down Expand Up @@ -1764,6 +1765,7 @@ int main(int argc, char **argv) {
{"prevent", required_argument, 0, 'p'},
{"additional", required_argument, 0, 'a'},
{"projection", required_argument, 0, 's'},
{"simplification", required_argument, 0, 'S'},

{"exclude-all", no_argument, 0, 'X'},
{"force", no_argument, 0, 'f'},
Expand Down Expand Up @@ -1812,7 +1814,7 @@ int main(int argc, char **argv) {
}
}

while ((i = getopt_long(argc, argv, "n:l:z:Z:B:d:D:m:o:x:y:r:b:t:g:p:a:XfFqvPL:A:s:", long_options, NULL)) != -1) {
while ((i = getopt_long(argc, argv, "n:l:z:Z:B:d:D:m:o:x:y:r:b:t:g:p:a:XfFqvPL:A:s:S:", long_options, NULL)) != -1) {
switch (i) {
case 0:
break;
Expand Down Expand Up @@ -1983,6 +1985,14 @@ int main(int argc, char **argv) {
set_projection_or_exit(optarg);
break;

case 'S':
simplification = atof(optarg);
if (simplification <= 0) {
fprintf(stderr, "%s: --simplification must be > 0\n", argv[0]);
exit(EXIT_FAILURE);
}
break;

default: {
int width = 7 + strlen(argv[0]);
fprintf(stderr, "Usage: %s", argv[0]);
Expand Down
6 changes: 6 additions & 0 deletions man/tippecanoe.1
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ compensate for the larger marker, or \-rf\fInumber\fP to allow at most \fInumber
.IP \(bu 2
\-g \fIgamma\fP or \-\-gamma=\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.
.RE
.SS Line and polygon simplification
.RS
.IP \(bu 2
\-S \fIscale\fP or \-\-simplify=\fIscale\fP: Multiply the tolerance for line and polygon simplification by \fIscale\fP\&. The standard tolerance tries to keep
the line or polygon within one tile unit of its proper location. You can probably go up to about 10 without too much visible difference.
.RE
.SS Doing more
.RS
.IP \(bu 2
Expand Down
5 changes: 5 additions & 0 deletions tests/empty-linestring/in.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.822234, 38.090503 ], [ -122.822194, 38.090533 ] ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.437925, 37.723073 ], [ -122.437896, 37.72311 ] ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.249823, 37.793307 ], [ -122.249782, 37.793336 ] ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.249784, 37.793334 ], [ -122.249825, 37.793305 ] ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.591126, 38.087619 ], [ -122.591099, 38.087581 ] ] } },
138 changes: 138 additions & 0 deletions tests/empty-linestring/out/-ac.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{ "type": "FeatureCollection", "properties": {
"bounds": "-122.822234,37.723073,-122.249782,38.090533",
"center": "-122.249782,37.796763,14",
"description": "tests/empty-linestring/out/-ac.json.check.mbtiles",
"format": "pbf",
"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 14, \"fields\": {} } ] }",
"maxzoom": "14",
"minzoom": "0",
"name": "tests/empty-linestring/out/-ac.json.check.mbtiles",
"type": "overlay",
"version": "2"
}, "features": [
{ "type": "FeatureCollection", "properties": { "zoom": 8, "x": 40, "y": 98 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.822342, 38.090526 ], [ -122.822342, 38.090796 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 9, "x": 81, "y": 197 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -122.822342, 38.090526 ], [ -122.822342, 38.090661 ] ], [ [ -122.591286, 38.087689 ], [ -122.591114, 38.087689 ] ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 162, "y": 394 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.822256, 38.090526 ], [ -122.822256, 38.090594 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 163, "y": 396 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.437992, 37.723139 ], [ -122.437906, 37.723139 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 163, "y": 395 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.437992, 37.723139 ], [ -122.437906, 37.723139 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 10, "x": 163, "y": 394 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.591200, 38.087621 ], [ -122.591114, 38.087621 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 11, "x": 325, "y": 789 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.822256, 38.090526 ], [ -122.822213, 38.090560 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 11, "x": 326, "y": 789 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.591157, 38.087621 ], [ -122.591114, 38.087587 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 11, "x": 327, "y": 791 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.437949, 37.723105 ], [ -122.437906, 37.723139 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 650, "y": 1578 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.822235, 38.090509 ], [ -122.822213, 38.090543 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 653, "y": 1578 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.591136, 38.087621 ], [ -122.591114, 38.087587 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 654, "y": 1583 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.437928, 37.723088 ], [ -122.437906, 37.723122 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 12, "x": 657, "y": 1582 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.249830, 37.793321 ], [ -122.249787, 37.793338 ], [ -122.249830, 37.793321 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1301, "y": 3157 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.822235, 38.090509 ], [ -122.822202, 38.090534 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1306, "y": 3157 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.591136, 38.087621 ], [ -122.591103, 38.087587 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1309, "y": 3167 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.437928, 37.723080 ], [ -122.437906, 37.723114 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 13, "x": 1314, "y": 3165 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.249830, 37.793313 ], [ -122.249787, 37.793338 ], [ -122.249830, 37.793313 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2602, "y": 6314 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.822235, 38.090505 ], [ -122.822197, 38.090534 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2612, "y": 6314 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.591130, 38.087621 ], [ -122.591103, 38.087583 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2619, "y": 6335 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.437928, 37.723075 ], [ -122.437901, 37.723114 ] ] } }
] }
] }
,
{ "type": "FeatureCollection", "properties": { "zoom": 14, "x": 2628, "y": 6331 }, "features": [
{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "LineString", "coordinates": [ [ -122.249824, 37.793308 ], [ -122.249787, 37.793338 ], [ -122.249830, 37.793308 ] ] } }
] }
] }
] }
4,022 changes: 4,022 additions & 0 deletions tests/ne_110m_admin_0_countries/out/-z4_-yname_-S4.json

Large diffs are not rendered by default.

16 changes: 10 additions & 6 deletions tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ struct partial {
int line_detail;
int maxzoom;
double spacing;
double simplification;
signed char t;
};

Expand Down Expand Up @@ -462,7 +463,7 @@ void *partial_feature_worker(void *v) {
geom = remove_noop(geom, t, 32 - z - line_detail);
}

drawvec ngeom = simplify_lines(geom, z, line_detail, !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]));
drawvec ngeom = simplify_lines(geom, z, line_detail, !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), (*partials)[i].simplification);

if (t != VT_POLYGON || ngeom.size() >= 3) {
geom = ngeom;
Expand Down Expand Up @@ -559,7 +560,7 @@ int manage_gap(unsigned long long index, unsigned long long *previndex, double s
return 0;
}

long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *stringpool, int z, unsigned tx, unsigned ty, int detail, int min_detail, int basezoom, std::vector<std::string> *layernames, sqlite3 *outdb, double droprate, int buffer, const char *fname, FILE **geomfile, int minzoom, int maxzoom, double todo, volatile long long *along, long long alongminus, double gamma, int nlayers, int child_shards, long long *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, volatile int *running) {
long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *stringpool, int z, unsigned tx, unsigned ty, int detail, int min_detail, int basezoom, std::vector<std::string> *layernames, sqlite3 *outdb, double droprate, int buffer, const char *fname, FILE **geomfile, int minzoom, int maxzoom, double todo, volatile long long *along, long long alongminus, double gamma, int nlayers, int child_shards, long long *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, volatile int *running, double simplification) {
int line_detail;
double fraction = 1;

Expand Down Expand Up @@ -860,6 +861,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
p.keys = metakeys;
p.values = metavals;
p.spacing = spacing;
p.simplification = simplification;
partials.push_back(p);
}
}
Expand Down Expand Up @@ -972,7 +974,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
if (features[j][x].coalesced && features[j][x].type == VT_LINE) {
features[j][x].geom = remove_noop(features[j][x].geom, features[j][x].type, 0);
features[j][x].geom = simplify_lines(features[j][x].geom, 32, 0,
!(prevent[P_CLIPPING] || prevent[P_DUPLICATION]));
!(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), simplification);
}

if (features[j][x].type == VT_POLYGON) {
Expand Down Expand Up @@ -1061,7 +1063,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s
oprogress = progress;
}

if (totalsize > 0) {
if (totalsize > 0 && tile.layers.size() > 0) {
if (totalsize > 200000 && !prevent[P_FEATURE_LIMIT]) {
fprintf(stderr, "tile %d/%u/%u has %lld features, >200000 \n", z, tx, ty, totalsize);
fprintf(stderr, "Try using -B to set a higher base zoom level.\n");
Expand Down Expand Up @@ -1139,6 +1141,7 @@ struct write_tile_args {
int minzoom;
int full_detail;
int low_detail;
double simplification;
volatile long long *most;
long long *meta_off;
long long *pool_off;
Expand Down Expand Up @@ -1186,7 +1189,7 @@ void *run_thread(void *vargs) {

// fprintf(stderr, "%d/%u/%u\n", z, x, y);

long long len = write_tile(geom, &geompos, arg->metabase, arg->stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->basezoom, arg->layernames, arg->outdb, arg->droprate, arg->buffer, arg->fname, arg->geomfile, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->nlayers, arg->child_shards, arg->meta_off, arg->pool_off, arg->initial_x, arg->initial_y, arg->running);
long long len = write_tile(geom, &geompos, arg->metabase, arg->stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->basezoom, arg->layernames, arg->outdb, arg->droprate, arg->buffer, arg->fname, arg->geomfile, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->nlayers, arg->child_shards, arg->meta_off, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification);

if (len < 0) {
int *err = &arg->err;
Expand Down Expand Up @@ -1237,7 +1240,7 @@ void *run_thread(void *vargs) {
return NULL;
}

int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, unsigned *midx, unsigned *midy, std::vector<std::string> &layernames, int maxzoom, int minzoom, int basezoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, const char *tmpdir, double gamma, int nlayers, int full_detail, int low_detail, int min_detail, long long *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y) {
int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpool, unsigned *midx, unsigned *midy, std::vector<std::string> &layernames, int maxzoom, int minzoom, int basezoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, const char *tmpdir, double gamma, int nlayers, int full_detail, int low_detail, int min_detail, long long *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification) {
int i;
for (i = 0; i <= maxzoom; i++) {
long long most = 0;
Expand Down Expand Up @@ -1358,6 +1361,7 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *metabase, char *stringpo
args[thread].gamma = gamma;
args[thread].nlayers = nlayers;
args[thread].child_shards = TEMP_FILES / threads;
args[thread].simplification = simplification;

args[thread].geomfd = geomfd;
args[thread].geom_size = geom_size;
Expand Down
Loading

0 comments on commit 26e21b0

Please sign in to comment.