diff --git a/Project.toml b/Project.toml index 2d738c1..4214dfd 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "LibGEOS" uuid = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb" license = "MIT" -version = "0.9.6" +version = "0.9.7" [deps] CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" @@ -21,11 +21,12 @@ LibGEOSRecipesBaseExt = "RecipesBase" Aqua = "0.8" CEnum = "0.2, 0.3, 0.4, 0.5" Extents = "0.1.1" -GEOS_jll = "3.13" +GEOS_jll = "3.14" GeoInterface = "1.5" Makie = "0.23, 0.24" Plots = "1" RecipesBase = "1" +TaskLocalValues = "0.1" Test = "<0.0.1,1" julia = "1.9" @@ -33,7 +34,8 @@ julia = "1.9" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +TaskLocalValues = "ed4db957-447d-4319-bfb6-7fa9ae7ecf34" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "Makie", "Plots", "RecipesBase", "Test"] +test = ["Aqua", "Makie", "Plots", "RecipesBase", "TaskLocalValues", "Test"] diff --git a/src/generated/libgeos_api.jl b/src/generated/libgeos_api.jl index e9c272f..d3d72d6 100644 --- a/src/generated/libgeos_api.jl +++ b/src/generated/libgeos_api.jl @@ -28,10 +28,18 @@ const GEOSBufParams_t = Cvoid const GEOSBufferParams = GEOSBufParams_t +const GEOSCoverageCleanParams_t = Cvoid + +const GEOSCoverageCleanParams = GEOSCoverageCleanParams_t + const GEOSMakeValidParams_t = Cvoid const GEOSMakeValidParams = GEOSMakeValidParams_t +const GEOSClusterInfo_t = Cvoid + +const GEOSClusterInfo = GEOSClusterInfo_t + const GEOSGeom = Ptr{GEOSGeometry} const GEOSCoordSeq = Ptr{GEOSCoordSequence} @@ -62,6 +70,13 @@ end GEOS_WKB_ISO = 2 end +@cenum GEOSOverlapMerge::UInt32 begin + GEOS_MERGE_LONGEST_BORDER = 0 + GEOS_MERGE_MAX_AREA = 1 + GEOS_MERGE_MIN_AREA = 2 + GEOS_MERGE_MIN_INDEX = 3 +end + # typedef void ( * GEOSQueryCallback ) ( void * item , void * userdata ) const GEOSQueryCallback = Ptr{Cvoid} @@ -71,19 +86,37 @@ const GEOSDistanceCallback = Ptr{Cvoid} # typedef int ( * GEOSTransformXYCallback ) ( double * x , double * y , void * userdata ) const GEOSTransformXYCallback = Ptr{Cvoid} +# typedef int ( * GEOSTransformXYZCallback ) ( double * x , double * y , double * z , void * userdata ) +const GEOSTransformXYZCallback = Ptr{Cvoid} + # typedef void ( GEOSInterruptCallback ) ( void ) const GEOSInterruptCallback = Cvoid +# typedef int ( GEOSContextInterruptCallback ) ( void * ) +const GEOSContextInterruptCallback = Cvoid + function GEOS_interruptRegisterCallback(cb) @ccall libgeos.GEOS_interruptRegisterCallback( cb::Ptr{GEOSInterruptCallback}, )::Ptr{GEOSInterruptCallback} end +function GEOSContext_setInterruptCallback_r(extHandle, cb, userData) + @ccall libgeos.GEOSContext_setInterruptCallback_r( + extHandle::GEOSContextHandle_t, + cb::Ptr{GEOSContextInterruptCallback}, + userData::Ptr{Cvoid}, + )::Ptr{GEOSContextInterruptCallback} +end + function GEOS_interruptRequest() @ccall libgeos.GEOS_interruptRequest()::Cvoid end +function GEOS_interruptThread() + @ccall libgeos.GEOS_interruptThread()::Cvoid +end + function GEOS_interruptCancel() @ccall libgeos.GEOS_interruptCancel()::Cvoid end @@ -134,6 +167,15 @@ function GEOSCoordSeq_create_r(handle, size, dims) )::Ptr{GEOSCoordSequence} end +function GEOSCoordSeq_createWithDimensions_r(handle, size, hasZ, hasM) + @ccall libgeos.GEOSCoordSeq_createWithDimensions_r( + handle::GEOSContextHandle_t, + size::Cuint, + hasZ::Cint, + hasM::Cint, + )::Ptr{GEOSCoordSequence} +end + function GEOSCoordSeq_copyFromBuffer_r(handle, buf, size, hasZ, hasM) @ccall libgeos.GEOSCoordSeq_copyFromBuffer_r( handle::GEOSContextHandle_t, @@ -190,6 +232,20 @@ function GEOSCoordSeq_destroy_r(handle, s) )::Cvoid end +function GEOSCoordSeq_hasZ_r(handle, s) + @ccall libgeos.GEOSCoordSeq_hasZ_r( + handle::GEOSContextHandle_t, + s::Ptr{GEOSCoordSequence}, + )::Cchar +end + +function GEOSCoordSeq_hasM_r(handle, s) + @ccall libgeos.GEOSCoordSeq_hasM_r( + handle::GEOSContextHandle_t, + s::Ptr{GEOSCoordSequence}, + )::Cchar +end + function GEOSCoordSeq_setX_r(handle, s, idx, val) @ccall libgeos.GEOSCoordSeq_setX_r( handle::GEOSContextHandle_t, @@ -217,6 +273,15 @@ function GEOSCoordSeq_setZ_r(handle, s, idx, val) )::Cint end +function GEOSCoordSeq_setM_r(handle, s, idx, val) + @ccall libgeos.GEOSCoordSeq_setM_r( + handle::GEOSContextHandle_t, + s::Ptr{GEOSCoordSequence}, + idx::Cuint, + val::Cdouble, + )::Cint +end + function GEOSCoordSeq_setXY_r(handle, s, idx, x, y) @ccall libgeos.GEOSCoordSeq_setXY_r( handle::GEOSContextHandle_t, @@ -275,6 +340,15 @@ function GEOSCoordSeq_getZ_r(handle, s, idx, val) )::Cint end +function GEOSCoordSeq_getM_r(handle, s, idx, val) + @ccall libgeos.GEOSCoordSeq_getM_r( + handle::GEOSContextHandle_t, + s::Ptr{GEOSCoordSequence}, + idx::Cuint, + val::Ptr{Cdouble}, + )::Cint +end + function GEOSCoordSeq_getXY_r(handle, s, idx, x, y) @ccall libgeos.GEOSCoordSeq_getXY_r( handle::GEOSContextHandle_t, @@ -655,6 +729,62 @@ function GEOSCoverageSimplifyVW_r(extHandle, input, tolerance, preserveBoundary) )::Ptr{GEOSGeometry} end +function GEOSCoverageCleanParams_create_r(extHandle) + @ccall libgeos.GEOSCoverageCleanParams_create_r( + extHandle::GEOSContextHandle_t, + )::Ptr{GEOSCoverageCleanParams} +end + +function GEOSCoverageCleanParams_destroy_r(extHandle, params) + @ccall libgeos.GEOSCoverageCleanParams_destroy_r( + extHandle::GEOSContextHandle_t, + params::Ptr{GEOSCoverageCleanParams}, + )::Cvoid +end + +function GEOSCoverageCleanParams_setSnappingDistance_r(extHandle, params, snappingDistance) + @ccall libgeos.GEOSCoverageCleanParams_setSnappingDistance_r( + extHandle::GEOSContextHandle_t, + params::Ptr{GEOSCoverageCleanParams}, + snappingDistance::Cdouble, + )::Cint +end + +function GEOSCoverageCleanParams_setGapMaximumWidth_r(extHandle, params, gapMaximumWidth) + @ccall libgeos.GEOSCoverageCleanParams_setGapMaximumWidth_r( + extHandle::GEOSContextHandle_t, + params::Ptr{GEOSCoverageCleanParams}, + gapMaximumWidth::Cdouble, + )::Cint +end + +function GEOSCoverageCleanParams_setOverlapMergeStrategy_r( + extHandle, + params, + overlapMergeStrategy, +) + @ccall libgeos.GEOSCoverageCleanParams_setOverlapMergeStrategy_r( + extHandle::GEOSContextHandle_t, + params::Ptr{GEOSCoverageCleanParams}, + overlapMergeStrategy::Cint, + )::Cint +end + +function GEOSCoverageCleanWithParams_r(extHandle, input, params) + @ccall libgeos.GEOSCoverageCleanWithParams_r( + extHandle::GEOSContextHandle_t, + input::Ptr{GEOSGeometry}, + params::Ptr{GEOSCoverageCleanParams}, + )::Ptr{GEOSGeometry} +end + +function GEOSCoverageClean_r(extHandle, input) + @ccall libgeos.GEOSCoverageClean_r( + extHandle::GEOSContextHandle_t, + input::Ptr{GEOSGeometry}, + )::Ptr{GEOSGeometry} +end + function GEOSEnvelope_r(handle, g) @ccall libgeos.GEOSEnvelope_r( handle::GEOSContextHandle_t, @@ -900,6 +1030,20 @@ function GEOSClipByRect_r(handle, g, xmin, ymin, xmax, ymax) )::Ptr{GEOSGeometry} end +function GEOSGridIntersectionFractions_r(handle, g, xmin, ymin, xmax, ymax, nx, ny, buf) + @ccall libgeos.GEOSGridIntersectionFractions_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + xmin::Cdouble, + ymin::Cdouble, + xmax::Cdouble, + ymax::Cdouble, + nx::Cuint, + ny::Cuint, + buf::Ptr{Cfloat}, + )::Cint +end + function GEOSPolygonize_r(handle, geoms, ngeoms) @ccall libgeos.GEOSPolygonize_r( handle::GEOSContextHandle_t, @@ -1502,6 +1646,15 @@ function GEOSisValidReason_r(handle, g) ) end +function GEOSisSimpleDetail_r(handle, g, findAllLocations, location) + @ccall libgeos.GEOSisSimpleDetail_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + findAllLocations::Cint, + location::Ptr{Ptr{GEOSGeometry}}, + )::Cchar +end + function GEOSisValidDetail_r(handle, g, flags, reason, location) @ccall libgeos.GEOSisValidDetail_r( handle::GEOSContextHandle_t, @@ -1931,6 +2084,91 @@ function GEOSGeom_transformXY_r(handle, g, callback, userdata) )::Ptr{GEOSGeometry} end +function GEOSGeom_transformXYZ_r(handle, g, callback, userdata) + @ccall libgeos.GEOSGeom_transformXYZ_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + callback::GEOSTransformXYZCallback, + userdata::Ptr{Cvoid}, + )::Ptr{GEOSGeometry} +end + +function GEOSClusterDBSCAN_r(handle, g, eps, minPoints) + @ccall libgeos.GEOSClusterDBSCAN_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + eps::Cdouble, + minPoints::Cuint, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterGeometryDistance_r(handle, g, d) + @ccall libgeos.GEOSClusterGeometryDistance_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + d::Cdouble, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterGeometryIntersects_r(handle, g) + @ccall libgeos.GEOSClusterGeometryIntersects_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterEnvelopeDistance_r(handle, g, d) + @ccall libgeos.GEOSClusterEnvelopeDistance_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + d::Cdouble, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterEnvelopeIntersects_r(handle, g) + @ccall libgeos.GEOSClusterEnvelopeIntersects_r( + handle::GEOSContextHandle_t, + g::Ptr{GEOSGeometry}, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterInfo_getNumClusters_r(arg1, clusters) + @ccall libgeos.GEOSClusterInfo_getNumClusters_r( + arg1::GEOSContextHandle_t, + clusters::Ptr{GEOSClusterInfo}, + )::Csize_t +end + +function GEOSClusterInfo_getClusterSize_r(arg1, clusters, i) + @ccall libgeos.GEOSClusterInfo_getClusterSize_r( + arg1::GEOSContextHandle_t, + clusters::Ptr{GEOSClusterInfo}, + i::Csize_t, + )::Csize_t +end + +function GEOSClusterInfo_getClustersForInputs_r(arg1, clusters) + @ccall libgeos.GEOSClusterInfo_getClustersForInputs_r( + arg1::GEOSContextHandle_t, + clusters::Ptr{GEOSClusterInfo}, + )::Ptr{Csize_t} +end + +function GEOSClusterInfo_getInputsForClusterN_r(arg1, clusters, i) + @ccall libgeos.GEOSClusterInfo_getInputsForClusterN_r( + arg1::GEOSContextHandle_t, + clusters::Ptr{GEOSClusterInfo}, + i::Csize_t, + )::Ptr{Csize_t} +end + +function GEOSClusterInfo_destroy_r(arg1, info) + @ccall libgeos.GEOSClusterInfo_destroy_r( + arg1::GEOSContextHandle_t, + info::Ptr{GEOSClusterInfo}, + )::Cvoid +end + function GEOSOrientationIndex_r(handle, Ax, Ay, Bx, By, Px, Py) @ccall libgeos.GEOSOrientationIndex_r( handle::GEOSContextHandle_t, @@ -2235,6 +2473,21 @@ function GEOSGeoJSONWriter_writeGeometry_r(handle, writer, g, indent) ) end +function GEOSGeoJSONWriter_setOutputDimension_r(handle, writer, dim) + @ccall libgeos.GEOSGeoJSONWriter_setOutputDimension_r( + handle::GEOSContextHandle_t, + writer::Ptr{GEOSGeoJSONWriter}, + dim::Cint, + )::Cvoid +end + +function GEOSGeoJSONWriter_getOutputDimension_r(handle, writer) + @ccall libgeos.GEOSGeoJSONWriter_getOutputDimension_r( + handle::GEOSContextHandle_t, + writer::Ptr{GEOSGeoJSONWriter}, + )::Cint +end + function GEOSFree_r(handle, buffer) @ccall libgeos.GEOSFree_r(handle::GEOSContextHandle_t, buffer::Ptr{Cvoid})::Cvoid end @@ -2262,6 +2515,14 @@ function GEOSCoordSeq_create(size, dims) @ccall libgeos.GEOSCoordSeq_create(size::Cuint, dims::Cuint)::Ptr{GEOSCoordSequence} end +function GEOSCoordSeq_createWithDimensions(size, hasZ, hasM) + @ccall libgeos.GEOSCoordSeq_createWithDimensions( + size::Cuint, + hasZ::Cint, + hasM::Cint, + )::Ptr{GEOSCoordSequence} +end + function GEOSCoordSeq_copyFromBuffer(buf, size, hasZ, hasM) @ccall libgeos.GEOSCoordSeq_copyFromBuffer( buf::Ptr{Cdouble}, @@ -2308,6 +2569,14 @@ function GEOSCoordSeq_destroy(s) @ccall libgeos.GEOSCoordSeq_destroy(s::Ptr{GEOSCoordSequence})::Cvoid end +function GEOSCoordSeq_hasZ(s) + @ccall libgeos.GEOSCoordSeq_hasZ(s::Ptr{GEOSCoordSequence})::Cchar +end + +function GEOSCoordSeq_hasM(s) + @ccall libgeos.GEOSCoordSeq_hasM(s::Ptr{GEOSCoordSequence})::Cchar +end + function GEOSCoordSeq_setX(s, idx, val) @ccall libgeos.GEOSCoordSeq_setX( s::Ptr{GEOSCoordSequence}, @@ -2332,6 +2601,14 @@ function GEOSCoordSeq_setZ(s, idx, val) )::Cint end +function GEOSCoordSeq_setM(s, idx, val) + @ccall libgeos.GEOSCoordSeq_setM( + s::Ptr{GEOSCoordSequence}, + idx::Cuint, + val::Cdouble, + )::Cint +end + function GEOSCoordSeq_setXY(s, idx, x, y) @ccall libgeos.GEOSCoordSeq_setXY( s::Ptr{GEOSCoordSequence}, @@ -2384,6 +2661,14 @@ function GEOSCoordSeq_getZ(s, idx, val) )::Cint end +function GEOSCoordSeq_getM(s, idx, val) + @ccall libgeos.GEOSCoordSeq_getM( + s::Ptr{GEOSCoordSequence}, + idx::Cuint, + val::Ptr{Cdouble}, + )::Cint +end + function GEOSCoordSeq_getXY(s, idx, x, y) @ccall libgeos.GEOSCoordSeq_getXY( s::Ptr{GEOSCoordSequence}, @@ -2469,7 +2754,7 @@ function GEOSGeom_createCircularString(s) )::Ptr{GEOSGeometry} end -# no prototype is found for this function at geos_c.h:2519:31, please use with caution +# no prototype is found for this function at geos_c.h:2810:31, please use with caution function GEOSGeom_createEmptyCircularString() @ccall libgeos.GEOSGeom_createEmptyCircularString()::Ptr{GEOSGeometry} end @@ -2481,7 +2766,7 @@ function GEOSGeom_createCompoundCurve(curves, ncurves) )::Ptr{GEOSGeometry} end -# no prototype is found for this function at geos_c.h:2538:31, please use with caution +# no prototype is found for this function at geos_c.h:2829:31, please use with caution function GEOSGeom_createEmptyCompoundCurve() @ccall libgeos.GEOSGeom_createEmptyCompoundCurve()::Ptr{GEOSGeometry} end @@ -2494,7 +2779,7 @@ function GEOSGeom_createCurvePolygon(shell, holes, nholes) )::Ptr{GEOSGeometry} end -# no prototype is found for this function at geos_c.h:2564:31, please use with caution +# no prototype is found for this function at geos_c.h:2855:31, please use with caution function GEOSGeom_createEmptyCurvePolygon() @ccall libgeos.GEOSGeom_createEmptyCurvePolygon()::Ptr{GEOSGeometry} end @@ -2689,6 +2974,14 @@ function GEOSisSimple(g) @ccall libgeos.GEOSisSimple(g::Ptr{GEOSGeometry})::Cchar end +function GEOSisSimpleDetail(g, findAllLocations, locations) + @ccall libgeos.GEOSisSimpleDetail( + g::Ptr{GEOSGeometry}, + findAllLocations::Cint, + locations::Ptr{Ptr{GEOSGeometry}}, + )::Cchar +end + function GEOSisValid(g) @ccall libgeos.GEOSisValid(g::Ptr{GEOSGeometry})::Cchar end @@ -2938,6 +3231,19 @@ function GEOSClipByRect(g, xmin, ymin, xmax, ymax) )::Ptr{GEOSGeometry} end +function GEOSGridIntersectionFractions(g, xmin, ymin, xmax, ymax, nx, ny, buf) + @ccall libgeos.GEOSGridIntersectionFractions( + g::Ptr{GEOSGeometry}, + xmin::Cdouble, + ymin::Cdouble, + xmax::Cdouble, + ymax::Cdouble, + nx::Cuint, + ny::Cuint, + buf::Ptr{Cfloat}, + )::Cint +end + function GEOSSharedPaths(g1, g2) @ccall libgeos.GEOSSharedPaths( g1::Ptr{GEOSGeometry}, @@ -2945,6 +3251,64 @@ function GEOSSharedPaths(g1, g2) )::Ptr{GEOSGeometry} end +function GEOSClusterDBSCAN(g, eps, minPoints) + @ccall libgeos.GEOSClusterDBSCAN( + g::Ptr{GEOSGeometry}, + eps::Cdouble, + minPoints::Cuint, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterGeometryDistance(g, d) + @ccall libgeos.GEOSClusterGeometryDistance( + g::Ptr{GEOSGeometry}, + d::Cdouble, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterGeometryIntersects(g) + @ccall libgeos.GEOSClusterGeometryIntersects(g::Ptr{GEOSGeometry})::Ptr{GEOSClusterInfo} +end + +function GEOSClusterEnvelopeDistance(g, d) + @ccall libgeos.GEOSClusterEnvelopeDistance( + g::Ptr{GEOSGeometry}, + d::Cdouble, + )::Ptr{GEOSClusterInfo} +end + +function GEOSClusterEnvelopeIntersects(g) + @ccall libgeos.GEOSClusterEnvelopeIntersects(g::Ptr{GEOSGeometry})::Ptr{GEOSClusterInfo} +end + +function GEOSClusterInfo_getNumClusters(clusters) + @ccall libgeos.GEOSClusterInfo_getNumClusters(clusters::Ptr{GEOSClusterInfo})::Csize_t +end + +function GEOSClusterInfo_getClusterSize(clusters, i) + @ccall libgeos.GEOSClusterInfo_getClusterSize( + clusters::Ptr{GEOSClusterInfo}, + i::Csize_t, + )::Csize_t +end + +function GEOSClusterInfo_getClustersForInputs(clusters) + @ccall libgeos.GEOSClusterInfo_getClustersForInputs( + clusters::Ptr{GEOSClusterInfo}, + )::Ptr{Csize_t} +end + +function GEOSClusterInfo_getInputsForClusterN(clusters, i) + @ccall libgeos.GEOSClusterInfo_getInputsForClusterN( + clusters::Ptr{GEOSClusterInfo}, + i::Csize_t, + )::Ptr{Csize_t} +end + +function GEOSClusterInfo_destroy(clusters) + @ccall libgeos.GEOSClusterInfo_destroy(clusters::Ptr{GEOSClusterInfo})::Cvoid +end + function GEOSBuffer(g, width, quadsegs) @ccall libgeos.GEOSBuffer( g::Ptr{GEOSGeometry}, @@ -3045,6 +3409,49 @@ function GEOSCoverageSimplifyVW(input, tolerance, preserveBoundary) )::Ptr{GEOSGeometry} end +# no prototype is found for this function at geos_c.h:4443:1, please use with caution +function GEOSCoverageCleanParams_create() + @ccall libgeos.GEOSCoverageCleanParams_create()::Ptr{GEOSCoverageCleanParams} +end + +function GEOSCoverageCleanParams_destroy(params) + @ccall libgeos.GEOSCoverageCleanParams_destroy( + params::Ptr{GEOSCoverageCleanParams}, + )::Cvoid +end + +function GEOSCoverageCleanParams_setSnappingDistance(params, snappingDistance) + @ccall libgeos.GEOSCoverageCleanParams_setSnappingDistance( + params::Ptr{GEOSCoverageCleanParams}, + snappingDistance::Cdouble, + )::Cint +end + +function GEOSCoverageCleanParams_setGapMaximumWidth(params, gapMaximumWidth) + @ccall libgeos.GEOSCoverageCleanParams_setGapMaximumWidth( + params::Ptr{GEOSCoverageCleanParams}, + gapMaximumWidth::Cdouble, + )::Cint +end + +function GEOSCoverageCleanParams_setOverlapMergeStrategy(params, overlapMergeStrategy) + @ccall libgeos.GEOSCoverageCleanParams_setOverlapMergeStrategy( + params::Ptr{GEOSCoverageCleanParams}, + overlapMergeStrategy::Cint, + )::Cint +end + +function GEOSCoverageCleanWithParams(input, params) + @ccall libgeos.GEOSCoverageCleanWithParams( + input::Ptr{GEOSGeometry}, + params::Ptr{GEOSCoverageCleanParams}, + )::Ptr{GEOSGeometry} +end + +function GEOSCoverageClean(input) + @ccall libgeos.GEOSCoverageClean(input::Ptr{GEOSGeometry})::Ptr{GEOSGeometry} +end + function GEOSEnvelope(g) @ccall libgeos.GEOSEnvelope(g::Ptr{GEOSGeometry})::Ptr{GEOSGeometry} end @@ -3265,6 +3672,14 @@ function GEOSGeom_transformXY(g, callback, userdata) )::Ptr{GEOSGeometry} end +function GEOSGeom_transformXYZ(g, callback, userdata) + @ccall libgeos.GEOSGeom_transformXYZ( + g::Ptr{GEOSGeometry}, + callback::GEOSTransformXYZCallback, + userdata::Ptr{Cvoid}, + )::Ptr{GEOSGeometry} +end + function GEOSSnap(input, snap_target, tolerance) @ccall libgeos.GEOSSnap( input::Ptr{GEOSGeometry}, @@ -3785,6 +4200,19 @@ function GEOSGeoJSONWriter_writeGeometry(writer, g, indent) ) end +function GEOSGeoJSONWriter_setOutputDimension(writer, dim) + @ccall libgeos.GEOSGeoJSONWriter_setOutputDimension( + writer::Ptr{GEOSGeoJSONWriter}, + dim::Cint, + )::Cvoid +end + +function GEOSGeoJSONWriter_getOutputDimension(writer) + @ccall libgeos.GEOSGeoJSONWriter_getOutputDimension( + writer::Ptr{GEOSGeoJSONWriter}, + )::Cint +end + function GEOSSingleSidedBuffer(g, width, quadsegs, joinStyle, mitreLimit, leftSide) @ccall libgeos.GEOSSingleSidedBuffer( g::Ptr{GEOSGeometry}, @@ -3950,21 +4378,21 @@ end const GEOS_VERSION_MAJOR = 3 -const GEOS_VERSION_MINOR = 13 +const GEOS_VERSION_MINOR = 14 -const GEOS_VERSION_PATCH = 0 +const GEOS_VERSION_PATCH = 1 -const GEOS_VERSION = "3.13.0" +const GEOS_VERSION = "3.14.1" const GEOS_JTS_PORT = "1.18.0" const GEOS_CAPI_VERSION_MAJOR = 1 -const GEOS_CAPI_VERSION_MINOR = 19 +const GEOS_CAPI_VERSION_MINOR = 20 -const GEOS_CAPI_VERSION_PATCH = 0 +const GEOS_CAPI_VERSION_PATCH = 5 -const GEOS_CAPI_VERSION = "3.13.0-CAPI-1.19.0" +const GEOS_CAPI_VERSION = "3.14.1-CAPI-1.20.5" const GEOS_CAPI_FIRST_INTERFACE = GEOS_CAPI_VERSION_MAJOR diff --git a/src/geo_interface.jl b/src/geo_interface.jl index 67cc15f..002b590 100644 --- a/src/geo_interface.jl +++ b/src/geo_interface.jl @@ -330,6 +330,8 @@ for f in ( :envelope, :minimumRotatedRectangle, :convexhull, + :concavehull, + :concavehull_of_polygons, :boundary, :unaryUnion, :pointOnSurface, diff --git a/src/geos_functions.jl b/src/geos_functions.jl index ce8233f..d1e2db1 100644 --- a/src/geos_functions.jl +++ b/src/geos_functions.jl @@ -677,6 +677,53 @@ function convexhull(obj::Geometry, context::GEOSContext = get_context(obj)) geomFromGEOS(result, context) end +""" + concavehull(geom, ratio; bylength = false, allow_holes = true) + +Compute the concave hull of a geometry, according to the chi-shape generated from +`geom` with the given `ratio`. + +If `bylength` is true, the chi-shape is generated from the length of the geometry, +calling `GEOSConcaveHullByLength`; otherwise it is generated from the area, calling +`GEOSConcaveHull`. + +If `allow_holes` is true, the concave hull may have holes. If false, then holes are disallowed. + +See also [`convexhull`](@ref), [`concavehull_of_polygons`](@ref). +""" +function concavehull(obj::Geometry; ratio::Union{Real, Nothing} = nothing, length_ratio::Union{Real, Nothing} = nothing, allow_holes::Bool = true, context::GEOSContext = get_context(obj)) + result = if isnothing(ratio) && isnothing(length_ratio) + throw(ArgumentError("Either `ratio` or `length_ratio` must be provided to `LibGEOS.concavehull`, none were provided.")) + elseif !isnothing(ratio) && !isnothing(length_ratio) + throw(ArgumentError("Only one of `ratio` or `length_ratio` may be provided to `LibGEOS.concavehull`; got both.")) + elseif !isnothing(ratio) && isnothing(length_ratio) + GEOSConcaveHull_r(context, obj, ratio, allow_holes) + elseif isnothing(ratio) && !isnothing(length_ratio) + GEOSConcaveHullByLength_r(context, obj, ratio, allow_holes) + end + if result == C_NULL + error("LibGEOS: Error in GEOSConcaveHull") + end + geomFromGEOS(result, context) +end + +""" + concavehull_of_polygons(obj::Geometry, ratio; tight = false, allow_holes = true) + +Compute the concave hull of a geometry, according to the chi-shape generated from +`obj` with the given `ratio`. + +If `tight` is true, the chi-shape is generated from the tightest fit of the geometry, +otherwise it is generated from the area. +""" +function concavehull_of_polygons(obj::Geometry, ratio::Real; tight::Bool = false, allow_holes::Bool = true, context::GEOSContext = get_context(obj)) + result = GEOSConcaveHullOfPolygons_r(context, obj, ratio, tight, allow_holes) + if result == C_NULL + error("LibGEOS: Error in GEOSConcaveHullOfPolygons") + end + geomFromGEOS(result, context) +end + function difference( obj1::Geometry, obj2::Geometry, diff --git a/test/test_geo_interface.jl b/test/test_geo_interface.jl index d249e89..64bca44 100644 --- a/test/test_geo_interface.jl +++ b/test/test_geo_interface.jl @@ -1,6 +1,7 @@ using Test, Makie, Plots, GeoInterface, LibGEOS, Extents const GI = GeoInterface const LG = LibGEOS +import LibGEOS: Point @testset "Geo interface" begin pt = LibGEOS.Point(1.0, 2.0) @@ -299,8 +300,8 @@ const LG = LibGEOS @test GeoInterface.geomtrait(geomcollection) == GeometryCollectionTrait() @test GeoInterface.is3d(geomcollection) == false @test GeoInterface.testgeometry(geomcollection) - @testset "Conversion" begin + _concavehull(x) = LG.concavehull(x; ratio = 0.3) one_arg_functions = ( LG.area, LG.geomLength, @@ -316,6 +317,7 @@ const LG = LibGEOS LG.delaunayTriangulationEdges, LG.delaunayTriangulation, LG.constrainedDelaunayTriangulation, + _concavehull, # these have different signatures # LG.simplify, LG.topologyPreserveSimplify, ) @@ -356,12 +358,16 @@ const LG = LibGEOS @test geom isa MultiPoint @test GeoInterface.coordinates(geom) == coords for f in one_arg_functions - @test f(LibGEOS.MultiPoint(coords)) == f(GeoInterface.MultiPoint(coords)) + @testset let current_function = f + @test f(LibGEOS.MultiPoint(coords)) == f(GeoInterface.MultiPoint(coords)) + end end coords2 = [[0.0, 10], [0.5, 10], [20.0, 20], [10.0, 10], [0.0, 10]] for f in two_arg_functions - @test f(LibGEOS.LineString(coords), LibGEOS.LineString(coords)) == - f(GeoInterface.LineString(coords), GeoInterface.LineString(coords)) + @testset let current_function = f + @test f(LibGEOS.LineString(coords), LibGEOS.LineString(coords)) == + f(GeoInterface.LineString(coords), GeoInterface.LineString(coords)) + end end coords = [[0.0, 0], [0.0, 10], [10.0, 10], [10.0, 0], [0.0, 0]] @@ -369,12 +375,16 @@ const LG = LibGEOS @test geom isa LineString @test GeoInterface.coordinates(geom) == coords for f in one_arg_functions - @test f(LibGEOS.LineString(coords)) == f(GeoInterface.LineString(coords)) + @testset let current_function = f + @test f(LibGEOS.LineString(coords)) == f(GeoInterface.LineString(coords)) + end end coords2 = [[0.0, 10], [0.5, 10], [20.0, 20], [10.0, 10], [0.0, 10]] for f in two_arg_functions - @test f(LibGEOS.LineString(coords), LibGEOS.LineString(coords)) == - f(GeoInterface.LineString(coords), GeoInterface.LineString(coords)) + @testset let current_function = f + @test f(LibGEOS.LineString(coords), LibGEOS.LineString(coords)) == + f(GeoInterface.LineString(coords), GeoInterface.LineString(coords)) + end end coords = [[[0.0, 0], [0.0, 10], [10.0, 10], [10.0, 0], [0.0, 0]]] @@ -382,13 +392,22 @@ const LG = LibGEOS @test geom isa MultiLineString @test GeoInterface.coordinates(geom) == coords for f in one_arg_functions - @test f(LibGEOS.MultiLineString(coords)) == - f(GeoInterface.MultiLineString(coords)) + @testset let current_function = f + @test f(LibGEOS.MultiLineString(coords)) == + f(GeoInterface.MultiLineString(coords)) + end end coords2 = [[[0.0, 10], [0.5, 10], [20.0, 20], [10.0, 10], [0.0, 10]]] for f in two_arg_functions - @test f(LibGEOS.MultiLineString(coords), LibGEOS.MultiLineString(coords2)) == - f(GeoInterface.MultiLineString(coords), LibGEOS.MultiLineString(coords2)) + @testset let current_function = f + @test f( + LibGEOS.MultiLineString(coords), + LibGEOS.MultiLineString(coords2), + ) == f( + GeoInterface.MultiLineString(coords), + LibGEOS.MultiLineString(coords2), + ) + end end coords = [[[0.0, 0], [0.0, 10], [10.0, 10], [10.0, 0], [0.0, 0]]] @@ -399,12 +418,16 @@ const LG = LibGEOS @test GeoInterface.nhole(geom) == 0 @test GeoInterface.coordinates(geom) == coords for f in one_arg_functions - @test f(LibGEOS.Polygon(coords)) == f(GeoInterface.Polygon(coords)) + @testset let current_function = f + @test f(LibGEOS.Polygon(coords)) == f(GeoInterface.Polygon(coords)) + end end coords2 = [[[0.0, 10], [0.5, 10], [20.0, 20], [10.0, 10], [0.0, 10]]] for f in two_arg_functions - @test f(LibGEOS.Polygon(coords), LibGEOS.Polygon(coords2)) == - f(GeoInterface.Polygon(coords), LibGEOS.Polygon(coords2)) + @testset let current_function = f + @test f(LibGEOS.Polygon(coords), LibGEOS.Polygon(coords2)) == + f(GeoInterface.Polygon(coords), LibGEOS.Polygon(coords2)) + end end pgeom = LibGEOS.prepareGeom(geom) @@ -416,12 +439,17 @@ const LG = LibGEOS @test geom isa MultiPolygon @test GeoInterface.coordinates(geom) == coords for f in one_arg_functions - @test f(LibGEOS.MultiPolygon(coords)) == f(GeoInterface.MultiPolygon(coords)) + @testset let current_function = f + @test f(LibGEOS.MultiPolygon(coords)) == + f(GeoInterface.MultiPolygon(coords)) + end end coords2 = [[[[0.0, 10], [0.5, 10], [20.0, 20], [10.0, 10], [0.0, 10]]]] for f in two_arg_functions - @test f(LibGEOS.MultiPolygon(coords), LibGEOS.MultiPolygon(coords2)) == - f(GeoInterface.MultiPolygon(coords), LibGEOS.MultiPolygon(coords2)) + @testset let current_function = f + @test f(LibGEOS.MultiPolygon(coords), LibGEOS.MultiPolygon(coords2)) == + f(GeoInterface.MultiPolygon(coords), LibGEOS.MultiPolygon(coords2)) + end end end diff --git a/test/test_geos_functions.jl b/test/test_geos_functions.jl index 178e35b..68abec4 100644 --- a/test/test_geos_functions.jl +++ b/test/test_geos_functions.jl @@ -1,6 +1,7 @@ using Test using LibGEOS import GeoInterface +import GeoInterface as GI using Extents @testset "WKTWriter" begin @@ -168,7 +169,9 @@ end @test LibGEOS.getCoordinates(cs_) == [[5.0, 3.0]] # z coordinate stays NaN for 2D geometry @test isnan(LibGEOS.getZ(cs_, 1)) - LibGEOS.setZ!(cs_, 1, 2.0) + # in GEOS v3.14 and above, setting a nonexistent ordinate errors + # cf. https://github.com/libgeos/geos/pull/1245 + @test_throws "GEOSError" LibGEOS.setZ!(cs_, 1, 2.0) @test isnan(LibGEOS.getZ(cs_, 1)) cs_2 = LibGEOS.createCoordSeq([5.0, 3.0]) @@ -958,7 +961,21 @@ end @test LibGEOS.reverse(readgeom("LINESTRING(0 0, 1 1)")) == readgeom("LINESTRING(1 1, 0 0)") + # NOTE: maximum inscribed circle is not an exact algorithm, + # and may be susceptible to both floating point error + # and changes to internal hashing order of the quadtree. + # So we need to test for what we really care about here: + # that the center is in the right place, and that the radius is correct. + # For example, between GEOS v3.13 and v3.14, the test broke + # because it had been checking that the returned linestring was precisely equal to a reference, + # but the side switched from the left to the right. Still a completely correct + # and valid result - but not the same as the reference and it settled in a different cell. + # The algorithm LibGEOS and jts use here is the same one that Mapbox uses + # as well as Polylabel.jl in Julia (soon to be integrated into GeometryOps). geo = readgeom("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))") mic = LibGEOS.maximumInscribedCircle(geo, 1e-4) - @test mic == readgeom("LINESTRING (0.5 0.5, 0 0.5)") + @test GI.coordinates(GI.getpoint(mic, 1)) ≈ [0.5, 0.5] atol = 1e-5 + x1, y1 = GI.coordinates(GI.getpoint(mic, 1)) + x2, y2 = GI.coordinates(GI.getpoint(mic, 2)) + @test hypot(x1 - x2, y1 - y2) ≈ 0.5 atol = 1e-5 end diff --git a/test/test_geos_types.jl b/test/test_geos_types.jl index 73f61f7..31f9ef7 100644 --- a/test/test_geos_types.jl +++ b/test/test_geos_types.jl @@ -1,3 +1,4 @@ +import TaskLocalValues @testset "open_issue_if_conversion_makes_sense" begin polygon = readgeom("POLYGON EMPTY") @@ -390,10 +391,10 @@ end @testset "Multi threading" begin function f91(n) # adapted from https://github.com/JuliaGeo/LibGEOS.jl/issues/91#issuecomment-1267732709 - contexts = [LibGEOS.GEOSContext() for i = 1:Threads.nthreads()] + contexts = TaskLocalValues.TaskLocalValue{LibGEOS.GEOSContext}(() -> LibGEOS.GEOSContext()) p = [[[-1.0, -1], [+1, -1], [+1, +1], [-1, +1], [-1, -1]]] - Threads.@threads :static for i = 1:n - ctx = contexts[Threads.threadid()] + Threads.@threads for i = 1:n + ctx = contexts[] g1 = LibGEOS.Polygon(p, ctx) g2 = LibGEOS.Polygon(p, ctx) for j = 1:n @@ -407,10 +408,10 @@ end @testset "clone" begin function f(n) # adapted from https://github.com/JuliaGeo/LibGEOS.jl/issues/91#issuecomment-1267732709 - contexts = [LibGEOS.GEOSContext() for i = 1:Threads.nthreads()] + contexts = TaskLocalValues.TaskLocalValue{LibGEOS.GEOSContext}(() -> LibGEOS.GEOSContext()) p = LibGEOS.Polygon([[[-1.0, -1], [+1, -1], [+1, +1], [-1, +1], [-1, -1]]]) - Threads.@threads :static for i = 1:n - ctx = contexts[Threads.threadid()] + Threads.@threads for i = 1:n + ctx = contexts[] g1 = LibGEOS.clone(p, ctx) g2 = LibGEOS.clone(p, ctx) for j = 1:n