void ViewPort::GetLLFromPix( const wxPoint &p, double *lat, double *lon ) { int dx = p.x - ( pix_width / 2 ); int dy = ( pix_height / 2 ) - p.y; double xpr = dx; double ypr = dy; // Apply VP Rotation double angle = rotation; if(!g_bskew_comp) angle += skew; if( angle ) { xpr = ( dx * cos( angle ) ) - ( dy * sin( angle ) ); ypr = ( dy * cos( angle ) ) + ( dx * sin( angle ) ); } double d_east = xpr / view_scale_ppm; double d_north = ypr / view_scale_ppm; double slat, slon; if( PROJECTION_TRANSVERSE_MERCATOR == m_projection_type ) { double tmceasting, tmcnorthing; toTM( clat, clon, 0., clon, &tmceasting, &tmcnorthing ); fromTM( d_east, d_north + tmcnorthing, 0., clon, &slat, &slon ); } else if( PROJECTION_POLYCONIC == m_projection_type ) { double polyeasting, polynorthing; toPOLY( clat, clon, 0., clon, &polyeasting, &polynorthing ); fromPOLY( d_east, d_north + polynorthing, 0., clon, &slat, &slon ); } //TODO This could be fromSM_ECC to better match some Raster charts // However, it seems that cm93 (and S57) prefer no eccentricity correction // Think about it.... else fromSM( d_east, d_north, clat, clon, &slat, &slon ); *lat = slat; if( slon < -180. ) slon += 360.; else if( slon > 180. ) slon -= 360.; *lon = slon; }
void ViewPort::GetLLFromPix( const wxPoint2DDouble &p, double *lat, double *lon ) { double dx = p.m_x - ( pix_width / 2.0 ); double dy = ( pix_height / 2.0 ) - p.m_y; double xpr = dx; double ypr = dy; // Apply VP Rotation double angle = rotation; if( angle ) { xpr = ( dx * cos( angle ) ) - ( dy * sin( angle ) ); ypr = ( dy * cos( angle ) ) + ( dx * sin( angle ) ); } double d_east = xpr / view_scale_ppm; double d_north = ypr / view_scale_ppm; double slat = 0.0, slon = 0.0; switch( m_projection_type ) { case PROJECTION_MERCATOR: //TODO This could be fromSM_ECC to better match some Raster charts // However, it seems that cm93 (and S57) prefer no eccentricity correction // Think about it.... fromSM( d_east, d_north, clat, clon, &slat, &slon ); break; case PROJECTION_TRANSVERSE_MERCATOR: { double tmceasting, tmcnorthing; toTM( clat, clon, 0., clon, &tmceasting, &tmcnorthing ); fromTM( d_east, d_north + tmcnorthing, 0., clon, &slat, &slon ); } break; case PROJECTION_POLYCONIC: { double polyeasting, polynorthing; toPOLY( clat, clon, 0., clon, &polyeasting, &polynorthing ); fromPOLY( d_east, d_north + polynorthing, 0., clon, &slat, &slon ); } break; case PROJECTION_ORTHOGRAPHIC: fromORTHO( d_east, d_north, clat, clon, &slat, &slon ); break; case PROJECTION_POLAR: fromPOLAR( d_east, d_north, clat, clon, &slat, &slon ); break; case PROJECTION_STEREOGRAPHIC: fromSTEREO( d_east, d_north, clat, clon, &slat, &slon ); break; case PROJECTION_GNOMONIC: fromGNO( d_east, d_north, clat, clon, &slat, &slon ); break; case PROJECTION_EQUIRECTANGULAR: fromEQUIRECT( d_east, d_north, clat, clon, &slat, &slon ); break; default: printf("unhandled projection\n"); } *lat = slat; if( slon < -180. ) slon += 360.; else if( slon > 180. ) slon -= 360.; *lon = slon; }
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; }
void endCallback(void *polyData) { PolyTessGeo *pThis = (PolyTessGeo *)polyData; // Create a TriPrim if(pThis->m_nvcall > pThis->m_nvmax) // keep track of largest number of triangle vertices pThis->m_nvmax = pThis->m_nvcall; switch(pThis->m_gltri_type) { case GL_TRIANGLE_FAN: case GL_TRIANGLE_STRIP: case GL_TRIANGLES: { TriPrim *pTPG = new TriPrim; if(NULL == pThis->m_pTPG_Last) { pThis->m_pTPG_Head = pTPG; pThis->m_pTPG_Last = pTPG; } else { pThis->m_pTPG_Last->p_next = pTPG; pThis->m_pTPG_Last = pTPG; } pTPG->p_next = NULL; pTPG->type = pThis->m_gltri_type; pTPG->nVert = pThis->m_nvcall; // Calculate bounding box double sxmax = -1e8; // this poly BBox double sxmin = 1e8; double symax = -1e8; double symin = 1e8; GLdouble *pvr = pThis->m_pwork_buf; for(int iv=0 ; iv < pThis->m_nvcall ; iv++) { GLdouble xd, yd; xd = *pvr++; yd = *pvr++; if(pThis->m_bcm93) { // cm93 hits here double valx = ( xd * pThis->mx_rate ) + pThis->mx_offset + pThis->m_feature_easting; double valy = ( yd * pThis->my_rate ) + pThis->my_offset + pThis->m_feature_northing; // 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 { // ENC hits here, values are SM measures from feature reference point double valx = ( xd * pThis->mx_rate ) + pThis->mx_offset + pThis->m_feature_easting; double valy = ( yd * pThis->my_rate ) + pThis->my_offset + pThis->m_feature_northing; sxmax = wxMax(valx, sxmax); sxmin = wxMin(valx, sxmin); symax = wxMax(valy, symax); symin = wxMin(valy, symin); } } // Compute the final LLbbox for this TriPrim chain if(pThis->m_bcm93) pTPG->tri_box.Set(symin, sxmin, symax, sxmax); else{ double minlat, minlon, maxlat, maxlon; fromSM(sxmin, symin, pThis->m_ref_lat, pThis->m_ref_lon, &minlat, &minlon); fromSM(sxmax, symax, pThis->m_ref_lat, pThis->m_ref_lon, &maxlat, &maxlon); pTPG->tri_box.Set(minlat, minlon, maxlat, maxlon); } // Transcribe this geometry to TriPrim, { GLdouble *pds = pThis->m_pwork_buf; pTPG->p_vertex = (double *)malloc(pThis->m_nvcall * 2 * sizeof(double)); GLdouble *pdd = (GLdouble*)pTPG->p_vertex; for(int ip = 0 ; ip < pThis->m_nvcall ; ip++) { double dlon = *pds++; double dlat = *pds++; *pdd++ = dlon + pThis->m_feature_easting; // adjust to feature ref coordinates *pdd++ = dlat + pThis->m_feature_northing; } } break; } default: { // sprintf(buf, "....begin Callback unknown\n"); break; } } }