Exemple #1
0
LLRegion ViewPort::GetLLRegion( const OCPNRegion &region )
{
    // todo: for these projecetions, improve this calculation by using the
    //       method in SetBoxes here
#ifndef ocpnUSE_GL
    return LLRegion(GetBBox());
#else    

    if(!glChartCanvas::CanClipViewport(*this))
        return LLRegion(GetBBox());

    OCPNRegionIterator it( region );
    LLRegion r;
    while( it.HaveRects() ) {
        wxRect rect = it.GetRect();

        int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
        int p[8] = {x1, y1, x2, y1, x2, y2, x1, y2};
        double pll[8];
        for(int i=0; i<8; i+=2)
            GetLLFromPix(wxPoint(p[i], p[i+1]), pll+i, pll+i+1);

        // resolve (this works even if rectangle crosses both 0 and 180)
        //if(LLRegion::PointsCCW(4, pll))
        for(int i=0; i<8; i+=2) {
            if(pll[i+1] <= clon - 180)
                pll[i+1] += 360;
            else if(pll[i+1] >= clon + 180)
                pll[i+1] -= 360;
        }

        r.Union(LLRegion(4, pll));
        it.NextRect();
    }
    return r;
#endif    
}
Exemple #2
0
void ViewPort::SetBoxes( void )
{

    //  In the case where canvas rotation is applied, we need to define a larger "virtual" pixel window size to ensure that
    //  enough chart data is fatched and available to fill the rotated screen.
    rv_rect = wxRect( 0, 0, pix_width, pix_height );

    //  Specify the minimum required rectangle in unrotated screen space which will supply full screen data after specified rotation
    if (( fabs( skew ) > .0001 ) || (fabs(rotation )>.0001 )) {

        double rotator = rotation;
        double lpixh = pix_height;
        double lpixw = pix_width;

        lpixh = wxMax(lpixh, (fabs(pix_height * cos(skew)) + fabs(pix_width * sin(skew))));
        lpixw = wxMax(lpixw, (fabs(pix_width * cos(skew)) + fabs(pix_height * sin(skew))));

        int dy = wxRound(
                     fabs( lpixh * cos( rotator ) ) + fabs( lpixw * sin( rotator ) ) );
        int dx = wxRound(
                     fabs( lpixw * cos( rotator ) ) + fabs( lpixh * sin( rotator ) ) );

        //  It is important for MSW build that viewport pixel dimensions be multiples of 4.....
        if( dy % 4 ) dy += 4 - ( dy % 4 );
        if( dx % 4 ) dx += 4 - ( dx % 4 );

        int inflate_x = wxMax(( dx - pix_width ) / 2, 0);
        int inflate_y = wxMax(( dy - pix_height ) / 2, 0);

        //  Grow the source rectangle appropriately
        rv_rect.Inflate( inflate_x, inflate_y );
    }

    //  Compute Viewport lat/lon reference points for co-ordinate hit testing

    //  This must be done in unrotated space with respect to full unrotated screen space calculated above
    double rotation_save = rotation;
    SetRotationAngle(0.0);

    wxPoint ul( rv_rect.x, rv_rect.y ), lr( rv_rect.x + rv_rect.width, rv_rect.y + rv_rect.height );
    double dlat_min, dlat_max, dlon_min, dlon_max;

    bool hourglass = false;
    switch(m_projection_type) {
    case PROJECTION_TRANSVERSE_MERCATOR:
    case PROJECTION_STEREOGRAPHIC:
    case PROJECTION_GNOMONIC:
        hourglass = true;
    case PROJECTION_POLYCONIC:
    case PROJECTION_POLAR:
    case PROJECTION_ORTHOGRAPHIC:
    {
        double d;

        if( clat > 0 ) { // north polar
            wxPoint u( rv_rect.x + rv_rect.width/2, rv_rect.y );
            wxPoint ur( rv_rect.x + rv_rect.width, rv_rect.y );
            GetLLFromPix( ul, &d, &dlon_min );
            GetLLFromPix( ur, &d, &dlon_max );
            GetLLFromPix( lr, &dlat_min, &d );
            GetLLFromPix( u, &dlat_max, &d );

            if(fabs(fabs(d - clon) - 180) < 1) { // the pole is onscreen
                dlat_max = 90;
                dlon_min = -180;
                dlon_max = 180;
            } else if(wxIsNaN(dlat_max))
                dlat_max = 90;

            if(hourglass) {
                // near equator, center may be less
                wxPoint l( rv_rect.x + rv_rect.width/2, rv_rect.y + rv_rect.height );
                double dlat_min2;
                GetLLFromPix( l, &dlat_min2, &d );
                dlat_min = wxMin(dlat_min, dlat_min2);
            }

            if(wxIsNaN(dlat_min)) //  world is off-screen
                dlat_min = clat - 90;
        } else { // south polar
            wxPoint l( rv_rect.x + rv_rect.width/2, rv_rect.y + rv_rect.height );
            wxPoint ll( rv_rect.x, rv_rect.y + rv_rect.height );
            GetLLFromPix( ul, &dlat_max, &d );
            GetLLFromPix( lr, &d, &dlon_max );
            GetLLFromPix( ll, &d, &dlon_min );
            GetLLFromPix( l, &dlat_min, &d );

            if(fabs(fabs(d - clon) - 180) < 1) { // the pole is onscreen
                dlat_min = -90;
                dlon_min = -180;
                dlon_max = 180;
            } else if(wxIsNaN(dlat_min))
                dlat_min = -90;

            if(hourglass) {
                // near equator, center may be less
                wxPoint u( rv_rect.x + rv_rect.width/2, rv_rect.y );
                double dlat_max2;
                GetLLFromPix( u, &dlat_max2, &d );
                dlat_max = wxMax(dlat_max, dlat_max2);
            }

            if(wxIsNaN(dlat_max)) //  world is off-screen
                dlat_max = clat + 90;
        }

        if(wxIsNaN(dlon_min)) {
            // if neither pole is visible, but left and right of the screen are in space
            // we can avoid drawing the far side of the earth
            if(dlat_max < 90 && dlat_min > -90) {
                dlon_min = clon - 90 - fabs(clat); // this logic is not optimal, is it always correct?
                dlon_max = clon + 90 + fabs(clat);
            } else {
                dlon_min = -180;
                dlon_max = 180;
            }
        }
    }
    break;

    default: // works for mercator and equirectangular
    {
        GetLLFromPix( ul, &dlat_max, &dlon_min );
        GetLLFromPix( lr, &dlat_min, &dlon_max );
    }
    }

    if( clon < dlon_min )
        dlon_min -= 360;
    else if(clon > dlon_max)
        dlon_max += 360;

    //  Set the viewport lat/lon bounding box appropriately
    vpBBox.Set( dlat_min, dlon_min, dlat_max, dlon_max );

    // Restore the rotation angle
    SetRotationAngle( rotation_save );
}
Exemple #3
0
OCPNRegion ViewPort::GetVPRegionIntersect( const OCPNRegion &Region, size_t nPoints, float *llpoints,
        int chart_native_scale, wxPoint *ppoints )
{
    //  Calculate the intersection between a given OCPNRegion (Region) and a polygon specified by lat/lon points.

    //    If the viewpoint is highly overzoomed wrt to chart native scale, the polygon region may be huge.
    //    This can be very expensive, and lead to crashes on some platforms (gtk in particular)
    //    So, look for this case and handle appropriately with respect to the given Region

    if( chart_scale < chart_native_scale / 10 ) {

        //    Scan the points one-by-one, so that we can get min/max to make a bbox
        float *pfp = llpoints;
        float lon_max = -10000.;
        float lon_min = 10000.;
        float lat_max = -10000.;
        float lat_min = 10000.;

        for( unsigned int ip = 0; ip < nPoints; ip++ ) {
            lon_max = wxMax(lon_max, pfp[1]);
            lon_min = wxMin(lon_min, pfp[1]);
            lat_max = wxMax(lat_max, pfp[0]);
            lat_min = wxMin(lat_min, pfp[0]);

            pfp += 2;
        }

        LLBBox chart_box;
        chart_box.Set( lat_min, lon_min, lat_max, lon_max );

        //    Case:  vpBBox is completely outside the chart box, or vice versa
        //    Return an empty region
        if( chart_box.IntersectOut( vpBBox ) )
            return OCPNRegion();

        //    Case:  vpBBox is completely inside the chart box
        //      Note that this test is not perfect, and will fail for some charts.
        //      The chart coverage may be  essentially triangular, and the viewport box
        //      may be in the "cut off" segment of the chart_box, and not actually
        //      exhibit any true overlap.  Results will be reported incorrectly.
        //      How to fix: maybe scrub the chart points and see if it is likely that
        //      a region may be safely built and intersection tested.

        if( chart_box.IntersectIn( vpBBox ) )
            return Region;

        wxPoint p1 = GetPixFromLL( lat_max, lon_min );  // upper left
        wxPoint p2 = GetPixFromLL( lat_min, lon_max );   // lower right

        OCPNRegion r( p1, p2 );
        r.Intersect( Region );
        return r;
    }

    //    More "normal" case

    wxPoint *pp;

    //    Use the passed point buffer if available
    if( ppoints == NULL ) pp = new wxPoint[nPoints];
    else
        pp = ppoints;

    float *pfp = llpoints;


    wxPoint p = GetPixFromLL( pfp[0], pfp[1] );
    int poly_x_max, poly_y_max, poly_x_min, poly_y_min;

    bool valid = false;
    for( unsigned int ip = 0; ip < nPoints; ip++ ) {
        wxPoint p = GetPixFromLL( pfp[0], pfp[1] );
        pp[ip] = p;
        if(p.x == INVALID_COORD)
            continue;

        if(valid) {
            poly_x_max = wxMax(poly_x_max, p.x);
            poly_y_max = wxMax(poly_y_max, p.y);
            poly_x_min = wxMin(poly_x_min, p.x);
            poly_y_min = wxMin(poly_y_min, p.y);
        } else {
            poly_x_max = p.x;
            poly_y_max = p.y;
            poly_x_min = p.x;
            poly_y_min = p.y;
            valid = true;
        }
        pfp += 2;
    }

    if(!valid)
    {
        delete[] pp;
        return OCPNRegion(); //empty;
    }

    //  We want to avoid processing regions with very large rectangle counts,
    //  so make some tests for special cases

    float_2Dpt p0, p1, p2, p3;

    //  First, calculate whether any segment of the input polygon intersects the specified Region
    int nrect = 0;
    bool b_intersect = false;
    OCPNRegionIterator screen_region_it1( Region );
    while( screen_region_it1.HaveRects() ) {
        wxRect rect = screen_region_it1.GetRect();

        double lat, lon;

        //  The screen region corners
        GetLLFromPix( wxPoint(rect.x, rect.y), &lat, &lon );
        p0.y = lat;
        p0.x = lon;

        GetLLFromPix( wxPoint(rect.x + rect.width, rect.y), &lat, &lon );
        p1.y = lat;
        p1.x = lon;

        GetLLFromPix( wxPoint(rect.x + rect.width, rect.y + rect.height), &lat, &lon );
        p2.y = lat;
        p2.x = lon;

        GetLLFromPix( wxPoint(rect.x, rect.y + rect.height), &lat, &lon );
        p3.y = lat;
        p3.x = lon;


        for(size_t i=0 ; i < nPoints-1 ; i++) {
            //  Quick check on y dimension
            int y0 = pp[i].y;
            int y1 = pp[i+1].y;

            if(y0 == INVALID_COORD || y1 == INVALID_COORD)
                continue;

            if( ((y0 < rect.y) && (y1 < rect.y)) ||
                    ((y0 > rect.y+rect.height) && (y1 > rect.y+rect.height)) )
                continue;               // both ends of line outside of box, top or bottom

            //  Look harder
            float_2Dpt f0;
            f0.y = llpoints[i * 2];
            f0.x = llpoints[(i * 2) + 1];
            float_2Dpt f1;
            f1.y = llpoints[(i+1) * 2];
            f1.x = llpoints[((i+1) * 2) + 1];
            b_intersect |= Intersect_FL( p0, p1, f0, f1) != 0;
            if(b_intersect) break;
            b_intersect |= Intersect_FL( p1, p2, f0, f1) != 0;
            if(b_intersect) break;
            b_intersect |= Intersect_FL( p2, p3, f0, f1) != 0;
            if(b_intersect) break;
            b_intersect |= Intersect_FL( p3, p0, f0, f1) != 0;
            if(b_intersect) break;

            //  Must check the case where the input polygon has been pre-normalized, eg (0 < lon < 360), as cm93
            f0.x -= 360.;
            f1.x -= 360.;
            b_intersect |= Intersect_FL( p0, p1, f0, f1) != 0;
            if(b_intersect) break;
            b_intersect |= Intersect_FL( p1, p2, f0, f1) != 0;
            if(b_intersect) break;
            b_intersect |= Intersect_FL( p2, p3, f0, f1) != 0;
            if(b_intersect) break;
            b_intersect |= Intersect_FL( p3, p0, f0, f1) != 0;
            if(b_intersect) break;

        }

        // Check segment, last point back to first point
        if(!b_intersect) {

            float_2Dpt f0;
            f0.y = llpoints[(nPoints-1) * 2];
            f0.x = llpoints[((nPoints-1) * 2) + 1];
            float_2Dpt f1;
            f1.y = llpoints[0];
            f1.x = llpoints[1];
            b_intersect |= Intersect_FL( p0, p1, f0, f1) != 0;
            b_intersect |= Intersect_FL( p1, p2, f0, f1) != 0;
            b_intersect |= Intersect_FL( p2, p3, f0, f1) != 0;
            b_intersect |= Intersect_FL( p3, p0, f0, f1) != 0;

            f0.x -= 360.;
            f1.x -= 360.;
            b_intersect |= Intersect_FL( p0, p1, f0, f1) != 0;
            b_intersect |= Intersect_FL( p1, p2, f0, f1) != 0;
            b_intersect |= Intersect_FL( p2, p3, f0, f1) != 0;
            b_intersect |= Intersect_FL( p3, p0, f0, f1) != 0;

        }

        screen_region_it1.NextRect();
        nrect++;
    }

    //  If there is no itersection, we need to consider the case where
    //  the subject polygon is entirely within the Region
    bool b_contained = false;
    if(!b_intersect) {
        OCPNRegionIterator screen_region_it2( Region );
        while( screen_region_it2.HaveRects() ) {
            wxRect rect = screen_region_it2.GetRect();

            for(size_t i=0 ; i < nPoints-1 ; i++) {
                int x0 = pp[i].x;
                int y0 = pp[i].y;
                if(x0 == INVALID_COORD)
                    continue;

                if((x0 < rect.x) || (x0 > rect.x+rect.width))
                    continue;

                if((y0 < rect.y) || (y0 > rect.y+rect.height))
                    continue;

                b_contained = true;
                break;
            }
            screen_region_it2.NextRect();
        }
    }

#if 1
    // and here is the payoff
    if(!b_contained && !b_intersect) {
        //  Two cases to consider
        wxRect rpoly( poly_x_min, poly_y_min, poly_x_max - poly_x_min , poly_y_max - poly_y_min);
        wxRect rRegion = Region.GetBox();
        if(rpoly.Contains(rRegion)) {
            //  subject poygon may be large enough to fully encompass the target Region,
            //  but it might not, especially for irregular or concave charts.
            //  So we cannot directly shortcut here
            //  Better check....

#if 1
            if(nrect == 1) {                // most common case
                // If the subject polygon contains the center of the target rectangle, then
                // the intersection must be the target rectangle
                float rlat = (p0.y + p2.y)/2.;
                float rlon = (p0.x + p1.x)/2.;

                if(G_PtInPolygon_FL((float_2Dpt *)llpoints, nPoints, rlon, rlat)) {
                    if( NULL == ppoints ) delete[] pp;
                    return Region;
                }
                rlon += 360.;
                if(G_PtInPolygon_FL((float_2Dpt *)llpoints, nPoints, rlon, rlat)) {
                    if( NULL == ppoints ) delete[] pp;
                    return Region;
                }

                //  otherwise, there is no intersection
                else {
                    if( NULL == ppoints ) delete[] pp;
                    wxRegion r;
                    return r;
                }
            }

#endif

        }
        else {
            //  Subject polygon is entirely outside of target Region
            //  so the intersection must be empty.
            if( NULL == ppoints ) delete[] pp;
            wxRegion r;
            return r;
        }
    }
    else if(b_contained && !b_intersect) {
        //  subject polygon is entirely withing the target Region,
        //  so the intersection is the subject polygon
        OCPNRegion r = OCPNRegion( nPoints, pp );
        if( NULL == ppoints ) delete[] pp;
        return r;
    }

#endif


#ifdef __WXGTK__
    sigaction(SIGSEGV, NULL, &sa_all_old);             // save existing action for this signal

    struct sigaction temp;
    sigaction(SIGSEGV, NULL, &temp);// inspect existing action for this signal

    temp.sa_handler = catch_signals;// point to my handler
    sigemptyset(&temp.sa_mask);// make the blocking set
    // empty, so that all
    // other signals will be
    // unblocked during my handler
    temp.sa_flags = 0;
    sigaction(SIGSEGV, &temp, NULL);

    if(sigsetjmp(env, 1))//  Something in the below code block faulted....
    {
        sigaction(SIGSEGV, &sa_all_old, NULL);        // reset signal handler

        return Region;

    }

    else
    {

        OCPNRegion r = OCPNRegion(nPoints, pp);
        if(NULL == ppoints)
            delete[] pp;

        sigaction(SIGSEGV, &sa_all_old, NULL);        // reset signal handler
        r.Intersect(Region);
        return r;
    }

#else
    OCPNRegion r = OCPNRegion( nPoints, pp );

    if( NULL == ppoints ) delete[] pp;

    r.Intersect( Region );
    return r;

#endif
}
Exemple #4
0
LLRegion ViewPort::GetLLRegion( const OCPNRegion &region )
{
    // todo: for these projecetions, improve this calculation by using the
    //       method in SetBoxes here
#ifndef ocpnUSE_GL
    return LLRegion(GetBBox());
#else

    if(!glChartCanvas::CanClipViewport(*this))
        return LLRegion(GetBBox());

    OCPNRegionIterator it( region );
    LLRegion r;
    while( it.HaveRects() ) {
        wxRect rect = it.GetRect();

        int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
        int p[8] = {x1, y1, x2, y1, x2, y2, x1, y2};
        double pll[540];
        int j;

        /* if the viewport is rotated, we must split the segments as straight lines in lat/lon
           coordinates map to curves in projected coordinate space */
        if(fabs( rotation ) >= 0.0001) {
            j=0;
            double lastlat, lastlon;
            int li = 6;
            GetLLFromPix(wxPoint(p[li], p[li+1]), &lastlat, &lastlon);
            for(int i=0; i<8; i+=2) {
                double lat, lon;
                GetLLFromPix(wxPoint(p[i], p[i+1]), &lat, &lon);

                // use 2 degree grid
                double grid = 2;
                int lat_splits = floor(fabs(lat-lastlat) / grid);
                double lond = fabs(lon-lastlon);
                int lon_splits = floor((lond > 180 ? 360-lond : lond) / grid);
                int splits = wxMax(lat_splits, lon_splits) + 1;

                for(int k = 1; k<splits; k++) {
                    float d = (float)k / splits;
                    GetLLFromPix(wxPoint((1-d)*p[li] + d*p[i], (1-d)*p[li+1] + d*p[i+1]), pll+j, pll+j+1);
                    j += 2;
                }
                pll[j++] = lat;
                pll[j++] = lon;
                li = i;
                lastlat = lat, lastlon = lon;
            }
        } else {
            j=8;
            for(int i=0; i<j; i+=2)
                GetLLFromPix(wxPoint(p[i], p[i+1]), pll+i, pll+i+1);
        }

        // resolve (this works even if rectangle crosses both 0 and 180)
        for(int i=0; i<j; i+=2) {
            if(pll[i+1] <= clon - 180)
                pll[i+1] += 360;
            else if(pll[i+1] >= clon + 180)
                pll[i+1] -= 360;
        }

        r.Union(LLRegion(j/2, pll));
        it.NextRect();
    }
    return r;
#endif
}
Exemple #5
0
void ViewPort::SetBoxes( void )
{

    //  In the case where canvas rotation is applied, we need to define a larger "virtual" pixel window size to ensure that
    //  enough chart data is fatched and available to fill the rotated screen.
    rv_rect = wxRect( 0, 0, pix_width, pix_height );

    //  Specify the minimum required rectangle in unrotated screen space which will supply full screen data after specified rotation
    if( ( g_bskew_comp && ( fabs( skew ) > .001 ) ) || ( fabs( rotation ) > .001 ) ) {

        double rotator = rotation;
        if(g_bskew_comp)
            rotator -= skew;

        int dy = wxRound(
                     fabs( pix_height * cos( rotator ) ) + fabs( pix_width * sin( rotator ) ) );
        int dx = wxRound(
                     fabs( pix_width * cos( rotator ) ) + fabs( pix_height * sin( rotator ) ) );

        //  It is important for MSW build that viewport pixel dimensions be multiples of 4.....
        if( dy % 4 ) dy += 4 - ( dy % 4 );
        if( dx % 4 ) dx += 4 - ( dx % 4 );

        int inflate_x = wxMax(( dx - pix_width ) / 2, 0);
        int inflate_y = wxMax(( dy - pix_height ) / 2, 0);
        
        //  Grow the source rectangle appropriately
        if( fabs( rotator ) > .001 )
            rv_rect.Inflate( inflate_x, inflate_y );

    }

    //  Compute Viewport lat/lon reference points for co-ordinate hit testing

    //  This must be done in unrotated space with respect to full unrotated screen space calculated above
    double rotation_save = rotation;
    SetRotationAngle( 0. );

    double lat_ul, lat_ur, lat_lr, lat_ll;
    double lon_ul, lon_ur, lon_lr, lon_ll;

    GetLLFromPix( wxPoint( rv_rect.x, rv_rect.y ), &lat_ul, &lon_ul );
    GetLLFromPix( wxPoint( rv_rect.x + rv_rect.width, rv_rect.y ), &lat_ur, &lon_ur );
    GetLLFromPix( wxPoint( rv_rect.x + rv_rect.width, rv_rect.y + rv_rect.height ), &lat_lr,
                  &lon_lr );
    GetLLFromPix( wxPoint( rv_rect.x, rv_rect.y + rv_rect.height ), &lat_ll, &lon_ll );

    if( clon < 0. ) {
        if( ( lon_ul > 0. ) && ( lon_ur < 0. ) ) {
            lon_ul -= 360.;
            lon_ll -= 360.;
        }
    } else {
        if( ( lon_ul > 0. ) && ( lon_ur < 0. ) ) {
            lon_ur += 360.;
            lon_lr += 360.;
        }
    }

    if( lon_ur < lon_ul ) {
        lon_ur += 360.;
        lon_lr += 360.;
    }

    if( lon_ur > 360. ) {
        lon_ur -= 360.;
        lon_lr -= 360.;
        lon_ul -= 360.;
        lon_ll -= 360.;
    }

    double dlat_min = lat_ul;
    dlat_min = fmin ( dlat_min, lat_ur );
    dlat_min = fmin ( dlat_min, lat_lr );
    dlat_min = fmin ( dlat_min, lat_ll );

    double dlon_min = lon_ul;
    dlon_min = fmin ( dlon_min, lon_ur );
    dlon_min = fmin ( dlon_min, lon_lr );
    dlon_min = fmin ( dlon_min, lon_ll );

    double dlat_max = lat_ul;
    dlat_max = fmax ( dlat_max, lat_ur );
    dlat_max = fmax ( dlat_max, lat_lr );
    dlat_max = fmax ( dlat_max, lat_ll );

    double dlon_max = lon_ur;
    dlon_max = fmax ( dlon_max, lon_ul );
    dlon_max = fmax ( dlon_max, lon_lr );
    dlon_max = fmax ( dlon_max, lon_ll );

    //  Set the viewport lat/lon bounding box appropriately
    vpBBox.SetMin( dlon_min, dlat_min );
    vpBBox.SetMax( dlon_max, dlat_max );

    // Restore the rotation angle
    SetRotationAngle( rotation_save );
}