/*********************************************************************** * GNU Lesser General Public License * * This file is part of the GFDL Flexible Modeling System (FMS). * * FMS is free software: you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * FMS is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FMS. If not, see . **********************************************************************/ #include #include #include #include #ifdef use_libMPI #include #endif #include "mosaic_util.h" #include "constant.h" #define HPI (0.5*M_PI) #define TPI (2.0*M_PI) #define TOLORENCE (1.e-6) #define EPSLN8 (1.e-8) #define EPSLN10 (1.e-10) #define EPSLN15 (1.e-15) #define EPSLN30 (1.e-30) /*********************************************************** void error_handler(char *str) error handler: will print out error message and then abort ***********************************************************/ int reproduce_siena = 0; void set_reproduce_siena_true(void) { reproduce_siena = 1; } #ifndef __AIX void set_reproduce_siena_true_(void) { reproduce_siena = 1; } #endif void error_handler(const char *msg) { fprintf(stderr, "FATAL Error: %s\n", msg ); #ifdef use_libMPI MPI_Abort(MPI_COMM_WORLD, -1); #else exit(1); #endif } /* error_handler */ /********************************************************************* int nearest_index(double value, const double *array, int ia) return index of nearest data point within "array" corresponding to "value". if "value" is outside the domain of "array" then nearest_index = 0 or = size(array)-1 depending on whether array(0) or array(ia-1) is closest to "value" Arguments: value: arbitrary data...same units as elements in "array" array: array of data points (must be monotonically increasing) ia : size of array. ********************************************************************/ int nearest_index(double value, const double *array, int ia) { int index, i; int keep_going; for(i=1; i array[ia-1]) index = ia-1; else { i=0; keep_going = 1; while (i < ia && keep_going) { i = i+1; if (value <= array[i]) { index = i; if (array[i]-value > value-array[i-1]) index = i-1; keep_going = 0; } } } return index; } /******************************************************************/ void tokenize(const char * const string, const char *tokens, unsigned int varlen, unsigned int maxvar, char * pstring, unsigned int * const nstr) { size_t i, j, nvar, len, ntoken; int found, n; nvar = 0; j = 0; len = strlen(string); ntoken = strlen(tokens); /* here we use the fact that C array [][] is contiguous in memory */ if(string[0] == 0)error_handler("Error from tokenize: to-be-parsed string is empty"); for(i = 0; i < len; i ++){ if(string[i] != ' ' && string[i] != '\t'){ found = 0; for(n=0; n= maxvar) error_handler("Error from tokenize: number of variables exceeds limit"); } } else { *(pstring + nvar*varlen + j++) = string[i]; if(j >= varlen ) error_handler("error from tokenize: variable name length exceeds limit during tokenization"); } } } *(pstring + nvar*varlen + j) = 0; *nstr = ++nvar; } /******************************************************************************* double maxval_double(int size, double *data) get the maximum value of double array *******************************************************************************/ double maxval_double(int size, const double *data) { int n; double maxval; maxval = data[0]; for(n=1; n maxval ) maxval = data[n]; } return maxval; } /* maxval_double */ /******************************************************************************* double minval_double(int size, double *data) get the minimum value of double array *******************************************************************************/ double minval_double(int size, const double *data) { int n; double minval; minval = data[0]; for(n=1; n M_PI) dx = dx - 2.0*M_PI; if(dx < -M_PI) dx = dx + 2.0*M_PI; return (dx*(sin(ur_lat)-sin(ll_lat))*RADIUS*RADIUS ) ; } /* box_area */ /*------------------------------------------------------------------------------ double poly_area(const x[], const y[], int n) obtains area of input polygon by line integrating -sin(lat)d(lon) Vertex coordinates must be in degrees. Vertices must be listed counter-clockwise around polygon. grid is in radians. ----------------------------------------------------------------------------*/ double poly_area_dimensionless(const double x[], const double y[], int n) { double area = 0.0; int i; for (i=0;i M_PI) dx = dx - 2.0*M_PI; if(dx < -M_PI) dx = dx + 2.0*M_PI; if (dx==0.0) continue; if ( fabs(lat1-lat2) < SMALL_VALUE) /* cheap area calculation along latitude */ area -= dx*sin(0.5*(lat1+lat2)); else { if(reproduce_siena) { area += dx*(cos(lat1)-cos(lat2))/(lat1-lat2); } else { dy = 0.5*(lat1-lat2); dat = sin(dy)/dy; area -= dx*sin(0.5*(lat1+lat2))*dat; } } } if(area < 0) return (-area/(4*M_PI)); else return (area/(4*M_PI)); } /* poly_area */ double poly_area(const double x[], const double y[], int n) { double area = 0.0; int i; for (i=0;i M_PI) dx = dx - 2.0*M_PI; if(dx < -M_PI) dx = dx + 2.0*M_PI; if (dx==0.0) continue; if ( fabs(lat1-lat2) < SMALL_VALUE) /* cheap area calculation along latitude */ area -= dx*sin(0.5*(lat1+lat2)); else { if(reproduce_siena) { area += dx*(cos(lat1)-cos(lat2))/(lat1-lat2); } else { dy = 0.5*(lat1-lat2); dat = sin(dy)/dy; area -= dx*sin(0.5*(lat1+lat2))*dat; } } } if(area < 0) return -area*RADIUS*RADIUS; else return area*RADIUS*RADIUS; } /* poly_area */ double poly_area_no_adjust(const double x[], const double y[], int n) { double area = 0.0; int i; for (i=0;i=n_ins;i--) { x[i+1] = x[i]; y[i+1] = y[i]; } x[n_ins] = lon_in; y[n_ins] = lat_in; return (n+1); } /* insert_vtx */ void v_print(double x[], double y[], int n) { int i; for (i=0;i=HPI-TOLORENCE) pole = 1; if (0&&pole) { printf("fixing pole cell\n"); v_print(x, y, nn); printf("---------"); } /* all pole points must be paired */ for (i=0;i=HPI-TOLORENCE) { int im=(i+nn-1)%nn, ip=(i+1)%nn; if (y[im]==y[i] && y[ip]==y[i]) { nn = delete_vtx(x, y, nn, i); i--; } else if (y[im]!=y[i] && y[ip]!=y[i]) { nn = insert_vtx(x, y, nn, i, x[i], y[i]); i++; } } /* first of pole pair has longitude of previous vertex */ /* second of pole pair has longitude of subsequent vertex */ for (i=0;i=HPI-TOLORENCE) { int im=(i+nn-1)%nn, ip=(i+1)%nn; if (y[im]!=y[i]){ x[i] = x[im]; } if (y[ip]!=y[i]){ x[i] = x[ip]; } } if (nn){ x_sum = x[0]; } else{ return(0); } for (i=1;i M_PI) dx_ = dx_ - TPI; x_sum += (x[i] = x[i-1] + dx_); } dx = (x_sum/nn)-tlon; if (dx < -M_PI){ for (i=0;i M_PI){ for (i=0;i angle \ \ p2 -----------------------------------------------------------------------------*/ double spherical_angle(const double *v1, const double *v2, const double *v3) { double angle; #ifdef NO_QUAD_PRECISION double px, py, pz, qx, qy, qz, ddd; #ifndef SQRT_ #define SQRT_ sqrt #else #error "SQRT_ Previously Defined" #endif /* SQRT_ */ #ifndef ABS_ #define ABS_ fabsl #else #error "ABS_ Previously Defined" #endif /* ABS_ */ #else long double px, py, pz, qx, qy, qz, ddd; #ifndef SQRT_ #define SQRT_ sqrtl #else #error "SQRT_ Previously Defined" #endif /* SQRT_ */ #ifndef ABS_ #define ABS_ fabs #else #error "ABS_ Previously Defined" #endif /* ABS_ */ #endif /* vector product between v1 and v2 */ px = v1[1]*v2[2] - v1[2]*v2[1]; py = v1[2]*v2[0] - v1[0]*v2[2]; pz = v1[0]*v2[1] - v1[1]*v2[0]; /* vector product between v1 and v3 */ qx = v1[1]*v3[2] - v1[2]*v3[1]; qy = v1[2]*v3[0] - v1[0]*v3[2]; qz = v1[0]*v3[1] - v1[1]*v3[0]; ddd = (px*px+py*py+pz*pz)*(qx*qx+qy*qy+qz*qz); if ( ddd <= 0.0 ) angle = 0. ; else { ddd = (px*qx+py*qy+pz*qz) / SQRT_(ddd); if( ABS_(ddd-1) < EPSLN30 ) ddd = 1; if( ABS_(ddd+1) < EPSLN30 ) ddd = -1; if ( ddd>1. || ddd<-1. ) { /*FIX (lmh) to correctly handle co-linear points (angle near pi or 0) */ if (ddd < 0.) angle = M_PI; else angle = 0.; } else angle = ((double)acosl( ddd )); } return angle; } /* spherical_angle */ /*------------------------------------------------------------------------------ double spherical_excess_area(p_lL, p_uL, p_lR, p_uR) get the surface area of a cell defined as a quadrilateral on the sphere. Area is computed as the spherical excess [area units are m^2] ----------------------------------------------------------------------------*/ double spherical_excess_area(const double* p_ll, const double* p_ul, const double* p_lr, const double* p_ur, double radius) { double area, ang1, ang2, ang3, ang4; double v1[3], v2[3], v3[3]; /* S-W: 1 */ latlon2xyz(1, p_ll, p_ll+1, v1, v1+1, v1+2); latlon2xyz(1, p_lr, p_lr+1, v2, v2+1, v2+2); latlon2xyz(1, p_ul, p_ul+1, v3, v3+1, v3+2); ang1 = spherical_angle(v1, v2, v3); /* S-E: 2 */ latlon2xyz(1, p_lr, p_lr+1, v1, v1+1, v1+2); latlon2xyz(1, p_ur, p_ur+1, v2, v2+1, v2+2); latlon2xyz(1, p_ll, p_ll+1, v3, v3+1, v3+2); ang2 = spherical_angle(v1, v2, v3); /* N-E: 3 */ latlon2xyz(1, p_ur, p_ur+1, v1, v1+1, v1+2); latlon2xyz(1, p_ul, p_ul+1, v2, v2+1, v2+2); latlon2xyz(1, p_lr, p_lr+1, v3, v3+1, v3+2); ang3 = spherical_angle(v1, v2, v3); /* N-W: 4 */ latlon2xyz(1, p_ul, p_ul+1, v1, v1+1, v1+2); latlon2xyz(1, p_ur, p_ur+1, v2, v2+1, v2+2); latlon2xyz(1, p_ll, p_ll+1, v3, v3+1, v3+2); ang4 = spherical_angle(v1, v2, v3); area = (ang1 + ang2 + ang3 + ang4 - 2.*M_PI) * radius* radius; return area; } /* spherical_excess_area */ /*---------------------------------------------------------------------- void vect_cross(e, p1, p2) Perform cross products of 3D vectors: e = P1 X P2 -------------------------------------------------------------------*/ void vect_cross(const double *p1, const double *p2, double *e ) { e[0] = p1[1]*p2[2] - p1[2]*p2[1]; e[1] = p1[2]*p2[0] - p1[0]*p2[2]; e[2] = p1[0]*p2[1] - p1[1]*p2[0]; } /* vect_cross */ /*---------------------------------------------------------------------- double* vect_cross(p1, p2) return cross products of 3D vectors: = P1 X P2 -------------------------------------------------------------------*/ double dot(const double *p1, const double *p2) { return( p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2] ); } double metric(const double *p) { return (sqrt(p[0]*p[0] + p[1]*p[1]+p[2]*p[2]) ); } /* ---------------------------------------------------------------- make a unit vector --------------------------------------------------------------*/ void normalize_vect(double *e) { double pdot; int k; pdot = e[0]*e[0] + e[1] * e[1] + e[2] * e[2]; pdot = sqrt( pdot ); for(k=0; k<3; k++) e[k] /= pdot; } /*------------------------------------------------------------------ void unit_vect_latlon(int size, lon, lat, vlon, vlat) calculate unit vector for latlon in cartesian coordinates ---------------------------------------------------------------------*/ void unit_vect_latlon(int size, const double *lon, const double *lat, double *vlon, double *vlat) { double sin_lon, cos_lon, sin_lat, cos_lat; int n; for(n=0; n MAXNODELIST) error_handler("getNext: curListPos >= MAXNODELIST"); return (temp); } void initNode(struct Node *node) { node->x = 0; node->y = 0; node->z = 0; node->u = 0; node->intersect = 0; node->inbound = 0; node->isInside = 0; node->Next = NULL; node->initialized=0; } void addEnd(struct Node *list, double x, double y, double z, int intersect, double u, int inbound, int inside) { struct Node *temp=NULL; if(list == NULL) error_handler("addEnd: list is NULL"); if(list->initialized) { /* (x,y,z) might already in the list when intersect is true and u=0 or 1 */ temp = list; while (temp) { if(samePoint(temp->x, temp->y, temp->z, x, y, z)) return; temp=temp->Next; } temp = list; while(temp->Next) temp=temp->Next; /* Append at the end of the list. */ temp->Next = getNext(); temp = temp->Next; } else { temp = list; } temp->x = x; temp->y = y; temp->z = z; temp->u = u; temp->intersect = intersect; temp->inbound = inbound; temp->initialized=1; temp->isInside = inside; } /* return 1 if the point (x,y,z) is added in the list, return 0 if it is already in the list */ int addIntersect(struct Node *list, double x, double y, double z, int intersect, double u1, double u2, int inbound, int is1, int ie1, int is2, int ie2) { double u1_cur, u2_cur; int i1_cur, i2_cur; struct Node *temp=NULL; if(list == NULL) error_handler("addEnd: list is NULL"); /* first check to make sure this point is not in the list */ u1_cur = u1; i1_cur = is1; u2_cur = u2; i2_cur = is2; if(u1_cur == 1) { u1_cur = 0; i1_cur = ie1; } if(u2_cur == 1) { u2_cur = 0; i2_cur = ie2; } if(list->initialized) { temp = list; while(temp) { if( temp->u == u1_cur && temp->subj_index == i1_cur) return 0; if( temp->u_clip == u2_cur && temp->clip_index == i2_cur) return 0; if( !temp->Next ) break; temp=temp->Next; } /* Append at the end of the list. */ temp->Next = getNext(); temp = temp->Next; } else { temp = list; } temp->x = x; temp->y = y; temp->z = z; temp->intersect = intersect; temp->inbound = inbound; temp->initialized=1; temp->isInside = 0; temp->u = u1_cur; temp->subj_index = i1_cur; temp->u_clip = u2_cur; temp->clip_index = i2_cur; return 1; } int length(struct Node *list) { struct Node *cur_ptr=NULL; int count=0; cur_ptr=list; while(cur_ptr) { if(cur_ptr->initialized ==0) break; cur_ptr=cur_ptr->Next; count++; } return(count); } /* two points are the same if there are close enough */ int samePoint(double x1, double y1, double z1, double x2, double y2, double z2) { if( fabs(x1-x2) > EPSLN10 || fabs(y1-y2) > EPSLN10 || fabs(z1-z2) > EPSLN10 ) return 0; else return 1; } int sameNode(struct Node node1, struct Node node2) { if( node1.x == node2.x && node1.y == node2.y && node1.z==node2.z ) return 1; else return 0; } void addNode(struct Node *list, struct Node inNode) { addEnd(list, inNode.x, inNode.y, inNode.z, inNode.intersect, inNode.u, inNode.inbound, inNode.isInside); } struct Node *getNode(struct Node *list, struct Node inNode) { struct Node *thisNode=NULL; struct Node *temp=NULL; temp = list; while( temp ) { if( sameNode( *temp, inNode ) ) { thisNode = temp; temp = NULL; break; } temp = temp->Next; } return thisNode; } struct Node *getNextNode(struct Node *list) { return list->Next; } void copyNode(struct Node *node_out, struct Node node_in) { node_out->x = node_in.x; node_out->y = node_in.y; node_out->z = node_in.z; node_out->u = node_in.u; node_out->intersect = node_in.intersect; node_out->inbound = node_in.inbound; node_out->Next = NULL; node_out->initialized = node_in.initialized; node_out->isInside = node_in.isInside; } void printNode(struct Node *list, char *str) { struct Node *temp; if(list == NULL) error_handler("printNode: list is NULL"); if(str) printf(" %s \n", str); temp = list; while(temp) { if(temp->initialized ==0) break; printf(" (x, y, z, interset, inbound, isInside) = (%19.15f,%19.15f,%19.15f,%d,%d,%d)\n", temp->x, temp->y, temp->z, temp->intersect, temp->inbound, temp->isInside); temp = temp->Next; } printf("\n"); } int intersectInList(struct Node *list, double x, double y, double z) { struct Node *temp; int found=0; temp = list; found = 0; while ( temp ) { if( temp->x == x && temp->y == y && temp->z == z ) { found = 1; break; } temp=temp->Next; } if (!found) error_handler("intersectInList: point (x,y,z) is not found in the list"); if( temp->intersect == 2 ) return 1; else return 0; } /* The following insert a intersection after non-intersect point (x2,y2,z2), if the point after (x2,y2,z2) is an intersection, if u is greater than the u value of the intersection, insert after, otherwise insert before */ void insertIntersect(struct Node *list, double x, double y, double z, double u1, double u2, int inbound, double x2, double y2, double z2) { struct Node *temp1=NULL, *temp2=NULL; struct Node *temp; double u_cur; int found=0; temp1 = list; found = 0; while ( temp1 ) { if( temp1->x == x2 && temp1->y == y2 && temp1->z == z2 ) { found = 1; break; } temp1=temp1->Next; } if (!found) error_handler("inserAfter: point (x,y,z) is not found in the list"); /* when u = 0 or u = 1, set the grid point to be the intersection point to solve truncation error isuse */ u_cur = u1; if(u1 == 1) { u_cur = 0; temp1 = temp1->Next; if(!temp1) temp1 = list; } if(u_cur==0) { temp1->intersect = 2; temp1->isInside = 1; temp1->u = u_cur; temp1->x = x; temp1->y = y; temp1->z = z; return; } /* when u2 != 0 and u2 !=1, can decide if one end of the point is outside depending on inbound value */ if(u2 != 0 && u2 != 1) { if(inbound == 1) { /* goes outside, then temp1->Next is an outside point */ /* find the next non-intersect point */ temp2 = temp1->Next; if(!temp2) temp2 = list; while(temp2->intersect) { temp2=temp2->Next; if(!temp2) temp2 = list; } temp2->isInside = 0; } else if(inbound ==2) { /* goes inside, then temp1 is an outside point */ temp1->isInside = 0; } } temp2 = temp1->Next; while ( temp2 ) { if( temp2->intersect == 1 ) { if( temp2->u > u_cur ) { break; } } else break; temp1 = temp2; temp2 = temp2->Next; } /* assign value */ temp = getNext(); temp->x = x; temp->y = y; temp->z = z; temp->u = u_cur; temp->intersect = 1; temp->inbound = inbound; temp->isInside = 1; temp->initialized = 1; temp1->Next = temp; temp->Next = temp2; } double gridArea(struct Node *grid) { double x[20], y[20], z[20]; struct Node *temp=NULL; double area; int n; temp = grid; n = 0; while( temp ) { x[n] = temp->x; y[n] = temp->y; z[n] = temp->z; n++; temp = temp->Next; } area = great_circle_area(n, x, y, z); return area; } int isIntersect(struct Node node) { return node.intersect; } int getInbound( struct Node node ) { return node.inbound; } struct Node *getLast(struct Node *list) { struct Node *temp1; temp1 = list; if( temp1 ) { while( temp1->Next ) { temp1 = temp1->Next; } } return temp1; } int getFirstInbound( struct Node *list, struct Node *nodeOut) { struct Node *temp=NULL; temp=list; while(temp) { if( temp->inbound == 2 ) { copyNode(nodeOut, *temp); return 1; } temp=temp->Next; } return 0; } void getCoordinate(struct Node node, double *x, double *y, double *z) { *x = node.x; *y = node.y; *z = node.z; } void getCoordinates(struct Node *node, double *p) { p[0] = node->x; p[1] = node->y; p[2] = node->z; } void setCoordinate(struct Node *node, double x, double y, double z) { node->x = x; node->y = y; node->z = z; } /* set inbound value for the points in interList that has inbound =0, this will also set some inbound value of the points in list1 */ void setInbound(struct Node *interList, struct Node *list) { struct Node *temp1=NULL, *temp=NULL; struct Node *temp1_prev=NULL, *temp1_next=NULL; int prev_is_inside, next_is_inside; /* for each point in interList, search through list to decide the inbound value the interList point */ /* For each inbound point, the prev node should be outside and the next is inside. */ if(length(interList) == 0) return; temp = interList; while(temp) { if( !temp->inbound) { /* search in grid1 to find the prev and next point of temp, when prev point is outside and next point is inside inbound = 2, else inbound = 1*/ temp1 = list; temp1_prev = NULL; temp1_next = NULL; while(temp1) { if(sameNode(*temp1, *temp)) { if(!temp1_prev) temp1_prev = getLast(list); temp1_next = temp1->Next; if(!temp1_next) temp1_next = list; break; } temp1_prev = temp1; temp1 = temp1->Next; } if(!temp1_next) error_handler("Error from create_xgrid.c: temp is not in list1"); if( temp1_prev->isInside == 0 && temp1_next->isInside == 1) temp->inbound = 2; /* go inside */ else temp->inbound = 1; } temp=temp->Next; } } int isInside(struct Node *node) { if(node->isInside == -1) error_handler("Error from mosaic_util.c: node->isInside is not set"); return(node->isInside); } /* #define debug_test_create_xgrid */ /* check if node is inside polygon list or not */ int insidePolygon( struct Node *node, struct Node *list) { int is_inside; double pnt0[3], pnt1[3], pnt2[3]; double anglesum; struct Node *p1=NULL, *p2=NULL; anglesum = 0; pnt0[0] = node->x; pnt0[1] = node->y; pnt0[2] = node->z; p1 = list; p2 = list->Next; is_inside = 0; while(p1) { pnt1[0] = p1->x; pnt1[1] = p1->y; pnt1[2] = p1->z; pnt2[0] = p2->x; pnt2[1] = p2->y; pnt2[2] = p2->z; if( samePoint(pnt0[0], pnt0[1], pnt0[2], pnt1[0], pnt1[1], pnt1[2]) ){ return 1; } anglesum += spherical_angle(pnt0, pnt2, pnt1); p1 = p1->Next; p2 = p2->Next; if(p2==NULL){ p2 = list; } } if( fabs(anglesum - 2*M_PI) < EPSLN8 ){ is_inside = 1; } else{ is_inside = 0; } #ifdef debug_test_create_xgrid printf("anglesum-2PI is %19.15f, is_inside = %d\n", anglesum- 2*M_PI, is_inside); #endif return is_inside; } int inside_a_polygon(double *lon1, double *lat1, int *npts, double *lon2, double *lat2) { double x2[20], y2[20], z2[20]; double x1, y1, z1; double min_x2, max_x2, min_y2, max_y2, min_z2, max_z2; int isinside, i; struct Node *grid1=NULL, *grid2=NULL; /* first convert to cartesian grid */ latlon2xyz(*npts, lon2, lat2, x2, y2, z2); latlon2xyz(1, lon1, lat1, &x1, &y1, &z1); max_x2 = maxval_double(*npts, x2); if(x1 >= max_x2+RANGE_CHECK_CRITERIA) return 0; min_x2 = minval_double(*npts, x2); if(min_x2 >= x1+RANGE_CHECK_CRITERIA) return 0; max_y2 = maxval_double(*npts, y2); if(y1 >= max_y2+RANGE_CHECK_CRITERIA) return 0; min_y2 = minval_double(*npts, y2); if(min_y2 >= y1+RANGE_CHECK_CRITERIA) return 0; max_z2 = maxval_double(*npts, z2); if(z1 >= max_z2+RANGE_CHECK_CRITERIA) return 0; min_z2 = minval_double(*npts, z2); if(min_z2 >= z1+RANGE_CHECK_CRITERIA) return 0; /* add x2,y2,z2 to a Node */ rewindList(); grid1 = getNext(); grid2 = getNext(); addEnd(grid1, x1, y1, z1, 0, 0, 0, -1); for(i=0; i<*npts; i++) addEnd(grid2, x2[i], y2[i], z2[i], 0, 0, 0, -1); isinside = insidePolygon(grid1, grid2); return isinside; } #ifndef __AIX int inside_a_polygon_(double *lon1, double *lat1, int *npts, double *lon2, double *lat2) { int isinside; isinside = inside_a_polygon(lon1, lat1, npts, lon2, lat2); return isinside; } #endif