Changeset 1d03645f in git
- Timestamp:
- 06/14/24 09:43:57 (11 months ago)
- Branches:
- [('stable-3.4', '122ad6f692cdd6068b6fc8b2b9532a9204572fa0')]
- Children:
- ['682776d3f96ab2400e662530b03f99a640fe0e19']
- Parents:
- ['bb73a3fea9cf995da73ef63226c693849584c081']
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
NEWS
rbb73a3fe r1d03645f 30 30 itself (Paul Ramsey) 31 31 - #5720, Correctly mangle special column names in shp2pgsql (Paul Ramsey) 32 - #5734, Estimate geography extent more correctly (Paul Ramsey) 32 33 33 34 PostGIS 3.4.2 -
liblwgeom/lwgeodetic.c
rbb73a3fe r1d03645f 3580 3580 return LW_FALSE; 3581 3581 } 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 */ 3590 int 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 153 153 int spheroid_project(const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g); 154 154 155 int gbox_geocentric_get_gbox_cartesian(const GBOX *gbox_geocentric, GBOX *gbox_planar); 155 156 156 157 #endif /* _LWGEODETIC_H */ -
postgis/gserialized_estimate.c
rbb73a3fe r1d03645f 117 117 #include "lwgeom_pg.h" /* For debugging macros. */ 118 118 #include "gserialized_gist.h" /* For index common functions */ 119 #include "lwgeodetic.h" 119 120 120 121 #include <math.h> … … 2285 2286 2286 2287 2288 static Oid 2289 get_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 2287 2300 2288 2301 /** … … 2304 2317 int key_type, att_num; 2305 2318 size_t sz; 2319 Oid atttypid; 2320 bool is_geography = false; 2306 2321 2307 2322 /* We need to initialize the internal cache to access it later via postgis_oid() */ … … 2347 2362 } 2348 2363 2364 atttypid = get_attype_by_name(tbl_oid, text_to_cstring(col)); 2365 if (atttypid == postgis_oid(GEOGRAPHYOID)) 2366 is_geography = true; 2367 2349 2368 /* Read the extent from the head of the spatial index, if there is one */ 2350 2351 2369 idx_oid = table_get_spatial_index(tbl_oid, col, &key_type, &att_num); 2352 2370 if (idx_oid) … … 2364 2382 2365 2383 /* 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); 2367 2385 2368 2386 /* Error out on no stats */ … … 2373 2391 2374 2392 /* 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); 2379 2394 gbox->xmin = nd_stats->extent.min[0]; 2380 2395 gbox->xmax = nd_stats->extent.max[0]; 2381 2396 gbox->ymin = nd_stats->extent.min[1]; 2382 2397 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 2383 2405 pfree(nd_stats); 2384 2406 } 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); 2385 2418 2386 2419 PG_RETURN_POINTER(gbox); … … 2557 2590 else if (key_type == STATISTIC_KIND_ND && bounds_gidx) 2558 2591 { 2592 lwflags_t flags = 0; 2559 2593 if (gidx_is_unknown(bounds_gidx)) 2560 2594 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); 2563 2599 } 2564 2600 else
Note:
See TracChangeset
for help on using the changeset viewer.