PostGIS  2.4.9dev-r@@SVN_REVISION@@

◆ mvt_geom()

LWGEOM* mvt_geom ( const 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.

Definition at line 710 of file mvt.c.

References AFFINE::afac, buffer(), AFFINE::efac, GBOX::flags, FLAGS_SET_GEODETIC, gbox_contains_2d(), gbox_init(), gbox_overlaps_2d(), AFFINE::ifac, gridspec_t::ipx, gridspec_t::ipy, lwgeom_affine(), lwgeom_calculate_gbox(), lwgeom_clip_by_rect(), lwgeom_clone_deep(), lwgeom_force_clockwise(), lwgeom_free(), lwgeom_get_basic_type(), lwgeom_get_bbox(), lwgeom_grid(), lwgeom_intersection(), lwgeom_is_empty(), lwgeom_make_valid(), lwgeom_reverse(), lwgeom_to_basic_type(), lwpoly_as_lwgeom(), lwpoly_construct_envelope(), lwpoly_free(), POLYGONTYPE, GBOX::xmax, GBOX::xmin, AFFINE::xoff, gridspec_t::xsize, GBOX::ymax, GBOX::ymin, AFFINE::yoff, and gridspec_t::ysize.

Referenced by ST_AsMVTGeom().

712 {
713  AFFINE affine;
714  gridspec grid;
715  LWGEOM *lwgeom_out = NULL;
716  double width = gbox->xmax - gbox->xmin;
717  double height = gbox->ymax - gbox->ymin;
718  double fx = extent / width;
719  double fy = -(extent / height);
720  uint8_t basic_type;
721  POSTGIS_DEBUG(2, "mvt_geom called");
722 
723  /* Short circuit out on EMPTY */
724  if (lwgeom_is_empty(lwgeom))
725  return NULL;
726 
727  if (width == 0 || height == 0)
728  elog(ERROR, "mvt_geom: bounds width or height cannot be 0");
729 
730  if (extent == 0)
731  elog(ERROR, "mvt_geom: extent cannot be 0");
732 
733  lwgeom_out = lwgeom_clone_deep(lwgeom);
734  basic_type = lwgeom_get_basic_type(lwgeom_out);
735 
736  /* transform to tile coordinate space */
737  memset(&affine, 0, sizeof(affine));
738  affine.afac = fx;
739  affine.efac = fy;
740  affine.ifac = 1;
741  affine.xoff = -gbox->xmin * fx;
742  affine.yoff = -gbox->ymax * fy;
743  lwgeom_affine(lwgeom_out, &affine);
744 
745  /* snap to integer precision, removing duplicate points */
746  memset(&grid, 0, sizeof(gridspec));
747  grid.ipx = 0;
748  grid.ipy = 0;
749  grid.xsize = 1;
750  grid.ysize = 1;
751  lwgeom_out = lwgeom_grid(lwgeom_out, &grid);
752 
753  if (lwgeom_out == NULL || lwgeom_is_empty(lwgeom_out))
754  return NULL;
755 
756  if (clip_geom)
757  {
758  GBOX bgbox, lwgeom_gbox;
759  gbox_init(&bgbox);
760  gbox_init(&lwgeom_gbox);
761  bgbox.xmax = bgbox.ymax = (double)extent + (double)buffer;
762  bgbox.xmin = bgbox.ymin = -(double)buffer;
763  FLAGS_SET_GEODETIC(lwgeom_gbox.flags, 0);
764  FLAGS_SET_GEODETIC(bgbox.flags, 0);
765  lwgeom_calculate_gbox(lwgeom_out, &lwgeom_gbox);
766 
767  if (!gbox_overlaps_2d(&lwgeom_gbox, &bgbox))
768  {
769  POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
770  return NULL;
771  }
772 
773  if (!gbox_contains_2d(&bgbox, &lwgeom_gbox))
774  {
775  LWGEOM *clipped_geom;
776 #if POSTGIS_GEOS_VERSION < 35
777  LWPOLY *lwenv = lwpoly_construct_envelope(0, bgbox.xmin, bgbox.ymin, bgbox.xmax, bgbox.ymax);
778  clipped_geom = lwgeom_intersection(lwgeom_out, lwpoly_as_lwgeom(lwenv));
779  lwpoly_free(lwenv);
780 #else
781  clipped_geom = lwgeom_clip_by_rect(lwgeom_out, bgbox.xmin, bgbox.ymin, bgbox.xmax, bgbox.ymax);
782 #endif
783  if (clipped_geom == NULL || lwgeom_is_empty(clipped_geom))
784  {
785  POSTGIS_DEBUG(3, "mvt_geom: no geometry after clip");
786  return NULL;
787  }
788 
789  /* For some polygons, the simplify step might have left them
790  * as invalid, which can cause clipping to return the complementary
791  * geometry of what it should */
792  if ((basic_type == POLYGONTYPE) &&
793  !gbox_contains_2d(&lwgeom_gbox, lwgeom_get_bbox(clipped_geom)))
794  {
795  /* TODO: Adapt this when and if Exception Policies are introduced.
796  * Other options would be to fix the geometry and retry
797  * or to calculate the difference between the 2 boxes.
798  */
799  POSTGIS_DEBUG(3, "mvt_geom: Invalid geometry after clipping");
800  lwgeom_free(clipped_geom);
801  return NULL;
802  }
803 
804  lwgeom_out = clipped_geom;
805  }
806  }
807 
808  if (lwgeom_out == NULL || lwgeom_is_empty(lwgeom_out))
809  return NULL;
810 
811  /* if polygon(s) make valid and force clockwise as per MVT spec */
812  if (basic_type == POLYGONTYPE)
813  {
814  lwgeom_out = lwgeom_make_valid(lwgeom_out);
815 
816  /* Because we are in image coordinates, we need to go to CCW in */
817  /* order to get a CW output in image space */
818  lwgeom_force_clockwise(lwgeom_out);
819  lwgeom_reverse(lwgeom_out);
820  }
821 
822  /* Make sure we return the most basic type after simplification and validation */
823  lwgeom_out = lwgeom_to_basic_type(lwgeom_out, basic_type);
824  if (basic_type != lwgeom_get_basic_type(lwgeom_out))
825  {
826  /* Drop type changes to play nice with MVT renderers */
827  POSTGIS_DEBUG(3, "mvt_geom: Dropping geometry after type change");
828  return NULL;
829  }
830 
831  if (lwgeom_out == NULL || lwgeom_is_empty(lwgeom_out))
832  return NULL;
833 
834 
835  /* Clipping and validation might produce float values. Grid again into int
836  * and pray that the output is still valid */
837  lwgeom_out = lwgeom_grid(lwgeom_out, &grid);
838 
839  if (lwgeom_out == NULL || lwgeom_is_empty(lwgeom_out))
840  return NULL;
841 
842  return lwgeom_out;
843 }
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:1912
#define POLYGONTYPE
Definition: liblwgeom.h:87
double xmax
Definition: liblwgeom.h:293
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1099
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:351
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
Definition: lwgeom.c:482
#define FLAGS_SET_GEODETIC(flags, value)
Definition: liblwgeom.h:149
static LWGEOM * lwgeom_to_basic_type(LWGEOM *geom, uint8 original_type)
In place process a collection to find a concrete geometry object and expose that as the actual object...
Definition: mvt.c:681
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:288
double ifac
Definition: liblwgeom.h:270
double xoff
Definition: liblwgeom.h:270
double afac
Definition: liblwgeom.h:270
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate bounding box of a geometry, automatically taking into account whether it is cartesian or ge...
Definition: lwgeom.c:701
LWPOLY * lwpoly_construct_envelope(int srid, double x1, double y1, double x2, double y2)
Definition: lwpoly.c:98
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Ensure the outer ring is clockwise oriented and all inner rings are counter-clockwise.
Definition: lwgeom.c:36
Datum buffer(PG_FUNCTION_ARGS)
double ymin
Definition: liblwgeom.h:294
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition: g_box.c:51
double xmin
Definition: liblwgeom.h:292
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:174
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:689
double ymax
Definition: liblwgeom.h:295
uint8_t flags
Definition: liblwgeom.h:291
void lwgeom_reverse(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:93
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:335
static uint8 lwgeom_get_basic_type(LWGEOM *geom)
Definition: mvt.c:645
double efac
Definition: liblwgeom.h:270
double yoff
Definition: liblwgeom.h:270
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:1346
unsigned char uint8_t
Definition: uthash.h:79
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:1735
Snap to grid.
Here is the call graph for this function:
Here is the caller graph for this function: