PostGIS  3.0.0dev-r@@SVN_REVISION@@

◆ mvt_geom()

LWGEOM* mvt_geom ( LWGEOM lwgeom,
const GBOX gbox,
uint32_t  extent,
uint32_t  buffer,
bool  clip_geom 
)

Transform a geometry into vector tile coordinate space.

Makes best effort to keep validity. Might collapse geometry into lower dimension.

NOTE: modifies in place if possible (not currently possible for polygons)

Definition at line 831 of file mvt.c.

References AFFINE::afac, LWGEOM::bbox, buffer(), COLLECTIONTYPE, AFFINE::efac, LWGEOM::flags, FLAGS_GET_BBOX, gbox_contains_2d(), gbox_expand(), gbox_overlaps_2d(), AFFINE::ifac, gridspec_t::ipx, gridspec_t::ipy, LINETYPE, LW_TRUE, lwgeom_affine(), lwgeom_clip_by_rect(), lwgeom_force_clockwise(), lwgeom_free(), lwgeom_get_bbox(), lwgeom_grid_in_place(), lwgeom_is_empty(), lwgeom_make_valid(), lwgeom_remove_repeated_points_in_place(), lwgeom_reverse_in_place(), lwgeom_simplify_in_place(), lwgeom_to_basic_type(), MULTILINETYPE, MULTIPOLYGONTYPE, POLYGONTYPE, window::res, LWGEOM::type, GBOX::xmax, GBOX::xmin, AFFINE::xoff, gridspec_t::xsize, GBOX::ymax, GBOX::ymin, AFFINE::yoff, and gridspec_t::ysize.

Referenced by ST_AsMVTGeom().

