Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
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;
      

}
Exemplo n.º 4
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;
        }
    }
}