Changeset 1d03645f in git


Ignore:
Timestamp:
06/14/24 09:43:57 (11 months ago)
Author:
Paul Ramsey <pramsey@…>
Branches:
[('stable-3.4', '122ad6f692cdd6068b6fc8b2b9532a9204572fa0')]
Children:
['682776d3f96ab2400e662530b03f99a640fe0e19']
Parents:
['bb73a3fea9cf995da73ef63226c693849584c081']
Message:

Support geography estimated extents, references #5734

Files:
4 edited

Legend:

Unmodified
Added
Removed
  • NEWS

    rbb73a3fe r1d03645f  
    3030          itself (Paul Ramsey)
    3131 - #5720, Correctly mangle special column names in shp2pgsql (Paul Ramsey)
     32 - #5734, Estimate geography extent more correctly (Paul Ramsey)
    3233
    3334PostGIS 3.4.2
  • liblwgeom/lwgeodetic.c

    rbb73a3fe r1d03645f  
    35803580        return LW_FALSE;
    35813581}
     3582
     3583
     3584
     3585/*
     3586* Given a geodetic bounding volume, calculate a lon/lat bounding
     3587* box that should contain the original feature that gave rise to
     3588* the geodetic box, in plate-carre space (planar lon/lat).
     3589*/
     3590int gbox_geocentric_get_gbox_cartesian(const GBOX *gbox_geocentric, GBOX *gbox_planar)
     3591{
     3592        /* Normalized corners of the bounding volume */
     3593        POINT3D corners[8];
     3594        POINT3D cap_center = {0,0,0};
     3595        double furthest_angle = 0.0;
     3596        double cap_angle = 0.0;
     3597        int all_longitudes = LW_FALSE;
     3598        double lon0 = -M_PI, lon1 = M_PI;
     3599        double lat0, lat1;
     3600        GEOGRAPHIC_POINT cap_center_g;
     3601
     3602        if (!gbox_geocentric || !gbox_planar)
     3603        {
     3604                lwerror("Null pointer passed to %s", __func__);
     3605                return LW_FALSE;
     3606        }
     3607
     3608#define CORNER_SET(ii, xx, yy, zz) { \
     3609        corners[ii].x = gbox_geocentric->xx; \
     3610        corners[ii].y = gbox_geocentric->yy; \
     3611        corners[ii].z = gbox_geocentric->zz; \
     3612        }
     3613
     3614        /*
     3615        * First find a "centered" vector to serve as the mid-point
     3616        * of the input bounding volume.
     3617        */
     3618        CORNER_SET(0, xmin, ymin, zmin);
     3619        CORNER_SET(1, xmax, ymin, zmin);
     3620        CORNER_SET(2, xmin, ymax, zmin);
     3621        CORNER_SET(3, xmax, ymax, zmin);
     3622        CORNER_SET(4, xmin, ymin, zmax);
     3623        CORNER_SET(5, xmax, ymin, zmax);
     3624        CORNER_SET(6, xmin, ymax, zmax);
     3625        CORNER_SET(7, xmax, ymax, zmax);
     3626
     3627        /*
     3628        * Normalize the volume corners
     3629        * and normalize the final vector.
     3630        */
     3631        for (uint32_t i = 0; i < 8; i++)
     3632        {
     3633                normalize(&(corners[i]));
     3634                cap_center.x += corners[i].x;
     3635                cap_center.y += corners[i].y;
     3636                cap_center.z += corners[i].z;
     3637        }
     3638        normalize(&cap_center);
     3639
     3640        /*
     3641        * Find the volume corner that is furthest from the center,
     3642        * and calculate the angle between the center and the corner.
     3643        * Now we have a "cap" (center and angle)
     3644        */
     3645        for (uint32_t i = 0; i < 8; i++)
     3646        {
     3647                double angle = vector_angle(&cap_center, &(corners[i]));
     3648                if (angle > furthest_angle)
     3649                        furthest_angle = angle;
     3650        }
     3651        cap_angle = furthest_angle;
     3652
     3653        /*
     3654        * Calculate the planar box that contains the cap.
     3655        * If the cap contains a pole, then we include all longitudes
     3656        */
     3657        cart2geog(&cap_center, &cap_center_g);
     3658
     3659        /* Check whether cap includes the south pole */
     3660        lat0 = cap_center_g.lat - cap_angle;
     3661        if (lat0 <= -M_PI_2)
     3662        {
     3663                lat0 = -M_PI_2;
     3664                all_longitudes = LW_TRUE;
     3665        }
     3666
     3667        /* Check whether cap includes the north pole */
     3668        lat1 = cap_center_g.lat + cap_angle;
     3669        if (lat1 >= M_PI_2)
     3670        {
     3671                lat1 = M_PI_2;
     3672                all_longitudes = LW_TRUE;
     3673        }
     3674
     3675        if (!all_longitudes)
     3676        {
     3677                // Compute the range of longitudes covered by the cap.  We use the law
     3678                // of sines for spherical triangles.  Consider the triangle ABC where
     3679                // A is the north pole, B is the center of the cap, and C is the point
     3680                // of tangency between the cap boundary and a line of longitude.  Then
     3681                // C is a right angle, and letting a,b,c denote the sides opposite A,B,C,
     3682                // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c).
     3683                // Here "a" is the cap angle, and "c" is the colatitude (90 degrees
     3684                // minus the latitude).  This formula also works for negative latitudes.
     3685                //
     3686                // The formula for sin(a) follows from the relationship h = 1 - cos(a).
     3687
     3688                double sin_a = sin(cap_angle);
     3689                double sin_c = cos(cap_center_g.lat);
     3690                if (sin_a <= sin_c)
     3691                {
     3692                        double angle_A = asin(sin_a / sin_c);
     3693                        lon0 = remainder(cap_center_g.lon - angle_A, 2 * M_PI);
     3694                        lon1 = remainder(cap_center_g.lon + angle_A, 2 * M_PI);
     3695                }
     3696                else
     3697                {
     3698                        lon0 = -M_PI;
     3699                        lon1 =  M_PI;
     3700                }
     3701        }
     3702
     3703        gbox_planar->xmin = rad2deg(lon0);
     3704        gbox_planar->ymin = rad2deg(lat0);
     3705        gbox_planar->xmax = rad2deg(lon1);
     3706        gbox_planar->ymax = rad2deg(lat1);
     3707        FLAGS_SET_GEODETIC(gbox_planar->flags, 0);
     3708        FLAGS_SET_Z(gbox_planar->flags, 0);
     3709        FLAGS_SET_M(gbox_planar->flags, 0);
     3710
     3711        return LW_TRUE;
     3712}
     3713
  • liblwgeom/lwgeodetic.h

    rbb73a3fe r1d03645f  
    153153int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g);
    154154
     155int gbox_geocentric_get_gbox_cartesian(const GBOX *gbox_geocentric, GBOX *gbox_planar);
    155156
    156157#endif /* _LWGEODETIC_H */
  • postgis/gserialized_estimate.c

    rbb73a3fe r1d03645f  
    117117#include "lwgeom_pg.h"       /* For debugging macros. */
    118118#include "gserialized_gist.h" /* For index common functions */
     119#include "lwgeodetic.h"
    119120
    120121#include <math.h>
     
    22852286
    22862287
     2288static Oid
     2289get_attype_by_name(Oid tbl, const char *colname)
     2290{
     2291        AttrNumber attnum = get_attnum(tbl, colname);
     2292        if (attnum == InvalidAttrNumber)
     2293                elog(ERROR, "cannot find column \"%s\" in table \"%s\"",
     2294                        colname,
     2295                        get_rel_name(tbl));
     2296
     2297        return get_atttype(tbl, attnum);
     2298}
     2299
    22872300
    22882301/**
     
    23042317        int key_type, att_num;
    23052318        size_t sz;
     2319        Oid atttypid;
     2320        bool is_geography = false;
    23062321
    23072322        /* We need to initialize the internal cache to access it later via postgis_oid() */
     
    23472362        }
    23482363
     2364        atttypid = get_attype_by_name(tbl_oid, text_to_cstring(col));
     2365        if (atttypid == postgis_oid(GEOGRAPHYOID))
     2366                is_geography = true;
     2367
    23492368        /* Read the extent from the head of the spatial index, if there is one */
    2350 
    23512369        idx_oid = table_get_spatial_index(tbl_oid, col, &key_type, &att_num);
    23522370        if (idx_oid)
     
    23642382
    23652383                /* Estimated extent only returns 2D bounds, so use mode 2 */
    2366                 nd_stats = pg_get_nd_stats_by_name(tbl_oid, col, 2, only_parent);
     2384                nd_stats = pg_get_nd_stats_by_name(tbl_oid, col, is_geography ? 3 : 2, only_parent);
    23672385
    23682386                /* Error out on no stats */
     
    23732391
    23742392                /* Construct the box */
    2375                 gbox = palloc(sizeof(GBOX));
    2376                 FLAGS_SET_GEODETIC(gbox->flags, 0);
    2377                 FLAGS_SET_Z(gbox->flags, 0);
    2378                 FLAGS_SET_M(gbox->flags, 0);
     2393                gbox = gbox_new(0);
    23792394                gbox->xmin = nd_stats->extent.min[0];
    23802395                gbox->xmax = nd_stats->extent.max[0];
    23812396                gbox->ymin = nd_stats->extent.min[1];
    23822397                gbox->ymax = nd_stats->extent.max[1];
     2398                if (is_geography)
     2399                {
     2400                        FLAGS_SET_Z(gbox->flags, 1);
     2401                        gbox->zmin = nd_stats->extent.min[2];
     2402                        gbox->zmax = nd_stats->extent.max[2];
     2403                }
     2404
    23832405                pfree(nd_stats);
    23842406        }
     2407
     2408        /* Convert geocentric geography box into a planar box */
     2409        /* that users understand */
     2410        if (is_geography)
     2411        {
     2412                GBOX *gbox_planar = gbox_new(0);
     2413                gbox_geocentric_get_gbox_cartesian(gbox, gbox_planar);
     2414                PG_RETURN_POINTER(gbox_planar);
     2415        }
     2416        else
     2417                PG_RETURN_POINTER(gbox);
    23852418
    23862419        PG_RETURN_POINTER(gbox);
     
    25572590        else if (key_type == STATISTIC_KIND_ND && bounds_gidx)
    25582591        {
     2592                lwflags_t flags = 0;
    25592593                if (gidx_is_unknown(bounds_gidx))
    25602594                        return NULL;
    2561                 gbox = gbox_new(0);
    2562                 gbox_from_gidx(bounds_gidx, gbox, 0);
     2595                FLAGS_SET_Z(flags, GIDX_NDIMS(bounds_gidx) > 2);
     2596                FLAGS_SET_M(flags, GIDX_NDIMS(bounds_gidx) > 3);
     2597                gbox = gbox_new(flags);
     2598                gbox_from_gidx(bounds_gidx, gbox, flags);
    25632599        }
    25642600        else
Note: See TracChangeset for help on using the changeset viewer.