bool spoly_eq ( const SPOLY * p1, const SPOLY * p2, bool dir ) { bool ret = FALSE; if ( p1->npts == p2->npts ){ int32 i, k, cntr, shift; for ( shift=0; shift<p1->npts; shift++ ) { cntr = 0; for ( i=0; i<p1->npts; i++ ){ k = (dir)?( p1->npts - i - 1 ):(i); k += shift; k = (k<p1->npts)?(k):(k - p1->npts); if ( spoint_eq ( &p1->p[i], &p2->p[k]) ){ cntr++; } } if ( cntr == p1->npts ){ ret = TRUE; break; } } // Try other direction, if not equal if ( !dir && !ret ){ ret = spoly_eq ( p1, p2, TRUE ); } } return ret; }
/* * Transforms a spherical vector from 'spb' to 'spe' into an inverse Euler * transformation. Returns true if the transformation was successful. */ static bool spherevector_to_euler_inv(SEuler *se, const SPoint *spb, const SPoint *spe) { if (spoint_eq(spb, spe)) { return false; } else { Vector3D vbeg, vend, vtmp; SPoint spt[2]; SEuler set; spoint_vector3d(&vbeg, spb); spoint_vector3d(&vend, spe); vector3d_cross(&vtmp, &vbeg, &vend); vector3d_spoint(&spt[0], &vtmp); set.phi = -spt[0].lng - PIH; set.theta = spt[0].lat - PIH; set.psi = 0.0; seuler_set_zxz(&set); euler_spoint_trans(&spt[1], spb, &set); set.psi = -spt[1].lng; memcpy((void *) se, (void *) &set, sizeof(SEuler)); } return true; }
bool strans_eq ( const SEuler * e1, const SEuler * e2 ) { static SPoint in[2], p[4] ; in[0].lng = 0.0; in[0].lat = 0.0; in[1].lng = PIH; in[1].lat = 0.0; euler_spoint_trans ( &p[0] , &in[0] , e1 ); euler_spoint_trans ( &p[1] , &in[1] , e1 ); euler_spoint_trans ( &p[2] , &in[0] , e2 ); euler_spoint_trans ( &p[3] , &in[1] , e2 ); return ( spoint_eq ( &p[0], &p[2] ) && spoint_eq ( &p[1], &p[3] ) ); }
bool scircle_eq ( const SCIRCLE * c1 , const SCIRCLE * c2 ) { return ( spoint_eq ( &c1->center , &c2->center ) && FPeq ( c1->radius , c2->radius ) ) ; }
Datum spherepoint_equal(PG_FUNCTION_ARGS) { SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); SPoint *p2 = (SPoint *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(spoint_eq(p1, p2)); }
Datum spherepoly_add_point(PG_FUNCTION_ARGS) { SPOLY * poly = ( SPOLY * ) PG_GETARG_POINTER ( 0 ) ; SPoint * p = ( SPoint * ) PG_GETARG_POINTER ( 1 ) ; int32 size = 0 ; SPOLY * poly_new = NULL; if ( p == NULL ){ PG_RETURN_POINTER ( poly ); } if ( poly == NULL ){ size = offsetof(SPOLY, p[0]) + sizeof(SPoint) ; poly = ( SPOLY * ) MALLOC ( size ); memcpy( (void*) &poly->p[0] , (void*) p, sizeof(SPoint) ); #if PG_VERSION_NUM < 80300 poly->size = size; #else SET_VARSIZE(poly, size); #endif poly->npts = 1; PG_RETURN_POINTER ( poly ); } poly = PG_GETARG_SPOLY( 0 ); // skip if equal if ( spoint_eq (p, &poly->p[ poly->npts - 1 ]) ){ PG_RETURN_POINTER ( poly ); } // Skip if distance is equal 180deg if ( FPeq ( spoint_dist ( p, &poly->p[ poly->npts - 1 ]) , PI ) ) { elog ( NOTICE , "spoly(spoint): Skip point, distance of previous point is 180deg" ); } size = offsetof(SPOLY, p[0]) + sizeof(SPoint) * ( poly->npts + 1 ); poly_new = palloc( size ); memcpy( (void*) poly , (void*) poly_new, VARSIZE(poly) ); poly_new->npts++; #if PG_VERSION_NUM < 80300 poly_new->size = size ; #else SET_VARSIZE( poly_new, size ); #endif memcpy( (void*) &poly_new->p[poly->npts] , (void*) p, sizeof(SPoint) ); PG_RETURN_POINTER ( poly_new ); }
bool spoly_contains_point ( const SPOLY * pg , const SPoint * sp ) { static int32 i; static SLine sl; bool res = FALSE ; static float8 scp; static Vector3D vc, vp; // First check, if point is outside polygon (behind) spherepoly_center ( &vc , pg ); spoint_vector3d ( &vp , sp ); scp = vector3d_scalar ( &vp , &vc ); if ( FPle ( scp, 0.0 ) ){ return false; } // Check whether point is edge for ( i=0; i<pg->npts; i++ ){ if ( spoint_eq ( &pg->p[i] , sp ) ){ return TRUE; } } // Check whether point is on a line segment for ( i=0; i<pg->npts; i++ ){ spoly_segment ( &sl , pg , i ); if ( spoint_at_sline( sp, &sl ) ){ return TRUE; } } do { SEuler se, te; SPoint p , lp[2]; bool a1, a2, eqa ; int32 cntr = 0; SPOLY * tmp = ( SPOLY * ) MALLOC ( VARSIZE(pg) ); /* Make a transformation, so point is (0,0) */ se.phi_a = EULER_AXIS_Z ; se.theta_a = EULER_AXIS_X ; se.psi_a = EULER_AXIS_Z ; se.phi = PIH - sp->lng ; se.theta = - sp->lat ; se.psi = -PIH ; euler_spoly_trans ( tmp , pg , &se ); p.lng = 0.0; p.lat = 0.0; // Check, whether an edge is on equator. // If yes, rotate randomized around 0,0 cntr = 0; do { eqa = FALSE; for ( i=0; i<pg->npts; i++ ){ if ( FPzero(tmp->p[i].lat) ){ if ( FPeq( cos(tmp->p[i].lng) , -1.0 ) ){ return false; } else { eqa = TRUE; break; } } } if ( eqa ){ SPOLY * ttt = ( SPOLY * ) MALLOC ( VARSIZE(pg) ); srand( cntr ); se.phi_a = se.theta_a = se.psi_a = EULER_AXIS_X ; se.phi = ( (double) rand() / RAND_MAX ) * PID ; se.theta = 0.0 ; se.psi = 0.0 ; euler_spoly_trans ( ttt , tmp , &se ); memcpy ( (void*) tmp, (void*) ttt, VARSIZE(pg) ); FREE(ttt); } if ( cntr>10000 ){ elog(WARNING ,"Bug found in spoly_contains_point"); elog(ERROR ,"Please report it to pg_sphere team!"); return false; } cntr++; } while ( eqa ); // Count line segment crossing "equator" cntr = 0; for ( i=0; i<pg->npts; i++ ){ // create a single line from segment spoly_segment ( &sl , tmp , i ); sline_begin ( &lp[0], &sl ); sline_end ( &lp[1], &sl ); a1 = ( FPgt(lp[0].lat,0.0) && FPlt(lp[1].lat,0.0) ); a2 = ( FPlt(lp[0].lat,0.0) && FPgt(lp[1].lat,0.0) ); if ( a1 || a2 ){ // if crossing sphereline_to_euler_inv ( &te, &sl ); if ( a2 ){ // crossing ascending p.lng = PID - te.phi; } else { p.lng = PI - te.phi; } spoint_check ( &p ); if ( p.lng < PI ){ // crossing between 0 and 180 deg cntr++; } } } FREE ( tmp ); if ( cntr % 2 ){ res = TRUE; } } while (0); return res; }
/*! \brief Converts an array of spherical points to SPOLY \param arr pointer to array of spherical points \param nelem count of elements \return pointer to created spherical polygon */ static SPOLY * spherepoly_from_array ( SPoint * arr, int32 nelem ) { SPOLY * poly = NULL; if ( nelem < 3 ){ elog ( ERROR , "spherepoly_from_array: more than two points needed" ); return NULL; } else { static int32 i; static float8 scheck ; int32 size; for ( i=0; i<nelem ; i++ ){ spoint_check ( &arr[i] ); } // check duplicate points i = 0; while ( i<(nelem-1) ){ if ( nelem < 3 ) break; if ( spoint_eq (&arr[i],&arr[i+1]) ){ if ( i<(nelem-2) ){ memmove ((void*) &arr[i+1], (void*) &arr[i+2], (nelem-i-2) * sizeof( SPoint ) ); } nelem--; continue; } i++; } if ( spoint_eq (&arr[0],&arr[nelem-1]) ){ nelem--; } if ( nelem < 3 ){ elog ( ERROR , "spherepoly_from_array: more than two points needed" ); return NULL; } size = offsetof(SPOLY, p[0]) + sizeof(SPoint) * nelem; poly = (SPOLY *) MALLOC ( size ) ; SET_VARSIZE(poly, size); poly->npts = nelem; for ( i=0; i<nelem ; i++ ){ if ( i==0 ){ scheck = spoint_dist ( &arr[nelem-1], &arr[0] ); } else { scheck = spoint_dist ( &arr[i-1] , &arr[i] ); } if (FPeq(scheck,PI)){ elog ( ERROR , "spherepoly_from_array: a polygon segment length must be not equal 180 degrees." ); return NULL; } memcpy( (void*) &poly->p[i], (void*) &arr[i], sizeof( SPoint ) ); } } if ( ! spherepoly_check ( poly ) ){ elog ( ERROR , "spherepoly_from_array: a line segment overlaps or polygon too large" ); FREE ( poly ) ; return NULL; } return ( poly ); }