PostGIS  2.5.0dev-r@@SVN_REVISION@@
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 682 of file mvt.c.

References AFFINE::afac, buffer(), COLLECTIONTYPE, AFFINE::efac, gbox_contains_2d(), gbox_expand(), gbox_overlaps_2d(), AFFINE::ifac, gridspec_t::ipx, gridspec_t::ipy, LW_TRUE, lwgeom_affine(), lwgeom_clip_by_rect(), lwgeom_force_clockwise(), lwgeom_get_bbox(), lwgeom_grid_in_place(), lwgeom_intersection(), lwgeom_is_empty(), lwgeom_make_valid(), lwgeom_remove_repeated_points_in_place(), lwgeom_simplify_in_place(), lwgeom_to_basic_type(), lwpoly_as_lwgeom(), lwpoly_construct_envelope(), lwpoly_free(), 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().

684 {
685  AFFINE affine;
686  gridspec grid;
687  double width = gbox->xmax - gbox->xmin;
688  double height = gbox->ymax - gbox->ymin;
689  double resx = width / extent;
690  double resy = height / extent;
691  double res = (resx < resy ? resx : resy)/2;
692  double fx = extent / width;
693  double fy = -(extent / height);
694  double buffer_map_xunits = resx * buffer;
695  int preserve_collapsed = LW_TRUE;
696  POSTGIS_DEBUG(2, "mvt_geom called");
697 
698  /* Short circuit out on EMPTY */
699  if (lwgeom_is_empty(lwgeom))
700  return NULL;
701 
702  if (width == 0 || height == 0)
703  elog(ERROR, "mvt_geom: bounds width or height cannot be 0");
704 
705  if (extent == 0)
706  elog(ERROR, "mvt_geom: extent cannot be 0");
707 
708  /* Remove all non-essential points (under the output resolution) */
710  lwgeom_simplify_in_place(lwgeom, res, preserve_collapsed);
711 
712  /* If geometry has disappeared, you're done */
713  if (lwgeom_is_empty(lwgeom))
714  return NULL;
715 
716  if (clip_geom)
717  {
718  const GBOX *lwgeom_gbox = lwgeom_get_bbox(lwgeom);;
719  GBOX bgbox = *gbox;
720  gbox_expand(&bgbox, buffer_map_xunits);
721  if (!gbox_overlaps_2d(lwgeom_gbox, &bgbox))
722  {
723  POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
724  return NULL;
725  }
726  if (!gbox_contains_2d(&bgbox, lwgeom_gbox))
727  {
728  double x0 = bgbox.xmin;
729  double y0 = bgbox.ymin;
730  double x1 = bgbox.xmax;
731  double y1 = bgbox.ymax;
732 #if POSTGIS_GEOS_VERSION < 35
733  LWPOLY *lwenv = lwpoly_construct_envelope(0, x0, y0, x1, y1);
734  lwgeom = lwgeom_intersection(lwgeom, lwpoly_as_lwgeom(lwenv));
735  lwpoly_free(lwenv);
736 #else
737  lwgeom = lwgeom_clip_by_rect(lwgeom, x0, y0, x1, y1);
738 #endif
739  POSTGIS_DEBUG(3, "mvt_geom: no geometry after clip");
740  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
741  return NULL;
742  }
743  }
744 
745  /* transform to tile coordinate space */
746  memset(&affine, 0, sizeof(affine));
747  affine.afac = fx;
748  affine.efac = fy;
749  affine.ifac = 1;
750  affine.xoff = -gbox->xmin * fx;
751  affine.yoff = -gbox->ymax * fy;
752  lwgeom_affine(lwgeom, &affine);
753 
754  /* snap to integer precision, removing duplicate points */
755  memset(&grid, 0, sizeof(gridspec));
756  grid.ipx = 0;
757  grid.ipy = 0;
758  grid.xsize = 1;
759  grid.ysize = 1;
760  lwgeom_grid_in_place(lwgeom, &grid);
761 
762  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
763  return NULL;
764 
765  /* if polygon(s) make valid and force clockwise as per MVT spec */
766  if (lwgeom->type == POLYGONTYPE ||
767  lwgeom->type == MULTIPOLYGONTYPE ||
768  lwgeom->type == COLLECTIONTYPE)
769  {
770  lwgeom = lwgeom_make_valid(lwgeom);
771  lwgeom_force_clockwise(lwgeom);
772  }
773 
774  /* if geometry collection extract highest dimensional geometry type */
775  if (lwgeom->type == COLLECTIONTYPE)
776  lwgeom_to_basic_type(lwgeom);
777 
778  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
779  return NULL;
780 
781  return lwgeom;
782 }
void lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance)
Definition: lwgeom.c:1596
tuple res
Definition: window.py:78
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
#define POLYGONTYPE
Definition: liblwgeom.h:86
double xmax
Definition: liblwgeom.h:292
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
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:319
double ifac
Definition: liblwgeom.h:269
double xoff
Definition: liblwgeom.h:269
double afac
Definition: liblwgeom.h:269
void lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition: lwgeom.c:1741
LWPOLY * lwpoly_construct_envelope(int srid, double x1, double y1, double x2, double y2)
Definition: lwpoly.c:98
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Force Right-hand-rule on LWGEOM polygons.
Definition: lwgeom.c:36
Datum buffer(PG_FUNCTION_ARGS)
double ymin
Definition: liblwgeom.h:293
double xmin
Definition: liblwgeom.h:291
LWGEOM * lwgeom_make_valid(LWGEOM *geom)
Attempts to make an invalid geometries valid w/out losing points.
void lwpoly_free(LWPOLY *poly)
Definition: lwpoly.c:173
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:75
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
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:727
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:649
double ymax
Definition: liblwgeom.h:294
#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:269
void lwgeom_grid_in_place(LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:2138
uint8_t type
Definition: liblwgeom.h:395
double yoff
Definition: liblwgeom.h:269
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
Definition: lwgeom.c:1386
#define COLLECTIONTYPE
Definition: liblwgeom.h:90
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:1962
Snap to grid.

Here is the call graph for this function:

Here is the caller graph for this function: