PostGIS  2.3.8dev-r@@SVN_REVISION@@

◆ geography_bestsrid()

Datum geography_bestsrid ( PG_FUNCTION_ARGS  )

Definition at line 790 of file geography_measurement.c.

References GBOX::flags, GSERIALIZED::flags, gbox_angular_height(), gbox_angular_width(), gbox_centroid(), gbox_to_string(), gbox_union(), geography_project(), gserialized_get_gbox_p(), gserialized_is_empty(), LW_FAILURE, LW_FALSE, PG_FUNCTION_INFO_V1(), POINT2D::x, and POINT2D::y.

Referenced by geography_covers().

791 {
792  GBOX gbox, gbox1, gbox2;
793  GSERIALIZED *g1 = NULL;
794  GSERIALIZED *g2 = NULL;
795  int empty1 = LW_FALSE;
796  int empty2 = LW_FALSE;
797  double xwidth, ywidth;
798  POINT2D center;
799 
800  Datum d1 = PG_GETARG_DATUM(0);
801  Datum d2 = PG_GETARG_DATUM(1);
802 
803  /* Get our geometry objects loaded into memory. */
804  g1 = (GSERIALIZED*)PG_DETOAST_DATUM(d1);
805  /* Synchronize our box types */
806  gbox1.flags = g1->flags;
807  /* Calculate if the geometry is empty. */
808  empty1 = gserialized_is_empty(g1);
809  /* Calculate a geocentric bounds for the objects */
810  if ( ! empty1 && gserialized_get_gbox_p(g1, &gbox1) == LW_FAILURE )
811  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g1, &gbox1)");
812 
813  POSTGIS_DEBUGF(4, "calculated gbox = %s", gbox_to_string(&gbox1));
814 
815  /* If we have a unique second argument, fill in all the necessary variables. */
816  if ( d1 != d2 )
817  {
818  g2 = (GSERIALIZED*)PG_DETOAST_DATUM(d2);
819  gbox2.flags = g2->flags;
820  empty2 = gserialized_is_empty(g2);
821  if ( ! empty2 && gserialized_get_gbox_p(g2, &gbox2) == LW_FAILURE )
822  elog(ERROR, "Error in geography_bestsrid calling gserialized_get_gbox_p(g2, &gbox2)");
823  }
824  /*
825  ** If no unique second argument, copying the box for the first
826  ** argument will give us the right answer for all subsequent tests.
827  */
828  else
829  {
830  gbox = gbox2 = gbox1;
831  }
832 
833  /* Both empty? We don't have an answer. */
834  if ( empty1 && empty2 )
835  PG_RETURN_NULL();
836 
837  /* One empty? We can use the other argument values as infill. Otherwise merge the boxen */
838  if ( empty1 )
839  gbox = gbox2;
840  else if ( empty2 )
841  gbox = gbox1;
842  else
843  gbox_union(&gbox1, &gbox2, &gbox);
844 
845  gbox_centroid(&gbox, &center);
846 
847  /* Width and height in degrees */
848  xwidth = 180.0 * gbox_angular_width(&gbox) / M_PI;
849  ywidth = 180.0 * gbox_angular_height(&gbox) / M_PI;
850 
851  POSTGIS_DEBUGF(2, "xwidth %g", xwidth);
852  POSTGIS_DEBUGF(2, "ywidth %g", ywidth);
853  POSTGIS_DEBUGF(2, "center POINT(%g %g)", center.x, center.y);
854 
855  /* Are these data arctic? Lambert Azimuthal Equal Area North. */
856  if ( center.y > 70.0 && ywidth < 45.0 )
857  {
858  PG_RETURN_INT32(SRID_NORTH_LAMBERT);
859  }
860 
861  /* Are these data antarctic? Lambert Azimuthal Equal Area South. */
862  if ( center.y < -70.0 && ywidth < 45.0 )
863  {
864  PG_RETURN_INT32(SRID_SOUTH_LAMBERT);
865  }
866 
867  /*
868  ** Can we fit these data into one UTM zone?
869  ** We will assume we can push things as
870  ** far as a half zone past a zone boundary.
871  ** Note we have no handling for the date line in here.
872  */
873  if ( xwidth < 6.0 )
874  {
875  int zone = floor((center.x + 180.0) / 6.0);
876 
877  if ( zone > 59 ) zone = 59;
878 
879  /* Are these data below the equator? UTM South. */
880  if ( center.y < 0.0 )
881  {
882  PG_RETURN_INT32( SRID_SOUTH_UTM_START + zone );
883  }
884  /* Are these data above the equator? UTM North. */
885  else
886  {
887  PG_RETURN_INT32( SRID_NORTH_UTM_START + zone );
888  }
889  }
890 
891  /*
892  ** Can we fit into a custom LAEA area? (30 degrees high, variable width)
893  ** We will allow overlap into adjoining areas, but use a slightly narrower test (25) to try
894  ** and minimize the worst case.
895  ** Again, we are hoping the dateline doesn't trip us up much
896  */
897  if ( ywidth < 25.0 )
898  {
899  int xzone = -1;
900  int yzone = 3 + floor(center.y / 30.0); /* (range of 0-5) */
901 
902  /* Equatorial band, 12 zones, 30 degrees wide */
903  if ( (yzone == 2 || yzone == 3) && xwidth < 30.0 )
904  {
905  xzone = 6 + floor(center.x / 30.0);
906  }
907  /* Temperate band, 8 zones, 45 degrees wide */
908  else if ( (yzone == 1 || yzone == 4) && xwidth < 45.0 )
909  {
910  xzone = 4 + floor(center.x / 45.0);
911  }
912  /* Arctic band, 4 zones, 90 degrees wide */
913  else if ( (yzone == 0 || yzone == 5) && xwidth < 90.0 )
914  {
915  xzone = 2 + floor(center.x / 90.0);
916  }
917 
918  /* Did we fit into an appropriate xzone? */
919  if ( xzone != -1 )
920  {
921  PG_RETURN_INT32(SRID_LAEA_START + 20 * yzone + xzone);
922  }
923  }
924 
925  /*
926  ** Running out of options... fall-back to Mercator
927  ** and hope for the best.
928  */
929  PG_RETURN_INT32(SRID_WORLD_MERCATOR);
930 
931 }
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box)
Read the bounding box off a serialization and calculate one if it is not already there.
Definition: g_serialized.c:398
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
Definition: g_box.c:404
int gbox_centroid(const GBOX *gbox, POINT2D *out)
Computes the average(ish) center of the box and returns success.
Definition: lwgeodetic.c:258
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: g_serialized.c:153
#define LW_FAILURE
Definition: liblwgeom.h:78
double x
Definition: liblwgeom.h:327
#define LW_FALSE
Definition: liblwgeom.h:76
double y
Definition: liblwgeom.h:327
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition: g_box.c:146
uint8_t flags
Definition: liblwgeom.h:290
double gbox_angular_height(const GBOX *gbox)
GBOX utility functions to figure out coverage/location on the globe.
Definition: lwgeodetic.c:179
uint8_t flags
Definition: liblwgeom.h:382
double gbox_angular_width(const GBOX *gbox)
Returns the angular width (longitudinal span) of the box in radians.
Definition: lwgeodetic.c:206
Here is the call graph for this function:
Here is the caller graph for this function: