| | 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 | |
|---|
| | 741 | enum |
|---|
| | 742 | { |
|---|
| | 743 | CONTAINS, |
|---|
| | 744 | IS_CONTAINED_BY, |
|---|
| | 745 | NOT_RELATED |
|---|
| | 746 | }; |
|---|
| | 747 | |
|---|
| | 748 | OGRGeometry* 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 | /************************************************************************/ |
|---|