Changeset 5240

Show
Ignore:
Timestamp:
02/14/10 14:59:57 (2 years ago)
Author:
strk
Message:

Implement ST_MakeValid(geom_in, collect_collapses) and stub ST_CleanGeometry. Add regression test for ST_MakeValid and polygons [RT-SIGTA]

Location:
trunk
Files:
2 added
3 modified

Legend:

Unmodified
Added
Removed
  • trunk/postgis/lwgeom_geos.c

    r5235 r5240  
    55 * http://postgis.refractions.net 
    66 * 
    7  * Copyright 2009 Sandro Santilli <strk@keybit.net> 
     7 * Copyright 2009-2010 Sandro Santilli <strk@keybit.net> 
    88 * Copyright 2008 Paul Ramsey <pramsey@cleverelephant.ca> 
    99 * Copyright 2001-2003 Refractions Research Inc. 
     
    2020 
    2121#include <string.h> 
    22  
    2322 
    2423/** 
     
    38733872} 
    38743873 
     3874/*------------------------------------------------------------- 
     3875 * 
     3876 * ST_MakeValid 
     3877 * 
     3878 * Attempts to make an invalid geometries valid w/out loosing 
     3879 * point sets. 
     3880 * 
     3881 * Polygons may become collection of polygons and lines. 
     3882 * Collapsed rings (or portions of rings) may be dissolved in 
     3883 * polygon area or transformed to linestring if outside any other 
     3884 * ring. 
     3885 * 
     3886 * Author: Sandro Santilli <strk@keybit.net> 
     3887 * 
     3888 * Work done for Regione Toscana - Sistema Informativo per il Governo 
     3889 * del Territorio e dell'Ambiente (RT-SIGTA). 
     3890 * 
     3891 * Thanks to Dr. Horst Duester for previous work on a plpgsql version 
     3892 * of the cleanup logic [1] 
     3893 * 
     3894 * Thanks to Andrea Peri for recommandations on constraints. 
     3895 * 
     3896 * [1] http://www.sogis1.so.ch/sogis/dl/postgis/cleanGeometry.sql 
     3897 * 
     3898 * 
     3899 *------------------------------------------------------------*/ 
     3900 
     3901LWGEOM * lwcollection_make_valid(LWCOLLECTION *g); 
     3902LWGEOM * lwline_make_valid(LWLINE *line); 
     3903LWGEOM * lwpoly_make_valid(LWPOLY *poly); 
     3904POINTARRAY* ring_make_valid(POINTARRAY* ring); 
     3905 
     3906/* 
     3907 * Ensure the geometry is "valid" 
     3908 * May return the input untouched (if already valid). 
     3909 * May return geometries of lower dimension (on collapses) 
     3910 */ 
     3911static LWGEOM * 
     3912lwgeom_make_valid(LWGEOM *geom) 
     3913{ 
     3914        LWDEBUGF(2, "lwgeom_make_valid enter (type %d)", TYPE_GETTYPE(geom->type)); 
     3915        switch (TYPE_GETTYPE(geom->type)) 
     3916        { 
     3917        case POINTTYPE: 
     3918        case MULTIPOINTTYPE: 
     3919                /* a point is always valid */ 
     3920                return geom; 
     3921                break; 
     3922 
     3923        case LINETYPE: 
     3924                /* lines need at least 2 points */ 
     3925                return lwline_make_valid((LWLINE *)geom); 
     3926                break; 
     3927 
     3928        case POLYGONTYPE: 
     3929                /* polygons need all rings closed */ 
     3930                return lwpoly_make_valid((LWPOLY *)geom); 
     3931                break; 
     3932 
     3933        case MULTILINETYPE: 
     3934        case MULTIPOLYGONTYPE: 
     3935        case COLLECTIONTYPE: 
     3936                return lwcollection_make_valid((LWCOLLECTION *)geom); 
     3937                break; 
     3938 
     3939        case CIRCSTRINGTYPE: 
     3940        case COMPOUNDTYPE: 
     3941        case CURVEPOLYTYPE: 
     3942        case MULTISURFACETYPE: 
     3943        case MULTICURVETYPE: 
     3944        default: 
     3945                lwerror("unsupported input geometry type: %d", TYPE_GETTYPE(geom->type)); 
     3946                break; 
     3947        } 
     3948        return 0; 
     3949} 
     3950 
     3951/* 
     3952 * Close the point array, if not already closed in 2d. 
     3953 * Returns the input if already closed in 2d, or a newly 
     3954 * constructed POINTARRAY. 
     3955 */ 
     3956POINTARRAY* ptarray_close2d(POINTARRAY* ring); 
     3957POINTARRAY* 
     3958ptarray_close2d(POINTARRAY* ring) 
     3959{ 
     3960        POINTARRAY* newring; 
     3961 
     3962        /* close the ring if not already closed (2d only) */ 
     3963        if ( ! ptarray_isclosed2d(ring) ) 
     3964        { 
     3965                /* close it up */ 
     3966                newring = ptarray_addPoint(ring, 
     3967                                           getPoint_internal(ring, 0), 
     3968                                           TYPE_NDIMS(ring->dims), 
     3969                                           ring->npoints); 
     3970                ring = newring; 
     3971        } 
     3972        return ring; 
     3973} 
     3974 
     3975/* May return the same input or a new one (never zero) */ 
     3976POINTARRAY* 
     3977ring_make_valid(POINTARRAY* ring) 
     3978{ 
     3979        POINTARRAY* closedring; 
     3980 
     3981        /* close the ring if not already closed (2d only) */ 
     3982        closedring = ptarray_close2d(ring); 
     3983        if (closedring != ring ) 
     3984        { 
     3985                ptarray_free(ring); /* should we do this ? */ 
     3986                ring = closedring; 
     3987        } 
     3988 
     3989        /* return 0 for collapsed ring (after closeup) */ 
     3990 
     3991        if ( ring->npoints < 4 ) 
     3992        { 
     3993                LWDEBUGF(4, "ring has %d points, adding another", ring->npoints); 
     3994                /* let's add another... */ 
     3995                closedring = ptarray_addPoint(closedring, 
     3996                                              getPoint_internal(closedring, 0), 
     3997                                              TYPE_NDIMS(closedring->dims), 
     3998                                              closedring->npoints); 
     3999                return closedring; 
     4000        } 
     4001 
     4002 
     4003        return ring; 
     4004} 
     4005 
     4006/* Return 0 if poly was collapsed, or the input with updated rings */ 
     4007LWGEOM * 
     4008lwpoly_make_valid(LWPOLY *poly) 
     4009{ 
     4010        LWGEOM* ret; 
     4011        POINTARRAY **new_rings; 
     4012        int new_nrings=0, i; 
     4013 
     4014        /* Allocate enough pointers for all rings */ 
     4015        new_rings = lwalloc(sizeof(POINTARRAY*)*poly->nrings); 
     4016 
     4017        /* All rings must be closed and have > 3 points */ 
     4018        for (i=0; i<poly->nrings; i++) 
     4019        { 
     4020                POINTARRAY* ring_in = poly->rings[i]; 
     4021                POINTARRAY* ring_out = ring_make_valid(ring_in); 
     4022 
     4023                if ( ring_in != ring_out ) 
     4024                { 
     4025                        LWDEBUGF(3, "lwpoly_make_valid: ring %d cleaned", i); 
     4026                        /* this may come right from 
     4027                         * the binary representation lands 
     4028                         */ 
     4029                        /*ptarray_free(ring_in); */ 
     4030                } 
     4031                else 
     4032                { 
     4033                        LWDEBUGF(3, "lwpoly_make_valid: ring %d untouched", i); 
     4034                } 
     4035 
     4036                if ( ring_out ) 
     4037                { 
     4038                        new_rings[new_nrings++] = ring_out; 
     4039                } 
     4040        } 
     4041 
     4042        if ( new_nrings ) 
     4043        { 
     4044                lwfree(poly->rings); 
     4045                poly->rings = new_rings; 
     4046                poly->nrings = new_nrings; 
     4047                ret = (LWGEOM*)poly; 
     4048        } 
     4049        else 
     4050        { 
     4051                /* was collapsed, will return zero */ 
     4052                LWDEBUG(3, "lwpoly_make_valid: all polygon rings collapsed"); 
     4053                lwpoly_release(poly); 
     4054 
     4055                /* Make a POLYGON EMPTY or  COLLECTION EMPTY ? */ 
     4056                /* COLLECTION EMPTY */ 
     4057#if 0 
     4058                ret = (LWGEOM*)lwcollection_construct_empty(poly->SRID, 
     4059                        TYPE_HASZ(poly->type), TYPE_HASM(poly->type)); 
     4060#else 
     4061                /* POLYGON EMPTY */ 
     4062                lwfree(poly->rings); 
     4063                poly->rings = new_rings; 
     4064                poly->nrings = new_nrings; 
     4065                ret = (LWGEOM*)poly; 
     4066#endif 
     4067        } 
     4068 
     4069        return ret; 
     4070} 
     4071 
     4072LWGEOM * 
     4073lwline_make_valid(LWLINE *line) 
     4074{ 
     4075        LWGEOM *ret; 
     4076 
     4077        if (line->points->npoints == 1) /* 0 is fine, 2 is fine */ 
     4078        { 
     4079                /* Turn into a point (dropping bbox, as we don't 
     4080                 * need it for points) 
     4081                 */ 
     4082                ret = (LWGEOM*)lwpoint_construct(line->SRID, 0, line->points); 
     4083                return ret; 
     4084        } 
     4085        else 
     4086        { 
     4087                return (LWGEOM*)line; 
     4088                /* return lwline_clone(line); */ 
     4089        } 
     4090} 
     4091 
     4092LWGEOM * 
     4093lwcollection_make_valid(LWCOLLECTION *g) 
     4094{ 
     4095        LWGEOM **new_geoms; 
     4096        uint32 i, new_ngeoms=0; 
     4097        LWCOLLECTION *ret; 
     4098 
     4099        /* enough space for all components */ 
     4100        new_geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms); 
     4101 
     4102        ret = lwalloc(sizeof(LWCOLLECTION)); 
     4103        memcpy(ret, g, sizeof(LWCOLLECTION)); 
     4104 
     4105        for (i=0; i<g->ngeoms; i++) 
     4106        { 
     4107                LWGEOM* newg = lwgeom_make_valid(g->geoms[i]); 
     4108                if ( newg ) new_geoms[new_ngeoms++] = newg; 
     4109        } 
     4110 
     4111        ret->bbox = 0; /* recompute later... */ 
     4112 
     4113        ret->ngeoms = new_ngeoms; 
     4114        if ( new_ngeoms ) 
     4115        { 
     4116                ret->geoms = new_geoms; 
     4117        } 
     4118        else 
     4119        { 
     4120                free(new_geoms); 
     4121                ret->geoms = 0; 
     4122        } 
     4123 
     4124        return (LWGEOM*)ret; 
     4125} 
     4126 
     4127Datum st_makevalid(PG_FUNCTION_ARGS); 
     4128PG_FUNCTION_INFO_V1(st_makevalid); 
     4129Datum st_makevalid(PG_FUNCTION_ARGS) 
     4130{ 
     4131        PG_LWGEOM *in, *out; 
     4132        GEOSGeom geosgeom, geos_bound, geos_bound_noded, geos_tmp_point; 
     4133        GEOSGeom geos_cut_edges, geos_area; 
     4134        LWGEOM *lwgeom_in, *lwgeom_out; 
     4135        /* LWGEOM *lwgeom_pointset; */ 
     4136        char ret_char; 
     4137        int is3d; 
     4138        int nargs; 
     4139        int collect_collapses = false; 
     4140 
     4141        in = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 
     4142        lwgeom_in = lwgeom_deserialize(SERIALIZED_FORM(in)); 
     4143 
     4144        is3d = TYPE_HASZ(lwgeom_in->type); 
     4145 
     4146        nargs = PG_NARGS(); 
     4147        if (nargs > 1) 
     4148        { 
     4149                collect_collapses = PG_GETARG_BOOL(1); 
     4150        } 
     4151 
     4152        /* 
     4153         * Step 1 : try to convert to GEOS, if impossible, clean that up first 
     4154         *          otherwise (adding only duplicates of existing points) 
     4155         */ 
     4156 
     4157        initGEOS(errorlogger, errorlogger); 
     4158 
     4159 
     4160        lwgeom_out = lwgeom_in; 
     4161        geosgeom = LWGEOM2GEOS(lwgeom_out); 
     4162        if ( ! geosgeom ) 
     4163        { 
     4164                POSTGIS_DEBUGF(4, 
     4165                               "Original geom can't be converted to GEOS (%s)" 
     4166                               " - will try cleaning that up first", 
     4167                               loggederror); 
     4168 
     4169 
     4170                lwgeom_out = lwgeom_make_valid(lwgeom_out); 
     4171                if ( ! lwgeom_out ) 
     4172                { 
     4173                        lwerror("Could not make a valid geometry out of input"); 
     4174                } 
     4175 
     4176                /* try again as we did cleanup now */ 
     4177                geosgeom = LWGEOM2GEOS(lwgeom_out); 
     4178                if ( ! geosgeom ) 
     4179                { 
     4180                        lwerror("Couldn't convert POSTGIS geom to GEOS: %s", 
     4181                                loggederror); 
     4182                        PG_RETURN_NULL(); 
     4183                } 
     4184 
     4185        } 
     4186        else 
     4187        { 
     4188                POSTGIS_DEBUG(4, "original geom converted to GEOS"); 
     4189                lwgeom_out = lwgeom_in; 
     4190        } 
     4191 
     4192 
     4193        /* 
     4194         * Step 2 : return the resulting geometry if it's now valid 
     4195         */ 
     4196 
     4197        ret_char = GEOSisValid(geosgeom); 
     4198        if ( ret_char == 2 ) 
     4199        { 
     4200                lwerror("GEOSisValid() threw an error: %s", loggederror); 
     4201                PG_RETURN_NULL(); /* I don't think should ever happen */ 
     4202        } 
     4203        else if ( ret_char ) 
     4204        { 
     4205                /* It's valid at this step, return what we have */ 
     4206 
     4207                GEOSGeom_destroy(geosgeom); 
     4208                geosgeom=0; 
     4209 
     4210                out = pglwgeom_serialize(lwgeom_out); 
     4211                lwgeom_release(lwgeom_out); 
     4212                lwgeom_out=0; 
     4213 
     4214                PG_FREE_IF_COPY(in, 0); 
     4215 
     4216                PG_RETURN_POINTER(out); 
     4217        } 
     4218 
     4219        POSTGIS_DEBUGF(3, 
     4220                       "Geometry [%s] is still not valid:", 
     4221                       lwgeom_to_ewkt(lwgeom_out, PARSER_CHECK_NONE)); 
     4222        POSTGIS_DEBUGF(3, " %s", loggederror); 
     4223        POSTGIS_DEBUG(3, " will try to clean up further"); 
     4224 
     4225        /* 
     4226         * Step 3 : make what we got now (geosgeom) valid 
     4227         */ 
     4228 
     4229        switch (GEOSGeomTypeId(geosgeom)) 
     4230        { 
     4231        case GEOS_MULTIPOINT: 
     4232        case GEOS_POINT: 
     4233                /* points are always valid, but we might have invalid ordinate values */ 
     4234                lwerror("PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up"); 
     4235                break; 
     4236 
     4237        case GEOS_LINESTRING: 
     4238        case GEOS_MULTILINESTRING: 
     4239                lwerror("ST_MakeValid: doesn't support linear types"); 
     4240                break; 
     4241 
     4242        case GEOS_POLYGON: 
     4243        case GEOS_MULTIPOLYGON: 
     4244                geos_bound = GEOSBoundary(geosgeom); 
     4245                if ( NULL == geos_bound ) 
     4246                { 
     4247                        GEOSGeom_destroy(geosgeom); 
     4248                        geosgeom=0; 
     4249                        lwgeom_release(lwgeom_in); 
     4250                        lwgeom_in=0; 
     4251                        lwgeom_release(lwgeom_out); 
     4252                        lwgeom_out=0; 
     4253                        PG_FREE_IF_COPY(in, 0); 
     4254                        lwerror("GEOSboundary() threw an error: %s", loggederror); 
     4255                        PG_RETURN_NULL(); /* never get here */ 
     4256                } 
     4257 
     4258                POSTGIS_DEBUGF(3, 
     4259                               "Boundaries: %s", 
     4260                               lwgeom_to_ewkt(GEOS2LWGEOM(geos_bound, is3d), 
     4261                                              PARSER_CHECK_NONE)); 
     4262 
     4263                /* 
     4264                 * Union with an empty point, obtaining full noding 
     4265                 * and dissolving of duplicated repeated points 
     4266                 * 
     4267                 * TODO: substitute this with UnaryUnion? 
     4268                 * 
     4269                 * need a point on the line here rather 
     4270                 * than an arbitrary one ? 
     4271                 */ 
     4272                geos_tmp_point = GEOSGeom_createPoint(0); 
     4273                // GEOSPointOnSurface(geos_bound); 
     4274                if ( ! geos_tmp_point ) 
     4275                { 
     4276                        lwerror("GEOSGeom_createPoint(0): %s", loggederror); 
     4277                        PG_RETURN_NULL(); /* never get here */ 
     4278                } 
     4279 
     4280                geos_bound_noded = GEOSUnion(geos_bound, geos_tmp_point); 
     4281                if ( NULL == geos_bound_noded ) 
     4282                { 
     4283                        GEOSGeom_destroy(geosgeom); 
     4284                        geosgeom=0; 
     4285                        GEOSGeom_destroy(geos_tmp_point); 
     4286                        geos_tmp_point=0; 
     4287                        geos_tmp_point=0; 
     4288                        GEOSGeom_destroy(geos_bound); 
     4289                        geos_bound=0; 
     4290                        lwgeom_release(lwgeom_in); 
     4291                        lwgeom_in=0; 
     4292                        lwgeom_release(lwgeom_out); 
     4293                        lwgeom_out=0; 
     4294                        PG_FREE_IF_COPY(in, 0); 
     4295                        lwerror("GEOSUnion() threw an error: %s", loggederror); 
     4296                        PG_RETURN_NULL(); /* never get here */ 
     4297                } 
     4298 
     4299                POSTGIS_DEBUGF(3, 
     4300                               "Noded: %s", 
     4301                               lwgeom_to_ewkt(GEOS2LWGEOM(geos_bound_noded, is3d), 
     4302                                              PARSER_CHECK_NONE)); 
     4303 
     4304                GEOSGeom_destroy(geos_tmp_point); 
     4305                geos_tmp_point=0; 
     4306                GEOSGeom_destroy(geosgeom); 
     4307                geosgeom=0; 
     4308 
     4309                geos_area = LWGEOM_GEOS_buildArea(geos_bound_noded); 
     4310                if ( ! geos_area ) /* must be an exception ... */ 
     4311                { 
     4312                        /* cleanup and throw */ 
     4313                        GEOSGeom_destroy(geos_bound); 
     4314                        geos_bound=0; 
     4315                        GEOSGeom_destroy(geos_bound_noded); 
     4316                        geos_bound_noded=0; 
     4317                        PG_FREE_IF_COPY(in, 0); 
     4318                        lwerror("LWGEOM_GEOS_buildArea() threw an error: %s", 
     4319                                loggederror); 
     4320                        PG_RETURN_NULL(); /* never get here */ 
     4321                } 
     4322 
     4323                if ( ! collect_collapses ) 
     4324                { 
     4325                        geosgeom = geos_area; 
     4326                } 
     4327                else /* collect_collapses */ 
     4328                { 
     4329                        /* Compute what's left out from original boundary 
     4330                         * (this would be the so called 'cut lines' */ 
     4331                        geos_cut_edges = GEOSDifference(geos_bound_noded, geos_area); 
     4332                        if ( ! geos_cut_edges )   /* an exception again */ 
     4333                        { 
     4334                                /* cleanup and throw */ 
     4335                                GEOSGeom_destroy(geos_bound); 
     4336                                geos_bound=0; 
     4337                                GEOSGeom_destroy(geos_area); 
     4338                                geos_area=0; 
     4339                                GEOSGeom_destroy(geos_bound_noded); 
     4340                                geos_bound_noded=0; 
     4341                                PG_FREE_IF_COPY(in, 0); 
     4342                                lwerror("GEOSDifference() threw an error: %s", 
     4343                                        loggederror); 
     4344                                PG_RETURN_NULL(); /* never get here */ 
     4345                        } 
     4346 
     4347                        GEOSGeom_destroy(geos_bound); 
     4348                        geos_bound=0; 
     4349                        GEOSGeom_destroy(geos_bound_noded); 
     4350                        geos_bound_noded=0; 
     4351 
     4352                        /* Finally put togheter cut edges and area 
     4353                         * (could become a collection) */ 
     4354                        geosgeom = GEOSUnion(geos_area, geos_cut_edges); 
     4355                        if ( ! geosgeom )   /* an exception again */ 
     4356                        { 
     4357                                /* cleanup and throw */ 
     4358                                GEOSGeom_destroy(geos_area); 
     4359                                geos_area=0; 
     4360                                GEOSGeom_destroy(geos_cut_edges); 
     4361                                geos_cut_edges=0; 
     4362                                PG_FREE_IF_COPY(in, 0); 
     4363                                lwerror("GEOSUnion() threw an error: %s", 
     4364                                        loggederror); 
     4365                                PG_RETURN_NULL(); /* never get here */ 
     4366                        } 
     4367                } 
     4368 
     4369                break; /* we've done */ 
     4370 
     4371        default: 
     4372        { 
     4373                char* tmp = GEOSGeomType(geosgeom); 
     4374                char* typname = pstrdup(tmp); 
     4375                GEOSFree(tmp); 
     4376                lwerror("ST_MakeValid: doesn't support geometry type: %d", 
     4377                        typname); 
     4378                break; 
     4379        } 
     4380        } 
     4381 
     4382 
     4383        if ( ! geosgeom ) PG_RETURN_NULL(); 
     4384 
     4385        /* Now check if every point of input is also found 
     4386         * in output, or abort by returning NULL 
     4387         * 
     4388         * Input geometry was lwgeom_in 
     4389         */ 
     4390 
     4391        out = GEOS2POSTGIS(geosgeom, is3d); 
     4392        PG_RETURN_POINTER(out); 
     4393} 
  • trunk/postgis/postgis.sql.in.c

    r5204 r5240  
    40164016 
    40174017-------------------------------------------------------------------------------- 
     4018-- ST_CleanGeometry / ST_MakeValid 
     4019-------------------------------------------------------------------------------- 
     4020 
     4021-- ST_MakeValid(in geometry) 
     4022-- 
     4023-- Makes the input valid discarding dimensional collapses 
     4024-- 
     4025-- Availability: 2.0.0 
     4026CREATE OR REPLACE FUNCTION ST_MakeValid(geometry) 
     4027       RETURNS geometry 
     4028       AS 'MODULE_PATHNAME', 'st_makevalid' 
     4029       LANGUAGE 'C' IMMUTABLE STRICT 
     4030       COST 100; 
     4031 
     4032-- ST_MakeValid(in geometry, collect_collapses bool) 
     4033-- 
     4034-- Makes the input valid, optionally collecting dimensional collapses. 
     4035-- 
     4036-- Availability: 2.0.0 
     4037CREATE OR REPLACE FUNCTION ST_MakeValid(geometry, bool) 
     4038       RETURNS geometry 
     4039       AS 'MODULE_PATHNAME', 'st_makevalid' 
     4040       LANGUAGE 'C' IMMUTABLE STRICT 
     4041       COST 100; 
     4042 
     4043-- ST_CleanGeometry(in geometry) 
     4044-- 
     4045-- Make input: 
     4046--      - Simple (if lineal) 
     4047--      - Valid (if polygonal) 
     4048--      - Obeying the RHR (if polygonal) 
     4049--      - Simplified of consecutive duplicated points  
     4050-- Ensuring: 
     4051--      - No input vertexes are discarded 
     4052--      - Output geometry type matches input 
     4053-- 
     4054-- Failing any constraint makes this function 
     4055-- return NULL. 
     4056--  
     4057-- Availability: 2.0.0 
     4058-- 
     4059CREATE OR REPLACE FUNCTION ST_CleanGeometry(geometry) 
     4060       RETURNS geometry 
     4061       AS $$ 
     4062DECLARE 
     4063  gin alias for $1; 
     4064  pin geometry; 
     4065  pout geometry; 
     4066  pdif geometry; 
     4067  gout geometry; 
     4068BEGIN 
     4069 
     4070  RAISE DEBUG 'ST_CleanGeometry: in: %', ST_GeometryType(gin); 
     4071 
     4072  SELECT INTO pin ST_Union(geom) 
     4073    FROM (select (ST_DumpPoints(gin)).geom) as foo; 
     4074 
     4075  RAISE DEBUG 'ST_CleanGeometry: in points: %', ST_asText(pin); 
     4076 
     4077  gout := _ST_CleanGeometry(gin); 
     4078 
     4079  SELECT INTO pout ST_Union(geom) 
     4080    FROM (select (ST_DumpPoints(gout)).geom) as foo; 
     4081 
     4082  RAISE DEBUG 'ST_CleanGeometry: out points: %', ST_asText(pout); 
     4083 
     4084  -- Check dimensionality is the same as input 
     4085  IF ST_Dimension(gin) != ST_Dimension(gout) THEN 
     4086    RAISE NOTICE 'ST_CleanGeometry: dimensional collapse (% to %)', 
     4087      ST_Dimension(gin), ST_Dimension(gout); 
     4088    RETURN NULL; 
     4089  END IF; 
     4090 
     4091  -- Now make sure all input points are also in output and if they  
     4092  -- are not, return null 
     4093  pdif := ST_Difference(pin, pout); 
     4094  IF NOT ST_isEmpty(pdif) THEN 
     4095    RAISE NOTICE 'ST_CleanGeometry: dropped vertices: %', ST_asText(pdif); 
     4096    RETURN NULL; 
     4097  END IF; 
     4098 
     4099  -- Force right-hand-rule (will only affect polygons) 
     4100  gout := ST_ForceRHR(gout); 
     4101 
     4102  -- Remove repeated duplicated points  
     4103  -- TODO!! 
     4104 
     4105  RETURN gout; 
     4106 
     4107END 
     4108 
     4109$$ LANGUAGE plpgsql; 
     4110 
     4111-------------------------------------------------------------------------------- 
    40184112-- Aggregates and their supporting functions 
    40194113-------------------------------------------------------------------------------- 
  • trunk/regress/Makefile.in

    r5148 r5240  
    7070        dumppoints \ 
    7171        wmsservers \ 
    72         tickets 
     72        tickets \ 
     73        clean 
    7374 
    7475# Styled buffer only if GEOS >= 3.2