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