Skip to content

Commit

Permalink
Merge pull request #29 from sanak/fix/polygonize-variants-types
Browse files Browse the repository at this point in the history
Fix polygonize variants types
  • Loading branch information
chrispahm authored Nov 25, 2024
2 parents 2cd08be + f0a1b49 commit 1530a01
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 8 deletions.
16 changes: 8 additions & 8 deletions src/allCFunctions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2181,21 +2181,21 @@ export function initCFunctions (config = {}) {
* and will report them as errors.
*
* @param {number} handle - A pointer to the GEOS context handle.
* @param {string} geoms - An array of valid Geometrys containing the linework to polygonize.
* @param {number} geoms - An array of valid Geometrys containing the linework to polygonize.
* @param {number} ngeoms - The number of valid Geometrys in the array.
* @returns {string} A GeometryCollection containing the polygons formed by the polygonization.
* @returns {number} A GeometryCollection containing the polygons formed by the polygonization.
* @alias module:geos
*/
geos.GEOSPolygonize_valid_r = Module.cwrap('GEOSPolygonize_valid_r', 'string', ['number', 'string', 'number'])
geos.GEOSPolygonize_valid_r = Module.cwrap('GEOSPolygonize_valid_r', 'number', ['number', 'number', 'number'])

/**
* Gets the list of line segments forming the boundary between
* inside and outside portions of a set of Geometrys which contain linework that
* represents the edges of a planar graph.
*
* @param {string} geoms - An array of Geometrys containing the linework to process.
* @param {number} geoms - An array of Geometrys containing the linework to process.
* @param {number} ngeoms - The number of Geometrys in the array.
* @returns {string} A MultiLineString containing the boundary segments.
* @returns {number} A MultiLineString containing the boundary segments.
* @alias module:geos
*/
geos.GEOSPolygonizer_getCutEdges = null
Expand All @@ -2206,12 +2206,12 @@ export function initCFunctions (config = {}) {
* represents the edges of a planar graph, using a GEOS context handle.
*
* @param {number} handle - A pointer to the GEOS context handle.
* @param {string} geoms - An array of Geometrys containing the linework to process.
* @param {number} geoms - An array of Geometrys containing the linework to process.
* @param {number} ngeoms - The number of Geometrys in the array.
* @returns {string} A MultiLineString containing the boundary segments.
* @returns {number} A MultiLineString containing the boundary segments.
* @alias module:geos
*/
geos.GEOSPolygonizer_getCutEdges_r = Module.cwrap('GEOSPolygonizer_getCutEdges_r', 'string', ['number', 'string', 'number'])
geos.GEOSPolygonizer_getCutEdges_r = Module.cwrap('GEOSPolygonizer_getCutEdges_r', 'number', ['number', 'number', 'number'])

/**
* Polygonizes a set of Geometrys which contain linework that
Expand Down
168 changes: 168 additions & 0 deletions test/tests/GEOSPolygonize.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function wktToGeom (wkt) {

// Create a helper function to convert a GEOS geometry to a WKT string
function geomToWkt (geomPtr) {
geos.GEOSNormalize(geomPtr)
const writer = geos.GEOSWKTWriter_create()
geos.GEOSWKTWriter_setRoundingPrecision(writer, 0)
const wktPtr = geos.GEOSWKTWriter_write(writer, geomPtr)
Expand Down Expand Up @@ -77,3 +78,170 @@ test('GEOSPolygonize', function (t) {
// End the test case
t.end()
})

// https://github.com/libgeos/geos/blob/3.12.0/tests/unit/capi/GEOSPolygonizeTest.cpp
test('GEOSPolygonize_getCutEdges - 1', function (t) {
const wkts = [
'LINESTRING(1 3, 3 3, 3 1, 1 1, 1 3)',
'LINESTRING(1 3, 3 3, 3 1, 1 1, 1 3)'
]

const geoms = wkts.map(wktToGeom)
const geomPtrs = new Int32Array(geoms)
const geomVecPtr = geos.Module._malloc(geomPtrs.length * geomPtrs.BYTES_PER_ELEMENT)
geos.Module.HEAP32.set(geomPtrs, geomVecPtr >> 2)

const gPtr = geos.GEOSPolygonizer_getCutEdges(geomVecPtr, geoms.length)

t.ok(gPtr !== 0)
t.equal(geos.GEOSGetNumGeometries(gPtr), 2)

freeGeom(gPtr)
geoms.forEach(freeGeom)

t.end()
})

test('GEOSPolygonize_getCutEdges - 2', function (t) {
// Example from JTS Developer's Guide, Chapter 6 - Polygonization
const wkts = [
'LINESTRING(0 0, 10 10)', // isolated edge
'LINESTRING(185 221, 100 100)', // dangling edge
'LINESTRING(185 221, 88 275, 180 316)',
'LINESTRING(185 221, 292 281, 180 316)',
'LINESTRING(189 98, 83 187, 185 221)',
'LINESTRING(189 98, 325 168, 185 221)'
]

const geoms = wkts.map(wktToGeom)
const geomPtrs = new Int32Array(geoms)
const geomVecPtr = geos.Module._malloc(geomPtrs.length * geomPtrs.BYTES_PER_ELEMENT)
geos.Module.HEAP32.set(geomPtrs, geomVecPtr >> 2)

const gPtr = geos.GEOSPolygonizer_getCutEdges(geomVecPtr, geoms.length)

t.ok(gPtr !== 0)
t.equal(geos.GEOSGetNumGeometries(gPtr), 0)

freeGeom(gPtr)
geoms.forEach(freeGeom)

t.end()
})

test('GEOSPolygonize_valid - 3', function (t) {
// Example from JTS Developer's Guide, Chapter 6 - Polygonization
const wkts = [
'LINESTRING (100 100, 100 300, 300 300, 300 100, 100 100)',
'LINESTRING (150 150, 150 250, 250 250, 250 150, 150 150)'
]

const geoms = wkts.map(wktToGeom)
const geomPtrs = new Int32Array(geoms)
const geomVecPtr = geos.Module._malloc(geomPtrs.length * geomPtrs.BYTES_PER_ELEMENT)
geos.Module.HEAP32.set(geomPtrs, geomVecPtr >> 2)

// GEOSPolygonize gives us a collection of two polygons
let gPtr = geos.GEOSPolygonize(geomVecPtr, geoms.length)
t.ok(gPtr !== 0)
t.equal(geos.GEOSGetNumGeometries(gPtr), 2)
t.equal(geos.GEOSGeomTypeId(gPtr), 7) // GEOS_GEOMETRYCOLLECTION
freeGeom(gPtr)

// GEOSPolygonize_valid gives us a single polygon with a hole
gPtr = geos.GEOSPolygonize_valid(geomVecPtr, geoms.length)
t.ok(gPtr !== 0)
t.equal(geos.GEOSGetNumGeometries(gPtr), 1)
t.equal(geos.GEOSGeomTypeId(gPtr), 3) // GEOS_POLYGON
freeGeom(gPtr)
geoms.forEach(freeGeom)

t.end()
})

test('GEOSPolygonize_valid - 4', function (t) {
const wkts = [
'LINESTRING (0 0, 1 1)'
]

const geoms = wkts.map(wktToGeom)
const geomPtrs = new Int32Array(geoms)
const geomVecPtr = geos.Module._malloc(geomPtrs.length * geomPtrs.BYTES_PER_ELEMENT)
geos.Module.HEAP32.set(geomPtrs, geomVecPtr >> 2)

const gPtr = geos.GEOSPolygonize_valid(geomVecPtr, geoms.length)

t.ok(gPtr !== 0)
t.equal(geos.GEOSGetNumGeometries(gPtr), 0)
t.equal(geos.GEOSGeomTypeId(gPtr), 7) // GEOS_GEOMETRYCOLLECTION

freeGeom(gPtr)
geoms.forEach(freeGeom)

t.end()
})

test('GEOSPolygonize_valid - 5', function (t) {
const wkts = [
'LINESTRING (0 0, 1 0, 1 1, 0 1, 0 0)',
'LINESTRING (1 1, 2 1, 2 2, 1 2, 1 1)'
]

const geoms = wkts.map(wktToGeom)
const geomPtrs = new Int32Array(geoms)
const geomVecPtr = geos.Module._malloc(geomPtrs.length * geomPtrs.BYTES_PER_ELEMENT)
geos.Module.HEAP32.set(geomPtrs, geomVecPtr >> 2)

const gPtr = geos.GEOSPolygonize_valid(geomVecPtr, geoms.length)

t.ok(gPtr !== 0)
t.equal(geos.GEOSGetNumGeometries(gPtr), 2)
t.equal(geos.GEOSGeomTypeId(gPtr), 6) // GEOS_MULTIPOLYGON

freeGeom(gPtr)
geoms.forEach(freeGeom)

t.end()
})

test('GEOSPolygonize_full - 6', function (t) {
const geom1Ptr = wktToGeom('MULTILINESTRING ((0 0, 1 0, 1 1, 0 1, 0 0), (0 0, 0.5 0.5), (1 1, 2 2, 1 2, 2 1, 1 1))')
const cutsPtr = geos.Module._malloc(4)
geos.Module.setValue(cutsPtr, 0, 'i32')
const danglesPtr = geos.Module._malloc(4)
geos.Module.setValue(danglesPtr, 0, 'i32')
const invalidRingsPtr = geos.Module._malloc(4)
geos.Module.setValue(invalidRingsPtr, 0, 'i32')

const result = geos.GEOSPolygonize_full(geom1Ptr, cutsPtr, danglesPtr, invalidRingsPtr)
const cuts = geos.Module.getValue(cutsPtr, 'i32')
const dangles = geos.Module.getValue(danglesPtr, 'i32')
const invalidRings = geos.Module.getValue(invalidRingsPtr, 'i32')
geos.Module._free(cutsPtr)
geos.Module._free(danglesPtr)
geos.Module._free(invalidRingsPtr)

const expected = wktToGeom('GEOMETRYCOLLECTION(POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)))')
const expectedCuts = wktToGeom('GEOMETRYCOLLECTION EMPTY')
const expectedDangles = wktToGeom('GEOMETRYCOLLECTION(LINESTRING (0 0, 0.5 0.5))')
const expectedInvalidRings = wktToGeom('GEOMETRYCOLLECTION(LINESTRING (1 1, 2 2, 1 2, 2 1, 1 1))')

t.equal(geomToWkt(result), geomToWkt(expected))
t.equal(geomToWkt(cuts), geomToWkt(expectedCuts))
t.equal(geomToWkt(dangles), geomToWkt(expectedDangles))
t.equal(geomToWkt(invalidRings), geomToWkt(expectedInvalidRings))

freeGeom(geom1Ptr)
freeGeom(result)
freeGeom(expected)

freeGeom(cuts)
freeGeom(dangles)
freeGeom(invalidRings)

freeGeom(expectedCuts)
freeGeom(expectedDangles)
freeGeom(expectedInvalidRings)

t.end()
})

0 comments on commit 1530a01

Please sign in to comment.