PostGIS  3.3.9dev-r@@SVN_REVISION@@

◆ geography_bestsrid()

Datum geography_bestsrid ( PG_FUNCTION_ARGS  )

Definition at line 795 of file geography_measurement.c.

796 {
797  GBOX gbox, gbox1, gbox2;
798  GSERIALIZED *g1 = NULL;
799  GSERIALIZED *g2 = NULL;
800  int empty1 = LW_FALSE;
801  int empty2 = LW_FALSE;
802  double xwidth, ywidth;
803  POINT2D center;
804  LWGEOM *lwgeom;
805 
806  /* Get our geometry objects loaded into memory. */
807  g1 = PG_GETARG_GSERIALIZED_P(0);
808  /* Synchronize our box types */
809  gbox1.flags = gserialized_get_lwflags(g1);
810  /* Calculate if the geometry is empty. */
811  empty1 = gserialized_is_empty(g1);
812 
813  /* Convert g1 to LWGEOM type */
814  lwgeom = lwgeom_from_gserialized(g1);
815 
816  /* Calculate a geocentric bounds for the objects */
817  if ( ! empty1 && gserialized_get_gbox_p(g1, &gbox1) == LW_FAILURE )
818  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g1, &gbox1)");
819 
820  POSTGIS_DEBUGF(4, "calculated gbox = %s", gbox_to_string(&gbox1));
821 
822  if ( !lwgeom_isfinite(lwgeom) ) {
823  elog(ERROR, "Error in geography_bestsrid calling with infinite coordinate geographies");
824  }
825  lwgeom_free(lwgeom);
826 
827  /* If we have a unique second argument, fill in all the necessary variables. */
828  if (PG_NARGS() > 1)
829  {
830  g2 = PG_GETARG_GSERIALIZED_P(1);
831  gbox2.flags = gserialized_get_lwflags(g2);
832  empty2 = gserialized_is_empty(g2);
833  if ( ! empty2 && gserialized_get_gbox_p(g2, &gbox2) == LW_FAILURE )
834  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g2, &gbox2)");
835 
836  /* Convert g2 to LWGEOM type */
837  lwgeom = lwgeom_from_gserialized(g2);
838 
839  if ( !lwgeom_isfinite(lwgeom) ) {
840  elog(ERROR, "Error in geography_bestsrid calling with second arg infinite coordinate geographies");
841  }
842  lwgeom_free(lwgeom);
843  }
844  /*
845  ** If no unique second argument, copying the box for the first
846  ** argument will give us the right answer for all subsequent tests.
847  */
848  else
849  {
850  gbox = gbox2 = gbox1;
851  }
852 
853  /* Both empty? We don't have an answer. */
854  if ( empty1 && empty2 )
855  PG_RETURN_NULL();
856 
857  /* One empty? We can use the other argument values as infill. Otherwise merge the boxen */
858  if ( empty1 )
859  gbox = gbox2;
860  else if ( empty2 )
861  gbox = gbox1;
862  else
863  gbox_union(&gbox1, &gbox2, &gbox);
864 
865  gbox_centroid(&gbox, &center);
866 
867  /* Width and height in degrees */
868  xwidth = 180.0 * gbox_angular_width(&gbox) / M_PI;
869  ywidth = 180.0 * gbox_angular_height(&gbox) / M_PI;
870 
871  POSTGIS_DEBUGF(2, "xwidth %g", xwidth);
872  POSTGIS_DEBUGF(2, "ywidth %g", ywidth);
873  POSTGIS_DEBUGF(2, "center POINT(%g %g)", center.x, center.y);
874 
875  /* Are these data arctic? Lambert Azimuthal Equal Area North. */
876  if ( center.y > 70.0 && ywidth < 45.0 )
877  {
878  PG_RETURN_INT32(SRID_NORTH_LAMBERT);
879  }
880 
881  /* Are these data antarctic? Lambert Azimuthal Equal Area South. */
882  if ( center.y < -70.0 && ywidth < 45.0 )
883  {
884  PG_RETURN_INT32(SRID_SOUTH_LAMBERT);
885  }
886 
887  /*
888  ** Can we fit these data into one UTM zone?
889  ** We will assume we can push things as
890  ** far as a half zone past a zone boundary.
891  ** Note we have no handling for the date line in here.
892  */
893  if ( xwidth < 6.0 )
894  {
895  int zone = floor((center.x + 180.0) / 6.0);
896 
897  if ( zone > 59 ) zone = 59;
898 
899  /* Are these data below the equator? UTM South. */
900  if ( center.y < 0.0 )
901  {
902  PG_RETURN_INT32( SRID_SOUTH_UTM_START + zone );
903  }
904  /* Are these data above the equator? UTM North. */
905  else
906  {
907  PG_RETURN_INT32( SRID_NORTH_UTM_START + zone );
908  }
909  }
910 
911  /*
912  ** Can we fit into a custom LAEA area? (30 degrees high, variable width)
913  ** We will allow overlap into adjoining areas, but use a slightly narrower test (25) to try
914  ** and minimize the worst case.
915  ** Again, we are hoping the dateline doesn't trip us up much
916  */
917  if ( ywidth < 25.0 )
918  {
919  int xzone = -1;
920  int yzone = 3 + floor(center.y / 30.0); /* (range of 0-5) */
921 
922  /* Equatorial band, 12 zones, 30 degrees wide */
923  if ( (yzone == 2 || yzone == 3) && xwidth < 30.0 )
924  {
925  xzone = 6 + floor(center.x / 30.0);
926  }
927  /* Temperate band, 8 zones, 45 degrees wide */
928  else if ( (yzone == 1 || yzone == 4) && xwidth < 45.0 )
929  {
930  xzone = 4 + floor(center.x / 45.0);
931  }
932  /* Arctic band, 4 zones, 90 degrees wide */
933  else if ( (yzone == 0 || yzone == 5) && xwidth < 90.0 )
934  {
935  xzone = 2 + floor(center.x / 90.0);
936  }
937 
938  /* Did we fit into an appropriate xzone? */
939  if ( xzone != -1 )
940  {
941  PG_RETURN_INT32(SRID_LAEA_START + 20 * yzone + xzone);
942  }
943  }
944 
945  /*
946  ** Running out of options... fall-back to Mercator
947  ** and hope for the best.
948  */
949  PG_RETURN_INT32(SRID_WORLD_MERCATOR);
950 
951 }
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition: gbox.c:135
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
Definition: gbox.c:392
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or calculate it if necessary.
Definition: gserialized.c:65
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:239
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:152
lwflags_t gserialized_get_lwflags(const GSERIALIZED *g)
Read the flags from a GSERIALIZED and return a standard lwflag integer.
Definition: gserialized.c:18
#define LW_FALSE
Definition: liblwgeom.h:109
#define LW_FAILURE
Definition: liblwgeom.h:111
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
int lwgeom_isfinite(const LWGEOM *lwgeom)
Check if a LWGEOM has any non-finite (NaN or Inf) coordinates.
Definition: lwgeom.c:2707
double gbox_angular_height(const GBOX *gbox)
GBOX utility functions to figure out coverage/location on the globe.
Definition: lwgeodetic.c:188
double gbox_angular_width(const GBOX *gbox)
Returns the angular width (longitudinal span) of the box in radians.
Definition: lwgeodetic.c:215
int gbox_centroid(const GBOX *gbox, POINT2D *out)
Computes the average(ish) center of the box and returns success.
Definition: lwgeodetic.c:267
lwflags_t flags
Definition: liblwgeom.h:368
double y
Definition: liblwgeom.h:405
double x
Definition: liblwgeom.h:405

References GBOX::flags, gbox_angular_height(), gbox_angular_width(), gbox_centroid(), gbox_to_string(), gbox_union(), gserialized_get_gbox_p(), gserialized_get_lwflags(), gserialized_is_empty(), LW_FAILURE, LW_FALSE, lwgeom_free(), lwgeom_from_gserialized(), lwgeom_isfinite(), POINT2D::x, and POINT2D::y.

Here is the call graph for this function: