Changeset 13316

Show
Ignore:
Timestamp:
12/10/07 23:47:57 (5 months ago)
Author:
warmerdam
Message:

added organizePolygons() method on OGRGeometryFactory (#1217)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/gdal/ogr/ogr_geometry.h

    r13143 r13316  
    606606    static OGRGeometry * forceToMultiLineString( OGRGeometry * ); 
    607607 
     608    static OGRGeometry * organizePolygons( OGRGeometry **papoPolygons, 
     609                                           int nPolygonCount, 
     610                                           int *pbResultValidGeometry ); 
     611 
    608612    static void *getGEOSGeometryFactory(); 
    609613 
  • trunk/gdal/ogr/ogrgeometryfactory.cpp

    r10645 r13316  
    712712 
    713713/************************************************************************/ 
     714/*                          organizePolygons()                          */ 
     715/************************************************************************/ 
     716 
     717/** 
     718 * Organize polygons based on geometries. 
     719 * 
     720 * Analyse a set of rings (passed as simple polygons), and based on a  
     721 * geometric analysis convert them into a polygon with inner rings,  
     722 * or a MultiPolygon if dealing with more than one polygon.  The  
     723 * contains analysis is done with GEOS if available, otherwise using a 
     724 * less robust approach based on ring envelopes.  
     725 * 
     726 * All the input geometries must be OGRPolygons with only an exterior 
     727 * ring and no interior rings.  
     728 * 
     729 * The passed in geometries become the responsibility of the method, but the 
     730 * papoPolygons "pointer array" remains owned by the caller. 
     731 *  
     732 * @param papoPolygons array of geometry pointers - should all be OGRPolygons. 
     733 * Ownership of the geometries is passed, but not of the array itself. 
     734 * @param nPolygonCount number of items in papoPolygons 
     735 * @param pbIsValidGeometry value will be set TRUE if result is valid or  
     736 * FALSE otherwise.  
     737 * 
     738 * @return a single resulting geometry (either OGRPolygon or OGRMultiPolygon). 
     739 */ 
     740 
     741enum 
     742{ 
     743    CONTAINS, 
     744    IS_CONTAINED_BY, 
     745    NOT_RELATED 
     746}; 
     747 
     748OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, 
     749                                                   int nPolygonCount, 
     750                                                   int *pbIsValidGeometry ) 
     751{ 
     752    int i, j; 
     753    OGRGeometry* geom = NULL; 
     754 
     755/* -------------------------------------------------------------------- */ 
     756/*      Trivial case of a single polygon.                               */ 
     757/* -------------------------------------------------------------------- */ 
     758    if (nPolygonCount == 1) 
     759    { 
     760        geom = papoPolygons[0]; 
     761        papoPolygons[0] = NULL; 
     762 
     763        if( pbIsValidGeometry ) 
     764            *pbIsValidGeometry = TRUE; 
     765 
     766        return geom; 
     767    } 
     768 
     769/* -------------------------------------------------------------------- */ 
     770/*      A wee bit of a warning.                                         */ 
     771/* -------------------------------------------------------------------- */ 
     772    static int firstTime = 1; 
     773    if (!haveGEOS() && firstTime) 
     774    { 
     775        CPLDebug("OGR", 
     776                 "GDAL should be built with GEOS support enabled in order the " 
     777                 "SHAPE driver to provide reliable results on complex polygons."); 
     778        firstTime = 0; 
     779    } 
     780 
     781/* -------------------------------------------------------------------- */ 
     782/*      Setup per polygon relation, envelope and area information.      */ 
     783/* -------------------------------------------------------------------- */ 
     784    OGREnvelope* envelopes = new OGREnvelope[nPolygonCount]; 
     785    int** relations = new int*[nPolygonCount]; 
     786    double* areas = new double[nPolygonCount]; 
     787    int go_on = TRUE; 
     788    int bMixedUpGeometries = FALSE; 
     789    int bNonPolygon = FALSE; 
     790 
     791    for(i=0;i<nPolygonCount;i++) 
     792    { 
     793        relations[i] = new int[nPolygonCount]; 
     794        papoPolygons[i]->getEnvelope(&envelopes[i]); 
     795 
     796        if( wkbFlatten(papoPolygons[i]->getGeometryType()) == wkbPolygon 
     797            && ((OGRPolygon *) papoPolygons[i])->getNumInteriorRings() == 0 ) 
     798        { 
     799            areas[i] = ((OGRPolygon *)papoPolygons[i])->get_Area(); 
     800        } 
     801        else 
     802        { 
     803            if( !bMixedUpGeometries ) 
     804            { 
     805                CPLError(  
     806                    CE_Warning, CPLE_AppDefined,  
     807                    "organizePolygons() received an unexpected geometry.\n" 
     808                    "Either a polygon with interior rings, or a non-Polygon\n" 
     809                    "geometry.  Return arguments as a collection." ); 
     810                bMixedUpGeometries = TRUE; 
     811            } 
     812            if( wkbFlatten(papoPolygons[i]->getGeometryType()) != wkbPolygon ) 
     813                bNonPolygon = TRUE; 
     814        } 
     815             
     816    } 
     817         
     818    /* This a several step algorithm : 
     819       1) Compute in a matrix how polygons relate to each other 
     820       (this is the moment for detecting pathological intersections and exiting) 
     821       2) For each polygon, find the smallest enclosing polygon 
     822       3) For each polygon, compute its inclusion depth (0 means toplevel) 
     823       4) For each polygon of odd depth (= inner ring), add it to its outer ring 
     824         
     825       Complexity : O(nPolygonCount^2) 
     826    */ 
     827         
     828    /* Compute how each polygon relate to the other ones 
     829       To save a bit of computation we always begin the computation by a test  
     830       on the enveloppe. We also take into account the areas to avoid some  
     831       useless tests.  (A contains B implies envelop(A) contains envelop(B)  
     832       and area(A) > area(B)) In practise, we can hope that few full geometry  
     833       intersection of inclusion test is done: 
     834       * if the polygons are well separated geographically (a set of islands  
     835       for example), no full geometry intersection or inclusion test is done.  
     836       (the envelopes don't intersect each other) 
     837 
     838       * if the polygons are 'lake inside an island inside a lake inside an  
     839       area' and that each polygon is much smaller than its enclosing one,  
     840       their bounding boxes are stricly contained into each oter, and thus,  
     841       no full geometry intersection or inclusion test is done 
     842    */ 
     843 
     844/* -------------------------------------------------------------------- */ 
     845/*      Compute relationships, if things seem well structured.          */ 
     846/* -------------------------------------------------------------------- */ 
     847    for(i=0; !bMixedUpGeometries && go_on && i<nPolygonCount; i++) 
     848    { 
     849        for(j=i+1; go_on && j<nPolygonCount;j++) 
     850        { 
     851            if (areas[i] > areas[j] 
     852                && envelopes[i].Contains(envelopes[j]) 
     853                && (!haveGEOS() ||papoPolygons[i]->Contains(papoPolygons[j]))) 
     854            { 
     855                relations[i][j] = CONTAINS; 
     856                relations[j][i] = IS_CONTAINED_BY; 
     857            } 
     858            else if (areas[j] > areas[i] 
     859                     && envelopes[j].Contains(envelopes[i]) 
     860                     && (!haveGEOS()  
     861                         || papoPolygons[j]->Contains(papoPolygons[i])) ) 
     862            { 
     863                relations[j][i] = CONTAINS; 
     864                relations[i][j] = IS_CONTAINED_BY; 
     865            } 
     866 
     867            /* We use Overlaps instead of Intersects to be more  
     868               tolerant about touching polygons */  
     869            else if ( !envelopes[i].Intersects(envelopes[j]) 
     870                     || (haveGEOS() && !papoPolygons[i]->Overlaps(papoPolygons[j])) ) 
     871            { 
     872                relations[i][j] = NOT_RELATED; 
     873                relations[j][i] = NOT_RELATED; 
     874            } 
     875            else 
     876            { 
     877                /* Bad... The polygons are intersecting but no one is 
     878                   contained inside the other one. This is a really broken 
     879                   case. We just make a multipolygon with the whole set of 
     880                   polygons */ 
     881                go_on = FALSE; 
     882#ifdef notdef 
     883                CPLDebug( "OGR",  
     884                          "Bad intersection for polygons %d and %d\n" 
     885                          "geom %d: %s\n" 
     886                          "geom %d: %s",  
     887                          i, j, i, wkt1, j, wkt2 ); 
     888                char* wkt1; 
     889                char* wkt2; 
     890                papoPolygons[i]->exportToWkt(&wkt1); 
     891                papoPolygons[j]->exportToWkt(&wkt2); 
     892                fprintf(stderr, "geom %d : %s\n", i, wkt1); 
     893                fprintf(stderr, "geom %d : %s\n", j, wkt2); 
     894                CPLFree(wkt1); 
     895                CPLFree(wkt2); 
     896#endif 
     897            } 
     898        } 
     899    } 
     900 
     901    if (pbIsValidGeometry) 
     902        *pbIsValidGeometry = go_on && !bMixedUpGeometries; 
     903 
     904/* -------------------------------------------------------------------- */ 
     905/*      Things broke down - just turn everything into a multipolygon.   */ 
     906/* -------------------------------------------------------------------- */ 
     907    if ( !go_on || bMixedUpGeometries ) 
     908    { 
     909        if( bNonPolygon ) 
     910            geom = new OGRGeometryCollection(); 
     911        else 
     912            geom = new OGRMultiPolygon(); 
     913 
     914        for( i=0; i < nPolygonCount; i++ ) 
     915        { 
     916            ((OGRGeometryCollection*)geom)-> 
     917                addGeometryDirectly( papoPolygons[i] ); 
     918        } 
     919    } 
     920 
     921/* -------------------------------------------------------------------- */ 
     922/*      Try to turn into one or more polygons based on the ring         */ 
     923/*      relationships.                                                  */ 
     924/* -------------------------------------------------------------------- */ 
     925    else 
     926    { 
     927        int* directContainerIndex = new int[nPolygonCount]; 
     928 
     929        /* Find the smallest enclosing polygon of each polygon */ 
     930        for(i=0;i<nPolygonCount;i++) 
     931        { 
     932            int jSmallestContainer = -1; 
     933            double areaSmallestContainer = 0; 
     934            for(j=0;j<nPolygonCount;j++) 
     935            { 
     936                if (i != j) 
     937                { 
     938                    if (relations[i][j] == IS_CONTAINED_BY) 
     939                    { 
     940                        if (jSmallestContainer < 0 || areas[j] < areaSmallestContainer) 
     941                        { 
     942                            jSmallestContainer = j; 
     943                            areaSmallestContainer = areas[j]; 
     944                        } 
     945                    } 
     946                } 
     947            } 
     948            directContainerIndex[i] = jSmallestContainer; 
     949        } 
     950 
     951        /* Compute the inclusion depth of each polygon */ 
     952        int* containedDepth = new int [nPolygonCount]; 
     953        for(i=0;i<nPolygonCount;i++) 
     954        { 
     955            int depth = 0; 
     956            int j = directContainerIndex[i]; 
     957            while (j >= 0) 
     958            { 
     959                j = directContainerIndex[j]; 
     960                depth++; 
     961            } 
     962            containedDepth[i] = depth; 
     963//          fprintf(stderr, "%d is of depth %d\n", i, depth); 
     964        } 
     965 
     966        int nbTopLevelPolygons = 0; 
     967        OGRPolygon** tempPolygons = new OGRPolygon*[nPolygonCount];  
     968 
     969        /* Create a copy of toplevel polygons */ 
     970        for(i=0;i<nPolygonCount;i++) 
     971        { 
     972            if ((containedDepth[i] % 2) == 0) 
     973            { 
     974                nbTopLevelPolygons ++; 
     975                tempPolygons[i] = (OGRPolygon*)papoPolygons[i]; 
     976                papoPolygons[i] = NULL; 
     977                if (nbTopLevelPolygons == 1) 
     978                    geom = tempPolygons[i]; 
     979            } 
     980        } 
     981 
     982        /* Add interior rings to toplevel polygons */ 
     983        for(i=0;i<nPolygonCount;i++) 
     984        { 
     985            if ((containedDepth[i] % 2) == 1) 
     986            { 
     987                tempPolygons[directContainerIndex[i]]->addRing( 
     988                    ((OGRPolygon *)papoPolygons[i])->getExteriorRing()); 
     989                delete papoPolygons[i]; 
     990            } 
     991        } 
     992 
     993        if (nbTopLevelPolygons > 1) 
     994        { 
     995            geom = new OGRMultiPolygon(); 
     996 
     997            /* Add toplevel polygons to the multipolygon */ 
     998            for(i=0;i<nPolygonCount;i++) 
     999            { 
     1000                if ((containedDepth[i] % 2) == 0) 
     1001                { 
     1002                    ((OGRMultiPolygon*)geom)->addGeometryDirectly( 
     1003                        tempPolygons[i]); 
     1004                    tempPolygons[i] = NULL; 
     1005                } 
     1006            } 
     1007        } 
     1008 
     1009        delete[] tempPolygons; 
     1010        delete[] directContainerIndex; 
     1011        delete[] containedDepth; 
     1012    } 
     1013 
     1014    for(i=0;i<nPolygonCount;i++) 
     1015    { 
     1016        delete[] relations[i]; 
     1017    } 
     1018    delete[] relations; 
     1019    delete[] areas; 
     1020    delete[] envelopes; 
     1021 
     1022    return geom; 
     1023} 
     1024 
     1025/************************************************************************/ 
    7141026/*                           createFromGML()                            */ 
    7151027/************************************************************************/