wxPoint2DDouble ViewPort::GetDoublePixFromLL( double lat, double lon ) { double easting = 0; double northing = 0; double xlon = lon; /* Make sure lon and lon0 are same phase */ if( xlon * clon < 0. ) { if( xlon < 0. ) xlon += 360.; else xlon -= 360.; } if( fabs( xlon - clon ) > 180. ) { if( xlon > clon ) xlon -= 360.; else xlon += 360.; } // update cache of trig functions used for projections if(clat != lat0_cache) { lat0_cache = clat; switch( m_projection_type ) { case PROJECTION_MERCATOR: cache0 = toSMcache_y30(clat); break; case PROJECTION_POLAR: cache0 = toPOLARcache_e(clat); break; case PROJECTION_ORTHOGRAPHIC: case PROJECTION_STEREOGRAPHIC: case PROJECTION_GNOMONIC: cache_phi0(clat, &cache0, &cache1); break; } } switch( m_projection_type ) { case PROJECTION_MERCATOR: #if 0 toSM( lat, xlon, clat, clon, &easting, &northing ); #else toSMcache( lat, xlon, cache0, clon, &easting, &northing ); #endif break; case PROJECTION_TRANSVERSE_MERCATOR: // We calculate northings as referenced to the equator // And eastings as though the projection point is midscreen. double tmeasting, tmnorthing; double tmceasting, tmcnorthing; toTM( clat, clon, 0., clon, &tmceasting, &tmcnorthing ); toTM( lat, xlon, 0., clon, &tmeasting, &tmnorthing ); northing = tmnorthing - tmcnorthing; easting = tmeasting - tmceasting; break; case PROJECTION_POLYCONIC: // We calculate northings as referenced to the equator // And eastings as though the projection point is midscreen. double pceasting, pcnorthing; toPOLY( clat, clon, 0., clon, &pceasting, &pcnorthing ); double peasting, pnorthing; toPOLY( lat, xlon, 0., clon, &peasting, &pnorthing ); easting = peasting; northing = pnorthing - pcnorthing; break; case PROJECTION_ORTHOGRAPHIC: toORTHO( lat, xlon, cache0, cache1, clon, &easting, &northing ); break; case PROJECTION_POLAR: toPOLAR( lat, xlon, cache0, clat, clon, &easting, &northing ); break; case PROJECTION_STEREOGRAPHIC: toSTEREO( lat, xlon, cache0, cache1, clon, &easting, &northing ); break; case PROJECTION_GNOMONIC: toGNO( lat, xlon, cache0, cache1, clon, &easting, &northing ); break; case PROJECTION_EQUIRECTANGULAR: toEQUIRECT( lat, xlon, clat, clon, &easting, &northing ); break; default: printf("unhandled projection\n"); } if( !wxFinite(easting) || !wxFinite(northing) ) return wxPoint2DDouble( easting, northing ); double epix = easting * view_scale_ppm; double npix = northing * view_scale_ppm; double dxr = epix; double dyr = npix; // Apply VP Rotation double angle = rotation; if( angle ) { dxr = epix * cos( angle ) + npix * sin( angle ); dyr = npix * cos( angle ) - epix * sin( angle ); } return wxPoint2DDouble(( pix_width / 2.0 ) + dxr, ( pix_height / 2.0 ) - dyr); }
bool ODSelect::IsSegmentSelected( float a, float b, float c, float d, float slat, float slon ) { double adder = 0.; if( ( c * d ) < 0. ) { // Arrange for points to be increasing longitude, c to d double dist, brg; DistanceBearingMercator_Plugin( a, c, b, d, &brg, &dist ); if( brg < 180. ) // swap points? { double tmp; tmp = c; c = d; d = tmp; tmp = a; a = b; b = tmp; } if( d < 0. ) // idl? { d += 360.; if( slon < 0. ) adder = 360.; } } // As a course test, use segment bounding box test if( ( slat >= ( fmin ( a,b ) - selectRadius ) ) && ( slat <= ( fmax ( a,b ) + selectRadius ) ) && ( ( slon + adder ) >= ( fmin ( c,d ) - selectRadius ) ) && ( ( slon + adder ) <= ( fmax ( c,d ) + selectRadius ) ) ) { // Use vectors to do hit test.... ODvector2D va, vb, vn; // Assuming a Mercator projection double ap, cp; toSM( a, c, 0., 0., &cp, &ap ); double bp, dp; toSM( b, d, 0., 0., &dp, &bp ); double slatp, slonp; toSM( slat, slon + adder, 0., 0., &slonp, &slatp ); va.x = slonp - cp; va.y = slatp - ap; vb.x = dp - cp; vb.y = bp - ap; double delta = vGetLengthOfNormal( &va, &vb, &vn ); if( fabs( delta ) < ( selectRadius * 1852 * 60 ) ) return true; } return false; }
// Build PolyTessGeo Object from OGR Polygon // Using OpenGL/GLU tesselator int PolyTessGeo::PolyTessGeoGL(OGRPolygon *poly, bool bSENC_SM, double ref_lat, double ref_lon) { #ifdef ocpnUSE_GL int iir, ip; int *cntr; GLdouble *geoPt; wxString sout; wxString sout1; wxString stemp; // Make a quick sanity check of the polygon coherence bool b_ok = true; OGRLineString *tls = poly->getExteriorRing(); if(!tls) { b_ok = false; } else { int tnpta = poly->getExteriorRing()->getNumPoints(); if(tnpta < 3 ) b_ok = false; } for( iir=0 ; iir < poly->getNumInteriorRings() ; iir++) { int tnptr = poly->getInteriorRing(iir)->getNumPoints(); if( tnptr < 3 ) b_ok = false; } if( !b_ok ) return ERROR_BAD_OGRPOLY; #ifdef __WXMSW__ // If using the OpenGL dlls provided with Windows, // load the dll and establish addresses of the entry points needed #ifdef USE_GLU_DLL if(!s_glu_dll_ready) { s_hGLU_DLL = LoadLibrary("glu32.dll"); if (s_hGLU_DLL != NULL) { s_lpfnTessProperty = (LPFNDLLTESSPROPERTY)GetProcAddress(s_hGLU_DLL,"gluTessProperty"); s_lpfnNewTess = (LPFNDLLNEWTESS)GetProcAddress(s_hGLU_DLL, "gluNewTess"); s_lpfnTessBeginContour = (LPFNDLLTESSBEGINCONTOUR)GetProcAddress(s_hGLU_DLL, "gluTessBeginContour"); s_lpfnTessEndContour = (LPFNDLLTESSENDCONTOUR)GetProcAddress(s_hGLU_DLL, "gluTessEndContour"); s_lpfnTessBeginPolygon = (LPFNDLLTESSBEGINPOLYGON)GetProcAddress(s_hGLU_DLL, "gluTessBeginPolygon"); s_lpfnTessEndPolygon = (LPFNDLLTESSENDPOLYGON)GetProcAddress(s_hGLU_DLL, "gluTessEndPolygon"); s_lpfnDeleteTess = (LPFNDLLDELETETESS)GetProcAddress(s_hGLU_DLL, "gluDeleteTess"); s_lpfnTessVertex = (LPFNDLLTESSVERTEX)GetProcAddress(s_hGLU_DLL, "gluTessVertex"); s_lpfnTessCallback = (LPFNDLLTESSCALLBACK)GetProcAddress(s_hGLU_DLL, "gluTessCallback"); s_glu_dll_ready = true; } else { return ERROR_NO_DLL; } } #endif #endif // Allocate a work buffer, which will be grown as needed #define NINIT_BUFFER_LEN 10000 s_pwork_buf = (GLdouble *)malloc(NINIT_BUFFER_LEN * 2 * sizeof(GLdouble)); s_buf_len = NINIT_BUFFER_LEN * 2; s_buf_idx = 0; // Create an array to hold pointers to allocated vertices created by "combine" callback, // so that they may be deleted after tesselation. s_pCombineVertexArray = new wxArrayPtrVoid; // Create tesselator GLUtessobj = gluNewTess(); // Register the callbacks gluTessCallback(GLUtessobj, GLU_TESS_BEGIN, (GLvoid (__CALL_CONVENTION *) ())&beginCallback); gluTessCallback(GLUtessobj, GLU_TESS_BEGIN, (GLvoid (__CALL_CONVENTION *) ())&beginCallback); gluTessCallback(GLUtessobj, GLU_TESS_VERTEX, (GLvoid (__CALL_CONVENTION *) ())&vertexCallback); gluTessCallback(GLUtessobj, GLU_TESS_END, (GLvoid (__CALL_CONVENTION *) ())&endCallback); gluTessCallback(GLUtessobj, GLU_TESS_COMBINE, (GLvoid (__CALL_CONVENTION *) ())&combineCallback); // gluTessCallback(GLUtessobj, GLU_TESS_ERROR, (GLvoid (__CALL_CONVENTION *) ())&errorCallback); // glShadeModel(GL_SMOOTH); gluTessProperty(GLUtessobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); // gluTess algorithm internally selects vertically oriented triangle strips and fans. // This orientation is not optimal for conventional memory-mapped raster display shape filling. // We can "fool" the algorithm by interchanging the x and y vertex values passed to gluTessVertex // and then reverting x and y on the resulting vertexCallbacks. // In this implementation, we will explicitely set the preferred orientation. //Set the preferred orientation tess_orient = TESS_HORZ; // prefer horizontal tristrips // PolyGeo BBox as lat/lon OGREnvelope Envelope; poly->getEnvelope(&Envelope); xmin = Envelope.MinX; ymin = Envelope.MinY; xmax = Envelope.MaxX; ymax = Envelope.MaxY; // Get total number of contours m_ncnt = 1; // always exterior ring int nint = poly->getNumInteriorRings(); // interior rings m_ncnt += nint; // Allocate cntr array cntr = (int *)malloc(m_ncnt * sizeof(int)); // Get total number of points(vertices) int npta = poly->getExteriorRing()->getNumPoints(); cntr[0] = npta; npta += 2; // fluff for( iir=0 ; iir < nint ; iir++) { int nptr = poly->getInteriorRing(iir)->getNumPoints(); cntr[iir+1] = nptr; npta += nptr + 2; } // printf("pPoly npta: %d\n", npta); geoPt = (GLdouble *)malloc((npta) * 3 * sizeof(GLdouble)); // vertex array // Grow the work buffer if necessary if((npta * 4) > s_buf_len) { s_pwork_buf = (GLdouble *)realloc(s_pwork_buf, npta * 4 * sizeof(GLdouble)); s_buf_len = npta * 4; } // Define the polygon gluTessBeginPolygon(GLUtessobj, NULL); // Create input structures // Exterior Ring int npte = poly->getExteriorRing()->getNumPoints(); cntr[0] = npte; GLdouble *ppt = geoPt; // Check and account for winding direction of ring bool cw = !(poly->getExteriorRing()->isClockwise() == 0); double x0, y0, x, y; OGRPoint p; if(cw) { poly->getExteriorRing()->getPoint(0, &p); x0 = p.getX(); y0 = p.getY(); } else { poly->getExteriorRing()->getPoint(npte-1, &p); x0 = p.getX(); y0 = p.getY(); } // Transcribe contour to an array of doubles, with duplicates eliminated double *DPbuffer = (double *)malloc(npte * 2 * sizeof(double)); double *DPrun = DPbuffer; int nPoints = npte; for(ip = 0 ; ip < npte ; ip++) { int pidx; if(cw) pidx = npte - ip - 1; else pidx = ip; poly->getExteriorRing()->getPoint(pidx, &p); x = p.getX(); y = p.getY(); if( ((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS))) { GLdouble *ppt_temp = ppt; if(tess_orient == TESS_VERT) { *DPrun++ = x; *DPrun++ = y; } else { *DPrun++ = y; *DPrun++ = x; } x0 = x; y0 = y; } else nPoints--; } if(nPoints > 5 && (m_LOD_meters > .01)){ index_keep.Clear(); index_keep.Add(0); index_keep.Add(nPoints-1); index_keep.Add(1); index_keep.Add(nPoints-2); DouglasPeucker(DPbuffer, 1, nPoints-2, m_LOD_meters/(1852 * 60), &index_keep); // printf("DP Reduction: %d/%d\n", index_keep.GetCount(), nPoints); g_keep += index_keep.GetCount(); g_orig += nPoints; // printf("...................Running: %g\n", (double)g_keep/g_orig); } else { index_keep.Clear(); for(int i = 0 ; i < nPoints ; i++) index_keep.Add(i); } cntr[0] = index_keep.GetCount(); // Mark the keepers by adding a simple constant to X for(unsigned int i=0 ; i < index_keep.GetCount() ; i++){ int k = index_keep.Item(i); DPbuffer[2*k] += 2000.; } // Declare the gluContour and copy the points gluTessBeginContour(GLUtessobj); DPrun = DPbuffer; for(ip = 0 ; ip < nPoints ; ip++) { x = *DPrun++; y = *DPrun++; if(x > 1000.){ GLdouble *ppt_top = ppt; *ppt++ = x-2000; *ppt++ = y; *ppt++ = 0; gluTessVertex( GLUtessobj, ppt_top, ppt_top ) ; } } gluTessEndContour(GLUtessobj); free(DPbuffer); // Now the interior contours for(iir=0 ; iir < nint ; iir++) { gluTessBeginContour(GLUtessobj); int npti = poly->getInteriorRing(iir)->getNumPoints(); // Check and account for winding direction of ring bool cw = !(poly->getInteriorRing(iir)->isClockwise() == 0); if(!cw) { poly->getInteriorRing(iir)->getPoint(0, &p); x0 = p.getX(); y0 = p.getY(); } else { poly->getInteriorRing(iir)->getPoint(npti-1, &p); x0 = p.getX(); y0 = p.getY(); } // Transcribe points to vertex array, in proper order with no duplicates // also, accounting for tess_orient for(int ip = 0 ; ip < npti ; ip++) { OGRPoint p; int pidx; if(!cw) // interior contours must be cw pidx = npti - ip - 1; else pidx = ip; poly->getInteriorRing(iir)->getPoint(pidx, &p); x = p.getX(); y = p.getY(); if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { GLdouble *ppt_temp = ppt; if(tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; } else { *ppt++ = y; *ppt++ = x; } *ppt++ = 0.0; gluTessVertex( GLUtessobj, ppt_temp, ppt_temp ) ; // printf("tess from Poly, internal vertex %d %g %g\n", ip, x, y); } else cntr[iir+1]--; x0 = x; y0 = y; } gluTessEndContour(GLUtessobj); } // Store some SM conversion data in static store, // for callback access s_ref_lat = ref_lat; s_ref_lon = ref_lon; s_bSENC_SM = bSENC_SM; s_bmerc_transform = false; // Ready to kick off the tesselator s_pTPG_Last = NULL; s_pTPG_Head = NULL; s_nvmax = 0; gluTessEndPolygon(GLUtessobj); // here it goes m_nvertex_max = s_nvmax; // record largest vertex count, updates in callback // Tesselation all done, so... // Create the data structures m_ppg_head = new PolyTriGroup; m_ppg_head->m_bSMSENC = s_bSENC_SM; m_ppg_head->nContours = m_ncnt; m_ppg_head->pn_vertex = cntr; // pointer to array of poly vertex counts m_ppg_head->data_type = DATA_TYPE_DOUBLE; // Transcribe the raw geometry buffer // Converting to float as we go, and // allowing for tess_orient // Also, convert to SM if requested // Recalculate the size of the geometry buffer int nptfinal = cntr[0] + 2; for(int i=0 ; i < nint ; i++) nptfinal += cntr[i+1] + 2; // No longer need the full geometry in the SENC, nptfinal = 1; m_nwkb = (nptfinal + 1) * 2 * sizeof(float); m_ppg_head->pgroup_geom = (float *)calloc(sizeof(float), (nptfinal + 1) * 2); float *vro = m_ppg_head->pgroup_geom; ppt = geoPt; float tx,ty; for(ip = 0 ; ip < nptfinal ; ip++) { if(TESS_HORZ == tess_orient) { ty = *ppt++; tx = *ppt++; } else { tx = *ppt++; ty = *ppt++; } if(bSENC_SM) { // Calculate SM from chart common reference point double easting, northing; toSM(ty, tx, ref_lat, ref_lon, &easting, &northing); *vro++ = easting; // x *vro++ = northing; // y } else { *vro++ = tx; // lon *vro++ = ty; // lat } ppt++; // skip z } m_ppg_head->tri_prim_head = s_pTPG_Head; // head of linked list of TriPrims // Convert the Triangle vertex arrays into a single memory allocation of floats // to reduce SENC size and enable efficient access later // First calculate the total byte size int total_byte_size = 2 * sizeof(float); TriPrim *p_tp = m_ppg_head->tri_prim_head; while( p_tp ) { total_byte_size += p_tp->nVert * 2 * sizeof(float); p_tp = p_tp->p_next; // pick up the next in chain } float *vbuf = (float *)malloc(total_byte_size); p_tp = m_ppg_head->tri_prim_head; float *p_run = vbuf; while( p_tp ) { float *pfbuf = p_run; GLdouble *pdouble_buf = (GLdouble *)p_tp->p_vertex; for( int i=0 ; i < p_tp->nVert * 2 ; ++i){ float x = (float)( *((GLdouble *)pdouble_buf) ); pdouble_buf++; *p_run++ = x; } free(p_tp->p_vertex); p_tp->p_vertex = (double *)pfbuf; p_tp = p_tp->p_next; // pick up the next in chain } m_ppg_head->bsingle_alloc = true; m_ppg_head->single_buffer = (unsigned char *)vbuf; m_ppg_head->single_buffer_size = total_byte_size; m_ppg_head->data_type = DATA_TYPE_FLOAT; gluDeleteTess(GLUtessobj); free( s_pwork_buf ); s_pwork_buf = NULL; free (geoPt); // Free up any "Combine" vertices created for(unsigned int i = 0; i < s_pCombineVertexArray->GetCount() ; i++) free (s_pCombineVertexArray->Item(i)); delete s_pCombineVertexArray; m_bOK = true; #endif // #ifdef ocpnUSE_GL return 0; }
wxPoint2DDouble ViewPort::GetDoublePixFromLL( double lat, double lon ) { double easting, northing; double xlon = lon; /* Make sure lon and lon0 are same phase */ if( xlon * clon < 0. ) { if( xlon < 0. ) xlon += 360.; else xlon -= 360.; } if( fabs( xlon - clon ) > 180. ) { if( xlon > clon ) xlon -= 360.; else xlon += 360.; } if( PROJECTION_TRANSVERSE_MERCATOR == m_projection_type ) { // We calculate northings as referenced to the equator // And eastings as though the projection point is midscreen. double tmeasting, tmnorthing; double tmceasting, tmcnorthing; toTM( clat, clon, 0., clon, &tmceasting, &tmcnorthing ); toTM( lat, xlon, 0., clon, &tmeasting, &tmnorthing ); northing = tmnorthing - tmcnorthing; easting = tmeasting - tmceasting; } else if( PROJECTION_POLYCONIC == m_projection_type ) { // We calculate northings as referenced to the equator // And eastings as though the projection point is midscreen. double pceasting, pcnorthing; toPOLY( clat, clon, 0., clon, &pceasting, &pcnorthing ); double peasting, pnorthing; toPOLY( lat, xlon, 0., clon, &peasting, &pnorthing ); easting = peasting; northing = pnorthing - pcnorthing; } else { #if 0 toSM( lat, xlon, clat, clon, &easting, &northing ); #else if(clat != toSM_lat0_cache) { toSM_lat0_cache = clat; toSM_y30_cache = toSMcache_y30(clat); } toSMcache( lat, xlon, toSM_y30_cache, clon, &easting, &northing ); #endif } if( !wxFinite(easting) || !wxFinite(northing) ) return wxPoint( 0, 0 ); double epix = easting * view_scale_ppm; double npix = northing * view_scale_ppm; double dxr = epix; double dyr = npix; // Apply VP Rotation double angle = rotation; if(!g_bskew_comp) angle += skew; if( angle ) { dxr = epix * cos( angle ) + npix * sin( angle ); dyr = npix * cos( angle ) - epix * sin( angle ); } return wxPoint2DDouble(( pix_width / 2 ) + dxr, ( pix_height / 2 ) - dyr); }
void __CALL_CONVENTION endCallback(void) { // Create a TriPrim char buf[40]; if(s_nvcall > s_nvmax) // keep track of largest number of triangle vertices s_nvmax = s_nvcall; switch(s_gltri_type) { case GL_TRIANGLE_FAN: case GL_TRIANGLE_STRIP: case GL_TRIANGLES: { TriPrim *pTPG = new TriPrim; if(NULL == s_pTPG_Last) { s_pTPG_Head = pTPG; s_pTPG_Last = pTPG; } else { s_pTPG_Last->p_next = pTPG; s_pTPG_Last = pTPG; } pTPG->p_next = NULL; pTPG->type = s_gltri_type; pTPG->nVert = s_nvcall; // Calculate bounding box float sxmax = -1000; // this poly BBox float sxmin = 1000; float symax = -90; float symin = 90; GLdouble *pvr = s_pwork_buf; for(int iv=0 ; iv < s_nvcall ; iv++) { GLdouble xd, yd; xd = *pvr++; yd = *pvr++; if(s_bmerc_transform) { double valx = ( xd * s_transform_x_rate ) + s_transform_x_origin; double valy = ( yd * s_transform_y_rate ) + s_transform_y_origin; // Convert to lat/lon double lat = ( 2.0 * atan ( exp ( valy/CM93_semimajor_axis_meters ) ) - PI/2. ) / DEGREE; double lon = ( valx / ( DEGREE * CM93_semimajor_axis_meters ) ); sxmax = wxMax(lon, sxmax); sxmin = wxMin(lon, sxmin); symax = wxMax(lat, symax); symin = wxMin(lat, symin); } else { sxmax = wxMax(xd, sxmax); sxmin = wxMin(xd, sxmin); symax = wxMax(yd, symax); symin = wxMin(yd, symin); } } pTPG->tri_box.Set(symin, sxmin, symax, sxmax); // Transcribe this geometry to TriPrim, converting to SM if called for if(s_bSENC_SM) { GLdouble *pds = s_pwork_buf; pTPG->p_vertex = (double *)malloc(s_nvcall * 2 * sizeof(double)); GLdouble *pdd = (GLdouble*)pTPG->p_vertex; for(int ip = 0 ; ip < s_nvcall ; ip++) { double dlon = *pds++; double dlat = *pds++; double easting, northing; toSM(dlat, dlon, s_ref_lat, s_ref_lon, &easting, &northing); *pdd++ = easting; *pdd++ = northing; } } else { pTPG->p_vertex = (double *)malloc(s_nvcall * 2 * sizeof(double)); memcpy(pTPG->p_vertex, s_pwork_buf, s_nvcall * 2 * sizeof(double)); } break; } default: { sprintf(buf, "....begin Callback unknown\n"); break; } } }
int PolyTessGeo::BuildTess(void) { // Setup the tesselator TESSalloc ma; TESStesselator* tess = 0; const int nvp = 3; // unsigned char* vflags = 0; #ifdef USE_POOL struct MemPool pool; unsigned char mem[4024*1024]; // int nvflags = 0; #else int allocated = 0; double t0 = 0, t1 = 0; #endif #ifdef USE_POOL pool.size = 0; pool.cap = sizeof(mem); pool.buf = mem; memset(&ma, 0, sizeof(ma)); ma.memalloc = poolAlloc; ma.memfree = poolFree; ma.userData = (void*)&pool; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. #else memset(&ma, 0, sizeof(ma)); ma.memalloc = stdAlloc; ma.memfree = stdFree; ma.userData = (void*)&allocated; ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices. #endif tess = tessNewTess(&ma); if (!tess) return -1; //tessSetOption(tess, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, 1); // Create the contour vertex arrays int iir, ip; // Get max number number of points(vertices) in any contour int npta = m_cntr[0]; npta += 2; // fluff for( iir=0 ; iir < m_ncnt-1 ; iir++){ int nptr = m_cntr[iir+1]; npta = wxMax(npta, nptr); } TESSreal *geoPt = (TESSreal *)malloc((npta) * 2 * sizeof(TESSreal)); // tess input vertex array TESSreal *ppt = geoPt; // Create input structures // Exterior Ring int npte = m_cntr[0]; unsigned int ptValid = npte; // Check and account for winding direction of ring double x0, y0, x, y; OGRPoint p; wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[0]; bool cw = isRingClockwise(pp, m_cntr[0]); //pp++; // skip 0? if(cw) { x0 = pp->m_x; y0 = pp->m_y; } else { x0 = pp[npte-1].m_x; y0 = pp[npte-1].m_y; } // Transcribe points to vertex array, in proper order with no duplicates // also, accounting for tess_orient for(ip = 0 ; ip < npte ; ip++) { int pidx; if(cw) pidx = npte - ip - 1; else pidx = ip; x = pp[pidx].m_x; y = pp[pidx].m_y; if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { if(m_tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; } else { *ppt++ = y; *ppt++ = x; } } else ptValid--; x0 = x; y0 = y; } // Apply LOD reduction int beforeLOD = ptValid; int afterLOD = beforeLOD; std::vector<bool> bool_keep; if(ptValid > 5 && (m_LOD_meters > .01)){ for(unsigned int i = 0 ; i < ptValid ; i++) bool_keep.push_back(false); // Keep a few key points bool_keep[0] = true; bool_keep[1] = true; bool_keep[ptValid-1] = true; bool_keep[ptValid-2] = true; DouglasPeuckerFI(geoPt, 1, ptValid-2, m_LOD_meters, bool_keep); // Create a new buffer float *LOD_result = (float *)malloc((npte) * 2 * sizeof(float)); float *plod = LOD_result; int kept_LOD =0; for(unsigned int i=0 ; i < ptValid ; i++){ if(bool_keep[i]){ float x = geoPt[i*2]; float y = geoPt[(i*2) + 1]; *plod++ = x; *plod++ = y; kept_LOD++; } } beforeLOD = ptValid; afterLOD = kept_LOD; tessAddContour(tess, 2, LOD_result, sizeof(float)*2, kept_LOD); free(LOD_result); } else { tessAddContour(tess, 2, geoPt, sizeof(float)*2, ptValid); } #if 1 // Now the interior contours for(iir=0; iir < m_ncnt-1; iir++) { ppt = geoPt; wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[iir + 1]; int npti = m_cntr[iir+1]; ptValid = npti; // Check and account for winding direction of ring bool cw = isRingClockwise(pp, m_cntr[iir+1]); if(!cw) { x0 = pp[0].m_x; y0 = pp[0].m_y; } else { x0 = pp[npti-1].m_x; y0 = pp[npti-1].m_y; } // Transcribe points to vertex array, in proper order with no duplicates // also, accounting for tess_orient for(int ip = 0 ; ip < npti ; ip++) { OGRPoint p; int pidx; if(!cw) // interior contours must be cw pidx = npti - ip - 1; else pidx = ip; x = pp[pidx].m_x; y = pp[pidx].m_y; if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { if(m_tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; } else { *ppt++ = y; *ppt++ = x; } } else ptValid--; x0 = x; y0 = y; } tessAddContour(tess, 2, geoPt, sizeof(float)*2, ptValid); } #endif // Ready to kick off the tesselator TriPrim *pTPG_Last = NULL; TriPrim *pTPG_Head = NULL; //s_nvmax = 0; //OCPNStopWatchTess tt0; if (!tessTesselate(tess, TESS_WINDING_POSITIVE, TESS_POLYGONS, nvp, 2, 0)) return -1; double tessTime = 0; //tt0.GetTime(); // Tesselation all done, so... // Create the data structures // Make a linked list of TriPrims from tess output arrays const TESSreal* verts = tessGetVertices(tess); const int* vinds = tessGetVertexIndices(tess); const int* elems = tessGetElements(tess); const int nverts = tessGetVertexCount(tess); int nelems = tessGetElementCount(tess); bool skip = false; //skip = true; double stripTime = 0; //tt0.Reset(); int bytes_needed_vbo = 0; float *vbo = 0; if(m_bstripify && nelems && !skip){ STRIPERCREATE sc; sc.DFaces = (udword *)elems; sc.NbFaces = nelems; sc.AskForWords = false; sc.ConnectAllStrips = false; sc.OneSided = false; sc.SGIAlgorithm = false; Striper Strip; Strip.Init(sc); STRIPERRESULT sr; Strip.Compute(sr); stripTime = 0; //tt0.GetTime(); /* fprintf(stdout, "Number of strips: %d\n", sr.NbStrips); fprintf(stdout, "Number of points: %d\n", nelems); uword* Refs = (uword*)sr.StripRuns; for(udword i=0;i<sr.NbStrips;i++) { fprintf(stdout, "Strip %d: ", i); udword NbRefs = sr.StripLengths[i]; for(udword j=0;j<NbRefs;j++) { fprintf(stdout, "%d ", *Refs++); } fprintf(stdout, "\n"); } */ // calculate and allocate the final (float) VBO-like buffer for this entire feature int *Refs = (int *)sr.StripRuns; for(unsigned int i=0;i<sr.NbStrips;i++){ unsigned int NbRefs = sr.StripLengths[i]; // vertices per strip bytes_needed_vbo += NbRefs * 2 * sizeof(float); } vbo = (float *)malloc(bytes_needed_vbo); float *vbo_run = vbo; for(unsigned int i=0;i<sr.NbStrips;i++){ unsigned int NbRefs = sr.StripLengths[i]; // vertices per strip if(NbRefs >= 3){ // this is a valid primitive TriPrim *pTPG = new TriPrim; if(NULL == pTPG_Last){ pTPG_Head = pTPG; pTPG_Last = pTPG; } else{ pTPG_Last->p_next = pTPG; pTPG_Last = pTPG; } pTPG->p_next = NULL; pTPG->nVert = NbRefs; // pTPG->p_vertex = (double *)malloc(NbRefs * 2 * sizeof(double)); // GLdouble *pdd = (GLdouble*)pTPG->p_vertex; pTPG->p_vertex = (double *)vbo_run; // Preset LLBox bounding box limits double sxmax = -1e8; double sxmin = 1e8; double symax = -1e8; double symin = 1e8; if(NbRefs >3) pTPG->type = GL_TRIANGLE_STRIP; else pTPG->type = GL_TRIANGLES; // Add the first two points int vindex[3]; vindex[0] = *Refs++; vindex[1] = *Refs++; unsigned int np = 2; // for the first triangle for(unsigned int i = 2; i < NbRefs ; i++){ vindex[np] = *Refs++; np++; for (unsigned int j = 0; j < np; ++j){ double yd = verts[vindex[j]*2]; double xd = verts[vindex[j]*2+1]; // Calculate LLBox bounding box for each Tri-prim if(m_bmerc_transform){ double valx = ( xd * mx_rate ) + mx_offset; double valy = ( yd * my_rate ) + my_offset; // quickly convert to lat/lon double lat = ( 2.0 * atan ( exp ( valy/CM93_semimajor_axis_meters ) ) - PI/2. ) / DEGREE; double lon= ( valx / ( DEGREE * CM93_semimajor_axis_meters ) ); sxmax = wxMax(lon, sxmax); sxmin = wxMin(lon, sxmin); symax = wxMax(lat, symax); symin = wxMin(lat, symin); } else{ sxmax = wxMax(xd, sxmax); sxmin = wxMin(xd, sxmin); symax = wxMax(yd, symax); symin = wxMin(yd, symin); } // Append this point to TriPrim vbo *vbo_run++ = (xd) + m_feature_easting; // adjust to chart ref coordinates *vbo_run++ = (yd) + m_feature_northing; } // For // Compute the final LLbbox for this TriPrim chain double minlat, minlon, maxlat, maxlon; fromSM(sxmin, symin, m_feature_ref_lat, m_feature_ref_lon, &minlat, &minlon); fromSM(sxmax, symax, m_feature_ref_lat, m_feature_ref_lon, &maxlat, &maxlon); pTPG->tri_box.Set(minlat, minlon, maxlat, maxlon); // set for next single point np = 0; } } else Refs += sr.StripLengths[i]; } // for strips } else{ // not stripified m_nvertex_max = nverts; // record largest vertex count bytes_needed_vbo = nelems * nvp * 2 * sizeof(float); vbo = (float *)malloc(bytes_needed_vbo); float *vbo_run = vbo; for (int i = 0; i < nelems; ++i){ const int* p = &elems[i*nvp]; TriPrim *pTPG = new TriPrim; if(NULL == pTPG_Last){ pTPG_Head = pTPG; pTPG_Last = pTPG; } else{ pTPG_Last->p_next = pTPG; pTPG_Last = pTPG; } pTPG->p_next = NULL; pTPG->type = GL_TRIANGLES; pTPG->nVert = nvp; // pTPG->p_vertex = (double *)malloc(nvp * 2 * sizeof(double)); // GLdouble *pdd = (GLdouble*)pTPG->p_vertex; pTPG->p_vertex = (double *)vbo_run; // Preset LLBox bounding box limits double sxmax = -1e8; double sxmin = 1e8; double symax = -1e8; double symin = 1e8; for (size_t j = 0; j < nvp && p[j] != TESS_UNDEF; ++j){ double yd = verts[p[j]*2]; double xd = verts[p[j]*2+1]; // Calculate LLBox bounding box for each Tri-prim if(m_bmerc_transform){ double valx = ( xd * mx_rate ) + mx_offset; double valy = ( yd * my_rate ) + my_offset; // quickly convert to lat/lon double lat = ( 2.0 * atan ( exp ( valy/CM93_semimajor_axis_meters ) ) - PI/2. ) / DEGREE; double lon= ( valx / ( DEGREE * CM93_semimajor_axis_meters ) ); sxmax = wxMax(lon, sxmax); sxmin = wxMin(lon, sxmin); symax = wxMax(lat, symax); symin = wxMin(lat, symin); } else{ sxmax = wxMax(xd, sxmax); sxmin = wxMin(xd, sxmin); symax = wxMax(yd, symax); symin = wxMin(yd, symin); } // Append this point to TriPrim, converting to SM if called for if(m_b_senc_sm){ double easting, northing; toSM(yd, xd, m_ref_lat, m_ref_lon, &easting, &northing); *vbo_run++ = easting; *vbo_run++ = northing; } else{ *vbo_run++ = xd; *vbo_run++ = yd; } } pTPG->tri_box.Set(symin, sxmin, symax, sxmax); } } // stripify if(m_printStats){ int nTri = 0; int nStrip = 0; TriPrim *p_tp = pTPG_Head; while( p_tp ) { if(p_tp->type == GL_TRIANGLES) nTri++; if(p_tp->type == GL_TRIANGLE_STRIP) nStrip++; p_tp = p_tp->p_next; // pick up the next in chain } if((nTri + nStrip) > 10000){ printf("LOD: %d/%d\n", afterLOD, beforeLOD); printf("Tess time(ms): %f\n", tessTime); printf("Strip time(ms): %f\n", stripTime); printf("Primitives: Tri: %5d Strip: %5d Total: %5d\n", nTri, nStrip, nTri + nStrip); printf("\n"); } } m_ppg_head = new PolyTriGroup; m_ppg_head->m_bSMSENC = m_b_senc_sm; m_ppg_head->nContours = m_ncnt; m_ppg_head->pn_vertex = m_cntr; // pointer to array of poly vertex counts m_ppg_head->tri_prim_head = pTPG_Head; // head of linked list of TriPrims //m_ppg_head->data_type = DATA_TYPE_DOUBLE; // Transcribe the raw geometry buffer // Converting to float as we go, and // allowing for tess_orient // Also, convert to SM if requested int nptfinal = npta; // No longer need the full geometry in the SENC, nptfinal = 1; m_nwkb = (nptfinal +1) * 2 * sizeof(float); m_ppg_head->pgroup_geom = (float *)malloc(m_nwkb); float *vro = m_ppg_head->pgroup_geom; ppt = geoPt; float tx,ty; for(ip = 0 ; ip < nptfinal ; ip++) { if(TESS_HORZ == m_tess_orient) { ty = *ppt++; tx = *ppt++; } else { tx = *ppt++; ty = *ppt++; } if(m_b_senc_sm) { // Calculate SM from chart common reference point double easting, northing; toSM(ty, tx, 0/*ref_lat*/, 0/*ref_lon*/, &easting, &northing); *vro++ = easting; // x *vro++ = northing; // y } else { *vro++ = tx; // lon *vro++ = ty; // lat } ppt++; // skip z } m_ppg_head->bsingle_alloc = true; m_ppg_head->single_buffer = (unsigned char *)vbo; m_ppg_head->single_buffer_size = bytes_needed_vbo; m_ppg_head->data_type = DATA_TYPE_FLOAT; free (geoPt); // All allocated buffers are owned now by the m_ppg_head // And will be freed on dtor of this object if (tess) tessDeleteTess(tess); m_bOK = true; return 0; }
// Build PolyTessGeo Object from OGR Polygon // Using OpenGL/GLU tesselator int PolyTessGeo::BuildTessGLU() { unsigned int iir, ip; GLdouble *geoPt; #if 0 // Make a quick sanity check of the polygon coherence bool b_ok = true; OGRLineString *tls = poly->getExteriorRing(); if(!tls) { b_ok = false; } else { int tnpta = poly->getExteriorRing()->getNumPoints(); if(tnpta < 3 ) b_ok = false; } for( iir=0 ; iir < poly->getNumInteriorRings() ; iir++) { int tnptr = poly->getInteriorRing(iir)->getNumPoints(); if( tnptr < 3 ) b_ok = false; } if( !b_ok ) return ERROR_BAD_OGRPOLY; #endif // Allocate a work buffer, which will be grown as needed #define NINIT_BUFFER_LEN 10000 m_pwork_buf = (GLdouble *)malloc(NINIT_BUFFER_LEN * 2 * sizeof(GLdouble)); m_buf_len = NINIT_BUFFER_LEN * 2; m_buf_idx = 0; // Create an array to hold pointers to allocated vertices created by "combine" callback, // so that they may be deleted after tesselation. m_pCombineVertexArray = new wxArrayPtrVoid; // Create tesselator GLUtessobj = gluNewTess(); // Register the callbacks gluTessCallback(GLUtessobj, GLU_TESS_BEGIN_DATA, (GLvoid (*) ())&beginCallback); gluTessCallback(GLUtessobj, GLU_TESS_BEGIN_DATA, (GLvoid (*) ())&beginCallback); gluTessCallback(GLUtessobj, GLU_TESS_VERTEX_DATA, (GLvoid (*) ())&vertexCallback); gluTessCallback(GLUtessobj, GLU_TESS_END_DATA, (GLvoid (*) ())&endCallback); gluTessCallback(GLUtessobj, GLU_TESS_COMBINE_DATA, (GLvoid (*) ())&combineCallback); gluTessCallback(GLUtessobj, GLU_TESS_ERROR_DATA, (GLvoid (*) ())&errorCallback); gluTessProperty(GLUtessobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); gluTessNormal(GLUtessobj, 0, 0, 1); // Get total number of points(vertices) to build a buffer int npta = 0; for( int i=0 ; i < m_ncnt ; i++) npta += m_cntr[i]; geoPt = (GLdouble *)malloc((npta + 6) * 3 * sizeof(GLdouble)); // vertex array // Grow the work buffer if necessary if((npta * 4) > m_buf_len) { m_pwork_buf = (GLdouble *)realloc(m_pwork_buf, npta * 4 * sizeof(GLdouble)); m_buf_len = npta * 4; } // Define the polygon gluTessBeginPolygon(GLUtessobj, this); // Check and account for winding direction of ring double x0, y0, x, y; // OGRPoint p; wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[0]; bool cw = !isRingClockwise(pp, m_cntr[0]); //pp++; // skip 0? if(cw) { x0 = pp->m_x; y0 = pp->m_y; } else { x0 = pp[m_cntr[0]-1].m_x; y0 = pp[m_cntr[0]-1].m_y; } unsigned int ptValid = m_cntr[0]; // Transcribe points to vertex array, in proper order with no duplicates // also, accounting for tess_orient GLdouble *ppt = geoPt; for(ip = 0 ; ip < (unsigned int)m_cntr[0] ; ip++) { int pidx; if(cw) pidx = m_cntr[0] - ip - 1; else pidx = ip; x = pp[pidx].m_x; y = pp[pidx].m_y; if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { if(m_tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; *ppt++ = 0; } else { *ppt++ = y; *ppt++ = x; *ppt++ = 0; } } else ptValid--; x0 = x; y0 = y; } // Apply LOD reduction int beforeLOD = ptValid; int afterLOD = beforeLOD; if(ptValid > 20 && (m_LOD_meters > .01)) { std::vector<bool> bool_keep(ptValid, false); // Keep a few key points bool_keep[0] = true; bool_keep[1] = true; bool_keep[ptValid-1] = true; bool_keep[ptValid-2] = true; DouglasPeuckerDI(geoPt, 1, ptValid-2, m_LOD_meters, bool_keep); // Create a new buffer double *LOD_result = (double *)malloc((m_cntr[0]) * 3 * sizeof(double)); double *plod = LOD_result; int kept_LOD =0; for(unsigned int i=0 ; i < ptValid ; i++){ if(bool_keep[i]){ double x = geoPt[i*3]; double y = geoPt[(i*3) + 1]; *plod++ = x; *plod++ = y; *plod++ = 0; kept_LOD++; } } beforeLOD = ptValid; afterLOD = kept_LOD; // Copy the lod points back into the vertex buffer memcpy(geoPt, LOD_result, kept_LOD * 3 * sizeof(double)); free(LOD_result); ptValid = kept_LOD; } // Declare the gluContour and copy the points gluTessBeginContour(GLUtessobj); double *DPrun = geoPt; for(ip = 0 ; ip < ptValid ; ip++) { gluTessVertex( GLUtessobj, DPrun, DPrun ) ; DPrun += 3; } gluTessEndContour(GLUtessobj); // Now the interior contours #if 1 int gpIndex = m_cntr[0]; for(iir=0; iir < (unsigned int)m_ncnt-1; iir++){ wxPoint2DDouble *pp = (wxPoint2DDouble *)m_vertexPtrArray[iir + 1]; int npti = m_cntr[iir+1]; ptValid = npti; double *ppt = &geoPt[gpIndex * 3]; // next available location in geoPT double *DPStart = ppt; // Check and account for winding direction of ring bool cw = isRingClockwise(pp, m_cntr[iir+1]); if(cw) { x0 = pp[0].m_x; y0 = pp[0].m_y; } else { x0 = pp[npti-1].m_x; y0 = pp[npti-1].m_y; } // Transcribe points to vertex array, in proper order with no duplicates // also, accounting for tess_orient for(int ip = 0 ; ip < npti ; ip++) { int pidx; if(cw) // interior contours must be cw pidx = npti - ip - 1; else pidx = ip; x = pp[pidx].m_x; y = pp[pidx].m_y; if((fabs(x-x0) > EQUAL_EPS) || (fabs(y-y0) > EQUAL_EPS)) { if(m_tess_orient == TESS_VERT) { *ppt++ = x; *ppt++ = y; *ppt++ = 0; } else { *ppt++ = y; *ppt++ = x; *ppt++ = 0; } } else ptValid--; x0 = x; y0 = y; } // Declare the gluContour and reference the points gluTessBeginContour(GLUtessobj); double *DPruni = DPStart; for(ip = 0 ; ip < ptValid ; ip++) { gluTessVertex( GLUtessobj, DPruni, DPruni ) ; DPruni += 3; } gluTessEndContour(GLUtessobj); gpIndex += m_cntr[iir+1]; } #endif m_pTPG_Last = NULL; m_pTPG_Head = NULL; m_nvmax = 0; // Ready to kick off the tesselator gluTessEndPolygon(GLUtessobj); // here it goes m_nvertex_max = m_nvmax; // record largest vertex count, updates in callback // Tesselation all done, so... // Create the data structures m_ppg_head = new PolyTriGroup; m_ppg_head->m_bSMSENC = m_b_senc_sm; m_ppg_head->nContours = m_ncnt; m_ppg_head->pn_vertex = m_cntr; // pointer to array of poly vertex counts m_ppg_head->data_type = DATA_TYPE_DOUBLE; // Transcribe the raw geometry buffer // Converting to float as we go, and // allowing for tess_orient // Also, convert to SM if requested // Recalculate the size of the geometry buffer unsigned int nptfinal = m_cntr[0] + 2; // for(int i=0 ; i < nint ; i++) // nptfinal += cntr[i+1] + 2; // No longer need the full geometry in the SENC, nptfinal = 1; m_nwkb = (nptfinal + 1) * 2 * sizeof(float); m_ppg_head->pgroup_geom = (float *)calloc(sizeof(float), (nptfinal + 1) * 2); float *vro = m_ppg_head->pgroup_geom; ppt = geoPt; float tx,ty; for(ip = 0 ; ip < nptfinal ; ip++) { if(TESS_HORZ == m_tess_orient) { ty = *ppt++; tx = *ppt++; } else { tx = *ppt++; ty = *ppt++; } if(m_b_senc_sm) { // Calculate SM from chart common reference point double easting, northing; toSM(ty, tx, m_ref_lat, m_ref_lon, &easting, &northing); *vro++ = easting; // x *vro++ = northing; // y } else { *vro++ = tx; // lon *vro++ = ty; // lat } ppt++; // skip z } m_ppg_head->tri_prim_head = m_pTPG_Head; // head of linked list of TriPrims // Convert the Triangle vertex arrays into a single memory allocation of floats // to reduce SENC size and enable efficient access later // First calculate the total byte size int total_byte_size = 2 * sizeof(float); TriPrim *p_tp = m_ppg_head->tri_prim_head; while( p_tp ) { total_byte_size += p_tp->nVert * 2 * sizeof(float); p_tp = p_tp->p_next; // pick up the next in chain } float *vbuf = (float *)malloc(total_byte_size); p_tp = m_ppg_head->tri_prim_head; float *p_run = vbuf; while( p_tp ) { float *pfbuf = p_run; GLdouble *pdouble_buf = (GLdouble *)p_tp->p_vertex; for( int i=0 ; i < p_tp->nVert * 2 ; ++i){ float x = (float)( *((GLdouble *)pdouble_buf) ); pdouble_buf++; *p_run++ = x; } free(p_tp->p_vertex); p_tp->p_vertex = (double *)pfbuf; p_tp = p_tp->p_next; // pick up the next in chain } m_ppg_head->bsingle_alloc = true; m_ppg_head->single_buffer = (unsigned char *)vbuf; m_ppg_head->single_buffer_size = total_byte_size; m_ppg_head->data_type = DATA_TYPE_FLOAT; gluDeleteTess(GLUtessobj); free( m_pwork_buf ); m_pwork_buf = NULL; free (geoPt); // Free up any "Combine" vertices created for(unsigned int i = 0; i < m_pCombineVertexArray->GetCount() ; i++) free (m_pCombineVertexArray->Item(i)); delete m_pCombineVertexArray; m_bOK = true; return 0; }
// Build PolyTessGeo Object from OGR Polygon PolyTessGeo::PolyTessGeo(OGRPolygon *poly, bool bSENC_SM, double ref_lat, double ref_lon, double LOD_meters) { m_printStats = false; ErrorCode = 0; m_ppg_head = NULL; m_pxgeom = NULL; m_tess_orient = TESS_HORZ; m_ref_lat = ref_lat; m_ref_lon = ref_lon; m_LOD_meters = LOD_meters; m_b_senc_sm = bSENC_SM; m_bmerc_transform = false; // PolyGeo BBox as lat/lon OGREnvelope Envelope; poly->getEnvelope(&Envelope); xmin = Envelope.MinX; ymin = Envelope.MinY; xmax = Envelope.MaxX; ymax = Envelope.MaxY; m_feature_ref_lat = ymin + (ymax - ymin)/2; m_feature_ref_lon = xmin + (xmax - xmin)/2; toSM(m_feature_ref_lat, m_feature_ref_lon, ref_lat, ref_lon, &m_feature_easting, &m_feature_northing); // Build the array of contour point counts // Get total number of contours m_ncnt = poly->getNumInteriorRings() + 1; // build the contour point countarray m_cntr = (int *)malloc(m_ncnt * sizeof(int)); m_cntr[0] = poly->getExteriorRing()->getNumPoints(); for(int i = 1; i < m_ncnt ; i++){ m_cntr[i] = poly->getInteriorRing( i-1 )->getNumPoints(); } // Build the point array index table OGRPoint p; m_vertexPtrArray = (double **)malloc(m_ncnt * sizeof(double *)); m_vertexPtrArray[0] = (double *)malloc(m_cntr[0] * sizeof(double) * 2); double *pp = m_vertexPtrArray[0]; for(int i = 0 ; i < m_cntr[0] ; i++){ poly->getExteriorRing()->getPoint(i, &p); // Calculate SM from feature reference point double easting, northing; toSM(p.getY(), p.getX(), m_feature_ref_lat, m_feature_ref_lon, &easting, &northing); *pp++ = easting; // x *pp++ = northing; // y } for(int i = 1; i < m_ncnt ; i++){ m_vertexPtrArray[i] = (double *)malloc(m_cntr[i] * sizeof(double) * 2); double *pp = m_vertexPtrArray[i]; for(int j = 0 ; j < m_cntr[i] ; j++){ poly->getInteriorRing(i-1)->getPoint(j, &p); double easting, northing; toSM(p.getY(), p.getX(), m_feature_ref_lat, m_feature_ref_lon, &easting, &northing); *pp++ = easting; // x *pp++ = northing; // y } } mx_rate = 1.0; mx_offset = 0.0; my_rate = 1.0; my_offset = 0.0; m_bstripify = true; earthAxis = CM93_semimajor_axis_meters * mercator_k0; m_bcm93 = false; BuildTessGLU(); // Free the working memory for(int i = 0; i < m_ncnt ; i++) free (m_vertexPtrArray[i]); free( m_vertexPtrArray ); m_vertexPtrArray = nullptr; }
bool Routeman::UpdateProgress() { bool bret_val = false; if( pActiveRoute ) { // Update bearing, range, and crosstrack error // Bearing is calculated as Mercator Sailing, i.e. a cartographic "bearing" double north, east; toSM( pActivePoint->m_lat, pActivePoint->m_lon, gLat, gLon, &east, &north ); double a = atan( north / east ); if( fabs( pActivePoint->m_lon - gLon ) < 180. ) { if( pActivePoint->m_lon > gLon ) CurrentBrgToActivePoint = 90. - ( a * 180 / PI ); else CurrentBrgToActivePoint = 270. - ( a * 180 / PI ); } else { if( pActivePoint->m_lon > gLon ) CurrentBrgToActivePoint = 270. - ( a * 180 / PI ); else CurrentBrgToActivePoint = 90. - ( a * 180 / PI ); } // Calculate range using Great Circle Formula double d5 = DistGreatCircle( gLat, gLon, pActivePoint->m_lat, pActivePoint->m_lon ); CurrentRngToActivePoint = d5; // Get the XTE vector, normal to current segment vector2D va, vb, vn; double brg1, dist1, brg2, dist2; DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon, pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, &brg1, &dist1 ); vb.x = dist1 * sin( brg1 * PI / 180. ); vb.y = dist1 * cos( brg1 * PI / 180. ); DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon, gLat, gLon, &brg2, &dist2 ); va.x = dist2 * sin( brg2 * PI / 180. ); va.y = dist2 * cos( brg2 * PI / 180. ); double sdelta = vGetLengthOfNormal( &va, &vb, &vn ); // NM CurrentXTEToActivePoint = sdelta; // Calculate the distance to the arrival line, which is perpendicular to the current route segment // Taking advantage of the calculated normal from current position to route segment vn vector2D vToArriveNormal; vSubtractVectors( &va, &vn, &vToArriveNormal ); CurrentRangeToActiveNormalCrossing = vVectorMagnitude( &vToArriveNormal ); // Compute current segment course // Using simple Mercater projection double x1, y1, x2, y2; toSM( pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, &x1, &y1 ); toSM( pActivePoint->m_lat, pActivePoint->m_lon, pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, &x2, &y2 ); double e1 = atan2( ( x2 - x1 ), ( y2 - y1 ) ); CurrentSegmentCourse = e1 * 180 / PI; if( CurrentSegmentCourse < 0 ) CurrentSegmentCourse += 360; // Compute XTE direction double h = atan( vn.y / vn.x ); if( vn.x > 0 ) CourseToRouteSegment = 90. - ( h * 180 / PI ); else CourseToRouteSegment = 270. - ( h * 180 / PI ); h = CurrentBrgToActivePoint - CourseToRouteSegment; if( h < 0 ) h = h + 360; if( h > 180 ) XTEDir = 1; else XTEDir = -1; // Determine Arrival bool bDidArrival = false; if( CurrentRangeToActiveNormalCrossing <= pActiveRoute->GetRouteArrivalRadius() ) { m_bArrival = true; UpdateAutopilot(); bDidArrival = true; if( !ActivateNextPoint( pActiveRoute, false ) ) // at the end? { Route *pthis_route = pActiveRoute; DeactivateRoute( true ); // this is an arrival if( pthis_route->m_bDeleteOnArrival ) { pConfig->DeleteConfigRoute( pthis_route ); DeleteRoute( pthis_route ); if( pRoutePropDialog ) { pRoutePropDialog->SetRouteAndUpdate( NULL ); pRoutePropDialog->UpdateProperties(); } if( pRouteManagerDialog ) pRouteManagerDialog->UpdateRouteListCtrl(); } } } if( !bDidArrival ) // Only once on arrival UpdateAutopilot(); bret_val = true; // a route is active } m_bDataValid = true; return bret_val; }
void WVSChart::RenderViewOnDC(wxMemoryDC& dc, ViewPort& VPoint) { float *platray = NULL; float *plonray = NULL; int *psegray = NULL; int x,y; if(!m_ok) return; // Set Color wxPen *pthispen = wxThePenList->FindOrCreatePen(GetGlobalColor(_T("BLUE3")), 1, wxSOLID); dc.SetPen(*pthispen); // Compute the 1 degree cell boundaries int lat_min = (int)floor(VPoint.GetBBox().GetMinY()); int lat_max = (int)ceil(VPoint.GetBBox().GetMaxY()); int lon_min = (int)floor(VPoint.GetBBox().GetMinX()); int lon_max = (int)ceil(VPoint.GetBBox().GetMaxX()); x = lon_min; y = lat_min; // printf("%d %d\n", lon_min, lon_max); // Make positive definite longitude for easier integer math lon_min += 720; lon_max += 720; double ref_lon = VPoint.clon; // Loop around the lat/lon spec to get and draw the vector segments for(y = lat_min ; y < lat_max ; y++) { for(x = lon_min ; x < lon_max ; x++) { // Get the arrays of lat/lon vector segments // Sanity Check int xt = x; int yt = y; // Check the cache first int ix = xt % 360; //xt + 180; // bias to positive int iy = yt + 90; if( (ix > 359) || (ix < 0) || (iy > 179) || (iy < 0) ) continue; if(-1 == nseg[ix][iy]) // no data yet { // so fill cache platray = NULL; plonray = NULL; psegray = NULL; int nsegments = wvsrtv (*pwvs_file_name, y, ix, &platray, &plonray, &psegray); plat_ray[ix][iy] = platray; plon_ray[ix][iy] = plonray; pseg_ray[ix][iy] = psegray; nseg[ix][iy] = nsegments; // printf("load at %d %d \n", ix, iy); } // else // printf(" from cache at %d %d \n", ix, iy); if(nseg[ix][iy]) { float *plat_seg = plat_ray[ix][iy]; float *plon_seg = plon_ray[ix][iy]; int *pseg_cnt = pseg_ray[ix][iy]; for(int iseg = 0 ; iseg < nseg[ix][iy] ; iseg++) { int seg_cnt = *pseg_cnt++; if(seg_cnt > cur_seg_cnt_max) { cur_seg_cnt_max = seg_cnt; ptp = (wxPoint *)realloc(ptp, seg_cnt * sizeof(wxPoint)); } wxPoint *pr = ptp; wxPoint p; for(int ip = 0 ; ip < seg_cnt ; ip++) { float plat = *plat_seg++; float plon = *plon_seg++; if(fabs(plon - ref_lon) > 180.) { if(plon > ref_lon) plon -= 360.; else plon += 360.; } double easting, northing; toSM(plat, plon + 360., VPoint.clat, ref_lon + 360., &easting, &northing); double epix = easting * VPoint.view_scale_ppm; double npix = northing * VPoint.view_scale_ppm; double dx = epix * cos(VPoint.skew) + npix * sin(VPoint.skew); double dy = npix * cos(VPoint.skew) - epix * sin(VPoint.skew); p.x = (int)round((VPoint.pix_width / 2) + dx); p.y = (int)round((VPoint.pix_height / 2) - dy); *pr = p; pr++; } dc.DrawLines(seg_cnt, ptp); } } } // for x } //for y platray = NULL; plonray = NULL; psegray = NULL; wvsrtv (_T("clean"), y, x, &platray, &plonray, &psegray); }