833 {
834  AFFINE affine;
835  gridspec grid;
836  double width = gbox->xmax - gbox->xmin;
837  double height = gbox->ymax - gbox->ymin;
838  double resx, resy, res, fx, fy;
839  int preserve_collapsed = LW_TRUE;
840  POSTGIS_DEBUG(2, "mvt_geom called");
841 
842  /* Short circuit out on EMPTY */
843  if (lwgeom_is_empty(lwgeom))
844  return NULL;
845 
846  if (width == 0 || height == 0)
847  elog(ERROR, "mvt_geom: bounds width or height cannot be 0");
848 
849  if (extent == 0)
850  elog(ERROR, "mvt_geom: extent cannot be 0");
851 
852  resx = width / extent;
853  resy = height / extent;
854  res = (resx < resy ? resx : resy)/2;
855  fx = extent / width;
856  fy = -(extent / height);
857 
858  if (FLAGS_GET_BBOX(lwgeom->flags) && lwgeom->bbox &&
859  (lwgeom->type == LINETYPE || lwgeom->type == MULTILINETYPE ||
860  lwgeom->type == POLYGONTYPE || lwgeom->type == MULTIPOLYGONTYPE))
861  {
862  // Shortcut to drop geometries smaller than the resolution
863  double bbox_width = lwgeom->bbox->xmax - lwgeom->bbox->xmin;
864  double bbox_height = lwgeom->bbox->ymax - lwgeom->bbox->ymin;
865  if (bbox_height * bbox_height + bbox_width * bbox_width < res * res)
866  return NULL;
867  }
868 
869  /* Remove all non-essential points (under the output resolution) */
871  lwgeom_simplify_in_place(lwgeom, res, preserve_collapsed);
872 
873  /* If geometry has disappeared, you're done */
874  if (lwgeom_is_empty(lwgeom))
875  return NULL;
876 
877  if (clip_geom)
878  {
879  // We need to add an extra half pixel to include the points that
880  // fall into the bbox only after the coordinate transformation
881  double buffer_map_xunits = nextafterf(res, 0.0) + resx * buffer;
882  GBOX bgbox;
883  const GBOX *lwgeom_gbox = lwgeom_get_bbox(lwgeom);
884  bgbox = *gbox;
885  gbox_expand(&bgbox, buffer_map_xunits);
886  if (!gbox_overlaps_2d(lwgeom_gbox, &bgbox))
887  {
888  POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
889  return NULL;
890  }
891  if (!gbox_contains_2d(&bgbox, lwgeom_gbox))
892  {
893  double x0 = bgbox.xmin;
894  double y0 = bgbox.ymin;
895  double x1 = bgbox.xmax;
896  double y1 = bgbox.ymax;
897  lwgeom = lwgeom_clip_by_rect(lwgeom, x0, y0, x1, y1);
898  POSTGIS_DEBUG(3, "mvt_geom: no geometry after clip");
899  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
900  return NULL;
901  }
902  }
903 
904  /* transform to tile coordinate space */
905  memset(&affine, 0, sizeof(affine));
906  affine.afac = fx;
907  affine.efac = fy;
908  affine.ifac = 1;
909  affine.xoff = -gbox->xmin * fx;
910  affine.yoff = -gbox->ymax * fy;
911  lwgeom_affine(lwgeom, &affine);
912 
913  /* snap to integer precision, removing duplicate points */
914  memset(&grid, 0, sizeof(gridspec));
915  grid.ipx = 0;
916  grid.ipy = 0;
917  grid.xsize = 1;
918  grid.ysize = 1;
919  lwgeom_grid_in_place(lwgeom, &grid);
920 
921  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
922  return NULL;
923 
924 
925  if (lwgeom->type == POLYGONTYPE ||
926  lwgeom->type == MULTIPOLYGONTYPE ||
927  lwgeom->type == COLLECTIONTYPE)
928  {
929  /* Force validation as per MVT spec */
930  lwgeom = lwgeom_make_valid(lwgeom);
931 
932  /* Drop type changes tp play nice with MVT renderers */
933  if (!(lwgeom->type == POLYGONTYPE ||
934  lwgeom->type == MULTIPOLYGONTYPE ||
935  lwgeom->type == COLLECTIONTYPE))
936  {
937  lwgeom_free(lwgeom);
938  return NULL;
939  }
940 
941  /* In image coordinates CW actually comes out a CCW, so we reverse */
942  lwgeom_force_clockwise(lwgeom);
943  lwgeom_reverse_in_place(lwgeom);
944  }
945 
946  /* if geometry collection extract highest dimensional geometry type */
947  if (lwgeom->type == COLLECTIONTYPE)
948  lwgeom_to_basic_type(lwgeom);
949 
950  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
951  return NULL;
952 
953  return lwgeom;
954 }
#define LINETYPE
Definition: liblwgeom.h:85
void lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance)
Definition: lwgeom.c:1544
tuple res
Definition: window.py:78
GBOX * bbox
Definition: liblwgeom.h:400
void gbox_expand(GBOX *g, double d)
Move the box minimums down and the maximums up by the distance provided.
Definition: g_box.c:104
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:102
#define POLYGONTYPE
Definition: liblwgeom.h:86
uint8_t flags
Definition: liblwgeom.h:399
double xmax
Definition: liblwgeom.h:295
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1128
LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1)
int gbox_contains_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the first GBOX contains the second on the 2d plane, LW_FALSE otherwise.
Definition: g_box.c:346
double ifac
Definition: liblwgeom.h:272
double xoff
Definition: liblwgeom.h:272
double afac
Definition: liblwgeom.h:272
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
Definition: lwinline.h:172
void lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition: lwgeom.c:1689
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Force Right-hand-rule on LWGEOM polygons.
Definition: lwgeom.c:37
Datum buffer(PG_FUNCTION_ARGS)
double ymin
Definition: liblwgeom.h:296
double xmin
Definition: liblwgeom.h:294
#define FLAGS_GET_BBOX(flags)
Definition: liblwgeom.h:141
LWGEOM * lwgeom_make_valid(LWGEOM *geom)
Attempts to make an invalid geometries valid w/out losing points.
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:75
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:725
static void lwgeom_to_basic_type(LWGEOM *geom)
In place process a collection to find a concrete geometry object and expose that as the actual object...
Definition: mvt.c:798
double ymax
Definition: liblwgeom.h:297
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:89
int gbox_overlaps_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps on the 2d plane, LW_FALSE otherwise.
Definition: g_box.c:330
double efac
Definition: liblwgeom.h:272
void lwgeom_grid_in_place(LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:2086
uint8_t type
Definition: liblwgeom.h:398
double yoff
Definition: liblwgeom.h:272
#define MULTILINETYPE
Definition: liblwgeom.h:88
#define COLLECTIONTYPE
Definition: liblwgeom.h:90
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:1910
Snap to grid.
Here is the call graph for this function:
Here is the caller graph for this function: