PostGIS  2.5.0beta2dev-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 692 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_is_empty(), lwgeom_make_valid(), lwgeom_remove_repeated_points_in_place(), lwgeom_reverse_in_place(), lwgeom_simplify_in_place(), lwgeom_to_basic_type(), 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().

694 {
695  AFFINE affine;
696  gridspec grid;
697  double width = gbox->xmax - gbox->xmin;
698  double height = gbox->ymax - gbox->ymin;
699  double resx, resy, res, fx, fy;
700  int preserve_collapsed = LW_TRUE;
701  POSTGIS_DEBUG(2, "mvt_geom called");
702 
703  /* Short circuit out on EMPTY */
704  if (lwgeom_is_empty(lwgeom))
705  return NULL;
706 
707  if (width == 0 || height == 0)
708  elog(ERROR, "mvt_geom: bounds width or height cannot be 0");
709 
710  if (extent == 0)
711  elog(ERROR, "mvt_geom: extent cannot be 0");
712 
713  resx = width / extent;
714  resy = height / extent;
715  res = (resx < resy ? resx : resy)/2;
716  fx = extent / width;
717  fy = -(extent / height);
718 
719  /* Remove all non-essential points (under the output resolution) */
721  lwgeom_simplify_in_place(lwgeom, res, preserve_collapsed);
722 
723  /* If geometry has disappeared, you're done */
724  if (lwgeom_is_empty(lwgeom))
725  return NULL;
726 
727  if (clip_geom)
728  {
729  // We need to add an extra half pixel to include the points that
730  // fall into the bbox only after the coordinate transformation
731  double buffer_map_xunits = nextafterf(res, 0.0) + resx * buffer;
732  GBOX bgbox;
733  const GBOX *lwgeom_gbox = lwgeom_get_bbox(lwgeom);
734  bgbox = *gbox;
735  gbox_expand(&bgbox, buffer_map_xunits);
736  if (!gbox_overlaps_2d(lwgeom_gbox, &bgbox))
737  {
738  POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
739  return NULL;
740  }
741  if (!gbox_contains_2d(&bgbox, lwgeom_gbox))
742  {
743  double x0 = bgbox.xmin;
744  double y0 = bgbox.ymin;
745  double x1 = bgbox.xmax;
746  double y1 = bgbox.ymax;
747  lwgeom = lwgeom_clip_by_rect(lwgeom, x0, y0, x1, y1);
748  POSTGIS_DEBUG(3, "mvt_geom: no geometry after clip");
749  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
750  return NULL;
751  }
752  }
753 
754  /* transform to tile coordinate space */
755  memset(&affine, 0, sizeof(affine));
756  affine.afac = fx;
757  affine.efac = fy;
758  affine.ifac = 1;
759  affine.xoff = -gbox->xmin * fx;
760  affine.yoff = -gbox->ymax * fy;
761  lwgeom_affine(lwgeom, &affine);
762 
763  /* snap to integer precision, removing duplicate points */
764  memset(&grid, 0, sizeof(gridspec));
765  grid.ipx = 0;
766  grid.ipy = 0;
767  grid.xsize = 1;
768  grid.ysize = 1;
769  lwgeom_grid_in_place(lwgeom, &grid);
770 
771  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
772  return NULL;
773 
774  /* if polygon(s) make valid and force clockwise as per MVT spec */
775  if (lwgeom->type == POLYGONTYPE ||
776  lwgeom->type == MULTIPOLYGONTYPE ||
777  lwgeom->type == COLLECTIONTYPE)
778  {
779  lwgeom = lwgeom_make_valid(lwgeom);
780  /* In image coordinates CW actually comes out a CCW, so */
781  /* we also reverse. ¯\_(ツ)_/¯ */
782  lwgeom_force_clockwise(lwgeom);
783  lwgeom_reverse_in_place(lwgeom);
784  }
785 
786  /* if geometry collection extract highest dimensional geometry type */
787  if (lwgeom->type == COLLECTIONTYPE)
788  lwgeom_to_basic_type(lwgeom);
789 
790  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
791  return NULL;
792 
793  return lwgeom;
794 }
void lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance)
Definition: lwgeom.c:1603
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
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:102
#define POLYGONTYPE
Definition: liblwgeom.h:86
double xmax
Definition: liblwgeom.h:295
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
void lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition: lwgeom.c:1748
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
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:734
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:658
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:2145
uint8_t type
Definition: liblwgeom.h:398
double yoff
Definition: liblwgeom.h:272
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:1393
#define COLLECTIONTYPE
Definition: liblwgeom.h:90
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:1969
Snap to grid.
Here is the call graph for this function:
Here is the caller graph for this function: